diff --git a/.editorconfig b/.editorconfig index 8bed5ad4df..45c01ff049 100644 --- a/.editorconfig +++ b/.editorconfig @@ -19,7 +19,7 @@ tab_width = 4 [*.{yml, yaml}] indent_size = 2 -[*.{js,jsx,ts,tsx}] +[*.{js,jsx,ts,tsx,mjs,mjsx,tsx,mtsx}] indent_size = 4 [*.html] diff --git a/e2e/zoneless-env/__tests__/zoneless-env.spec.ts b/e2e/zoneless-env/__tests__/zoneless-env.spec.ts new file mode 100644 index 0000000000..ea305d8c1b --- /dev/null +++ b/e2e/zoneless-env/__tests__/zoneless-env.spec.ts @@ -0,0 +1,18 @@ +import { TestBed } from '@angular/core/testing'; + +import { FooComponent } from '../foo.component'; + +describe('FooComponent', () => { + it('should trigger change detection without fixture.detectChanges', () => { + TestBed.configureTestingModule({ + imports: [FooComponent], + }); + const fixture = TestBed.createComponent(FooComponent); + + expect(fixture.componentInstance.value1()).toBe('val1'); + + fixture.componentRef.setInput('value1', 'hello'); + + expect(fixture.componentInstance.value1()).toBe('hello'); + }); +}); diff --git a/e2e/zoneless-env/foo.component.html b/e2e/zoneless-env/foo.component.html new file mode 100644 index 0000000000..cdd69d0efa --- /dev/null +++ b/e2e/zoneless-env/foo.component.html @@ -0,0 +1,10 @@ + +

Line 1

