ng-mocks icon indicating copy to clipboard operation
ng-mocks copied to clipboard

Bug: ComponentStore observale not working with ng-mock >= 13.3.0

Open kekel87 opened this issue 2 years ago • 12 comments

Description of the bug

Hi, I just migrated an angular project from version 12 to 13. Everything goes well except for ComponentStore (NGRX) tests. The observables complete directly, without emiting value.

For the context:

  • Angular 13.3.11
  • Jest 27.5.1
  • @ngrx/component-store 13.2.0
  • jest-marbles 3.0.2

I tested all versions of ng-mocks to find the one that causes problems, and it's this one: 13.3.0. Everything works before (ex 13.2.0).

Do you have any idea of the modification that causes this?

Thanks

Minimal reproduction example:

ComponentStore to be tested:

@Injectable()
export class MyComponentStore extends ComponentStore<{ value: string }> {
  constructor() {
    super({ value: 'value' });
  }

  readonly values$ = this.select(({ value }) => value);
}

Failling test case:

describe('MyComponentStore', () => {
  let componentStore: MyComponentStore;

  beforeEach(() => MockBuilder(MyComponentStore));

  beforeEach(() => {
    componentStore = MockRender(MyComponentStore).point.componentInstance;
  });

  // This test no longer works with ng-mocks >= 13.3.0
  it('should initial state and value', () => {
    expect(componentStore.state$).toBeObservable(cold('a', { a: { value: 'value' } }));
    expect(componentStore.values$).toBeObservable(cold('a', { a: 'value' }));
  });
});

Working test case without jest-marble:

describe('MyComponentStore', () => {
  let componentStore: MyComponentStore;

  beforeEach(() => MockBuilder(MyComponentStore));

  beforeEach(() => {
    componentStore = MockRender(MyComponentStore).point.componentInstance;
  });

  const testScheduler = new TestScheduler((actual, expected) => {
    expect(actual).toEqual(expected);
  });

  // However, this one works all the time (without jest-marble)
  it('should have initial state', () => {
    testScheduler.run((helpers) => {
      const { expectObservable } = helpers;

      expectObservable(componentStore.state$).toBe('a', { a: { value: 'value' } });
      expectObservable(componentStore.values$).toBe('a', { a: 'value' });
    });
  });
});

It works fine without an ng-mocks context (to show that it doesn't come from jest-marble):

describe('MyComponentStore', () => {
  let componentStore: MyComponentStore;

  beforeEach(() => {
    componentStore = new MyComponentStore();
  });

  // Work
  it('should initial state and value', () => {
    expect(componentStore.state$).toBeObservable(cold('a', { a: { value: 'value' } }));
    expect(componentStore.values$).toBeObservable(cold('a', { a: 'value' }));
  });
});

Expected vs actual behavior

Actual:

● MyComponentStore › should initial state and value

    expect(received).toBeNotifications(expected)

    Expected notifications to be:
      [{"frame": 0, "notification": {"error": undefined, "kind": "N", "value": {"value": "value"}}}]
    But got:
      [{"frame": 0, "notification": {"error": undefined, "kind": "C", "value": undefined}}]

Expected: Test should passed

kekel87 avatar Jun 22 '22 09:06 kekel87

Hi there. Thanks for the report. I'll take a look, hopefully, this week.

The release: https://github.com/ike18t/ng-mocks/releases/tag/v13.3.0

satanTime avatar Jun 22 '22 10:06 satanTime

Hi @kekel87, could you provide a min repo with the issue?

in my case I always get

	TypeError: Cannot call a class as a function
	    at _classCallCheck (node_modules/@babel/runtime/helpers/esm/classCallCheck.js:3:1)
	    at new Observable (node_modules/rxjs/dist/esm/internal/Observable.js:9:28)

For now, I would assume it has root providers which are mocked by MockBuilder. Could you keep the related token? https://ng-mocks.sudo.eu/api/MockBuilder#ng_mocks_root_providers-token

satanTime avatar Jun 23 '22 05:06 satanTime

Here is a minimal example of reproduction https://stackblitz.com/edit/github-ec7feu?file=package.json,src%2Ftest.spec.ts

I tested in Angular 14 before downgrading to Angular 13 in Stackblitz, and it seemed to work.

I did not know this token, I will try to test this afternoon.

Thanks

kekel87 avatar Jun 24 '22 09:06 kekel87

For me it's still,

Chrome Headless 103.0.5058.0 (Mac OS 10.15.7) issue-2862 should initial state and value FAILED
	TypeError: Cannot call a class as a function
	    at _classCallCheck (node_modules/@babel/runtime/helpers/esm/classCallCheck.js:3:1)
	    at new Observable (node_modules/rxjs/dist/esm/internal/Observable.js:9:28)
	    at ColdObservable._createSuperInternal (node_modules/@babel/runtime/helpers/esm/createSuper.js:12:1)
	    at new ColdObservable (node_modules/rxjs/dist/esm/internal/testing/ColdObservable.js:8:9)
	    at TestScheduler.createColdObservable (node_modules/rxjs/dist/esm/internal/testing/TestScheduler.js:38:22)
	    at new TestColdObservable (node_modules/jasmine-marbles/es6/src/test-observables.js:10:32)
	    at cold (node_modules/jasmine-marbles/es6/index.js:9:12)
	    at UserContext.apply (src/issue-2862/test.spec.ts:29:11)
	    at _ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:409:30)
	    at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/zone-testing.js:303:43)

