Jest-preset-angular: Missing dependency, and other failures with 9.0.0-next.2

Created on 11 Nov 2020  ·  22Comments  ·  Source: thymikee/jest-preset-angular

🐛 Bug Report

While testing Ivy support using 9.0.0-next.2, I ran into some significant issues. They are as follows:

Background: We have a medium-sized project with a few hundred tests using jest-preset-angular for some time. It does not have many external dependencies by most Angular project standards.

  1. After first upgrading to [email protected], I received this error for every test:
 FAIL  libs/ui-components/dialog/src/dialog-close.directive.spec.ts
  ● Test suite failed to run

    Cannot find module '@ngtools/webpack/src/diagnostics'
    Require stack:
    - C:\src\ease\WebUi\node_modules\jest-preset-angular\build\compiler\ng-jest-compiler.js
    - C:\src\ease\WebUi\node_modules\jest-preset-angular\build\index.js
    - C:\src\ease\WebUi\node_modules\@jest\transform\build\ScriptTransformer.js
    - C:\src\ease\WebUi\node_modules\@jest\transform\build\index.js
    - C:\src\ease\WebUi\node_modules\jest-runtime\build\index.js
    - C:\src\ease\WebUi\node_modules\jest-runner\build\testWorker.js
    - C:\src\ease\WebUi\node_modules\jest-worker\build\workers\processChild.js

      at Object.<anonymous> (node_modules/jest-preset-angular/build/compiler/ng-jest-compiler.js:6:21)

To fix this, I tried:

> yarn add -D @ngtools/webpack

...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ @ngtools/[email protected]
info All dependencies
└─ @ngtools/[email protected]   

This may be the cause of the following problems.

  1. Now, while running tests, I experienced a "hang" for 8+ minutes before any tests were run. It may have been longer - I tried attaching a debugger but wasn't able to. After this, the starting "hang" seems shorter - a minute or two.

Eventually some tests were run, but I'm seeing a large number of odd compile + runtime errors. A single digit number of tests passed successfully.

The main compile error I'm seeing is:

    libs/ui-components/form/src/text-field/text-field.component.ts:50:43 - error TS2340: Only public and protected methods of the base class are accessible via the 'super' keyword.

    50     return this._input?.disabled ?? super.disabled;

Runtime errors include a lot of these (intermixed):

1:

