Skip to content

Commit

Permalink
feat(@schematics/angular): generate functional resolvers and guards b…
Browse files Browse the repository at this point in the history
…y default

As Angular v15.2 deprecates class-based resolvers and guards, this commit switches the `functional` flag default value to `true`.

BREAKING CHANGE: `ng g resolver` and `ng g guard` now generate a functional resolver or guard by default. It is still possible to generate a (deprecated) class-based resolver or guard by using `ng g resolver --no-functional` or `ng g guard --no-functional`.
  • Loading branch information
cexbrayat authored and angular-robot[bot] committed Feb 16, 2023
1 parent 2435b46 commit 22fdd7d
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 32 deletions.
36 changes: 20 additions & 16 deletions packages/schematics/angular/guard/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,12 @@ describe('Guard Schematic', () => {
appTree = await schematicRunner.runSchematic('application', appOptions, appTree);
});

it('should create a guard', async () => {
const tree = await schematicRunner.runSchematic('guard', defaultOptions, appTree);
it('should create a (deprecated) class-based guard with --no-functional', async () => {
const tree = await schematicRunner.runSchematic(
'guard',
{ ...defaultOptions, functional: false },
appTree,
);

const files = tree.files;
expect(files).toContain('/projects/bar/src/app/foo.guard.spec.ts');
Expand Down Expand Up @@ -78,7 +82,7 @@ describe('Guard Schematic', () => {
});

it('should respect the implements value', async () => {
const options = { ...defaultOptions, implements: ['CanActivate'] };
const options = { ...defaultOptions, implements: ['CanActivate'], functional: false };
const tree = await schematicRunner.runSchematic('guard', options, appTree);
const fileString = tree.readContent('/projects/bar/src/app/foo.guard.ts');
expect(fileString).toContain('CanActivate');
Expand All @@ -89,8 +93,8 @@ describe('Guard Schematic', () => {
expect(fileString).not.toContain('canMatch');
});

it('should respect the functional guard value', async () => {
const options = { ...defaultOptions, implements: ['CanActivate'], functional: true };
it('should generate a functional guard by default', async () => {
const options = { ...defaultOptions, implements: ['CanActivate'] };
const tree = await schematicRunner.runSchematic('guard', options, appTree);
const fileString = tree.readContent('/projects/bar/src/app/foo.guard.ts');
expect(fileString).toContain('export const fooGuard: CanActivateFn = (route, state) => {');
Expand All @@ -101,7 +105,7 @@ describe('Guard Schematic', () => {
});

it('should generate a helper function to execute the guard in a test', async () => {
const options = { ...defaultOptions, implements: ['CanActivate'], functional: true };
const options = { ...defaultOptions, implements: ['CanActivate'] };
const tree = await schematicRunner.runSchematic('guard', options, appTree);
const fileString = tree.readContent('/projects/bar/src/app/foo.guard.spec.ts');
expect(fileString).toContain('const executeGuard: CanActivateFn = (...guardParameters) => ');
Expand All @@ -111,7 +115,7 @@ describe('Guard Schematic', () => {
});

it('should generate CanDeactivateFn with unknown functional guard', async () => {
const options = { ...defaultOptions, implements: ['CanDeactivate'], functional: true };
const options = { ...defaultOptions, implements: ['CanDeactivate'] };
const tree = await schematicRunner.runSchematic('guard', options, appTree);
const fileString = tree.readContent('/projects/bar/src/app/foo.guard.ts');
expect(fileString).toContain(
Expand All @@ -120,9 +124,9 @@ describe('Guard Schematic', () => {
);
});

it('should respect the implements values', async () => {
it('should respect the implements values in (deprecated) class-based guards', async () => {
const implementationOptions = ['CanActivate', 'CanDeactivate', 'CanActivateChild'];
const options = { ...defaultOptions, implements: implementationOptions };
const options = { ...defaultOptions, implements: implementationOptions, functional: false };
const tree = await schematicRunner.runSchematic('guard', options, appTree);
const fileString = tree.readContent('/projects/bar/src/app/foo.guard.ts');

Expand All @@ -134,19 +138,19 @@ describe('Guard Schematic', () => {
});
});

it('should add correct imports based on CanMatch implementation', async () => {
it('should add correct imports based on CanMatch implementation in (deprecated) class-based guards', async () => {
const implementationOptions = ['CanMatch'];
const options = { ...defaultOptions, implements: implementationOptions };
const options = { ...defaultOptions, implements: implementationOptions, functional: false };
const tree = await schematicRunner.runSchematic('guard', options, appTree);
const fileString = tree.readContent('/projects/bar/src/app/foo.guard.ts');
const expectedImports = `import { CanMatch, Route, UrlSegment, UrlTree } from '@angular/router';`;

expect(fileString).toContain(expectedImports);
});

it('should add correct imports based on CanActivate implementation', async () => {
it('should add correct imports based on CanActivate implementation in (deprecated) class-based guards', async () => {
const implementationOptions = ['CanActivate'];
const options = { ...defaultOptions, implements: implementationOptions };
const options = { ...defaultOptions, implements: implementationOptions, functional: false };
const tree = await schematicRunner.runSchematic('guard', options, appTree);
const fileString = tree.readContent('/projects/bar/src/app/foo.guard.ts');
const expectedImports = `import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree } from '@angular/router';`;
Expand All @@ -155,17 +159,17 @@ describe('Guard Schematic', () => {
});

it('should add correct imports based on canActivate functional guard', async () => {
const options = { ...defaultOptions, implements: ['CanActivate'], functional: true };
const options = { ...defaultOptions, implements: ['CanActivate'] };
const tree = await schematicRunner.runSchematic('guard', options, appTree);
const fileString = tree.readContent('/projects/bar/src/app/foo.guard.ts');
const expectedImports = `import { CanActivateFn } from '@angular/router';`;

expect(fileString).toContain(expectedImports);
});

it('should add correct imports if multiple implementations was selected', async () => {
it('should add correct imports if multiple implementations was selected in (deprecated) class-based guards', async () => {
const implementationOptions = ['CanActivate', 'CanMatch', 'CanActivateChild'];
const options = { ...defaultOptions, implements: implementationOptions };
const options = { ...defaultOptions, implements: implementationOptions, functional: false };
const tree = await schematicRunner.runSchematic('guard', options, appTree);
const fileString = tree.readContent('/projects/bar/src/app/foo.guard.ts');
const expectedImports =
Expand Down
2 changes: 1 addition & 1 deletion packages/schematics/angular/guard/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"functional": {
"type": "boolean",
"description": "Specifies whether to generate a guard as a function.",
"default": false
"default": true
},
"implements": {
"alias": "guardType",
Expand Down
22 changes: 10 additions & 12 deletions packages/schematics/angular/resolver/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,17 @@ describe('resolver Schematic', () => {
appTree = await schematicRunner.runSchematic('application', appOptions, appTree);
});

it('should create a resolver', async () => {
const tree = await schematicRunner.runSchematic('resolver', defaultOptions, appTree);
it('should create a (deprecated) class-based resolver with --no-functional', async () => {
const tree = await schematicRunner.runSchematic(
'resolver',
{ ...defaultOptions, functional: false },
appTree,
);
const files = tree.files;
expect(files).toContain('/projects/bar/src/app/foo.resolver.spec.ts');
expect(files).toContain('/projects/bar/src/app/foo.resolver.ts');
const fileString = tree.readContent('/projects/bar/src/app/foo.resolver.ts');
expect(fileString).toContain('export class FooResolver implements Resolve<boolean>');
});

it('should respect the skipTests flag', async () => {
Expand Down Expand Up @@ -75,23 +81,15 @@ describe('resolver Schematic', () => {
});

it('should create a functional resolver', async () => {
const tree = await schematicRunner.runSchematic(
'resolver',
{ ...defaultOptions, functional: true },
appTree,
);
const tree = await schematicRunner.runSchematic('resolver', defaultOptions, appTree);
const fileString = tree.readContent('/projects/bar/src/app/foo.resolver.ts');
expect(fileString).toContain(
'export const fooResolver: ResolveFn<boolean> = (route, state) => {',
);
});

it('should create a helper function to run a functional resolver in a test', async () => {
const tree = await schematicRunner.runSchematic(
'resolver',
{ ...defaultOptions, functional: true },
appTree,
);
const tree = await schematicRunner.runSchematic('resolver', defaultOptions, appTree);
const fileString = tree.readContent('/projects/bar/src/app/foo.resolver.spec.ts');
expect(fileString).toContain(
'const executeResolver: ResolveFn<boolean> = (...resolverParameters) => ',
Expand Down
2 changes: 1 addition & 1 deletion packages/schematics/angular/resolver/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"functional": {
"type": "boolean",
"description": "Creates the resolver as a `ResolveFn`.",
"default": false
"default": true
},
"path": {
"type": "string",
Expand Down
5 changes: 4 additions & 1 deletion tests/legacy-cli/e2e/tests/generate/guard/guard-basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ export default async function () {
await ng('generate', 'guard', 'test-guard');
await expectFileToExist(guardDir);
await expectFileToExist(join(guardDir, 'test-guard.guard.ts'));
await expectFileToMatch(join(guardDir, 'test-guard.guard.ts'), /implements CanActivate/);
await expectFileToMatch(
join(guardDir, 'test-guard.guard.ts'),
/export const testGuardGuard: CanActivateFn/,
);
await expectFileToExist(join(guardDir, 'test-guard.guard.spec.ts'));
await ng('test', '--watch=false');
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default async function () {
await ng('generate', 'guard', 'match', '--implements=CanMatch');
await expectFileToExist(guardDir);
await expectFileToExist(join(guardDir, 'match.guard.ts'));
await expectFileToMatch(join(guardDir, 'match.guard.ts'), /implements CanMatch/);
await expectFileToMatch(join(guardDir, 'match.guard.ts'), /export const matchGuard: CanMatch/);
await expectFileToExist(join(guardDir, 'match.guard.spec.ts'));
await ng('test', '--watch=false');
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ export default async function () {
// Does not create a sub directory.
const guardDir = join('src', 'app');

// multiple implements are only supported in (deprecated) class-based guards
await ng(
'generate',
'guard',
'multiple',
'--implements=CanActivate',
'--implements=CanDeactivate',
'--no-functional',
);
await expectFileToExist(guardDir);
await expectFileToExist(join(guardDir, 'multiple.guard.ts'));
Expand Down

0 comments on commit 22fdd7d

Please sign in to comment.