satanTime avatar Jun 25 '22 07:06 satanTime

Ah, I think this is exactly the issue

Expected $[0].notification to be a kind of Object, but was Notification({ kind: 'N', value: 'value', error: undefined, hasValue: true }).

checking, thanks!

satanTime avatar Jun 25 '22 07:06 satanTime

So far I can see the error on "target": "es5", whereas "target": "es2015" works well. Could you share your tsconfig.json?

satanTime avatar Jun 25 '22 07:06 satanTime

Something is wrong: here I use [email protected] (also I've tried 13.2.0), but it still fails: https://stackblitz.com/edit/github-ec7feu-pwtrks?file=src%2Ftest.spec.ts

Could you share here a zip or github repo where I could simply switch ng-mocks from 13.3.0 to 13.2.0 and get different behavior?

satanTime avatar Jun 25 '22 07:06 satanTime

Hi @kekel87, could you check what I'm missing?

satanTime avatar Jul 11 '22 18:07 satanTime

Looks like it's something with Angular and es5 vs es2015.

I've created a repo without ng-mocks and it still fails: https://github.com/satanTime/github-ec7feu-pwtrks/tree/classic There is an example with ng-mocks: https://github.com/satanTime/github-ec7feu-pwtrks

satanTime avatar Jul 11 '22 19:07 satanTime

Sorry, I'm on vacation without a computer. I just followed the Angular CLI configuration for this. If you can't find a reason, we can just wait to upgrade to Angular 14 (where the problem doesn't arise).

kekel87 avatar Jul 12 '22 11:07 kekel87

Oki, have good vacation. Please, ping me when you are back.

satanTime avatar Jul 13 '22 14:07 satanTime

Hi @kekel87, I hope this finds you well. Are you back to check my comments?

satanTime avatar Aug 06 '22 07:08 satanTime

Hi @kekel87,

how is like? Did you have a change to take a look at my comment above?

satanTime avatar Aug 21 '22 05:08 satanTime

Hi @kekel87,

any update on this?

satanTime avatar Sep 09 '22 12:09 satanTime

So sorry.

I left the problem aside by setting the ng-mocks version to 13.2.0. We haven't migrated to Angular 14 yet. If we are the only ones to report the problem, maybe it's not worth spending time on it.

For the tsconfig.json/tsconfig.spec.json, we align with @angular/cli config, so is : "target": "es2017",

Sorry again for my late reply.

kekel87 avatar Sep 09 '22 13:09 kekel87

aha, with es2017 it works now. Can you try [email protected]? It works with all Angular versions.

satanTime avatar Sep 09 '22 16:09 satanTime

I've updates min examples: no ng-mocks - works well: https://github.com/satanTime/github-ec7feu-pwtrks/tree/classic with [email protected] - also works well: https://github.com/satanTime/github-ec7feu-pwtrks

it would be great if you had a chance to take a look why the second one doesn't fail.

satanTime avatar Sep 09 '22 16:09 satanTime

Hi @kekel87, I'm going to close the issue. Feel free to reopen it when you have more info.

satanTime avatar Sep 24 '22 07:09 satanTime