Skip to content

Commit 6b29a9f

Browse files
committed
build: prepare schematics for v8
* Prepares the schematics for V8 * Dynamically resolves the test cases so that we don't need to hardcode them for each version
1 parent d22f48c commit 6b29a9f

16 files changed

+168
-206
lines changed

src/cdk/schematics/migration.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
"description": "Updates the Angular CDK to v7",
1212
"factory": "./ng-update/index#updateToV7"
1313
},
14+
"migration-v8": {
15+
"version": "8",
16+
"description": "Updates the Angular CDK to v8",
17+
"factory": "./ng-update/index#updateToV8"
18+
},
1419
"ng-post-update": {
1520
"description": "Prints out results after ng-update.",
1621
"factory": "./ng-update/index#postUpdate",

src/cdk/schematics/ng-update/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ export function updateToV7(): Rule {
3535
return createUpgradeRule(TargetVersion.V7, tslintUpgradeConfig);
3636
}
3737

38+
/** Entry point for the migration schematics with target of Angular Material 8.0.0 */
39+
export function updateToV8(): Rule {
40+
return createUpgradeRule(TargetVersion.V8, tslintUpgradeConfig);
41+
}
42+
3843
/** Post-update schematic to be called when update is finished. */
3944
export function postUpdate(): Rule {
4045
return () => {

src/cdk/schematics/ng-update/target-version.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,14 @@
1010
export enum TargetVersion {
1111
V6,
1212
V7,
13+
V8,
14+
}
15+
16+
/**
17+
* Returns all versions that are supported by "ng update". The versions are determined
18+
* based on the "TargetVersion" enum.
19+
*/
20+
export function getAllVersionNames(): string[] {
21+
return Object.keys(TargetVersion)
22+
.filter(enumValue => typeof TargetVersion[enumValue] === 'number');
1323
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,18 @@
1+
import {defineJasmineTestCases, findBazelVersionTestCases} from '@angular/cdk/schematics/testing';
2+
import {getAllVersionNames} from '../target-version';
3+
14
/** Path to the schematic collection that includes the migrations. */
25
export const migrationCollection = require.resolve('../../migration.json');
6+
7+
describe('CDK upgrade test cases', () => {
8+
9+
const versionNames = getAllVersionNames().map(versionName => versionName.toLowerCase());
10+
const testCasesMap = findBazelVersionTestCases(
11+
'angular_material/src/cdk/schematics/ng-update/test-cases');
12+
13+
// Setup the test cases for each target version. The test cases will be automatically
14+
// detected through Bazel's runfiles manifest.
15+
versionNames.forEach(version => describe(`${version} update`, () => {
16+
defineJasmineTestCases(version, migrationCollection, testCasesMap.get(version));
17+
}));
18+
});

src/cdk/schematics/ng-update/test-cases/misc/method-call-checks.spec.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import {runTestCases} from '../../../testing';
44
describe('v6 method call checks', () => {
55

66
it('should properly report invalid method calls', async () => {
7-
const {logOutput, removeTempDir} = await runTestCases('migration-v6', migrationCollection, {
8-
'method-call-checks': require.resolve('./method-call-checks_input.ts')
9-
});
7+
const {logOutput, removeTempDir} = await runTestCases('migration-v6', migrationCollection,
8+
[require.resolve('./method-call-checks_input.ts')]);
109

1110
expect(logOutput)
1211
.toMatch(/\[15,.*Found call to "FocusMonitor\.monitor".*renderer.*has been removed/);

src/cdk/schematics/ng-update/test-cases/v6-test-cases.spec.ts

Lines changed: 0 additions & 48 deletions
This file was deleted.

src/cdk/schematics/ng-update/test-cases/v7-test-cases.spec.ts

Lines changed: 0 additions & 44 deletions
This file was deleted.

src/cdk/schematics/ng-update/update-schematic.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ targets a specific Angular CDK or Angular Material version.
1414
As of right now, we have two migration entry-points that handle the breaking changes for the
1515
given target version:
1616

17-
| Target Version | Description |
18-
|----------------|-------------|
17+
| Target Version | Description |
18+
|----------------|------------------------|
1919
| V6 | Upgrade from any version to v6.0.0 |
2020
| V7 | Upgrade from any version to v7.0.0 |
21+
| V8 | Upgrade from any version to v8.0.0 |
2122

2223
Note that the migrations run _in order_ if multiple versions are transitively targeted. For
2324
example, consider an application which uses Angular Material v5.0.0. In case the developer runs

src/cdk/schematics/testing/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@ ts_library(
66
name = "testing",
77
module_name = "@angular/cdk/schematics/testing",
88
srcs = glob(["**/*.ts"]),
9+
tsconfig = "tsconfig.json",
910
deps = [
1011
"@matdeps//@angular-devkit/core",
1112
"@matdeps//@angular-devkit/schematics",
1213
"@matdeps//@schematics/angular",
1314
"@matdeps//@types/node",
1415
"@matdeps//@types/fs-extra",
16+
"@matdeps//@types/jasmine",
17+
"@matdeps//@types/glob",
1518
"@matdeps//fs-extra",
1619
"@matdeps//rxjs",
1720
],

src/cdk/schematics/testing/test-case-setup.ts

Lines changed: 97 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,16 @@ import {TempScopedNodeJsSyncHost} from '@angular-devkit/core/node/testing';
1111
import * as virtualFs from '@angular-devkit/core/src/virtual-fs/host';
1212
import {SchematicTestRunner} from '@angular-devkit/schematics/testing';
1313
import {mkdirpSync, readFileSync, writeFileSync, removeSync} from 'fs-extra';
14-
import {dirname, join} from 'path';
14+
import {sync as globSync} from 'glob';
15+
import {dirname, join, basename, relative, sep} from 'path';
1516
import {createTestApp, runPostScheduledTasks} from '../testing';
1617

18+
/** Suffix that indicates whether a given file is a test case input. */
19+
const TEST_CASE_INPUT_SUFFIX = '_input.ts';
20+
21+
/** Suffix that indicates whether a given file is an expected output of a test case. */
22+
const TEST_CASE_OUTPUT_SUFFIX = '_expected_output.ts';
23+
1724
/** Reads the UTF8 content of the specified file. Normalizes the path and ensures that */
1825
export function readFileContent(filePath: string): string {
1926
return readFileSync(filePath, 'utf8');
@@ -38,10 +45,9 @@ export function createFileSystemTestApp(runner: SchematicTestRunner) {
3845
}
3946

4047
export async function runTestCases(migrationName: string, collectionPath: string,
41-
inputs: {[name: string]: string}) {
48+
inputFiles: string[]) {
4249

4350
const runner = new SchematicTestRunner('schematics', collectionPath);
44-
const inputNames = Object.keys(inputs);
4551
const initialWorkingDir = process.cwd();
4652

4753
let logOutput = '';
@@ -51,11 +57,12 @@ export async function runTestCases(migrationName: string, collectionPath: string
5157

5258
// Write each test-case input to the file-system. This is necessary because otherwise
5359
// TSLint won't be able to pick up the test cases.
54-
inputNames.forEach(inputName => {
55-
const tempInputPath = join(tempPath, `projects/cdk-testing/src/test-cases/${inputName}.ts`);
60+
inputFiles.forEach(inputFilePath => {
61+
const inputTestName = basename(inputFilePath);
62+
const tempInputPath = join(tempPath, `projects/cdk-testing/src/test-cases/${inputTestName}.ts`);
5663

5764
mkdirpSync(dirname(tempInputPath));
58-
writeFileSync(tempInputPath, readFileContent(inputs[inputName]));
65+
writeFileSync(tempInputPath, readFileContent(inputFilePath));
5966
});
6067

6168
runner.runSchematic(migrationName, {}, appTree);
@@ -73,3 +80,87 @@ export async function runTestCases(migrationName: string, collectionPath: string
7380

7481
return {tempPath, logOutput, removeTempDir};
7582
}
83+
84+
/**
85+
* Resolves all test cases for specified path using Bazel's runfile manifest. Note that we
86+
* cannot just use "glob" since the test case files are not copied to the Bazel bin directory
87+
* and are just runfiles.
88+
*/
89+
export function findBazelVersionTestCases(basePath: string) {
90+
const testCasesMap = new Map<string, string[]>();
91+
const manifestPath = process.env['RUNFILES_MANIFEST_FILE']!;
92+
const runfilesDir = process.env['RUNFILES'];
93+
94+
// In case we are not on Windows where runfiles are symlinked, we just find all
95+
// test case files by using "glob" and store them in our result map.
96+
if (!manifestPath) {
97+
const runfilesBaseDir = join(runfilesDir, basePath);
98+
const inputFiles = globSync(`**/*${TEST_CASE_INPUT_SUFFIX}`, {cwd: runfilesBaseDir});
99+
100+
inputFiles.forEach(inputFile => {
101+
// The target version of an input file will be determined from the first
102+
// path segment. (e.g. "v6/my_rule_input.ts" will be for "v6")
103+
const targetVersion = inputFile.split(sep)[0];
104+
const resolvedInputPath = join(runfilesBaseDir, inputFile);
105+
106+
testCasesMap.set(targetVersion,
107+
(testCasesMap.get(targetVersion) || []).concat(resolvedInputPath));
108+
});
109+
110+
return testCasesMap;
111+
}
112+
113+
// In case runfiles are not symlinked (e.g. on Windows), we resolve all test case files using
114+
// the Bazel runfiles manifest. Read more about the manifest here: https://git.io/fhIZE
115+
readFileSync(manifestPath, 'utf8').split('\n').forEach(line => {
116+
const [runfilePath, realPath] = line.split(' ');
117+
118+
// In case the mapped runfile starts with the specified base path and ends with "_input.ts",
119+
// we store it in our result map because we assume that this is a test case.
120+
if (runfilePath.startsWith(basePath) && runfilePath.endsWith(TEST_CASE_INPUT_SUFFIX)) {
121+
// The target version of an input file will be determined from the first
122+
// path segment. (e.g. "v6/my_rule_input.ts" will be for "v6")
123+
const targetVersion = relative(basePath, runfilePath).split(sep)[0];
124+
testCasesMap.set(targetVersion, (testCasesMap.get(targetVersion) || []).concat(realPath));
125+
}
126+
});
127+
128+
return testCasesMap;
129+
}
130+
131+
/**
132+
* Sets up the specified test cases using Jasmine by creating the appropriate jasmine
133+
* spec definitions. This should be used within a "describe" jasmine closure.
134+
*/
135+
export function defineJasmineTestCases(versionName: string, collectionFile: string,
136+
inputFiles: string[] | undefined) {
137+
// No test cases for the given version are available. Skip setting up tests for that
138+
// version.
139+
if (!inputFiles) {
140+
return;
141+
}
142+
143+
let testCasesOutputPath: string;
144+
let cleanupTestApp: () => void;
145+
146+
beforeAll(async () => {
147+
const {tempPath, removeTempDir} =
148+
await runTestCases(`migration-${versionName}`, collectionFile, inputFiles);
149+
150+
testCasesOutputPath = join(tempPath, 'projects/cdk-testing/src/test-cases/');
151+
cleanupTestApp = removeTempDir;
152+
});
153+
154+
afterAll(() => cleanupTestApp());
155+
156+
// Iterates through every test case directory and generates a jasmine test block that will
157+
// verify that the update schematics properly updated the test input to the expected output.
158+
inputFiles.forEach(inputFile => {
159+
const inputTestName = basename(inputFile);
160+
161+
it(`should apply update schematics to test case: ${inputTestName}`, () => {
162+
expect(readFileContent(join(testCasesOutputPath, `${inputTestName}.ts`)))
163+
.toBe(readFileContent(inputFile.replace(TEST_CASE_INPUT_SUFFIX, TEST_CASE_OUTPUT_SUFFIX)));
164+
});
165+
});
166+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"compilerOptions": {
3+
"lib": ["es2015"],
4+
"types": ["node", "jasmine", "glob"]
5+
}
6+
}
Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,18 @@
1-
/** Path to the schematic collection that includes the Angular Material migrations. */
1+
import {defineJasmineTestCases, findBazelVersionTestCases} from '@angular/cdk/schematics/testing';
2+
import {getAllVersionNames} from '@angular/cdk/schematics';
3+
4+
/** Path to the schematic collection that includes the migrations. */
25
export const migrationCollection = require.resolve('../../migration.json');
6+
7+
describe('Material upgrade test cases', () => {
8+
9+
const versionNames = getAllVersionNames().map(versionName => versionName.toLowerCase());
10+
const testCasesMap = findBazelVersionTestCases(
11+
'angular_material/src/lib/schematics/ng-update/test-cases');
12+
13+
// Setup the test cases for each target version. The test cases will be automatically
14+
// detected through Bazel's runfiles manifest.
15+
versionNames.forEach(version => describe(`${version} update`, () => {
16+
defineJasmineTestCases(version, migrationCollection, testCasesMap.get(version));
17+
}));
18+
});

src/lib/schematics/ng-update/test-cases/misc/constructor-checks.spec.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import {runTestCases} from '@angular/cdk/schematics/testing';
44
describe('constructor checks', () => {
55

66
it('should properly report invalid constructor expression signatures', async () => {
7-
const {logOutput, removeTempDir} = await runTestCases('migration-v6', migrationCollection, {
8-
'constructor-checks': require.resolve('./constructor-checks_input.ts')
9-
});
7+
const {logOutput, removeTempDir} = await runTestCases('migration-v6', migrationCollection,
8+
[require.resolve('./constructor-checks_input.ts')]);
109

1110
expect(logOutput).toMatch(/\[22.*Found "NativeDateAdapter"/,
1211
'Expected the constructor checks to report if an argument is not assignable.');

src/lib/schematics/ng-update/test-cases/misc/import-checks.spec.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import {migrationCollection} from '../index.spec';
44
describe('v6 import misc checks', () => {
55

66
it('should report imports for deleted animation constants', async () => {
7-
const {logOutput, removeTempDir} = await runTestCases('migration-v6', migrationCollection, {
8-
'import-checks': require.resolve('./import-checks_input.ts')
9-
});
7+
const {logOutput, removeTempDir} = await runTestCases('migration-v6', migrationCollection,
8+
[require.resolve('./import-checks_input.ts')]);
109

1110
expect(logOutput).toMatch(/Found deprecated symbol "SHOW_ANIMATION"/);
1211
expect(logOutput).toMatch(/Found deprecated symbol "HIDE_ANIMATION"/);

0 commit comments

Comments
 (0)