Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Promise flushing with fakeAsync and tick changed in v13.0.0 #1994

Closed
Olster opened this issue Mar 3, 2023 · 5 comments · Fixed by #2002
Closed

[Bug]: Promise flushing with fakeAsync and tick changed in v13.0.0 #1994

Olster opened this issue Mar 3, 2023 · 5 comments · Fixed by #2002

Comments

@Olster
Copy link

Olster commented Mar 3, 2023

Version

13.0.0

Steps to reproduce

  1. Checkout jest-preset-angular main branch and run yarn install
  2. Add following code to examples/example-app-v14/src/app/app.component.spec.ts
async function testMe(): Promise<void> {
  try {
    console.log(await Promise.resolve('foo'));
  } catch (e) {
    console.error(e);
  } finally {
    console.log('Finally!');
  }
}

fdescribe('my test', () => {
  it('should work', fakeAsync(() => {
    const spy = jest.fn();

    console.log('Call testMe');
    testMe().then(spy);

    console.log('Tick');
    tick();

    console.log('Expect');
    expect(spy).toHaveBeenCalled();
  }));
});
  1. Run node scripts/test-examples.js

Expected behavior

Tests run successfully, log output should be:

PASS  src/app/app.component.spec.ts
  ● Console

    console.log
      Call testMe

      at src/app/app.component.spec.ts:123:13

    console.log
      Tick

      at src/app/app.component.spec.ts:126:13

    console.log
      foo

      at src/app/app.component.spec.ts:111:13

    console.log
      Finally!

      at src/app/app.component.spec.ts:115:13

    console.log
      Expect

      at src/app/app.component.spec.ts:129:13

Actual behavior

Test fails with an error:

 FAIL  src/app/app.component.spec.ts
  ● my test › should work

    expect(jest.fn()).toHaveBeenCalled()

    Expected number of calls: >= 1
    Received number of calls:    0

      128 |
      129 |     console.log('Expect');
    > 130 |     expect(spy).toHaveBeenCalled();
          |                 ^
      131 |   }));
      132 | });

      at src/app/app.component.spec.ts:130:17

Log output looks out of order too, seems like the promise is resolved first when tests exits.

FAIL  src/app/app.component.spec.ts
  ● Console

    console.log
      Call testMe

      at src/app/app.component.spec.ts:123:13

    console.log
      Tick

      at src/app/app.component.spec.ts:126:13

    console.log
      Expect

      at src/app/app.component.spec.ts:129:13

    console.log
      foo

      at testMe (src/app/app.component.spec.ts:111:13)

    console.log
      Finally!

      at testMe (src/app/app.component.spec.ts:115:13)

Additional context

This looks like a regression from v12.2.6 where the same test runs successfully and the promise is resolved after calling tick.
This was first discovered after updating to nx v15.8.1 and reported there: nrwl/nx#15390

Environment

System:
    OS: macOS 13.2.1
    CPU: (10) arm64 Apple M1 Pro
  Binaries:
    Node: 18.12.0 - /usr/local/bin/node
    Yarn: 3.4.1 - ~/.yarn/bin/yarn
    npm: 9.6.0 - /usr/local/bin/npm
  npmPackages:
    jest: ^29.4.3 => 29.4.3
@dzonatan
Copy link

dzonatan commented Mar 6, 2023

Yet another example which I think is related to the same root issue:

// ✅ passes  
it('should work', fakeAsync(async () => {
  tick();
}));

// ❌ fails with:
// The code should be running in the fakeAsync zone to call this function
// at _getFakeAsyncZoneSpec (node_modules/zone.js/bundles/zone-testing-bundle.umd.js:5666:27)
it('should work', fakeAsync(async () => {
  await Promise.resolve();
  tick(); // <-- captured line
}));

Previously, with jest@v28 and jest-preset-angular@v12 this was working as expected.

@Olster
Copy link
Author

Olster commented Mar 6, 2023

So far I can see one difference in execution between v12.2.6 and main:
When I step into the async testMe function in v12.2.6, I'm immediately taken into tslib __awaiter.
v12_2_6

I also see tslib_1 available in the closure.

When I step into it in main I'm taken directly into the function skipping tslib __awaiter.
main

tslib_1 is not available in the closure.
Can't say why it's happening though 🤔

@dzonatan
Copy link

dzonatan commented Mar 7, 2023

I think I found the root cause which is #1788.

Angular doesn't support native async/await in testing with target higher than ES2016, see angular/components#21632 (comment)

Adding "target": "ES2016" in tsconfig.spec.json fixes the problem. By default, new projects inherit the target from tsconfig.json which is ES2022 at the moment.

@ahnpnl I think this is something worth mentioning in the release notes as it was quite difficult to reverse engineer this. I see this as a breaking change.

@ahnpnl
Copy link
Collaborator

ahnpnl commented Mar 7, 2023

Thanks for the investigation, I will update CHANGELOG.

@Olster
Copy link
Author

Olster commented Mar 7, 2023

To be fair, it has been mentioned in the docs and in the README for release 29 😅
https://thymikee.github.io/jest-preset-angular/docs/getting-started/installation#dependencies

**IMPORTANT**

Angular doesn't support native `async/await` in testing with `target` higher than `ES2016`, see https://github.com/angular/components/issues/21632#issuecomment-764975917

But I didn't think about downgrading the target of the test tsconfigs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants