Skip to content

build: prepare schematics for v8 #14667

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
5 changes: 5 additions & 0 deletions src/cdk/schematics/migration.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
"description": "Updates the Angular CDK to v7",
"factory": "./ng-update/index#updateToV7"
},
"migration-v8": {
"version": "8",
"description": "Updates the Angular CDK to v8",
"factory": "./ng-update/index#updateToV8"
},
"ng-post-update": {
"description": "Prints out results after ng-update.",
"factory": "./ng-update/index#postUpdate",
Expand Down
5 changes: 5 additions & 0 deletions src/cdk/schematics/ng-update/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export function updateToV7(): Rule {
return createUpgradeRule(TargetVersion.V7, tslintUpgradeConfig);
}

/** Entry point for the migration schematics with target of Angular Material 8.0.0 */
export function updateToV8(): Rule {
return createUpgradeRule(TargetVersion.V8, tslintUpgradeConfig);
}

/** Post-update schematic to be called when update is finished. */
export function postUpdate(): Rule {
return () => {
Expand Down
10 changes: 10 additions & 0 deletions src/cdk/schematics/ng-update/target-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,14 @@
export enum TargetVersion {
V6,
V7,
V8,
}

/**
* Returns all versions that are supported by "ng update". The versions are determined
* based on the "TargetVersion" enum.
*/
export function getAllVersionNames(): string[] {
return Object.keys(TargetVersion)
.filter(enumValue => typeof TargetVersion[enumValue] === 'number');
}
16 changes: 16 additions & 0 deletions src/cdk/schematics/ng-update/test-cases/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,18 @@
import {defineJasmineTestCases, findBazelVersionTestCases} from '@angular/cdk/schematics/testing';
import {getAllVersionNames} from '../target-version';

/** Path to the schematic collection that includes the migrations. */
export const migrationCollection = require.resolve('../../migration.json');

describe('CDK upgrade test cases', () => {

const versionNames = getAllVersionNames().map(versionName => versionName.toLowerCase());
const testCasesMap = findBazelVersionTestCases(
'angular_material/src/cdk/schematics/ng-update/test-cases');

// Setup the test cases for each target version. The test cases will be automatically
// detected through Bazel's runfiles manifest.
versionNames.forEach(version => describe(`${version} update`, () => {
defineJasmineTestCases(version, migrationCollection, testCasesMap.get(version));
}));
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import {runTestCases} from '../../../testing';
describe('v6 method call checks', () => {

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

expect(logOutput)
.toMatch(/\[15,.*Found call to "FocusMonitor\.monitor".*renderer.*has been removed/);
Expand Down
48 changes: 0 additions & 48 deletions src/cdk/schematics/ng-update/test-cases/v6-test-cases.spec.ts

This file was deleted.

44 changes: 0 additions & 44 deletions src/cdk/schematics/ng-update/test-cases/v7-test-cases.spec.ts

This file was deleted.

5 changes: 3 additions & 2 deletions src/cdk/schematics/ng-update/update-schematic.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ targets a specific Angular CDK or Angular Material version.
As of right now, we have two migration entry-points that handle the breaking changes for the
given target version:

| Target Version | Description |
|----------------|-------------|
| Target Version | Description |
|----------------|------------------------|
| V6 | Upgrade from any version to v6.0.0 |
| V7 | Upgrade from any version to v7.0.0 |
| V8 | Upgrade from any version to v8.0.0 |

Note that the migrations run _in order_ if multiple versions are transitively targeted. For
example, consider an application which uses Angular Material v5.0.0. In case the developer runs
Expand Down
3 changes: 3 additions & 0 deletions src/cdk/schematics/testing/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ ts_library(
name = "testing",
module_name = "@angular/cdk/schematics/testing",
srcs = glob(["**/*.ts"]),
tsconfig = "tsconfig.json",
deps = [
"@matdeps//@angular-devkit/core",
"@matdeps//@angular-devkit/schematics",
"@matdeps//@schematics/angular",
"@matdeps//@types/node",
"@matdeps//@types/fs-extra",
"@matdeps//@types/jasmine",
"@matdeps//@types/glob",
"@matdeps//fs-extra",
"@matdeps//rxjs",
],
Expand Down
103 changes: 97 additions & 6 deletions src/cdk/schematics/testing/test-case-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,16 @@ import {TempScopedNodeJsSyncHost} from '@angular-devkit/core/node/testing';
import * as virtualFs from '@angular-devkit/core/src/virtual-fs/host';
import {SchematicTestRunner} from '@angular-devkit/schematics/testing';
import {mkdirpSync, readFileSync, writeFileSync, removeSync} from 'fs-extra';
import {dirname, join} from 'path';
import {sync as globSync} from 'glob';
import {dirname, join, basename, relative, sep} from 'path';
import {createTestApp, runPostScheduledTasks} from '../testing';

/** Suffix that indicates whether a given file is a test case input. */
const TEST_CASE_INPUT_SUFFIX = '_input.ts';

/** Suffix that indicates whether a given file is an expected output of a test case. */
const TEST_CASE_OUTPUT_SUFFIX = '_expected_output.ts';

/** Reads the UTF8 content of the specified file. Normalizes the path and ensures that */
export function readFileContent(filePath: string): string {
return readFileSync(filePath, 'utf8');
Expand All @@ -38,10 +45,9 @@ export function createFileSystemTestApp(runner: SchematicTestRunner) {
}

export async function runTestCases(migrationName: string, collectionPath: string,
inputs: {[name: string]: string}) {
inputFiles: string[]) {

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

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

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

mkdirpSync(dirname(tempInputPath));
writeFileSync(tempInputPath, readFileContent(inputs[inputName]));
writeFileSync(tempInputPath, readFileContent(inputFilePath));
});

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

return {tempPath, logOutput, removeTempDir};
}

/**
* Resolves all test cases for specified path using Bazel's runfile manifest. Note that we
* cannot just use "glob" since the test case files are not copied to the Bazel bin directory
* and are just runfiles.
*/
export function findBazelVersionTestCases(basePath: string) {
const testCasesMap = new Map<string, string[]>();
const manifestPath = process.env['RUNFILES_MANIFEST_FILE']!;
const runfilesDir = process.env['RUNFILES'];

// In case we are not on Windows where runfiles are symlinked, we just find all
// test case files by using "glob" and store them in our result map.
if (!manifestPath) {
const runfilesBaseDir = join(runfilesDir, basePath);
const inputFiles = globSync(`**/*${TEST_CASE_INPUT_SUFFIX}`, {cwd: runfilesBaseDir});

inputFiles.forEach(inputFile => {
// The target version of an input file will be determined from the first
// path segment. (e.g. "v6/my_rule_input.ts" will be for "v6")
const targetVersion = inputFile.split(sep)[0];
const resolvedInputPath = join(runfilesBaseDir, inputFile);

testCasesMap.set(targetVersion,
(testCasesMap.get(targetVersion) || []).concat(resolvedInputPath));
});

return testCasesMap;
}

// In case runfiles are not symlinked (e.g. on Windows), we resolve all test case files using
// the Bazel runfiles manifest. Read more about the manifest here: https://git.io/fhIZE
readFileSync(manifestPath, 'utf8').split('\n').forEach(line => {
const [runfilePath, realPath] = line.split(' ');

// In case the mapped runfile starts with the specified base path and ends with "_input.ts",
// we store it in our result map because we assume that this is a test case.
if (runfilePath.startsWith(basePath) && runfilePath.endsWith(TEST_CASE_INPUT_SUFFIX)) {
// The target version of an input file will be determined from the first
// path segment. (e.g. "v6/my_rule_input.ts" will be for "v6")
const targetVersion = relative(basePath, runfilePath).split(sep)[0];
testCasesMap.set(targetVersion, (testCasesMap.get(targetVersion) || []).concat(realPath));
}
});

return testCasesMap;
}

/**
* Sets up the specified test cases using Jasmine by creating the appropriate jasmine
* spec definitions. This should be used within a "describe" jasmine closure.
*/
export function defineJasmineTestCases(versionName: string, collectionFile: string,
inputFiles: string[] | undefined) {
// No test cases for the given version are available. Skip setting up tests for that
// version.
if (!inputFiles) {
return;
}

let testCasesOutputPath: string;
let cleanupTestApp: () => void;

beforeAll(async () => {
const {tempPath, removeTempDir} =
await runTestCases(`migration-${versionName}`, collectionFile, inputFiles);

testCasesOutputPath = join(tempPath, 'projects/cdk-testing/src/test-cases/');
cleanupTestApp = removeTempDir;
});

afterAll(() => cleanupTestApp());

// Iterates through every test case directory and generates a jasmine test block that will
// verify that the update schematics properly updated the test input to the expected output.
inputFiles.forEach(inputFile => {
const inputTestName = basename(inputFile);

it(`should apply update schematics to test case: ${inputTestName}`, () => {
expect(readFileContent(join(testCasesOutputPath, `${inputTestName}.ts`)))
.toBe(readFileContent(inputFile.replace(TEST_CASE_INPUT_SUFFIX, TEST_CASE_OUTPUT_SUFFIX)));
});
});
}
6 changes: 6 additions & 0 deletions src/cdk/schematics/testing/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"compilerOptions": {
"lib": ["es2015"],
"types": ["node", "jasmine", "glob"]
}
}
18 changes: 17 additions & 1 deletion src/lib/schematics/ng-update/test-cases/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,18 @@
/** Path to the schematic collection that includes the Angular Material migrations. */
import {defineJasmineTestCases, findBazelVersionTestCases} from '@angular/cdk/schematics/testing';
import {getAllVersionNames} from '@angular/cdk/schematics';

/** Path to the schematic collection that includes the migrations. */
export const migrationCollection = require.resolve('../../migration.json');

describe('Material upgrade test cases', () => {

const versionNames = getAllVersionNames().map(versionName => versionName.toLowerCase());
const testCasesMap = findBazelVersionTestCases(
'angular_material/src/lib/schematics/ng-update/test-cases');

// Setup the test cases for each target version. The test cases will be automatically
// detected through Bazel's runfiles manifest.
versionNames.forEach(version => describe(`${version} update`, () => {
defineJasmineTestCases(version, migrationCollection, testCasesMap.get(version));
}));
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import {runTestCases} from '@angular/cdk/schematics/testing';
describe('constructor checks', () => {

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

expect(logOutput).toMatch(/\[22.*Found "NativeDateAdapter"/,
'Expected the constructor checks to report if an argument is not assignable.');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import {migrationCollection} from '../index.spec';
describe('v6 import misc checks', () => {

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

expect(logOutput).toMatch(/Found deprecated symbol "SHOW_ANIMATION"/);
expect(logOutput).toMatch(/Found deprecated symbol "HIDE_ANIMATION"/);
Expand Down
Loading