+
+
+ {{ value1() }} +
+ + {{ value2() }} + +
diff --git a/e2e/zoneless-env/foo.component.scss b/e2e/zoneless-env/foo.component.scss new file mode 100644 index 0000000000..c2393c27c0 --- /dev/null +++ b/e2e/zoneless-env/foo.component.scss @@ -0,0 +1,3 @@ +p { + font-size: 1.6rem; +} diff --git a/e2e/zoneless-env/foo.component.ts b/e2e/zoneless-env/foo.component.ts new file mode 100644 index 0000000000..62407aed74 --- /dev/null +++ b/e2e/zoneless-env/foo.component.ts @@ -0,0 +1,25 @@ +import { NgIf } from '@angular/common'; +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'foo', + standalone: true, + templateUrl: './foo.component.html', + styleUrls: ['./foo.component.scss'], + // we have to setup styles this way, since simple styles/styleUrs properties will be removed (jest does not unit test styles) + styles: [ + ` + p { + color: red; + } + `, + ], + imports: [NgIf], +}) +export class FooComponent { + readonly value1 = input('val1'); + readonly value2 = input('val2'); + + protected readonly condition1 = true; + protected readonly condition2 = false; +} diff --git a/e2e/zoneless-env/jest-cjs.config.ts b/e2e/zoneless-env/jest-cjs.config.ts new file mode 100644 index 0000000000..13b7970b6e --- /dev/null +++ b/e2e/zoneless-env/jest-cjs.config.ts @@ -0,0 +1,24 @@ +import type { JestConfigWithTsJest } from 'ts-jest'; + +const config: JestConfigWithTsJest = { + displayName: 'e2e-zoneless-env', + testEnvironment: 'jsdom', + snapshotSerializers: [ + '/../../build/serializers/html-comment', + '/../../build/serializers/ng-snapshot', + '/../../build/serializers/no-ng-attributes', + ], + setupFilesAfterEnv: ['./setup-zoneless-env.ts'], + transform: { + '^.+\\.(ts|js|mjs|html)$': [ + '/../../build/index.js', + { + tsconfig: '/tsconfig-cjs.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], +}; + +export default config; diff --git a/e2e/zoneless-env/jest-esm.config.ts b/e2e/zoneless-env/jest-esm.config.ts new file mode 100644 index 0000000000..c521da99eb --- /dev/null +++ b/e2e/zoneless-env/jest-esm.config.ts @@ -0,0 +1,28 @@ +import type { JestConfigWithTsJest } from 'ts-jest'; + +const config: JestConfigWithTsJest = { + displayName: 'e2e-zoneless-env', + testEnvironment: 'jsdom', + snapshotSerializers: [ + '/../../build/serializers/html-comment', + '/../../build/serializers/ng-snapshot', + '/../../build/serializers/no-ng-attributes', + ], + setupFilesAfterEnv: ['./setup-zoneless-env.mts'], + moduleNameMapper: { + rxjs: '/../../node_modules/rxjs/dist/bundles/rxjs.umd.js', + }, + extensionsToTreatAsEsm: ['.ts', '.mts'], + transform: { + '^.+\\.(ts|mts|js|mjs|html)$': [ + '/../../build/index.js', + { + useESM: true, + tsconfig: '/tsconfig-esm.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, +}; + +export default config; diff --git a/e2e/zoneless-env/jest-transpile-cjs.config.ts b/e2e/zoneless-env/jest-transpile-cjs.config.ts new file mode 100644 index 0000000000..46d6084eee --- /dev/null +++ b/e2e/zoneless-env/jest-transpile-cjs.config.ts @@ -0,0 +1,25 @@ +import type { JestConfigWithTsJest } from 'ts-jest'; + +const config: JestConfigWithTsJest = { + displayName: 'e2e-snapshot-serializers', + testEnvironment: 'jsdom', + snapshotSerializers: [ + '/../../build/serializers/html-comment', + '/../../build/serializers/ng-snapshot', + '/../../build/serializers/no-ng-attributes', + ], + setupFilesAfterEnv: ['./setup-zoneless-env.ts'], + transform: { + '^.+\\.(ts|js|mjs|html)$': [ + '/../../build/index.js', + { + tsconfig: '/tsconfig-cjs.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + isolatedModules: true, + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], +}; + +export default config; diff --git a/e2e/zoneless-env/jest-transpile-esm.config.ts b/e2e/zoneless-env/jest-transpile-esm.config.ts new file mode 100644 index 0000000000..1e57840617 --- /dev/null +++ b/e2e/zoneless-env/jest-transpile-esm.config.ts @@ -0,0 +1,29 @@ +import type { JestConfigWithTsJest } from 'ts-jest'; + +const config: JestConfigWithTsJest = { + displayName: 'e2e-snapshot-serializers', + testEnvironment: 'jsdom', + snapshotSerializers: [ + '/../../build/serializers/html-comment', + '/../../build/serializers/ng-snapshot', + '/../../build/serializers/no-ng-attributes', + ], + setupFilesAfterEnv: ['./setup-zoneless-env.mts'], + moduleNameMapper: { + rxjs: '/../../node_modules/rxjs/dist/bundles/rxjs.umd.js', + }, + extensionsToTreatAsEsm: ['.ts', '.mts'], + transform: { + '^.+\\.(ts|mts|js|mjs|html)$': [ + '/../../build/index.js', + { + useESM: true, + tsconfig: '/tsconfig-esm.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + isolatedModules: true, + }, + ], + }, +}; + +export default config; diff --git a/e2e/zoneless-env/setup-zoneless-env.mts b/e2e/zoneless-env/setup-zoneless-env.mts new file mode 100644 index 0000000000..6375b3715a --- /dev/null +++ b/e2e/zoneless-env/setup-zoneless-env.mts @@ -0,0 +1,3 @@ +import { setupZonelessTestEnv } from '../../setup-env/zoneless/index.mjs'; + +setupZonelessTestEnv(); diff --git a/e2e/zoneless-env/setup-zoneless-env.ts b/e2e/zoneless-env/setup-zoneless-env.ts new file mode 100644 index 0000000000..b16ec598be --- /dev/null +++ b/e2e/zoneless-env/setup-zoneless-env.ts @@ -0,0 +1,3 @@ +import { setupZonelessTestEnv } from '../../setup-env/zoneless'; + +setupZonelessTestEnv(); diff --git a/e2e/zoneless-env/tsconfig-cjs.spec.json b/e2e/zoneless-env/tsconfig-cjs.spec.json new file mode 100644 index 0000000000..f65fc7b4f1 --- /dev/null +++ b/e2e/zoneless-env/tsconfig-cjs.spec.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig-base.spec.json" +} diff --git a/e2e/zoneless-env/tsconfig-esm.spec.json b/e2e/zoneless-env/tsconfig-esm.spec.json new file mode 100644 index 0000000000..2007b41c6e --- /dev/null +++ b/e2e/zoneless-env/tsconfig-esm.spec.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig-base.spec.json", + "compilerOptions": { + "module": "ES2022", + "esModuleInterop": true + } +} diff --git a/setup-env/utils.mjs b/setup-env/utils.js similarity index 71% rename from setup-env/utils.mjs rename to setup-env/utils.js index eceb631a4e..41b592793a 100644 --- a/setup-env/utils.mjs +++ b/setup-env/utils.js @@ -1,13 +1,13 @@ -import { TextDecoder, TextEncoder } from 'util'; +const { TextDecoder, TextEncoder } = require('util'); -export const polyfillEncoder = () => { +const polyfillEncoder = () => { if (typeof globalThis.TextEncoder === 'undefined') { globalThis.TextEncoder = TextEncoder; globalThis.TextDecoder = TextDecoder; } }; -export const resolveTestEnvOptions = (options) => { +const resolveTestEnvOptions = (options) => { const globalTestEnvOptions = globalThis.ngJest?.testEnvironmentOptions; if (globalTestEnvOptions) { console.warn( @@ -17,3 +17,8 @@ export const resolveTestEnvOptions = (options) => { return globalTestEnvOptions ?? options; }; + +module.exports = { + polyfillEncoder, + resolveTestEnvOptions, +}; diff --git a/setup-env/zone/index.js b/setup-env/zone/index.js index b2b2d2a17a..31301cbf85 100644 --- a/setup-env/zone/index.js +++ b/setup-env/zone/index.js @@ -1,38 +1,20 @@ require('zone.js'); require('zone.js/testing'); -const { TextEncoder, TextDecoder } = require('util'); - const { getTestBed } = require('@angular/core/testing'); const { BrowserDynamicTestingModule, platformBrowserDynamicTesting, } = require('@angular/platform-browser-dynamic/testing'); -const polyfillEncoder = () => { - if (typeof globalThis.TextEncoder === 'undefined') { - globalThis.TextEncoder = TextEncoder; - globalThis.TextDecoder = TextDecoder; - } -}; - -const resolveTestEnvOptions = (options) => { - const globalTestEnvOptions = globalThis.ngJest?.testEnvironmentOptions; - if (globalTestEnvOptions) { - console.warn( - 'Setting testEnvironmentOptions via globalThis.ngJest is deprecated. Please provide testEnvironmentOptions via function argument', - ); - } - - return globalTestEnvOptions ?? options; -}; +const { polyfillEncoder, resolveTestEnvOptions } = require('../utils'); const setupZoneTestEnv = (options) => { polyfillEncoder(); const testEnvironmentOptions = resolveTestEnvOptions(options); getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, + [BrowserDynamicTestingModule], platformBrowserDynamicTesting(), testEnvironmentOptions, ); diff --git a/setup-env/zone/index.mjs b/setup-env/zone/index.mjs index fe0d2a993c..68fe0afbb0 100644 --- a/setup-env/zone/index.mjs +++ b/setup-env/zone/index.mjs @@ -4,15 +4,19 @@ import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; -import { polyfillEncoder, resolveTestEnvOptions } from '../utils.mjs'; +import { polyfillEncoder, resolveTestEnvOptions } from '../utils'; -export const setupZoneTestEnv = (options) => { +const setupZoneTestEnv = (options) => { polyfillEncoder(); const testEnvironmentOptions = resolveTestEnvOptions(options); getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, + [BrowserDynamicTestingModule], platformBrowserDynamicTesting(), testEnvironmentOptions, ); }; + +export { + setupZoneTestEnv, +} diff --git a/setup-env/zoneless/index.js b/setup-env/zoneless/index.js index b725a157ad..6ef884f540 100644 --- a/setup-env/zoneless/index.js +++ b/setup-env/zoneless/index.js @@ -1,8 +1,48 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const setupZonelessTestEnv = (_options) => { +const { provideExperimentalZonelessChangeDetection, NgModule, ErrorHandler } = require('@angular/core'); +const { getTestBed } = require('@angular/core/testing'); +const { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting, +} = require('@angular/platform-browser-dynamic/testing'); + +const { polyfillEncoder, resolveTestEnvOptions } = require('../utils'); + +const provideZonelessConfig = () => { + class TestModule {} + NgModule({ + providers: [ + provideExperimentalZonelessChangeDetection(), + { + provide: ErrorHandler, + useValue: { + handleError: (e) => { + throw e; + }, + }, + }, + ], + })(TestModule); + + return TestModule; +}; + +const setupZonelessTestEnv = (options) => { + polyfillEncoder(); + if (typeof provideExperimentalZonelessChangeDetection !== 'undefined') { + const testEnvironmentOptions = resolveTestEnvOptions(options); + + getTestBed().initTestEnvironment( + [BrowserDynamicTestingModule, provideZonelessConfig()], + platformBrowserDynamicTesting(), + testEnvironmentOptions, + ); + + return; + } + throw Error( - 'Zoneless testing environment only works when running Jest in ESM mode with Jest 29. ' + - 'Jest 30+ will support to work with CommonJS mode.', + 'Cannot find provideExperimentalZonelessChangeDetection() to setup zoneless testing environment. ' + + 'Please use setupZoneTestEnv() from jest-preset-angular/setup-env/setup-zone-env.mjs instead.', ); }; diff --git a/setup-env/zoneless/index.mjs b/setup-env/zoneless/index.mjs index b63cd62e6e..ce66a4cc90 100644 --- a/setup-env/zoneless/index.mjs +++ b/setup-env/zoneless/index.mjs @@ -2,29 +2,34 @@ import { ErrorHandler, NgModule, provideExperimentalZonelessChangeDetection } fr import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; -import { polyfillEncoder, resolveTestEnvOptions } from '../utils.mjs'; +import { polyfillEncoder, resolveTestEnvOptions } from '../utils'; -export const setupZonelessTestEnv = (options) => { - if (typeof provideExperimentalZonelessChangeDetection !== 'undefined') { - polyfillEncoder(); - const testEnvironmentOptions = resolveTestEnvOptions(options); - @NgModule({ - providers: [ - provideExperimentalZonelessChangeDetection(), - { - provide: ErrorHandler, - useValue: { - handleError: (e) => { - throw e; - }, +const provideZonelessConfig = () => { + class TestModule {} + NgModule({ + providers: [ + provideExperimentalZonelessChangeDetection(), + { + provide: ErrorHandler, + useValue: { + handleError: (e) => { + throw e; }, }, - ], - }) - export class TestModule {} + }, + ], + })(TestModule); + + return TestModule; +}; + +const setupZonelessTestEnv = (options) => { + polyfillEncoder(); + if (typeof provideExperimentalZonelessChangeDetection !== 'undefined') { + const testEnvironmentOptions = resolveTestEnvOptions(options); getTestBed().initTestEnvironment( - [BrowserDynamicTestingModule, TestModule], + [BrowserDynamicTestingModule, provideZonelessConfig()], platformBrowserDynamicTesting(), testEnvironmentOptions, ); @@ -34,6 +39,10 @@ export const setupZonelessTestEnv = (options) => { throw Error( 'Cannot find provideExperimentalZonelessChangeDetection() to setup zoneless testing environment. ' + - 'Please use setupZoneTestEnv() from jest-preset-angular/setup-env/setup-zone-env.mjs instead.', + 'Please use setupZoneTestEnv() from jest-preset-angular/setup-env/setup-zone-env.mjs instead.', ); }; + +export { + setupZonelessTestEnv +} diff --git a/setup-jest.js b/setup-jest.js index 4528ff46e6..4ac9724c84 100644 --- a/setup-jest.js +++ b/setup-jest.js @@ -25,4 +25,8 @@ if (typeof globalThis.TextEncoder === 'undefined') { const testEnvironmentOptions = globalThis.ngJest?.testEnvironmentOptions ?? Object.create(null); -getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), testEnvironmentOptions); +getTestBed().initTestEnvironment( + [BrowserDynamicTestingModule], + platformBrowserDynamicTesting(), + testEnvironmentOptions, +); diff --git a/setup-jest.mjs b/setup-jest.mjs index 19752ef617..e4bd512a42 100644 --- a/setup-jest.mjs +++ b/setup-jest.mjs @@ -22,4 +22,4 @@ if (typeof globalThis.TextEncoder === 'undefined') { const testEnvironmentOptions = globalThis.ngJest?.testEnvironmentOptions ?? Object.create(null); -getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), testEnvironmentOptions); +getTestBed().initTestEnvironment([BrowserDynamicTestingModule], platformBrowserDynamicTesting(), testEnvironmentOptions); diff --git a/src/config/setup-jest.spec.ts b/src/config/setup-jest.spec.ts index 1de0e5bd21..634f6fbbec 100644 --- a/src/config/setup-jest.spec.ts +++ b/src/config/setup-jest.spec.ts @@ -28,6 +28,7 @@ jest.mock('@angular/core/testing', () => { class BrowserDynamicTestingModuleStub {} class PlatformRefStub {} +class ErrorHandlerStub {} const mockPlatformBrowserDynamicTesting = jest.fn(() => new PlatformRefStub()); jest.mock('@angular/platform-browser-dynamic/testing', () => { return { @@ -35,14 +36,31 @@ jest.mock('@angular/platform-browser-dynamic/testing', () => { platformBrowserDynamicTesting: mockPlatformBrowserDynamicTesting, }; }); +const mockProvideExperimentalZonelessChangeDetection = jest.fn(); +jest.mock('@angular/core', () => { + return { + provideExperimentalZonelessChangeDetection: mockProvideExperimentalZonelessChangeDetection, + ErrorHandler: ErrorHandlerStub, + NgModule: () => { + return jest.fn(); + }, + }; +}); describe('setup-jest', () => { const assertOnInitTestEnv = (): void => { expect(mockGetTestBed).toHaveBeenCalled(); - expect(mockInitTestEnvironment).toHaveBeenCalled(); - expect(mockInitTestEnvironment.mock.calls[0][0]).toBeInstanceOf(BrowserDynamicTestingModuleStub); + expect(mockInitTestEnvironment.mock.calls[0][0][0]).toBeInstanceOf(BrowserDynamicTestingModuleStub); expect(mockPlatformBrowserDynamicTesting).toHaveBeenCalled(); expect(mockPlatformBrowserDynamicTesting.mock.results[0].value).toBeInstanceOf(PlatformRefStub); + expect(mockInitTestEnvironment.mock.calls[0][2]).toEqual({ + teardown: { + destroyAfterEach: false, + rethrowErrors: true, + }, + errorOnUnknownElements: true, + errorOnUnknownProperties: true, + }); }; beforeEach(() => { @@ -53,7 +71,7 @@ describe('setup-jest', () => { }); describe('for CJS setup-jest, test environment initialization', () => { - test('should call getTestBed() and initTestEnvironment() with the testEnvironmentOptions passed to ngJest', async () => { + it('should setup test environment with setup-jest file', async () => { globalThis.ngJest = { testEnvironmentOptions: { teardown: { @@ -65,28 +83,15 @@ describe('setup-jest', () => { }, }; - await import('../../setup-jest'); + await import('../../setup-jest.js'); + expect(globalThis.TextEncoder).toBeDefined(); expect(mockZoneJs).toHaveBeenCalled(); expect(mockZoneJsTesting).toHaveBeenCalled(); assertOnInitTestEnv(); - expect(mockInitTestEnvironment.mock.calls[0][2]).toEqual({ - teardown: { - destroyAfterEach: false, - rethrowErrors: true, - }, - errorOnUnknownElements: true, - errorOnUnknownProperties: true, - }); }); - test('should always have TextEncoder in globalThis', async () => { - await import('../../setup-jest'); - - expect(globalThis.TextEncoder).toBeDefined(); - }); - - it('should call getTestBed() and initTestEnvironment() with the testEnvironmentOptions passed as argument with setupZoneTestEnv()', async () => { + it('should setup test environment with setupZoneTestEnv()', async () => { const { setupZoneTestEnv } = await import('../../setup-env/zone/index.js'); setupZoneTestEnv({ @@ -98,10 +103,16 @@ describe('setup-jest', () => { errorOnUnknownProperties: true, }); + expect(globalThis.TextEncoder).toBeDefined(); expect(mockZoneJs).toHaveBeenCalled(); expect(mockZoneJsTesting).toHaveBeenCalled(); assertOnInitTestEnv(); - expect(mockInitTestEnvironment.mock.calls[0][2]).toEqual({ + }); + + it('should setup test environment with setupZonelessTestEnv()', async () => { + const { setupZonelessTestEnv } = await import('../../setup-env/zoneless/index.js'); + + setupZonelessTestEnv({ teardown: { destroyAfterEach: false, rethrowErrors: true, @@ -109,19 +120,17 @@ describe('setup-jest', () => { errorOnUnknownElements: true, errorOnUnknownProperties: true, }); - }); - - it('should always have TextEncoder in globalThis with setupZoneTestEnv()', async () => { - const { setupZoneTestEnv } = await import('../../setup-env/zone/index.js'); - - setupZoneTestEnv(); expect(globalThis.TextEncoder).toBeDefined(); + expect(mockZoneJs).not.toHaveBeenCalled(); + expect(mockZoneJsTesting).not.toHaveBeenCalled(); + assertOnInitTestEnv(); + expect(mockProvideExperimentalZonelessChangeDetection).toHaveBeenCalled(); }); }); describe('for ESM setup-jest, test environment initialization', () => { - test('should call getTestBed() and initTestEnvironment() with the testEnvironmentOptions passed to ngJest', async () => { + it('should setup test environment with setup-jest file', async () => { globalThis.ngJest = { testEnvironmentOptions: { teardown: { @@ -135,26 +144,13 @@ describe('setup-jest', () => { await import('../../setup-jest.mjs'); + expect(globalThis.TextEncoder).toBeDefined(); expect(mockZoneJs).toHaveBeenCalled(); expect(mockZoneJsTesting).toHaveBeenCalled(); assertOnInitTestEnv(); - expect(mockInitTestEnvironment.mock.calls[0][2]).toEqual({ - teardown: { - destroyAfterEach: false, - rethrowErrors: true, - }, - errorOnUnknownElements: true, - errorOnUnknownProperties: true, - }); }); - test('should always have TextEncoder in globalThis', async () => { - await import('../../setup-jest.mjs'); - - expect(globalThis.TextEncoder).toBeDefined(); - }); - - it('should call getTestBed() and initTestEnvironment() with the testEnvironmentOptions passed as argument with setupZoneTestEnv()', async () => { + it('should setup test environment with setupZoneTestEnv()', async () => { const { setupZoneTestEnv } = await import('../../setup-env/zone/index.mjs'); setupZoneTestEnv({ @@ -166,10 +162,16 @@ describe('setup-jest', () => { errorOnUnknownProperties: true, }); + expect(globalThis.TextEncoder).toBeDefined(); expect(mockZoneJs).toHaveBeenCalled(); expect(mockZoneJsTesting).toHaveBeenCalled(); assertOnInitTestEnv(); - expect(mockInitTestEnvironment.mock.calls[0][2]).toEqual({ + }); + + it('should setup test environment with setupZonelessTestEnv()', async () => { + const { setupZonelessTestEnv } = await import('../../setup-env/zoneless/index.mjs'); + + setupZonelessTestEnv({ teardown: { destroyAfterEach: false, rethrowErrors: true, @@ -177,14 +179,12 @@ describe('setup-jest', () => { errorOnUnknownElements: true, errorOnUnknownProperties: true, }); - }); - - it('should always have TextEncoder in globalThis with setupZoneTestEnv()', async () => { - const { setupZoneTestEnv } = await import('../../setup-env/zone/index.mjs'); - - setupZoneTestEnv(); expect(globalThis.TextEncoder).toBeDefined(); + expect(mockZoneJs).not.toHaveBeenCalled(); + expect(mockZoneJsTesting).not.toHaveBeenCalled(); + assertOnInitTestEnv(); + expect(mockProvideExperimentalZonelessChangeDetection).toHaveBeenCalled(); }); }); }); diff --git a/website/docs/getting-started/test-environment.md b/website/docs/getting-started/test-environment.md index a1bd45a73f..f7a666ec97 100644 --- a/website/docs/getting-started/test-environment.md +++ b/website/docs/getting-started/test-environment.md @@ -82,12 +82,6 @@ export default jestConfig; Configures a test environment that **DOESN'T** use `zone.js`, as described in [Angular experimental zoneless guide](https://angular.dev/guide/experimental/zoneless). It is designed for projects that have disabled `zone.js`, which can lead to improved performance and simplified testing. -:::important - -This function is only supported in Jest `ESM` mode in [Jest 29](https://github.com/jestjs/jest/issues/10962). Jest 30+ will support to use for `CommonJS` mode. - -::: - You can customize the environment by providing options as function arguments. #### Parameters @@ -98,6 +92,15 @@ You can customize the environment by providing options as function arguments. - Create a Jest setup file: +```ts tab={"label": "TypeScript CJS"} +// setup-jest.ts +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; + +setupZonelessTestEnv({ + //...options +}); +``` + ```ts tab={"label": "TypeScript ESM"} // setup-jest.ts import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless/index.mjs'; @@ -109,6 +112,18 @@ setupZonelessTestEnv({ - Update your Jest configuration: +```ts tab={"label": "TypeScript CJS"} +// jest.config.mts +import type { Config } from 'jest'; + +const jestConfig: Config = { + preset: 'jest-preset-angular', + setupFilesAfterEnv: ['/setup-jest.ts'], +}; + +export default jestConfig; +``` + ```ts tab={"label": "TypeScript ESM"} // jest.config.mts import type { Config } from 'jest'; diff --git a/website/versioned_docs/version-14.3/getting-started/test-environment.md b/website/versioned_docs/version-14.3/getting-started/test-environment.md index a1bd45a73f..f7a666ec97 100644 --- a/website/versioned_docs/version-14.3/getting-started/test-environment.md +++ b/website/versioned_docs/version-14.3/getting-started/test-environment.md @@ -82,12 +82,6 @@ export default jestConfig; Configures a test environment that **DOESN'T** use `zone.js`, as described in [Angular experimental zoneless guide](https://angular.dev/guide/experimental/zoneless). It is designed for projects that have disabled `zone.js`, which can lead to improved performance and simplified testing. -:::important - -This function is only supported in Jest `ESM` mode in [Jest 29](https://github.com/jestjs/jest/issues/10962). Jest 30+ will support to use for `CommonJS` mode. - -::: - You can customize the environment by providing options as function arguments. #### Parameters @@ -98,6 +92,15 @@ You can customize the environment by providing options as function arguments. - Create a Jest setup file: +```ts tab={"label": "TypeScript CJS"} +// setup-jest.ts +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; + +setupZonelessTestEnv({ + //...options +}); +``` + ```ts tab={"label": "TypeScript ESM"} // setup-jest.ts import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless/index.mjs'; @@ -109,6 +112,18 @@ setupZonelessTestEnv({ - Update your Jest configuration: +```ts tab={"label": "TypeScript CJS"} +// jest.config.mts +import type { Config } from 'jest'; + +const jestConfig: Config = { + preset: 'jest-preset-angular', + setupFilesAfterEnv: ['/setup-jest.ts'], +}; + +export default jestConfig; +``` + ```ts tab={"label": "TypeScript ESM"} // jest.config.mts import type { Config } from 'jest';