Stencil: JEST: Cannot read property '$hostElement$' of undefined

Created on 3 Dec 2019  路  8Comments  路  Source: ionic-team/stencil

Stencil version:

 @stencil/[email protected]

I'm submitting a:

[x] bug report
[ ] feature request
[ ] support request => Please do not submit support requests here, use one of these channels: https://stencil-worldwide.herokuapp.com/ or https://forum.ionicframework.com/

Current behavior:

When running unit tests, i sporadically get the following error. It is not bound to a specific test case and occurs with a varying frequency:

TypeError: Cannot read property '$hostElement$' of undefined
    at getElement (/Users/guidoknoll/Development/widget/node_modules/@stencil/core/dist/runtime/index.js:2242:88)
    at CustomSelect.get el [as el] (/Users/guidoknoll/Development/widget/src/components/filter/custom-select/custom-select/custom-select.tsx:3799:12)
    at Timeout.setTimeout (/Users/guidoknoll/Development/widget/src/components/filter/custom-select/custom-select/custom-select.tsx:3070:12)
    at listOnTimeout (timers.js:327:15)
    at processTimers (timers.js:271:5)
/Users/guidoknoll/Development/widget/node_modules/@stencil/core/dist/runtime/index.js:2242
const getElement = (ref) => buildConditionals.BUILD.lazyLoad ? platform.getHostRef(ref).$hostElement$ : ref;

Expected behavior:

Error is not thrown.

Steps to reproduce:

Can't really tell why the error occurs. If you need some parts of my code i can send it to you but putting it here public is not possible.

Related code:

// insert any relevant code here

Other information:

triage

Most helpful comment

As I see you are calling setTimeout function in your code,
I am assuming, your have called setTimeout function in your next() method,
try with the following changes
it('should change shown searchterm', async () => { const subject = new ReplaySubject<string>(); const page = await newSpecPage({ components: [SearchtermInput], template: () => ( <searchterm-input searchTermChange={subject} /> ) }); jest.clearAllTimers(); await page.waitForChanges(); subject.next('test'); jest.advanceTimersByTime(200); await page.waitForChanges(); expect((page.root.querySelector('.search-input') as HTMLInputElement).value).toBe('test'); });

When writing test cases to cover setTimeout/ setInterval functions, do the below things,
In your test case add jest.clearAllTimers(); before subject.next('test'); and after that add jest.advanceTimersByTime(200);
and at the top of you spec.ts file add below line before describe block
jest.useFakeTimers();

All 8 comments

any update about this? I've the same error

Thank you for opening the issue. To help us debug this further, would you be able to provide a way to replicate this? Thanks

I hope i'll find some time to create a minimal example for reproduction the next days. As far as i can see this error happens in combination with the use of rxjs subjects/observables.

Here is another error log:

TypeError: Cannot read property '$hostElement$' of undefined
    at setValue (/Users/guidoknoll/Development/widget/node_modules/@stencil/core/dist/runtime/index.js:1351:60)
    at CustomOption.set [as selected] (/Users/guidoknoll/Development/widget/node_modules/@stencil/core/dist/runtime/index.js:1430:25)
    at SafeSubscriber._next (/Users/guidoknoll/Development/widget/src/components/filter/custom-select/custom-option/custom-option.tsx:25:20)
    at SafeSubscriber.Object.<anonymous>.SafeSubscriber.__tryOrUnsub (/Users/guidoknoll/Development/widget/node_modules/rxjs/src/internal/Subscriber.ts:265:10)
    at SafeSubscriber.Object.<anonymous>.SafeSubscriber.next (/Users/guidoknoll/Development/widget/node_modules/rxjs/src/internal/Subscriber.ts:207:14)
    at Subscriber.Object.<anonymous>.Subscriber._next (/Users/guidoknoll/Development/widget/node_modules/rxjs/src/internal/Subscriber.ts:139:22)
    at Subscriber.Object.<anonymous>.Subscriber.next (/Users/guidoknoll/Development/widget/node_modules/rxjs/src/internal/Subscriber.ts:99:12)
    at Subject.Object.<anonymous>.Subject.next (/Users/guidoknoll/Development/widget/node_modules/rxjs/src/internal/Subject.ts:70:17)
    at CustomOption.select (/Users/guidoknoll/Development/widget/src/components/filter/custom-select/custom-option/custom-option.tsx:60:33)
    at HostElement.onClick (/Users/guidoknoll/Development/widget/src/components/filter/custom-select/custom-option/custom-option.tsx:79:33)

I get the same Error testing a component using popper.js

TypeError: Cannot read property '$hostElement$' of undefined
    at setValue (/Code/ui-components/node_modules/@stencil/core/dist/runtime/index.js:1368:60)
    at MyPopover.set [as placedAt] (/Code/ui-components/node_modules/@stencil/core/dist/runtime/index.js:1447:25)
    at Object.onCreate (/Code/ui-components/src/components/my-popover/my-popover.tsx:835:21)
    at Popper.call (/Code/ui-components/node_modules/popper.js/src/methods/update.js:71:18)
    at Popper.update$$1 (/Code/ui-components/node_modules/popper.js/src/index.js:94:19)
    at Timeout._onTimeout (/Code/ui-components/node_modules/popper.js/dist/umd/popper.js:64:9)
    at listOnTimeout (internal/timers.js:531:17)
    at processTimers (internal/timers.js:475:7)

As I further inspected this error, it seems like it mostly has to to with rxjs. I often use subjects for managing state across multiple components so i hand over a subject to my stencil web component as a parameter. When i hand over a subject in an unit test spec page and call .next() on it, i get the Cannot read property '$hostElement$' of undefined error.
This is such an unit test for example:

it('should change shown searchterm', async () => {
    const subject = new ReplaySubject<string>();
    const page = await newSpecPage({
        components: [SearchtermInput],
        template: () => (
            <searchterm-input searchTermChange={subject} />
        )
    });
    await page.waitForChanges();
    subject.next('test');
    await page.waitForChanges();
    expect((page.root.querySelector('.search-input') as HTMLInputElement).value).toBe('test');
  });

Error output:

/Users/guidoknoll/Development/widget/node_modules/rxjs/internal/util/hostReportError.js:4
    setTimeout(function () { throw err; }, 0);
                             ^
TypeError: Cannot read property '$hostElement$' of undefined
    at setValue (/Users/guidoknoll/Development/widget/node_modules/@stencil/core/dist/runtime/index.js:1368:60)
    at SearchtermInput.set [as searchTerm] (/Users/guidoknoll/Development/widget/node_modules/@stencil/core/dist/runtime/index.js:1447:25)
    at SafeSubscriber._next (/Users/guidoknoll/Development/widget/src/components/searchterm-input/searchterm-input.tsx:23:22)
    at SafeSubscriber.Object.<anonymous>.SafeSubscriber.__tryOrUnsub (/Users/guidoknoll/Development/widget/node_modules/rxjs/src/internal/Subscriber.ts:265:10)
    at SafeSubscriber.Object.<anonymous>.SafeSubscriber.next (/Users/guidoknoll/Development/widget/node_modules/rxjs/src/internal/Subscriber.ts:207:14)
    at Subscriber.Object.<anonymous>.Subscriber._next (/Users/guidoknoll/Development/widget/node_modules/rxjs/src/internal/Subscriber.ts:139:22)
    at Subscriber.Object.<anonymous>.Subscriber.next (/Users/guidoknoll/Development/widget/node_modules/rxjs/src/internal/Subscriber.ts:99:12)
    at ReplaySubject.Object.<anonymous>.Subject.next (/Users/guidoknoll/Development/widget/node_modules/rxjs/src/internal/Subject.ts:70:17)
    at ReplaySubject.Object.<anonymous>.ReplaySubject.nextInfiniteTimeWindow (/Users/guidoknoll/Development/widget/node_modules/rxjs/src/internal/ReplaySubject.ts:46:15)
    at Object.<anonymous> (/Users/guidoknoll/Development/widget/src/components/searchterm-input/searchterm-input.spec.tsx:41:13)

Sadly i can't think of a solution for this, can anyone help?

As I see you are calling setTimeout function in your code,
I am assuming, your have called setTimeout function in your next() method,
try with the following changes
it('should change shown searchterm', async () => { const subject = new ReplaySubject<string>(); const page = await newSpecPage({ components: [SearchtermInput], template: () => ( <searchterm-input searchTermChange={subject} /> ) }); jest.clearAllTimers(); await page.waitForChanges(); subject.next('test'); jest.advanceTimersByTime(200); await page.waitForChanges(); expect((page.root.querySelector('.search-input') as HTMLInputElement).value).toBe('test'); });

When writing test cases to cover setTimeout/ setInterval functions, do the below things,
In your test case add jest.clearAllTimers(); before subject.next('test'); and after that add jest.advanceTimersByTime(200);
and at the top of you spec.ts file add below line before describe block
jest.useFakeTimers();

Worked like a charm !!

Thank you @rahul-rautwar, this worked for me too. I figured out that using

testing: {
    timers: 'fake'
}

in the stencil.config resolves these errors, too.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ckrack picture ckrack  路  3Comments

MrMcGibblets picture MrMcGibblets  路  3Comments

lcswillems picture lcswillems  路  3Comments

elmariofredo picture elmariofredo  路  3Comments

kensodemann picture kensodemann  路  3Comments