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
Initial experimental Jest support #25055
Conversation
packages/angular_devkit/build_angular/src/builders/jest/schema.json
Outdated
Show resolved
Hide resolved
packages/angular_devkit/build_angular/src/builders/jest/index.ts
Outdated
Show resolved
Hide resolved
packages/angular_devkit/build_angular/src/builders/jest/index.ts
Outdated
Show resolved
Hide resolved
packages/angular_devkit/build_angular/src/builders/jest/index.ts
Outdated
Show resolved
Hide resolved
packages/angular_devkit/build_angular/src/builders/jest/schema.json
Outdated
Show resolved
Hide resolved
packages/angular_devkit/build_angular/src/builders/jest/index.ts
Outdated
Show resolved
Hide resolved
19f5ad6
to
b492080
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dgp1130, please also squash the commits as in the CLI we do not use fixup.
packages/angular_devkit/build_angular/src/builders/jest/index.ts
Outdated
Show resolved
Hide resolved
packages/angular_devkit/build_angular/src/builders/jest/schema.json
Outdated
Show resolved
Hide resolved
packages/angular_devkit/build_angular/src/builders/jest/schema.json
Outdated
Show resolved
Hide resolved
packages/angular_devkit/build_angular/src/builders/jest/tests/test-files_spec.ts
Show resolved
Hide resolved
packages/angular_devkit/build_angular/src/builders/jest/index.ts
Outdated
Show resolved
Hide resolved
packages/angular_devkit/build_angular/src/builders/jest/index.ts
Outdated
Show resolved
Hide resolved
For now this just runs ESBuild-er to build test code, Jest is not actually invoked yet. This uses `glob` to find test files matching the given pattern. I went out of my way to limit `glob` functionality as much as possible in case we change the implementation later.
This runs Jest on the outputs of the built test files and reports the results of the test execution. It depends on `jest` and `jest-environment-jsdom` as optional peer deps validated in the builder, so there isn't a strict dependency on Jest for applications which don't use it. Jest exports a `runJest()` function, however it can't be used directly because we need to opt-in to `--experimental-vm-modules` in ordre to run Jest on native ESM JavaScript as produced by `browser-esbuild`. This means we need a Node subprocess in order to add this flag, because the `ng` command cannot add a Node flag to its own current execution. This unfortunately means we can't just `import * as jest from 'jest';` or even `require.resolve('jest')` because that returns the library entry point exporting `runJest()`, rather than a script which actually runs Jest on load. Fortunately, Jest exports it's `node_modules/.bin/jest` entry point from its `package.json` under `jest/bin/jest`, so we `require.resolve()` _that_ to get the path to the correct file. Executing Jest is fairly straightforward, running on the output of the `browser-esbuild` execution with outputs streamed to the console. We opted to use JSDom over Domino in order to align with the existing Jest ecosystem.
This configures polyfills to set up the environment before executing Jest tests. We need to do three things: 1. Set the global `jest` symbol. Jest executing in ESM does not provide the `jest` global and users are expected to import from `@jest/globals` or `import.meta.jest`. Zone.js is not compatible with this yet, so we need to manually define the `jest` global for Zone to read it. 2. Run user polyfills, (typically including `zone.js` and `zone.js/testing`). Zone reads the `jest` global to recognize the environment it is in and patch the relevant functions to load fake async properly. Users can override this part if they are building a Zoneless application or have custom polyfills for other browser functionality. 3. Initalize `TestBed`. This configures the `TestBed` environment so users don't have to manually configure it for each test file. Ordering is very important for these operations, which complicates the implementation somewhat. `zone.js/testing` does not include an import on `zone.js`, meaning there was no guarnatee the bundler would sort their executions in the correct order. Similarly, `zone.js` does not import anything from Jest, so it is not trivial to inject the `globalThis.jest = import.meta.jest;` line before Zone loads. Even setting polyfills to `[jestGlobal, 'zone.js, 'zone.js/testing', initTestBed]` doesn't work because code splitting rearranges the order of operations in an incompatible way. Instead, these are implemented as distinct entry points in `browser-esbuild` with Jest's `--setupFilesAfterEnv` option executing them in the correct order. Ideally, we could drop the global initialization altogether once Zone.js knows to look for `import.meta.jest` in an ESM context. Also we might be able to reduce down to a single polyfills entry point if `zone.js/testing` had an import on `zone.js` to apply correct ordering.
This test generates an `ng new` project, updates it to use Jest for test executions, and then runs `ng test` and checks for the experimental notice.
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
PR Checklist
Please check to confirm your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the new behavior?
This PR adds initial experimental Jest support. This is experimental and not ready for production use yet. For now, it it sufficient to pass the tests generated by the
ng new
application and we will continue to expand on this design going forward.Enable the Jest runner by updating the
test
builder in yourangular.json
to be:The current approach is to pre-build application tests with the
browser-esbuild
Angular builder under the hood, and then run Jest on the JavaScript outputs. We'll continue to experiment with this architecture and others to see what the best path forward is in terms of performance, maintenance, and developer ergonomics.Does this PR introduce a breaking change?