Unable to test an input which is in ng-template

Viewed 3644

As it says in the title, i'm trying to test an input which is in a ng-template. The problem is, that i can't access the input. If i take the input out from the ng-template, it works, but i need it inside the ng-template.

The code for the emp.html is from the primefaces documentation. Link:https://www.primefaces.org/primeng/#/table Filtering section

emp.html

<ng-template pTemplate="caption">         
 <div style="text-align: right">
  <i class="fa fa-search" style="margin:4px 4px 0 0"></i>
   <input type="text" pInputText size="50" placeholder="Global Filter" 
   (input)="dt.filterGlobal($event.target.value, 'contains')"
   style="width:auto">
 </div>
</ng-template>

emp.component.spec.ts

describe('EmpComponent', () => {
let component: EmpComponent;
let fixture: ComponentFixture<EmpComponent>;

beforeEach(async(() => {
  TestBed.configureTestingModule({
    imports: [SharedModule, HttpClientTestingModule],
    declarations: [ EmpComponent, TestTableComponent ],
    schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
    providers: [
      {provide: EmpService, useClass: MockEmpService},
    ]
  })
  .compileComponents();
}));

beforeEach(() => {
  fixture = TestBed.createComponent(EmpComponent);
  component = fixture.componentInstance;

  fixture.detectChanges();
});

it('should create', () => {
  expect(component).toBeDefined();
});

it('check input', fakeAsync(() => {

  tick();
  fixture.detectChanges();

  const inputElement = 
  fixture.debugElement.query(By.css('input')).nativeElement;// Here it fails

  inputElement.value = 'some value';
  inputElement.dispatchEvent(new Event('input'));

  fixture.whenStable().then(() => {
    fixture.detectChanges();
    expect(inputElement.value).toEqual('some value');
  });  
}))

It gives the following error: Cannot read property 'nativeElement' of null

1 Answers

You could write a TestComponent which mocks the table around your template (meaning it has the same selector) which just outputs the templates within.

In your test you could then test everything as you did before.

Update 1

Just to make it a bit clearer what I meant earlier. Here is an example on how the TestTableComponent could look like. I haven't written this with an editor, so this is just an example and might not work out of the box, but just so you get an idea of what I meant:

@Component({
 selector: 'p-table',
 template: `<ng-content></ng-content>`
})
class TestTableComponent {
  // I haven't worked with PrimeNG before, not sure whether this type gets exported
  @ContentChildren(PrimeTemplate) templates: QueryList<PrimeTemplate>;
}

The <ng-content></ng-content> should display all content children which are of type PrimeTemplate. I haven't tried it, though. It might be necessary to get the templateRef from the PrimeTemplate elements as PrimeNG does it. If that's the case your TestTableComponent would look something like this:

@Component({
 selector: 'p-table',
 template: `<ng-container *ngFor="let template of templates"><ng-container *ngTemplateOutlet="template"></ng-container></ng-container>`
})
class TestTableComponent implements NgAfterContentInit{
  // I haven't worked with PrimeNG before, not sure whether this type gets exported
  @ContentChildren(PrimeTemplate) primeTemplates: QueryList<PrimeTemplate>;

  templates: TemplateRef<any>[] = [];

  ngAfterContentInit() {
    this.templates.forEach((item) => {
      this.templates.push(item.template);
    });
}

Have a look here for a bit more details about content projection and it might help to have a look on how PrimeNG itself uses the content projection, to use the templates you provide to generate the tables here.

Update 2

Here is a stackblitz which demonstrates what I meant. I didn't want to add PrimeNG modules since I don't know which of them you are using and it isn't required to get the hang of what I've done. Therefore I copied PrimeNG's directive (PrimeTemplate) into the stackblitz. The directive is used to determine all content children. In your case all your ng-templates within the PrimeNG components have this directive.

The only remaining question is, whether PrimeTemplate can be imported from PrimeNG. You need to try that one out. If PimeNG doesn't export PrimeTemplate you might need to write your own directive and add it to all your templates you want to test. You can than use your own directive to collect the ng-templates. This wouldn't be as nice because you would add a directive only for testing purposes. So I hope PrimeNG exports this directive.

Related