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

shallow-render > 14.0.0 error: the code should be running in the fakeAsync zone to call this function #233

Closed
Timebutt opened this issue Dec 19, 2022 · 7 comments · Fixed by #240

Comments

@Timebutt
Copy link

Timebutt commented Dec 19, 2022

I have found an issue when using shallow-render in a fakeAsync() zone in Angular 15.x when upgrading my project. I know shallow-render officially doesn't support Angular 15 yet, but even when I upgraded shallow-render locally to version 15 and used that to run my tests, the issue I faced remained so I decided to file an issue.

When running a shallow-render unit test inside fakeAsync(), the following error is thrown:

The code should be running in the fakeAsync zone to call this function

A basic example would be the following code snippet:

it('should create the app', fakeAsync(async () => {
    // FakeAsyncTestZoneSpec still defined before running shallow.render()
    console.log(Zone.current.get('FakeAsyncTestZoneSpec'));

    await shallow.render();

    // FakeAsyncTestZoneSpec no longer defined after running shallow.render()
    console.log(Zone.current.get('FakeAsyncTestZoneSpec'));

    // Below tick() now throws an error because of this line of code in Angular:
    // https://github.com/angular/angular/blob/main/packages/zone.js/lib/zone-spec/fake-async-test.ts#L310
    tick(1000);
  }));

This worked fine pre Angular 15 and is a valid test case according to Angular tests.
A similar example that does work is when I don't include shallow.render() like so:

it('should create the app', fakeAsync(async () => {
    await Promise.resolve();

    // FakeAsyncTestZoneSpec properly defined
    console.log(Zone.current.get('FakeAsyncTestZoneSpec'));

    // No problem here
    tick(1000);
  }));

Somehow shallow-render is modifying/replacing Zone which leads to FakeAsyncTestZoneSpec going missing, which then causes the error message down the line. I'm taking a look to see if this was the same for older versions of Angular, or if something new is going on here.

Functional repository to offer a minimal reproduction of the issue:
https://github.com/Timebutt/shallow-render-issue

You can check out the code, run npm i and npm run test to see the issue at work.

Let me know if I can help!

EDIT: from the looks of it, the Angular version has nothing to do with this. In the example repo I provided the tests run fine when I installed shallow-render@13.0.1. Zone.current.get('FakeAsyncTestZoneSpec') is still defined and no error is thrown. Seems like this is a regression in the code of shallow-render when upgrading to version 14 then?

@Timebutt Timebutt changed the title Angular 15: the code should be running in the fakeAsync zone to call this function shallow-render > 14.0.0 error: the code should be running in the fakeAsync zone to call this function Dec 19, 2022
@getsaf
Copy link
Owner

getsaf commented Dec 23, 2022

I'm not sure this is related to shallow-render.

I pulled down your project and replaced everything in the file node_modules/shallow-render/dist/lib/shallow.js with:

class Shallow {
    async render() {
        return;
    }
}
exports.Shallow = Shallow;

The test still failed on the tick() line.

Other things I've tried:

  • The same test run in a Karma-runner NG14 project runs without issue.
  • The same test run inside the shallow-render (NG14) project with Jest runs without issue.

@Timebutt
Copy link
Author

Timebutt commented Dec 24, 2022

If you downgrade shallow-render to ^13.0.0 in the repository I provided above and run npm run test, all tests pass though. The only variable that changed in the setup is the version of shallow-render, which is a direct indication shallow-render somehow interferes with the test runs? I was hoping you'd be able to tell what's off straight away from seeing the error message or the situation. I'm not entirely familiar with the codebase, but does shallow-render somehow explicitly make changes to Zone or the way it is mocked?

Edit: if I repeat the scenario with all Angular dependencies downgraded to version 14 and shallow-render 14.2.1 the tests still don't run, so I'm not sure how you were able to run those tests using NG14?

@Timebutt
Copy link
Author

Timebutt commented Jan 31, 2023

I meanwhile investigated this issue closer, as this is currently causing a dependency mismatch in my Angular 15 repository. I'm still running shallow-render 13.0.x, which runs all of our tests just fine (we don't have any standalone components yet but want to use them, so have to upgrade to the latest shallow-render).

The main difference I'm seeing when comparing 13.0.x runs vs 15.0.x runs, is that the Zone.current changes. For both, Zone.current starts out as the ProxyZone, which as I understand is correctly set up to be able to run fakeAsync tests, but right after running compileComponents() (line 130 of renderer.ts) both versions of shallow-render diverge for some reason.

In the case of 13.0.x the Zone.current remains the ProxyZone and all is good. In the case of 15.0.x the Zone.current switches to a newly created Zone, which does not have all the required testing properties configured, and as such throws the error.

I'm by no means a zone.js expert, but after some initial reading it seems to me like new asynchronous work (a Task) was scheduled in the Angular internal compileComponents() somehow, which never seems to finish. I'll be looking deeper into what's exactly going on, but that's about as far as I got right now. I'm also unsure as to why this is happening, as the exact same methods are called between 13.0.x and 15.0.x, but copying code over from 13.0.x doesn't address the issue either. Maybe a configuration I'm missing somewhere ...

@getsaf
Copy link
Owner

getsaf commented Feb 1, 2023

Thanks for the detailed info. Strange indeed. I wondered if it has anything to do with the ES Target changes between versions (see this line from the NG14 PR).

I did some searching and it turns out that anything > ES2015 breaks zone.js 😢

From this page:
https://angular.io/guide/angular-package-format#esm-declaration

This format is used due to the requirements of zone.js, which does not support native async/await ES2017 syntax. Therefore, Node is instructed to use ES2015 code, where async/await structures have been downleveled into Promises.

I just published a test build (15.0.2-beta) that switches back to the ES2015 target.

I think it will solve your problem, it works locally. If this solves zone issues for you, I'll publish a proper release and backport the change to the v14 release.

@Timebutt
Copy link
Author

Timebutt commented Feb 1, 2023

Yes sir, that does the trick! Tests in the example I posted are now passing with the new 15.0.2-beta build, awesome! I'll test it out in my big Angular 15 project as extra validation, but should produce identical results AFAIK. I'll let you know!

@Timebutt
Copy link
Author

Timebutt commented Feb 7, 2023

I've meanwhile been running 15.0.2-beta in the production pipeline of a very sizeable application for a few days now, everything is working just perfect! Any chance we can release this in the main release flow?

@getsaf
Copy link
Owner

getsaf commented Feb 9, 2023

Just published 15.0.2 and backported the change to 14.2.4

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

Successfully merging a pull request may close this issue.

2 participants