stencil-store icon indicating copy to clipboard operation
stencil-store copied to clipboard

bug: Type error when running tests for component with onChange listener

Open samshareski opened this issue 2 years ago • 12 comments

Prerequisites

Stencil Store Version

2.0.0

Stencil Version

2.13.0

Current Behavior

When a component has an onChange listener which has a function that manipulates a https://github.com/State property:

import { onChange } from '../a-store';

export class AppProfile { 
  @State() something = 0;

  componentWillLoad() {
    onChange('clicks', (value) => {
      this.something++;
    });
  }
// ...
}

when I try to unit test the component I get a type error like:

● app-profile › clicks the button again

TypeError: Cannot read properties of undefined (reading '$instanceValues$')

  12 |   componentWillLoad() {
  13 |     onChange('clicks', (value) => {
> 14 |       this.something++;
     |       ^
  15 |     });
  16 |   }
  17 |   

  at getValue (node_modules/@stencil/core/internal/testing/index.js:538:282)
  at AppProfile.get [as something] (node_modules/@stencil/core/internal/testing/index.js:565:13)
  at src/components/app-profile/app-profile.tsx:14:7
  at node_modules/@stencil/store/dist/index.js:144:43
  at node_modules/@stencil/store/dist/index.js:88:40
      at Array.forEach (<anonymous>)
  at reset (node_modules/@stencil/store/dist/index.js:88:24)
  at Object.dispose (node_modules/@stencil/store/dist/index.js:94:9)
  at Object.<anonymous> (src/components/app-profile/app-profile.spec.ts:7:5)

The error only occurs if I'm referencing a @State property, if I reference a component method that just logs something, for example, there is no error.

However, this error doesn't happen with the first test in the suite, only subsequent tests. I'm calling store.dispose() before each test.

Expected Behavior

Tests should run without throwing an error and failing.

Steps to Reproduce

Download repo, run npm install then npm run test

OR

  1. Create a component which registers an onChange listener that references a @State variable

  2. Write a unit test suite for the component with at least 2 tests

  3. Run the unit tests

Code Reproduction URL

https://github.com/samshareski/store-test-bug

Additional Information

No response

samshareski avatar Jun 27 '22 20:06 samshareski

Hello @samshareski! Thanks for filing this issue and for providing a helpful reproduction. I can confirm that the bug you described is happening, and after a bit of investigation I'm not sure what the source of the problem is but I believe there may be a bug in stencil-store's logic w/ the onChange handler, with variable scope and this binding, or something along those lines.

I'm going to label this for further investigation and ingestion into our backlog.

Thanks again for reporting!

alicewriteswrongs avatar Jul 08 '22 17:07 alicewriteswrongs

@samshareski are you using store.dispose() between tests?

Serabe avatar Jul 12 '22 16:07 Serabe

@samshareski are you using store.dispose() between tests?

yes

samshareski avatar Jul 12 '22 17:07 samshareski

Do you have a repo with a reproduction of this issue I can take a look into?

Serabe avatar Jul 12 '22 19:07 Serabe

Do you have a repo with a reproduction of this issue I can take a look into?

it's linked in the first comment

samshareski avatar Jul 12 '22 19:07 samshareski

the failing tests are in this spec https://github.com/samshareski/store-test-bug/blob/master/src/components/app-profile/app-profile.spec.ts

samshareski avatar Jul 12 '22 19:07 samshareski

I see what's going on. Handlers are never disposed, you need to unregister them in your component. onChange returns a function that when called will unregister everything.

Serabe avatar Jul 12 '22 19:07 Serabe

@Serabe https://github.com/samshareski/store-test-bug/commit/24be6fef71b777f6e3f1b3dfca276b46b2832368 is this how that would be set up?

this version still has the test failures occurring

samshareski avatar Jul 12 '22 19:07 samshareski

Yes. I'll need to investigate then.

Serabe avatar Jul 12 '22 20:07 Serabe

@Serabe I believe this is a bug in stencil itself, so I believe this issue can be closed and cross referenced in stencil:

https://github.com/ionic-team/stencil/issues/4053

As a workaround, in your spec tests, you can manually call disconnectedCallback:

page.rootInstance.disconnectedCallback();

ajgagnon avatar Apr 05 '23 21:04 ajgagnon

Hey @ajgagnon, I tried calling page.rootInstance.disconnectedCallback(); in my jest test like this:

afterEach(() => {
    page.rootInstance.disconnectedCallback();
});

unfortunatly I get an TypeError: page.rootInstance.disconnectedCallback is not a function after each run. Do you maybe have a working example?

christophsaile avatar Jun 27 '23 09:06 christophsaile

@christophsaile This is probably because you do not have a disconnectedCallback in your component. You need to add this method to your component:

private removeListener: any;

componentWillLoad() {
    this.removeListener = onChange('clicks', (value) => {
      this.something++;
    });
 }

disconnectedCallback() {
   this.removeListener();
}

ajgagnon avatar Sep 07 '23 13:09 ajgagnon