Skip to content

Commit

Permalink
fix(material/schematics): import async animations and remove deprecat…
Browse files Browse the repository at this point in the history
…ed function usages (#28481)

* Reworks the `ng add` schematic so it doesn't depend on deprecated functions.
* Imports the async animations instead of the eagerly-loaded ones.
  • Loading branch information
crisbeto committed Jan 26, 2024
1 parent 2578305 commit b6a9ac8
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 272 deletions.
207 changes: 64 additions & 143 deletions src/material/schematics/ng-add/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {normalize, workspaces, logging} from '@angular-devkit/core';
import {Tree} from '@angular-devkit/schematics';
import {SchematicTestRunner} from '@angular-devkit/schematics/testing';
import {
addModuleImportToRootModule,
getProjectFromWorkspace,
getProjectIndexFiles,
getProjectStyleFile,
Expand Down Expand Up @@ -186,151 +185,71 @@ describe('ng-add schematic', () => {
);
});

describe('animations enabled', () => {
it('should add the BrowserAnimationsModule to the project module', async () => {
const tree = await runner.runSchematic('ng-add-setup-project', baseOptions, appTree);
const fileContent = getFileContent(tree, '/projects/material/src/app/app.module.ts');

expect(fileContent)
.withContext('Expected the project app module to import the "BrowserAnimationsModule".')
.toContain('BrowserAnimationsModule');
});

it('should not add BrowserAnimationsModule if NoopAnimationsModule is set up', async () => {
const workspace = await getWorkspace(appTree);
const project = getProjectFromWorkspace(workspace, baseOptions.project);

// Simulate the case where a developer uses `ng-add` on an Angular CLI project which already
// explicitly uses the `NoopAnimationsModule`. It would be wrong to forcibly enable browser
// animations without knowing what other components would be affected. In this case, we
// just print a warning message.
addModuleImportToRootModule(
appTree,
'NoopAnimationsModule',
'@angular/platform-browser/animations',
project,
);

await runner.runSchematic('ng-add-setup-project', baseOptions, appTree);

expect(errorOutput.length).toBe(1);
expect(errorOutput[0]).toMatch(/Could not set up "BrowserAnimationsModule"/);
});

it('should add the provideAnimations to a bootstrapApplication call', async () => {
appTree.delete('/projects/material/src/app/app.module.ts');
appTree.create(
'/projects/material/src/app/app.config.ts',
`
export const appConfig = {
providers: [{ provide: 'foo', useValue: 1 }]
};
`,
);
appTree.overwrite(
'/projects/material/src/main.ts',
`
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig);
`,
);

const tree = await runner.runSchematic('ng-add-setup-project', baseOptions, appTree);
const fileContent = getFileContent(tree, '/projects/material/src/app/app.config.ts');
it('should add provideAnimationsAsync to the project module', async () => {
const tree = await runner.runSchematic('ng-add-setup-project', baseOptions, appTree);
const fileContent = getFileContent(tree, '/projects/material/src/app/app.module.ts');

expect(fileContent).toContain(
`import { provideAnimations } from '@angular/platform-browser/animations';`,
);
expect(fileContent).toContain(`[{ provide: 'foo', useValue: 1 }, provideAnimations()]`);
});
expect(fileContent).toContain('provideAnimationsAsync()');
expect(fileContent).toContain(
`import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';`,
);
});

it('should not add provideAnimations if provideNoopAnimations is set up in a bootstrapApplication call', async () => {
appTree.delete('/projects/material/src/app/app.module.ts');
appTree.create(
'/projects/material/src/app/app.config.ts',
`
import { provideNoopAnimations } from '@angular/platform-browser/animations';
it('should add the provideAnimationsAsync to a bootstrapApplication call', async () => {
appTree.delete('/projects/material/src/app/app.module.ts');
appTree.create(
'/projects/material/src/app/app.config.ts',
`
export const appConfig = {
providers: [{ provide: 'foo', useValue: 1 }]
};
`,
);
appTree.overwrite(
'/projects/material/src/main.ts',
`
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
export const appConfig = {
providers: [{ provide: 'foo', useValue: 1 }, provideNoopAnimations()]
};
bootstrapApplication(AppComponent, appConfig);
`,
);
appTree.overwrite(
'/projects/material/src/main.ts',
`
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig);
`,
);
);

await runner.runSchematic('ng-add-setup-project', baseOptions, appTree);
const tree = await runner.runSchematic('ng-add-setup-project', baseOptions, appTree);
const fileContent = getFileContent(tree, '/projects/material/src/app/app.config.ts');

expect(errorOutput.length).toBe(1);
expect(errorOutput[0]).toMatch(
/Could not add "provideAnimations" because "provideNoopAnimations" is already provided/,
);
});
expect(fileContent).toContain(
`import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';`,
);
expect(fileContent).toContain(`[{ provide: 'foo', useValue: 1 }, provideAnimationsAsync()]`);
});

describe('animations disabled', () => {
it('should add the NoopAnimationsModule to the project module', async () => {
const tree = await runner.runSchematic(
'ng-add-setup-project',
{...baseOptions, animations: 'disabled'},
appTree,
);
const fileContent = getFileContent(tree, '/projects/material/src/app/app.module.ts');

expect(fileContent)
.withContext('Expected the project app module to import the "NoopAnimationsModule".')
.toContain('NoopAnimationsModule');
});

it('should not add NoopAnimationsModule if BrowserAnimationsModule is set up', async () => {
const workspace = await getWorkspace(appTree);
const project = getProjectFromWorkspace(workspace, baseOptions.project);

// Simulate the case where a developer uses `ng-add` on an Angular CLI project which already
// explicitly uses the `BrowserAnimationsModule`. It would be wrong to forcibly change
// to noop animations.
addModuleImportToRootModule(
appTree,
'BrowserAnimationsModule',
'@angular/platform-browser/animations',
project,
);

const fileContent = getFileContent(appTree, '/projects/material/src/app/app.module.ts');
it("should add the provideAnimationAsync('noop') to the project module if animations are disabled", async () => {
const tree = await runner.runSchematic(
'ng-add-setup-project',
{...baseOptions, animations: 'disabled'},
appTree,
);
const fileContent = getFileContent(tree, '/projects/material/src/app/app.module.ts');

expect(fileContent)
.not.withContext(
'Expected the project app module to not import the "NoopAnimationsModule".',
)
.toContain('NoopAnimationsModule');
});
expect(fileContent).toContain(`provideAnimationsAsync('noop')`);
expect(fileContent).toContain(
`import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';`,
);
});

describe('animations excluded', () => {
it('should not add any animations code if animations are excluded', async () => {
const tree = await runner.runSchematic(
'ng-add-setup-project',
{...baseOptions, animations: 'excluded'},
appTree,
);
const fileContent = getFileContent(tree, '/projects/material/src/app/app.module.ts');
it('should not add any animations code if animations are excluded', async () => {
const tree = await runner.runSchematic(
'ng-add-setup-project',
{...baseOptions, animations: 'excluded'},
appTree,
);
const fileContent = getFileContent(tree, '/projects/material/src/app/app.module.ts');

expect(fileContent).not.toContain('NoopAnimationsModule');
expect(fileContent).not.toContain('BrowserAnimationsModule');
expect(fileContent).not.toContain('@angular/platform-browser/animations');
expect(fileContent).not.toContain('@angular/animations');
});
expect(fileContent).not.toContain('provideAnimationsAsync');
expect(fileContent).not.toContain('@angular/platform-browser/animations');
expect(fileContent).not.toContain('@angular/animations');
});

describe('custom project builders', () => {
Expand Down Expand Up @@ -376,7 +295,7 @@ describe('ng-add schematic', () => {
overwriteTargetBuilder(appTree, 'build', 'thirdparty-builder');
await expectAsync(
runner.runSchematic('ng-add-setup-project', baseOptions, appTree),
).toBeRejectedWithError(/not using the default builders.*build/);
).toBeRejected();
});

it('should warn if the "test" target has been changed', async () => {
Expand Down Expand Up @@ -658,13 +577,14 @@ describe('ng-add schematic', () => {
);
});

it('should add the BrowserAnimationsModule to the project module', async () => {
it('should add the provideAnimationsAsync to the project module', async () => {
const tree = await runner.runSchematic('ng-add-setup-project', baseOptions, appTree);
const fileContent = getFileContent(tree, '/projects/material/src/app/app.module.ts');

expect(fileContent)
.withContext('Expected the project app module to import the "BrowserAnimationsModule".')
.toContain('BrowserAnimationsModule');
expect(fileContent).toContain('provideAnimationsAsync()');
expect(fileContent).toContain(
`import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';`,
);
});
});

Expand Down Expand Up @@ -727,13 +647,14 @@ describe('ng-add schematic', () => {
);
});

it('should add the BrowserAnimationsModule to the project module', async () => {
it('should add the provideAnimationsAsync to the project module', async () => {
const tree = await runner.runSchematic('ng-add-setup-project', baseOptions, appTree);
const fileContent = getFileContent(tree, '/projects/material/src/app/app.module.ts');

expect(fileContent)
.withContext('Expected the project app module to import the "BrowserAnimationsModule".')
.toContain('BrowserAnimationsModule');
expect(fileContent).toContain('provideAnimationsAsync()');
expect(fileContent).toContain(
`import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';`,
);
});
});
});
Expand Down

0 comments on commit b6a9ac8

Please sign in to comment.