I've been having a bear of a time with this.
I'm using a third-part listbox widget (jqx-listbox) and trying to test it. Everything works fine under ng serve
and even looking at the Karma output in Chrome, I can see the entire UI, with the listbox, and items, which is what I want to test.
The problem is that querying for the element within the test:
let openListbox = fixture.debugElement.query(By.css('.jqx-listitem-element'));
returns null, while if I put a breakpoint on that line, and use jQuery in the Chrome console $('.jqx-listitem-element)
then the DOM element is returned!
If I change the lookup in the test to the element as defined in the template (<jqxlistbox>
) like so: let openListbox = fixture.debugElement.query(By.css('jqxlistbox'))
, that will return the debugElement, and I can even inspect inside that thing's native element right at that breakpoint and see it has stuff in it, but no matter what I've tried (returning that element then doing a query on it), I can't seem to query any deeper into the object, so my test keeps failing.
I've also tried using fakeAsync
and fixture.whenStable
but that just seems to get entirely skipped (or it never gets stable).
Here's the rendered template code generated by the widget at runtime (It's that innermost span that I'm trying to inspect)
<jqxlistbox _ngcontent-c0="" class="" style="" ng-reflect-attr-display-member="message" ng-reflect-attr-source="[object Object]"
ng-reflect-attr-value-member="errorType" ng-reflect-attr-width="100%">
<div class="ng-tns-c0-0 jqx-listbox jqx-reset jqx-rc-all jqx-widget jqx-widget-content jqx-disableselect" id="jqxWidgete238403f0fb8"
style="width: 100%; height: 200px;" aria-multiselectable="false" role="listbox" tabindex="1">
<div style="-webkit-appearance: none; background: transparent; outline: none; width:100%; height: 100%; align:left; border: 0px; padding: 0px; margin: 0px; left: 0px; top: 0px; valign:top; position: relative;">
<div style="-webkit-appearance: none; border: none; background: transparent; outline: none; width:100%; height: 100%; padding: 0px; margin: 0px; align:left; left: 0px; top: 0px; valign:top; position: relative;">
<div id="listBoxContentjqxWidgete238403f0fb8" style="-webkit-appearance: none; background: transparent; outline: none; border: none; padding: 0px; overflow: hidden; margin: 0px; left: 0px; top: 0px; position: absolute; width: 328px; height: 198px;">
<div style="outline: none 0px; overflow: hidden; width: 329px; position: relative; height: 404px;">
<div role="option" id="listitem0jqxWidgete238403f0fb8" class="jqx-listitem-element" style="height: 24px; top: 0px; left: 0px;"
aria-selected="true">
<span style="white-space: pre; display: block; visibility: inherit; width: 318px;" class="jqx-listitem-state-normal jqx-item jqx-rc-all jqx-listitem-state-selected jqx-fill-state-pressed">Open hard error</span>
</div>
</div>
</div>
</div>
</div>
</div>
</jqxlistbox>
EDIT
I can get the outer element and it's innerText
is the text of that span: "Open hard error" but that's just because it's the only text node in the whole element.
With this test code, the first assertion passes, the second fails with an error of "Cannot read property nativeElement of null" which is just so weird to me because if it can find the innerText, then it has the whole DOM element. I'm wondering if there's something wrong with querying a DebugElement returned by a query?:
fixture.detectChanges();
let openListbox = fixture.debugElement.query(By.css('#open jqxlistbox'));
expect(openListbox.nativeElement.innerText).toContain('Open hard error', 'item in listbox');
fixture.detectChanges();
let openListboxItem: HTMLElement = openListbox.query(By.css('span')).nativeElement;
expect(openListboxItem.textContent).toBe('Open hard error', 'item in listbox');