Failed: "Zone is needed for the waitForAsync() test helper but could not be found. Please make sure that your environment includes zone.js/dist/zone.js"

      17 |   let intersectionObserverService: IntersectionObserverServiceMock;
      18 | 
    > 19 |   beforeEach(waitForAsync(() => {
         |   ^
      20 |     configureTestBed(TestDefaultComponent);
      21 | 
      22 |     fixture = createTestComponent(TestDefaultComponent);

      at Env.beforeEach (node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:46:24)

2:


    TypeError: Cannot read property 'debugElement' of undefined

      181 | 
      182 |   it('should unsubscribe from intersection observer', () => {
    > 183 |     const button = fixture.debugElement.query(By.directive(VisibilityTrackingDirective));
          |                            ^
      184 |     expect(button).toBeDefined();
      185 | 
      186 |     showElement(button, 1000, 0.1, intersectionObserverService);

To Reproduce

Let me know if you'd like me to create a public repro with a few tests exhibiting these problems. I'm happy to, but wanted to start with a report.

envinfo

System:
    OS: Windows 10

Npm packages:
   [email protected]
   [email protected]
   [email protected]

Related to #409

Bug Confirmed

All 22 comments

Hi, I haven't seen these issues before. It would be helpful to have a repo to debug this. Thanks :)

Will do. I really appreciate your efforts on this.

I have one additional error to report, that may provide some clues:

    TypeError: Cannot read property 'getComponentFromError' of null

      at TestBedViewEngine.Object.<anonymous>.TestBedViewEngine._initIfNeeded (../packages/core/testing/src/test_bed.ts:391:46)
      at TestBedViewEngine.Object.<anonymous>.TestBedViewEngine.inject (../packages/core/testing/src/test_bed.ts:480:10)
      at Function.Object.<anonymous>.TestBedViewEngine.inject (../packages/core/testing/src/test_bed.ts:227:36)
      at Object.<anonymous> (libs/ui-tracking/src/tracking/tracking.service.spec.ts:165:25)

This indicates to me that we may be getting the wrong version of the TestBed . As far as I can tell all of our tsconfig files inherit from our base tsconfig, which has "enableIvy": true. Any suggestions for switching the TestBed to use Ivy?

    TypeError: Cannot read property 'getComponentFromError' of null

      at TestBedViewEngine.Object.<anonymous>.TestBedViewEngine._initIfNeeded (../packages/core/testing/src/test_bed.ts:391:46)
      at TestBedViewEngine.Object.<anonymous>.TestBedViewEngine.inject (../packages/core/testing/src/test_bed.ts:480:10)
      at Function.Object.<anonymous>.TestBedViewEngine.inject (../packages/core/testing/src/test_bed.ts:227:36)
      at Object.<anonymous> (libs/ui-tracking/src/tracking/tracking.service.spec.ts:165:25)

This indicates to me that we may be getting the wrong version of the TestBed . As far as I can tell all of our tsconfig files inherit from our base tsconfig, which has "enableIvy": true. Any suggestions for switching the TestBed to use Ivy?

Currently we haven't integrated automatical ngcc in the compilation process yet. Therefore you need to make sure that ngcc is run before running tests. The error looks to me that ngcc hasn't been run yet.

Got it - thank you. I just figured out the same.

I had previously run ngcc, but then cleaned things out and forgot to run ngcc again. After re-running ngcc I'm still seeing the errors listed above, and getting these call stacks

    In this configuration Angular requires Zone.js

      at new NgZone (../packages/core/src/zone/ng_zone.ts:124:13)
      at R3TestBedCompiler.Object.<anonymous>.R3TestBedCompiler.compileTestModule (../packages/core/testing/src/r3_test_bed_compiler.ts:659:20)
      at R3TestBedCompiler.Object.<anonymous>.R3TestBedCompiler.finalize (../packages/core/testing/src/r3_test_bed_compiler.ts:245:10)
      at TestBedRender3.get (../packages/core/testing/src/r3_test_bed.ts:379:43)
      at TestBedRender3.Object.<anonymous>.TestBedRender3.inject (../packages/core/testing/src/r3_test_bed.ts:278:25)
      at Function.Object.<anonymous>.TestBedRender3.inject (../packages/core/testing/src/r3_test_bed.ts:167:33)

which indicate that I'm getting the right TestBed. Thanks!

I had previously run ngcc, but then cleaned things out and forgot to run ngcc again. After re-running ngcc I'm still seeing the errors listed above, and getting these call stacks


    In this configuration Angular requires Zone.js



      at new NgZone (../packages/core/src/zone/ng_zone.ts:124:13)

      at R3TestBedCompiler.Object.<anonymous>.R3TestBedCompiler.compileTestModule (../packages/core/testing/src/r3_test_bed_compiler.ts:659:20)

      at R3TestBedCompiler.Object.<anonymous>.R3TestBedCompiler.finalize (../packages/core/testing/src/r3_test_bed_compiler.ts:245:10)

      at TestBedRender3.get (../packages/core/testing/src/r3_test_bed.ts:379:43)

      at TestBedRender3.Object.<anonymous>.TestBedRender3.inject (../packages/core/testing/src/r3_test_bed.ts:278:25)

      at Function.Object.<anonymous>.TestBedRender3.inject (../packages/core/testing/src/r3_test_bed.ts:167:33)

I think this is because zone.js is not loaded in setupFilesAfterEnv. Do you have import 'jest-preset-angular/setup-jest' in your setup jest file ?

@ahnpnl : I've created a partial repro in this repo :

https://github.com/johncrim/repro-ivy-jest-preset-angular

It doesn't reproduce all the errors I've seen, but it does exhibit the compile error. Curiously, it doesn't exhibit the Cannot find module '@ngtools/webpack/src/diagnostics' error - I'll try to get that to reproduce as well.

This repo is currently using the release version of jest-preset-angular, so the following should work:

git clone https://github.com/johncrim/repro-ivy-jest-preset-angular
yarn
yarn test

(jest takes 3.5s on my laptop)

Then upgrade and test:

yarn upgrade jest-preset-angular@next
yarn test

Gives me:

yarn run v1.22.5
$ jest
 FAIL  libs/ui-lib/expando/src/expando-panel.component.spec.ts
  ● Test suite failed to run

    libs/ui-lib/expando/src/expando-panel.component.ts:33:16 - error TS2340: Only public and protected methods of the base class are accessible via the 'super' keyword.

    33     if (!super.disabled) {
                      ~~~~~~~~
    libs/ui-lib/expando/src/expando-panel.component.ts:40:16 - error TS2340: Only public and protected methods of the base class are accessible via the 'super' keyword.

    40     if (!super.disabled && !this.expanded) {
                      ~~~~~~~~
    libs/ui-lib/expando/src/expando-panel.component.ts:47:16 - error TS2340: Only public and protected methods of the base class are accessible via the 'super' keyword.

    47     if (!super.disabled && this.expanded) {

I'll work on reproducing the other problems I saw.

I've created a branch, which sidesteps the compile error, and exhibits the runtime errors I was seeing:

The branch is named sidestep-compile-error.

Same as the master branch, the tests succeed using jest-preset-angular 8.3.2. After upgrading to 9.0.0-next.2, I get these runtime errors:

❯ yarn test
yarn run v1.22.5
$ jest
 FAIL  libs/ui-lib/expando/src/expando-panel.component.spec.ts (6.734 s)
  ui-expando-panel
    × panel should be hidden by default (48 ms)
    × should expand on toggle (1 ms)

  ● ui-expando-panel › panel should be hidden by default

    Failed: "Zone is needed for the waitForAsync() test helper but could not be found. Please make sure that your environment includes zone.js/dist/zone.js"

      20 |   let fixture: ComponentFixture<TestHostComponent>;
      21 | 
    > 22 |   beforeEach(waitForAsync(() => {
         |   ^
      23 |     TestBed.configureTestingModule({
      24 |       declarations: [
      25 |         ExpandoPanelComponent,

      at Env.beforeEach (node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:46:24)
      at Suite.<anonymous> (libs/ui-lib/expando/src/expando-panel.component.spec.ts:22:3)
      at Object.<anonymous> (libs/ui-lib/expando/src/expando-panel.component.spec.ts:17:1)

  ● ui-expando-panel › panel should be hidden by default

    TypeError: Cannot read property 'getComponentFromError' of null

      at TestBedViewEngine.Object.<anonymous>.TestBedViewEngine._initIfNeeded (../packages/core/testing/src/test_bed.ts:391:46)
      at TestBedViewEngine.Object.<anonymous>.TestBedViewEngine.createComponent (../packages/core/testing/src/test_bed.ts:598:10)
      at Function.Object.<anonymous>.TestBedViewEngine.createComponent (../packages/core/testing/src/test_bed.ts:245:36)
      at Object.<anonymous> (libs/ui-lib/expando/src/expando-panel.component.spec.ts:37:23)

  ● ui-expando-panel › panel should be hidden by default

    TypeError: Cannot read property 'expanded' of undefined

      42 | 
      43 |   it('panel should be hidden by default', () => {
    > 44 |     expect(component.expanded).toBe(false);
         |                      ^
      45 | 
      46 |     const panelDiv = fixture.debugElement.query(By.css('div'));
      47 |     expect(panelDiv).toBe(null);

      at Object.<anonymous> (libs/ui-lib/expando/src/expando-panel.component.spec.ts:44:22)

  ● ui-expando-panel › should expand on toggle

    Failed: "Zone is needed for the waitForAsync() test helper but could not be found. Please make sure that your environment includes zone.js/dist/zone.js"

      20 |   let fixture: ComponentFixture<TestHostComponent>;
      21 | 
    > 22 |   beforeEach(waitForAsync(() => {
         |   ^
      23 |     TestBed.configureTestingModule({
      24 |       declarations: [
      25 |         ExpandoPanelComponent,

      at Env.beforeEach (node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:46:24)
      at Suite.<anonymous> (libs/ui-lib/expando/src/expando-panel.component.spec.ts:22:3)
      at Object.<anonymous> (libs/ui-lib/expando/src/expando-panel.component.spec.ts:17:1)

  ● ui-expando-panel › should expand on toggle

    TypeError: Cannot read property 'getComponentFromError' of null

      at TestBedViewEngine.Object.<anonymous>.TestBedViewEngine._initIfNeeded (../packages/core/testing/src/test_bed.ts:391:46)
      at TestBedViewEngine.Object.<anonymous>.TestBedViewEngine.createComponent (../packages/core/testing/src/test_bed.ts:598:10)
      at Function.Object.<anonymous>.TestBedViewEngine.createComponent (../packages/core/testing/src/test_bed.ts:245:36)
      at Object.<anonymous> (libs/ui-lib/expando/src/expando-panel.component.spec.ts:37:23)

  ● ui-expando-panel › should expand on toggle

    Failed: "Zone is needed for the waitForAsync() test helper but could not be found. Please make sure that your environment includes zone.js/dist/zone.js"

      48 |   });
      49 | 
    > 50 |   it('should expand on toggle', waitForAsync(() => {
         |   ^
      51 |     expect(component.expanded).toBe(false);
      52 | 
      53 |     fixture.debugElement.query(By.css('button')).nativeElement.click();

      at Env.it (node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:95:24)
      at Suite.<anonymous> (libs/ui-lib/expando/src/expando-panel.component.spec.ts:50:3)
      at Object.<anonymous> (libs/ui-lib/expando/src/expando-panel.component.spec.ts:17:1)

Test Suites: 1 failed, 1 total
Tests:       2 failed, 2 total
Snapshots:   0 total
Time:        9.23 s
Ran all test suites.

I hope these test cases are helpful - thank you again for all your efforts on this project.

I think this is because zone.js is not loaded in setupFilesAfterEnv. Do you have import 'jest-preset-angular/setup-jest' in your setup jest file ?

You're right! I had this in my setup-jest.ts, which I thought was correct (because it was working up until now):

import 'jest-preset-angular';

replacing it with

import 'jest-preset-angular/setup-jest';

fixed the runtime errors in my test repro. I'll try that change on the master branch, and see if it fixes the compile error, and if it works for our internal repo.

The NgZone failures and Testbed undefined failures were both fixed by switching to import 'jest-preset-angular/setup-jest';; however the compile error (with base class dependencies in a peer submodule) is still there. Try the master branch for a repro:

https://github.com/johncrim/repro-ivy-jest-preset-angular/tree/master

I think this is because zone.js is not loaded in setupFilesAfterEnv. Do you have import 'jest-preset-angular/setup-jest' in your setup jest file ?

You're right! I had this in my setup-jest.ts, which I thought was correct (because it was working up until now):

import 'jest-preset-angular';

replacing it with

import 'jest-preset-angular/setup-jest';

fixed the runtime errors in my test repro. I'll try that change on the master branch, and see if it fixes the compile error, and if it works for our internal repo.

It is a breaking change which was mentioned in 9.0.0-next.0

image
I don't see any errors like you had. What are the steps to reproduce ? Maybe you can try clear jest cache and rerun.

I left the repo using the release version of jest-preset-angular, so you can see that the tests work before updating to 9.0.0-next.2.

So the repro instructions are:

git clone https://github.com/johncrim/repro-ivy-jest-preset-angular
yarn
yarn test # expected to work, using 8.3.2

Then upgrade and test:

yarn upgrade jest-preset-angular@next
yarn test # should fail with compile errors

I think the error is valid. I've checked and it comes from diagnostics which are retrieved from Program. However, I can't explain why v8.3.2 doesn't throw the error. With v8.3.2, ts-jest compilation is different from v9.0.0-next.2.

v9.0.0-next.2 uses angular public apis to create TypeScript Program and compile codes. This error I've seen before, reference here https://github.com/microsoft/TypeScript/issues/338 . Workaround is super[<property_name>]

This is valid typescript and Angular code - the components compile successfully and run properly in Angular. (yarn build works in this repo).

It's the addition of 9.0.0-next.2 that causes compile errors. I thought it might have something to do with the fact that the base class is in a different sub-module, but I was wrong about that - I still get the same compile error if I move the base class into the same sub-module:

❯ yarn test
yarn run v1.22.5
$ jest
 FAIL  libs/ui-lib/expando/src/expando-panel.component.spec.ts
  ● Test suite failed to run

    libs/ui-lib/expando/src/expando-panel.component.ts:33:16 - error TS2340: Only public and protected methods of the base class are accessible via the 'super' keyword.     

    33     if (!super.disabled) {
                      ~~~~~~~~
    libs/ui-lib/expando/src/expando-panel.component.ts:40:16 - error TS2340: Only public and protected methods of the base class are accessible via the 'super' keyword.     

    40     if (!super.disabled && !this.expanded) {
                      ~~~~~~~~
    libs/ui-lib/expando/src/expando-panel.component.ts:47:16 - error TS2340: Only public and protected methods of the base class are accessible via the 'super' keyword.     

    47     if (!super.disabled && this.expanded) {
                      ~~~~~~~~

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        9.742 s
Ran all test suites.
error Command failed with exit code 1.

I've checked compiled output produced by v9.0.0-next.2. It matches with dist/ui-lib/bundles/this-ui-lib-expando.umd.js. For example this code

public toggle() {
    if (!super.disabled) {
      this.expanded = !(this.expanded);
      this._changeDetector.markForCheck();
    }
  }

becomes

ExpandoPanelComponent.prototype.toggle = function () {
     if (!_super.prototype.disabled) {
        this.expanded = !(this.expanded);
        this._changeDetector.markForCheck();
     }
};

meanwhile with v8.3.2 the output is different from ng build

toggle() {
        if (!super.disabled) {
            this.expanded = !(this.expanded);
            this._changeDetector.markForCheck();
        }
 }

I can't explain why ng build doesn't throw errors. One thing for sure is v9.0.0-next.2 produces exact same output as ng build

We're also seeing another kind of compile error using 9.0.0-next.2. Right now all of our compile errors are either complaints about base class accessibility, or complaints about test matchers. We're using Spectator, which includes matcher extensions. The errors look like this:

IL  apps/ease/src/modules/app/app-root.component.spec.ts
  ● Test suite failed to run

    libs/ui-components/focus/src/focus-manager.spec.ts:54:46 - error TS2339: Property 'toHaveClass' does not exist on type 'Matchers<void, HTMLElement>'.

    54       expect(componentElt.nativeElement).not.toHaveClass('focused');
                                                    ~~~~~~~~~~~
    libs/ui-components/focus/src/focus-manager.spec.ts:57:42 - error TS2339: Property 'toHaveClass' does not exist on type 'JestMatchersShape<Matchers<void, HTMLElement>, Matchers<Promise<void>, HTMLElement>>'.

    57       expect(componentElt.nativeElement).toHaveClass('focused');
                                                ~~~~~~~~~~~
    libs/ui-components/focus/src/focus-manager.spec.ts:65:46 - error TS2339: Property 'toHaveClass' does not exist on type 'Matchers<void, HTMLElement>'.

    65       expect(componentElt.nativeElement).not.toHaveClass('focused');
                                                    ~~~~~~~~~~~
    libs/ui-components/focus/src/focus-manager.spec.ts:68:46 - error TS2339: Property 'toHaveClass' does not exist on type 'Matchers<void, HTMLElement>'.

    68       expect(componentElt.nativeElement).not.toHaveClass('focused');

One interesting thing about this error report is that the failing test, and the test file where the errors are reported, are different. I'm also seeing this (failing test and reported errors are different files) in the base class accessibility compile errors. Again, everything works as expected in Angular, in Karma, and with jest-preset-angular 8.3.2.

I'll try to reproduce this in the test repository I created.

One interesting thing about this error report is that the failing test, and the test file where the errors are reported, are different.

This was fixed in https://github.com/thymikee/jest-preset-angular/pull/562/commits/bf29c20b710d84d86605c064d33cb162432a9234

About the other errors I'm not sure why. Btw when I run karma test on your repo, it says no tests found.

I've checked compiled output produced by v9.0.0-next.2. It matches with dist/ui-lib/bundles/this-ui-lib-expando.umd.js. For example this code

public toggle() {
    if (!super.disabled) {
      this.expanded = !(this.expanded);
      this._changeDetector.markForCheck();
    }
  }

becomes

ExpandoPanelComponent.prototype.toggle = function () {
     if (!_super.prototype.disabled) {
      this.expanded = !(this.expanded);
      this._changeDetector.markForCheck();
     }
};

That's interesting - I didn't know jest used UMD. I thought we were using ES2015 and ES modules with class support - that might explain it. However, things are still building correctly using ng-packagr, I'm not getting the same base class accessibility errors.

For example, if code is referenced correctly, and the prototype chain is figured out, this shouldn't be a problem: if (!_super.prototype.disabled) {.

(An aside: I know ng-packagr has been considering deprecating UMD, and I thought that was in the works.)

Jest is currently working on support ESM. In the meantime it still uses commonjs and UMD is compatible with commonjs. With Jest 27, we might be able to switch to ESM but still the whole thing about ESM, jest and nodejs is still on development.

Ah ha I know how to produce the error like v9.0.0-next.2 does. You can do follow steps:

  • Set target: es5 and module: commonjs in tsconfig.base.json

  • Run yarn build. This will produce compiled version of ui-lib

  • Run ngc -p libs/ui-lib/tsconfig.lib.json and bumper
    image

The error only occurs when using target: es5. With ES6+, the error won't occur. This is a bug with 9.0.0-next.2, which is here https://github.com/thymikee/jest-preset-angular/pull/562/files#diff-e52c610e65e23abc1929eec81187c168c8763dd301741122b84d8945409225e3R57

This will be fixed in the next release of next tag :)

That's great - nice job! I just found another way to reproduce the same compile error in karma:

I copied the jest *.spec.ts to *.karma-spec.ts, and updated a few things to get karma tests running. If you:

git pull # master
yarn karma

You should see the karma tests running successfully. If you then change libs\ui-lib\tsconfig.karma-spec.json to "target": "es5" you'll get the same compile errors.

I'll check to make sure the other compile errors I'm seeing are related to es5/es2015.

This issue https://github.com/microsoft/TypeScript/issues/15842 also says that ES5 doesn't work while ES6 does

v9.0.0-next.3 is out. It should fix this issue.

I can confirm that all these issues are fixed in v9.0.0-next.3. Thank you!!

Performance is still an issue - right now this setup is taking about 10x longer than v8. Most of that time is a long delay (~10min) before any of the tests start.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

SrHuevo picture SrHuevo  ·  6Comments

thymikee picture thymikee  ·  6Comments

gustavobmichel picture gustavobmichel  ·  7Comments

gagle picture gagle  ·  5Comments

chris5287 picture chris5287  ·  7Comments