diff --git a/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.spec.ts.template b/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.spec.ts.template index 155b20acd0d6..2ee952baf1e3 100644 --- a/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.spec.ts.template +++ b/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.spec.ts.template @@ -20,9 +20,9 @@ describe('App', () => { expect(app).toBeTruthy(); }); - it('should render title', () => { + it('should render title', <% if(zoneless) { %> async <% } %>() => { const fixture = TestBed.createComponent(App); - fixture.detectChanges(); + <%= zoneless ? 'await fixture.whenStable();' : 'fixture.detectChanges();' %> const compiled = fixture.nativeElement as HTMLElement; expect(compiled.querySelector('h1')?.textContent).toContain('Hello, <%= name %>'); }); diff --git a/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.spec.ts.template b/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.spec.ts.template index 5e235dfb423e..0b0f0c34c4bb 100644 --- a/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.spec.ts.template +++ b/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.spec.ts.template @@ -14,9 +14,9 @@ describe('App', () => { expect(app).toBeTruthy(); }); - it('should render title', () => { + it('should render title', <% if(zoneless) { %> async <% } %>() => { const fixture = TestBed.createComponent(App); - fixture.detectChanges(); + <%= zoneless ? 'await fixture.whenStable();' : 'fixture.detectChanges();' %> const compiled = fixture.nativeElement as HTMLElement; expect(compiled.querySelector('h1')?.textContent).toContain('Hello, <%= name %>'); }); diff --git a/packages/schematics/angular/application/index_spec.ts b/packages/schematics/angular/application/index_spec.ts index ba0033f8f119..96916066343c 100644 --- a/packages/schematics/angular/application/index_spec.ts +++ b/packages/schematics/angular/application/index_spec.ts @@ -831,6 +831,35 @@ describe('Application Schematic', () => { }); }); + it('should add fixture.whenStable() when zoneless application', async () => { + const tree = await schematicRunner.runSchematic( + 'application', + { + ...defaultOptions, + }, + workspaceTree, + ); + + const content = tree.readContent('/projects/foo/src/app/app.spec.ts'); + expect(content).toContain('fixture.whenStable()'); + expect(content).not.toContain('fixture.detectChanges()'); + }); + + it('should not add fixture.whenStable() in initial spec files when its not zoneless application', async () => { + const tree = await schematicRunner.runSchematic( + 'application', + { + ...defaultOptions, + zoneless: false, + }, + workspaceTree, + ); + + const content = tree.readContent('/projects/foo/src/app/app.spec.ts'); + expect(content).not.toContain('fixture.whenStable()'); + expect(content).toContain('fixture.detectChanges()'); + }); + it('should call the tailwind schematic when style is tailwind', async () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const options = { ...defaultOptions, style: 'tailwind' as any }; diff --git a/packages/schematics/angular/component/files/__name@dasherize@if-flat__/__name@dasherize__.__type@dasherize__.spec.ts.template b/packages/schematics/angular/component/files/__name@dasherize@if-flat__/__name@dasherize__.__type@dasherize__.spec.ts.template index 174bfe4e537d..74d858ea1e64 100644 --- a/packages/schematics/angular/component/files/__name@dasherize@if-flat__/__name@dasherize__.__type@dasherize__.spec.ts.template +++ b/packages/schematics/angular/component/files/__name@dasherize@if-flat__/__name@dasherize__.__type@dasherize__.spec.ts.template @@ -14,7 +14,7 @@ describe('<%= classifiedName %>', () => { fixture = TestBed.createComponent(<%= classifiedName %>); component = fixture.componentInstance; - fixture.detectChanges(); + <%= zoneless ? 'await fixture.whenStable();' : 'fixture.detectChanges();' %> }); it('should create', () => { diff --git a/packages/schematics/angular/component/index.ts b/packages/schematics/angular/component/index.ts index 1d98f616de37..c3c4fe1c3448 100644 --- a/packages/schematics/angular/component/index.ts +++ b/packages/schematics/angular/component/index.ts @@ -24,6 +24,7 @@ import { addDeclarationToNgModule } from '../utility/add-declaration-to-ng-modul import { findModuleFromOptions } from '../utility/find-module'; import { parseName } from '../utility/parse-name'; import { createProjectSchematic } from '../utility/project'; +import { isZonelessApp } from '../utility/project-targets'; import { validateClassName, validateHtmlSelector } from '../utility/validation'; import { buildDefaultPath } from '../utility/workspace'; import { Schema as ComponentOptions, Style } from './schema'; @@ -59,6 +60,7 @@ export default createProjectSchematic((options, { project, tre strings.classify(options.name) + (options.addTypeToClassName && options.type ? strings.classify(options.type) : ''); validateClassName(classifiedName); + const zoneless = isZonelessApp(project); const skipStyleFile = options.inlineStyle || options.style === Style.None; const templateSource = apply(url('./files'), [ @@ -72,6 +74,7 @@ export default createProjectSchematic((options, { project, tre ...options, // Add a new variable for the classified name, conditionally including the type classifiedName, + zoneless, }), !options.type ? forEach(((file) => { diff --git a/packages/schematics/angular/component/index_spec.ts b/packages/schematics/angular/component/index_spec.ts index 7b5217c71ea5..3aea03309e12 100644 --- a/packages/schematics/angular/component/index_spec.ts +++ b/packages/schematics/angular/component/index_spec.ts @@ -554,4 +554,38 @@ describe('Component Schematic', () => { const specContent = tree.readContent('/projects/bar/src/app/foo/foo.component.spec.ts'); expect(specContent).toContain("import { FooComponent } from './foo.component';"); }); + + it('should add fixture.whenStable() in spec file when zoneless and standalone apps', async () => { + const tree = await schematicRunner.runSchematic('component', { ...defaultOptions }, appTree); + const tsContent = tree.readContent('/projects/bar/src/app/foo/foo.component.spec.ts'); + + expect(tsContent).toContain('fixture.whenStable()'); + expect(tsContent).not.toContain('fixture.detectChanges()'); + }); + + describe('with zone.js application', () => { + const zoneAppOptions: ApplicationOptions = { + ...appOptions, + name: 'baz', + zoneless: false, + }; + + it('should add not fixture.whenStable() in spec file for standalone', async () => { + appTree = await schematicRunner.runSchematic( + 'application', + { ...zoneAppOptions, standalone: true }, + appTree, + ); + const tree = await schematicRunner.runSchematic( + 'component', + { ...defaultOptions, standalone: true, project: 'baz' }, + appTree, + ); + + const tsContent = tree.readContent('/projects/baz/src/app/foo/foo.component.spec.ts'); + + expect(tsContent).not.toContain('fixture.whenStable()'); + expect(tsContent).toContain('fixture.detectChanges()'); + }); + }); }); diff --git a/packages/schematics/angular/utility/project-targets.ts b/packages/schematics/angular/utility/project-targets.ts index 8897a3ddab66..a018d69c175a 100644 --- a/packages/schematics/angular/utility/project-targets.ts +++ b/packages/schematics/angular/utility/project-targets.ts @@ -21,3 +21,15 @@ export function isUsingApplicationBuilder(project: ProjectDefinition): boolean { return isUsingApplicationBuilder; } + +export function isZonelessApp(project: ProjectDefinition): boolean { + const buildTarget = project.targets.get('build'); + if (!buildTarget?.options?.polyfills) { + return true; + } + + const polyfills = buildTarget.options.polyfills as string[] | string; + const polyfillsList = Array.isArray(polyfills) ? polyfills : [polyfills]; + + return !polyfillsList.includes('zone.js'); +} diff --git a/tests/legacy-cli/e2e/tests/jest/no-zoneless.ts b/tests/legacy-cli/e2e/tests/jest/no-zoneless.ts index 411378839b7f..9a74a0295c4e 100644 --- a/tests/legacy-cli/e2e/tests/jest/no-zoneless.ts +++ b/tests/legacy-cli/e2e/tests/jest/no-zoneless.ts @@ -1,3 +1,4 @@ +import { replaceInFile } from '../../utils/fs'; import { applyJestBuilder } from '../../utils/jest'; import { installPackage, uninstallPackage } from '../../utils/packages'; import { ng } from '../../utils/process'; @@ -8,8 +9,15 @@ export default async function (): Promise { polyfills: ['zone.js', 'zone.js/testing'], }); + await replaceInFile( + 'src/app/app.spec.ts', + 'await fixture.whenStable();', + 'fixture.detectChanges();', + ); + try { await installPackage('zone.js'); + const { stderr } = await ng('test'); if (!stderr.includes('Jest builder is currently EXPERIMENTAL')) {