Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 %>');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 %>');
});
Expand Down
29 changes: 29 additions & 0 deletions packages/schematics/angular/application/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('<%= classifiedName %>', () => {

fixture = TestBed.createComponent(<%= classifiedName %>);
component = fixture.componentInstance;
fixture.detectChanges();
<%= zoneless ? 'await fixture.whenStable();' : 'fixture.detectChanges();' %>
});

it('should create', () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/schematics/angular/component/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -59,6 +60,7 @@ export default createProjectSchematic<ComponentOptions>((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'), [
Expand All @@ -72,6 +74,7 @@ export default createProjectSchematic<ComponentOptions>((options, { project, tre
...options,
// Add a new variable for the classified name, conditionally including the type
classifiedName,
zoneless,
}),
!options.type
? forEach(((file) => {
Expand Down
34 changes: 34 additions & 0 deletions packages/schematics/angular/component/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()');
});
});
});
12 changes: 12 additions & 0 deletions packages/schematics/angular/utility/project-targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
8 changes: 8 additions & 0 deletions tests/legacy-cli/e2e/tests/jest/no-zoneless.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -8,8 +9,15 @@ export default async function (): Promise<void> {
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')) {
Expand Down