feat(schematics): support for ng add (#13319)

* Introduces support for `ng add @angular/cdk`. Note that this currently just sets up the `package.json` but is necessary because otherwise the CLI will complain about the `ng-add` schematic not being available.
* Upgrades Bazel workspace rules in favor of removing Bazel manual-copying workarounds for the schematics. This also allows us to switch over to fine-grained dependencies in a follow-up (making it consistent with angular/angular).
* Use fine grained dependencies in schematics bazel rules
This commit is contained in:
Paul Gschwendtner
2018-09-27 16:44:49 +02:00
committed by Jeremy Elbourn
parent 4e475f91c7
commit ac3f8c4d1a
27 changed files with 779 additions and 441 deletions
+4 -8
View File
@@ -7,11 +7,8 @@
# To validate changes, use an online parser, eg.
# http://yaml-online-parser.appspot.com/
## IMPORTANT
# If you change the `docker_image` version, also change the `cache_key` suffix and the version of
# `com_github_bazelbuild_buildtools` in the `/WORKSPACE` file.
var_1: &docker_image angular/ngcontainer:0.3.3
var_2: &cache_key v2-ng-mat-{{ .Branch }}-{{ checksum "package-lock.json" }}-0.3.3
var_1: &docker_image angular/ngcontainer:0.6.0
var_2: &cache_key v2-ng-mat-{{ .Branch }}-{{ checksum "package-lock.json" }}-0.6.0
# Define common ENV vars
var_3: &define_env_vars
@@ -48,9 +45,8 @@ jobs:
- restore_cache:
key: *cache_key
- run: bazel run @nodejs//:npm install
# TODO(jelbourn): Update this command to run all tests if the Bazel issues have been fixed.
- run: bazel test src/lib/schematics:unit_tests
# TODO(jelbourn): Update this command to run all tests if the Bazel issues have been fixed.
- run: bazel test src/{cdk,lib}/schematics:unit_tests
- save_cache:
key: *cache_key
+4 -40
View File
@@ -2,49 +2,13 @@ package(default_visibility = ["//visibility:public"])
# TODO(jelbourn): figure out if these workarounds are still needed
# This rule belongs in node_modules/BUILD
# It's here as a workaround for
# https://github.com/bazelbuild/bazel/issues/374#issuecomment-296217940
filegroup(
# TODO: Replace with fine-grained node_modules using `npm_install` / `yarn_install`.
# TODO: See https://github.com/bazelbuild/rules_nodejs/wiki#migrating-to-rules_nodejs-013
alias(
name = "node_modules",
# Performance workaround: list individual files
# Reduces the number of files as inputs to nodejs_binary:
# bazel query "deps(:node_modules)" | wc -l
# This won't scale in the general case.
# TODO(alexeagle): figure out what to do
srcs = glob(["/".join(["node_modules", pkg, "**", ext]) for pkg in [
"@angular",
"@angular-devkit",
"@schematics",
"@types",
"bytebuffer",
"hammerjs",
"jasmine",
"minimist",
"moment",
"parse5",
"protobufjs",
"protractor",
"reflect-metadata",
"rxjs",
"tsickle",
"tslib",
"tslint",
"typescript",
"zone.js",
] for ext in [
"*.js",
"*.json",
"*.d.ts",
]] + [
"node_modules/http-server/**",
# Reference all files of the "@schematics/angular" package because the schematic
# tests depend on the template files of the angular schematics.
"node_modules/@schematics/angular/**",
]),
actual = "@npm//:node_modules",
)
# Glob pattern that matches all Angular testing bundles.
ANGULAR_TESTING = [
"node_modules/@angular/*/bundles/*-testing.umd.js",
+32 -30
View File
@@ -1,44 +1,52 @@
workspace(name = "angular_material")
# Add nodejs rules
# Load NodeJS rules. Note that this is technically not needed because
# `rules_typescript_dependencies()` would also load the NodeJS rules, but we specifically need
# at least v0.14.1 which includes: https://github.com/bazelbuild/rules_nodejs/pull/341
http_archive(
name = "build_bazel_rules_nodejs",
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.10.1.zip",
strip_prefix = "rules_nodejs-0.10.1",
sha256 = "634206524d90dc03c52392fa3f19a16637d2bcf154910436fe1d669a0d9d7b9c",
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.14.1.zip",
strip_prefix = "rules_nodejs-0.14.1",
sha256 = "813eb51733d3632f456f3bb581d940ed64e80dab417595c93bf5ad19079898e2"
)
# NOTE: this rule installs nodejs, npm, and yarn, but does NOT install
# your npm dependencies. You must still run the package manager.
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories")
# Add TypeScript rules
http_archive(
name = "build_bazel_rules_typescript",
url = "https://github.com/bazelbuild/rules_typescript/archive/0.18.0.zip",
strip_prefix = "rules_typescript-0.18.0",
sha256 = "4726e07a2f8d23b5e3af166f3b2a6e8aa75adad94b35ab4d959e8fe875f90272",
)
check_bazel_version("0.15.0")
node_repositories(package_json = ["//:package.json"])
# Fetch transient dependencies of the TypeScript bazel rules.
load("@build_bazel_rules_typescript//:package.bzl", "rules_typescript_dependencies")
rules_typescript_dependencies()
# Add sass rules
http_archive(
name = "io_bazel_rules_sass",
url = "https://github.com/bazelbuild/rules_sass/archive/0.1.0.zip",
strip_prefix = "rules_sass-0.1.0",
sha256 = "b243c4d64f054c174051785862ab079050d90b37a1cef7da93821c6981cb9ad4",
url = "https://github.com/bazelbuild/rules_sass/archive/1.13.4.zip",
strip_prefix = "rules_sass-1.13.4",
sha256 = "5ddde0d3df96978fa537f76e766538c031dee4d29f91a895f4b1345b5e3f9b16",
)
load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
sass_repositories()
# Add TypeScript rules
http_archive(
name = "build_bazel_rules_typescript",
url = "https://github.com/bazelbuild/rules_typescript/archive/0.15.1.zip",
strip_prefix = "rules_typescript-0.15.1",
sha256 = "3792cc20ef13bb1d1d8b1760894c3320f02a87843e3a04fed7e8e454a75328b6",
)
# NOTE: this rule installs nodejs, npm, and yarn, but does NOT install
# your npm dependencies. You must still run the package manager.
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories",
"npm_install")
http_archive(
name = "io_bazel_rules_webtesting",
url = "https://github.com/bazelbuild/rules_webtesting/archive/7ffe970bbf380891754487f66c3d680c087d67f2.zip",
strip_prefix = "rules_webtesting-7ffe970bbf380891754487f66c3d680c087d67f2",
sha256 = "4fb0dca8c9a90547891b7ef486592775a523330fc4555c88cd8f09270055c2ce",
check_bazel_version("0.15.0")
node_repositories()
# Use Bazel managed node modules. See more below:
# https://github.com/bazelbuild/rules_nodejs#bazel-managed-vs-self-managed-dependencies
npm_install(
name = "npm",
package_json = "//:package.json",
package_lock_json = "//:package-lock.json",
)
# Setup TypeScript Bazel workspace
@@ -56,9 +64,3 @@ local_repository(
name = "rxjs",
path = "node_modules/rxjs/src",
)
# This commit matches the version of buildifier in angular/ngcontainer
# If you change this, also check if it matches the version in the angular/ngcontainer
# version in /.circleci/config.yml
BAZEL_BUILDTOOLS_VERSION = "82b21607e00913b16fe1c51bec80232d9d6de31c"
+423 -203
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -12,7 +12,6 @@
"node": ">= 5.4.1"
},
"scripts": {
"postinstall": "ngc -p angular.tsconfig.json",
"build": "gulp :publish:build-releases",
"demo-app": "gulp serve:devapp",
"test": "gulp test",
@@ -54,6 +53,7 @@
"@angular/router": "7.0.0-beta.5",
"@angular/upgrade": "7.0.0-beta.5",
"@bazel/ibazel": "0.3.1",
"@bazel/typescript": "^0.18.0",
"@google-cloud/storage": "^1.1.1",
"@octokit/rest": "^15.9.4",
"@schematics/angular": "^0.9.0-beta.2",
@@ -131,6 +131,7 @@
"ts-node": "^3.0.4",
"tsconfig-paths": "^2.3.0",
"tslint": "^5.11.0",
"tsutils": "^3.0.0",
"typescript": "3.0.3",
"uglify-js": "^2.8.14"
}
+1 -6
View File
@@ -34,12 +34,7 @@
},
"schematics": "./schematics/collection.json",
"ng-update": {
"migrations": "./schematics/migration.json",
"packageGroup": [
"@angular/material",
"@angular/cdk",
"@angular/material-moment-adapter"
]
"migrations": "./schematics/migration.json"
},
"sideEffects": false
}
+26 -3
View File
@@ -6,7 +6,7 @@ load("//:packages.bzl", "VERSION_PLACEHOLDER_REPLACEMENTS")
filegroup(
name = "schematics_assets",
srcs = ["README.md"],
srcs = glob(["**/*.json"]) + ["README.md"],
)
ts_library(
@@ -14,6 +14,19 @@ ts_library(
module_name = "@angular/cdk/schematics",
srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]),
tsconfig = ":tsconfig.json",
deps = [
"@npm//:@schematics/angular",
"@npm//:@angular-devkit/schematics",
# TODO(devversion): Only include jasmine for test sources.
"@npm//:@types/jasmine",
"@npm//:@types/node",
"@npm//:glob",
"@npm//:jasmine",
"@npm//:parse5",
"@npm//:rxjs",
"@npm//:tslint",
"@npm//:typescript"
],
)
# This package is intended to be combined into the main @angular/cdk package as a dep.
@@ -29,13 +42,23 @@ npm_package(
jasmine_node_test(
name = "unit_tests",
srcs = [":schematics_test_sources"],
data = [":schematics_assets"]
data = [":schematics_assets"],
deps = ["@npm//:jasmine"],
)
ts_library(
name = "schematics_test_sources",
srcs = glob(["**/*.spec.ts"]),
deps = [":schematics"],
deps = [
":schematics",
"@npm//:@schematics/angular",
"@npm//:@angular-devkit/schematics",
"@npm//:@types/jasmine",
"@npm//:@types/node",
"@npm//:mock-fs",
"@npm//:tslint",
"@npm//:typescript",
],
tsconfig = ":tsconfig.json",
testonly = True,
)
+8 -1
View File
@@ -1,4 +1,11 @@
{
"$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {}
"schematics": {
"ng-add": {
"description": "Installs the Angular CDK",
"factory": "./ng-add/index",
"schema": "./ng-add/schema.json",
"aliases": ["install"]
}
}
}
+1
View File
@@ -7,4 +7,5 @@
*/
export * from './utils';
export * from './testing';
export * from './ng-update/public-api';
+24
View File
@@ -0,0 +1,24 @@
import {Tree} from '@angular-devkit/schematics';
import {SchematicTestRunner} from '@angular-devkit/schematics/testing';
import {getFileContent} from '@schematics/angular/utility/test';
import {createTestApp} from '../testing';
describe('CDK ng-add', () => {
let runner: SchematicTestRunner;
let appTree: Tree;
beforeEach(() => {
runner = new SchematicTestRunner('schematics', require.resolve('../collection.json'));
appTree = createTestApp(runner);
});
it('should update the package.json', () => {
const tree = runner.runSchematic('ng-add', {}, appTree);
const packageJson = JSON.parse(getFileContent(tree, '/package.json'));
const dependencies = packageJson.dependencies;
expect(dependencies['@angular/cdk']).toBeDefined();
expect(Object.keys(dependencies)).toEqual(Object.keys(dependencies).sort(),
'Expected the modified "dependencies" to be sorted alphabetically.');
});
});
+34
View File
@@ -0,0 +1,34 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Rule, Tree} from '@angular-devkit/schematics';
import {addPackageToPackageJson} from './package-config';
/** Name of the Angular CDK version that is shipped together with the schematics. */
export const cdkVersion = loadPackageVersionGracefully('@angular/cdk');
/**
* Schematic factory entry-point for the `ng-add` schematic. The ng-add schematic will be
* automatically executed if developers run `ng add @angular/cdk`.
*/
export default function(): Rule {
return (host: Tree) => {
// By default, the CLI already installs the package that has been installed through `ng add`.
// We just store the version in the `package.json` in case the package manager didn't.
addPackageToPackageJson(host, '@angular/cdk', `^${cdkVersion}`);
};
}
/** Loads the full version from the given Angular package gracefully. */
function loadPackageVersionGracefully(packageName: string): string | null {
try {
return require(`${packageName}/package.json`).version;
} catch {
return null;
}
}
@@ -0,0 +1,39 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Tree} from '@angular-devkit/schematics';
/**
* Sorts the keys of the given object.
* @returns A new object instance with sorted keys
*/
function sortObjectByKeys(obj: object) {
return Object.keys(obj).sort().reduce((result, key) => (result[key] = obj[key]) && result, {});
}
/** Adds a package to the package.json in the given host tree. */
export function addPackageToPackageJson(host: Tree, pkg: string, version: string): Tree {
if (host.exists('package.json')) {
const sourceText = host.read('package.json')!.toString('utf-8');
const json = JSON.parse(sourceText);
if (!json.dependencies) {
json.dependencies = {};
}
if (!json.dependencies[pkg]) {
json.dependencies[pkg] = version;
json.dependencies = sortObjectByKeys(json.dependencies);
}
host.overwrite('package.json', JSON.stringify(json, null, 2));
}
return host;
}
+16
View File
@@ -0,0 +1,16 @@
{
"$schema": "http://json-schema.org/schema",
"id": "angular-cdk-ng-add",
"title": "Angular CDK ng-add",
"type": "object",
"properties": {
"project": {
"type": "string",
"description": "The name of the project.",
"$default": {
"$source": "projectName"
}
}
},
"required": []
}
+13
View File
@@ -0,0 +1,13 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export interface Schema {
/** Name of the project to target. */
project: string;
}
+10
View File
@@ -0,0 +1,10 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export * from './post-scheduled-tasks';
export * from './test-app';
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {EngineHost, TaskScheduler} from '@angular-devkit/schematics';
import {EngineHost, TaskExecutor, TaskScheduler} from '@angular-devkit/schematics';
import {SchematicTestRunner} from '@angular-devkit/schematics/testing';
import {from as observableFrom, Observable} from 'rxjs';
import {concatMap, filter, last} from 'rxjs/operators';
@@ -20,18 +20,20 @@ import {concatMap, filter, last} from 'rxjs/operators';
* only when all tasks finished executing.
*/
export function runPostScheduledTasks(runner: SchematicTestRunner, taskName: string)
: Observable<void> {
: Observable<any> {
// Workaround until there is a public API to run scheduled tasks in the @angular-devkit.
// See: https://github.com/angular/angular-cli/issues/11739
const host = runner.engine['_host'] as EngineHost<{}, {}>;
const tasks = runner.engine['_taskSchedulers'] as TaskScheduler[];
const createTaskExecutor = (name: string) =>
(host.createTaskExecutor(name) as any) as Observable<TaskExecutor<any>>;
return observableFrom(tasks).pipe(
concatMap(scheduler => scheduler.finalize()),
filter(task => task.configuration.name === taskName),
concatMap(task => {
return host.createTaskExecutor(task.configuration.name)
return createTaskExecutor(task.configuration.name)
.pipe(concatMap(executor => executor(task.configuration.options, task.context)));
}),
// Only emit the last emitted value because there can be multiple tasks with the same name.
+21
View File
@@ -0,0 +1,21 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
/** Create a base app used for testing. */
export function createTestApp(runner: SchematicTestRunner, appOptions = {}): UnitTestTree {
const workspaceTree = runner.runExternalSchematic('@schematics/angular', 'workspace', {
name: 'workspace',
version: '6.0.0',
newProjectRoot: 'projects',
});
return runner.runExternalSchematic('@schematics/angular', 'application',
{...appOptions, name: 'material'}, workspaceTree);
}
+21 -2
View File
@@ -18,7 +18,17 @@ ts_library(
"test-setup/**/*",
"ng-update/test-cases/**/*",
]),
deps = ["//src/cdk/schematics"],
deps = [
"//src/cdk/schematics",
"@npm//:@angular-devkit/schematics",
"@npm//:@schematics/angular",
# TODO(devversion): Only include jasmine for test sources.
"@npm//:@types/jasmine",
"@npm//:@types/node",
"@npm//:parse5",
"@npm//:tslint",
"@npm//:typescript",
],
tsconfig = ":tsconfig.json",
)
@@ -36,12 +46,21 @@ jasmine_node_test(
name = "unit_tests",
srcs = [":schematics_test_sources"],
data = [":schematics_assets", ":schematics_test_cases"],
deps = ["@npm//:jasmine"]
)
ts_library(
name = "schematics_test_sources",
srcs = glob(["**/*.spec.ts", "test-setup/**/*.ts"], exclude=["**/files/**/*"]),
deps = ["//src/cdk/schematics", ":schematics"],
deps = [
":schematics",
"//src/cdk/schematics",
"@npm//:@schematics/angular",
"@npm//:@angular-devkit/schematics",
"@npm//:@types/jasmine",
"@npm//:@types/node",
"@npm//:fs-extra",
],
tsconfig = ":tsconfig.json",
testonly = True,
)
+13 -12
View File
@@ -1,5 +1,5 @@
import {SchematicTestRunner} from '@angular-devkit/schematics/testing';
import {collectionPath, createTestApp} from '../test-setup/test-app';
import {createTestApp} from '@angular/cdk/schematics';
import {getFileContent} from '@schematics/angular/utility/test';
import {Schema} from './schema';
@@ -12,11 +12,11 @@ describe('Material address-form schematic', () => {
};
beforeEach(() => {
runner = new SchematicTestRunner('schematics', collectionPath);
runner = new SchematicTestRunner('schematics', require.resolve('../collection.json'));
});
it('should create address-form files and add them to module', () => {
const tree = runner.runSchematic('address-form', baseOptions, createTestApp());
const tree = runner.runSchematic('address-form', baseOptions, createTestApp(runner));
const files = tree.files;
expect(files).toContain('/projects/material/src/app/foo/foo.component.css');
@@ -30,7 +30,7 @@ describe('Material address-form schematic', () => {
});
it('should add address-form imports to module', () => {
const tree = runner.runSchematic('address-form', baseOptions, createTestApp());
const tree = runner.runSchematic('address-form', baseOptions, createTestApp(runner));
const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts');
expect(moduleContent).toContain('MatInputModule');
@@ -43,13 +43,14 @@ describe('Material address-form schematic', () => {
describe('styleext option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'address-form', {styleext: 'scss', ...baseOptions}, createTestApp());
'address-form', {styleext: 'scss', ...baseOptions}, createTestApp(runner));
expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.scss');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic('address-form', baseOptions, createTestApp({style: 'less'}));
const tree = runner.runSchematic(
'address-form', baseOptions, createTestApp(runner, {style: 'less'}));
expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.less');
});
@@ -58,14 +59,14 @@ describe('Material address-form schematic', () => {
describe('inlineStyle option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'address-form', {inlineStyle: true, ...baseOptions}, createTestApp());
'address-form', {inlineStyle: true, ...baseOptions}, createTestApp(runner));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic(
'address-form', baseOptions, createTestApp({inlineStyle: true}));
'address-form', baseOptions, createTestApp(runner, {inlineStyle: true}));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css');
});
@@ -74,14 +75,14 @@ describe('Material address-form schematic', () => {
describe('inlineTemplate option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'address-form', {inlineTemplate: true, ...baseOptions}, createTestApp());
'address-form', {inlineTemplate: true, ...baseOptions}, createTestApp(runner));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic(
'address-form', baseOptions, createTestApp({inlineTemplate: true}));
'address-form', baseOptions, createTestApp(runner, {inlineTemplate: true}));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html');
});
@@ -90,14 +91,14 @@ describe('Material address-form schematic', () => {
describe('spec option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'address-form', {spec: false, ...baseOptions}, createTestApp());
'address-form', {spec: false, ...baseOptions}, createTestApp(runner));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic(
'address-form', baseOptions, createTestApp({skipTests: true}));
'address-form', baseOptions, createTestApp(runner, {skipTests: true}));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts');
});
+14 -12
View File
@@ -1,5 +1,5 @@
import {SchematicTestRunner} from '@angular-devkit/schematics/testing';
import {collectionPath, createTestApp} from '../test-setup/test-app';
import {createTestApp} from '@angular/cdk/schematics';
import {getFileContent} from '@schematics/angular/utility/test';
import {Schema} from './schema';
@@ -12,11 +12,11 @@ describe('material-dashboard-schematic', () => {
};
beforeEach(() => {
runner = new SchematicTestRunner('schematics', collectionPath);
runner = new SchematicTestRunner('schematics', require.resolve('../collection.json'));
});
it('should create dashboard files and add them to module', () => {
const tree = runner.runSchematic('dashboard', baseOptions, createTestApp());
const tree = runner.runSchematic('dashboard', baseOptions, createTestApp(runner));
const files = tree.files;
expect(files).toContain('/projects/material/src/app/foo/foo.component.css');
@@ -30,7 +30,7 @@ describe('material-dashboard-schematic', () => {
});
it('should add dashboard imports to module', () => {
const tree = runner.runSchematic('dashboard', baseOptions, createTestApp());
const tree = runner.runSchematic('dashboard', baseOptions, createTestApp(runner));
const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts');
expect(moduleContent).toContain('MatGridListModule');
@@ -47,13 +47,14 @@ describe('material-dashboard-schematic', () => {
describe('styleext option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'dashboard', {styleext: 'scss', ...baseOptions}, createTestApp());
'dashboard', {styleext: 'scss', ...baseOptions}, createTestApp(runner));
expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.scss');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic('dashboard', baseOptions, createTestApp({style: 'less'}));
const tree = runner.runSchematic(
'dashboard', baseOptions, createTestApp(runner, {style: 'less'}));
expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.less');
});
@@ -62,14 +63,14 @@ describe('material-dashboard-schematic', () => {
describe('inlineStyle option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'dashboard', {inlineStyle: true, ...baseOptions}, createTestApp());
'dashboard', {inlineStyle: true, ...baseOptions}, createTestApp(runner));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic(
'dashboard', baseOptions, createTestApp({inlineStyle: true}));
'dashboard', baseOptions, createTestApp(runner, {inlineStyle: true}));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css');
});
@@ -78,14 +79,14 @@ describe('material-dashboard-schematic', () => {
describe('inlineTemplate option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'dashboard', {inlineTemplate: true, ...baseOptions}, createTestApp());
'dashboard', {inlineTemplate: true, ...baseOptions}, createTestApp(runner));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic(
'dashboard', baseOptions, createTestApp({inlineTemplate: true}));
'dashboard', baseOptions, createTestApp(runner, {inlineTemplate: true}));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html');
});
@@ -94,13 +95,14 @@ describe('material-dashboard-schematic', () => {
describe('spec option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'dashboard', {spec: false, ...baseOptions}, createTestApp());
'dashboard', {spec: false, ...baseOptions}, createTestApp(runner));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic('dashboard', baseOptions, createTestApp({skipTests: true}));
const tree = runner.runSchematic(
'dashboard', baseOptions, createTestApp(runner, {skipTests: true}));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts');
});
+18 -12
View File
@@ -1,7 +1,7 @@
import {SchematicTestRunner} from '@angular-devkit/schematics/testing';
import {Schema} from './schema';
import {getFileContent} from '@schematics/angular/utility/test';
import {collectionPath, createTestApp} from '../test-setup/test-app';
import {createTestApp} from '@angular/cdk/schematics';
describe('material-nav-schematic', () => {
let runner: SchematicTestRunner;
@@ -12,11 +12,11 @@ describe('material-nav-schematic', () => {
};
beforeEach(() => {
runner = new SchematicTestRunner('schematics', collectionPath);
runner = new SchematicTestRunner('schematics', require.resolve('../collection.json'));
});
it('should create nav files and add them to module', () => {
const tree = runner.runSchematic('nav', baseOptions, createTestApp());
const tree = runner.runSchematic('nav', baseOptions, createTestApp(runner));
const files = tree.files;
expect(files).toContain('/projects/material/src/app/foo/foo.component.css');
@@ -30,7 +30,7 @@ describe('material-nav-schematic', () => {
});
it('should add nav imports to module', () => {
const tree = runner.runSchematic('nav', baseOptions, createTestApp());
const tree = runner.runSchematic('nav', baseOptions, createTestApp(runner));
const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts');
expect(moduleContent).toContain('LayoutModule');
@@ -48,13 +48,15 @@ describe('material-nav-schematic', () => {
describe('styleext option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic('nav', {styleext: 'scss', ...baseOptions}, createTestApp());
const tree = runner.runSchematic(
'nav', {styleext: 'scss', ...baseOptions}, createTestApp(runner));
expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.scss');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic('nav', baseOptions, createTestApp({style: 'less'}));
const tree = runner.runSchematic(
'nav', baseOptions, createTestApp(runner, {style: 'less'}));
expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.less');
});
@@ -63,13 +65,14 @@ describe('material-nav-schematic', () => {
describe('inlineStyle option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'nav', {inlineStyle: true, ...baseOptions}, createTestApp());
'nav', {inlineStyle: true, ...baseOptions}, createTestApp(runner));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic('nav', baseOptions, createTestApp({inlineStyle: true}));
const tree = runner.runSchematic(
'nav', baseOptions, createTestApp(runner, {inlineStyle: true}));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css');
});
@@ -78,13 +81,14 @@ describe('material-nav-schematic', () => {
describe('inlineTemplate option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'nav', {inlineTemplate: true, ...baseOptions}, createTestApp());
'nav', {inlineTemplate: true, ...baseOptions}, createTestApp(runner));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic('nav', baseOptions, createTestApp({inlineTemplate: true}));
const tree = runner.runSchematic(
'nav', baseOptions, createTestApp(runner, {inlineTemplate: true}));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html');
});
@@ -92,13 +96,15 @@ describe('material-nav-schematic', () => {
describe('spec option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic('nav', {spec: false, ...baseOptions}, createTestApp());
const tree = runner.runSchematic(
'nav', {spec: false, ...baseOptions}, createTestApp(runner));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic('nav', baseOptions, createTestApp({skipTests: true}));
const tree = runner.runSchematic(
'nav', baseOptions, createTestApp(runner, {skipTests: true}));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts');
});
+7 -5
View File
@@ -2,13 +2,13 @@ import {normalize} from '@angular-devkit/core';
import {Tree} from '@angular-devkit/schematics';
import {SchematicTestRunner} from '@angular-devkit/schematics/testing';
import {
createTestApp,
getProjectFromWorkspace,
getProjectStyleFile,
getProjectTargetOptions,
} from '@angular/cdk/schematics';
import {getWorkspace, WorkspaceProject} from '@schematics/angular/utility/config';
import {getFileContent} from '@schematics/angular/utility/test';
import {collectionPath, createTestApp} from '../test-setup/test-app';
import {getIndexHtmlPath} from './fonts/project-index-html';
describe('ng-add schematic', () => {
@@ -16,8 +16,8 @@ describe('ng-add schematic', () => {
let appTree: Tree;
beforeEach(() => {
appTree = createTestApp();
runner = new SchematicTestRunner('schematics', collectionPath);
runner = new SchematicTestRunner('schematics', require.resolve('../collection.json'));
appTree = createTestApp(runner);
});
/** Expects the given file to be in the styles of the specified workspace project. */
@@ -63,7 +63,8 @@ describe('ng-add schematic', () => {
});
it('should support adding a custom theme', () => {
appTree = createTestApp({style: 'scss'});
// TODO(devversion): do not re-create test app here.
appTree = createTestApp(runner, {style: 'scss'});
const tree = runner.runSchematic('ng-add-setup-project', {theme: 'custom'}, appTree);
@@ -79,7 +80,8 @@ describe('ng-add schematic', () => {
});
it('should create a custom theme file if no SCSS file could be found', () => {
appTree = createTestApp({style: 'css'});
// TODO(devversion): do not re-create test app here.
appTree = createTestApp(runner, {style: 'css'});
const tree = runner.runSchematic('ng-add-setup-project', {theme: 'custom'}, appTree);
const workspace = getWorkspace(tree);
@@ -2,10 +2,12 @@ import {getSystemPath, normalize} from '@angular-devkit/core';
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 {runPostScheduledTasks} from '../../test-setup/post-scheduled-tasks';
import {readFileSync, writeFileSync, mkdirpSync} from 'fs-extra';
import {join, dirname} from 'path';
import {createTestApp, migrationCollection} from '../../test-setup/test-app';
import {createTestApp, runPostScheduledTasks} from '@angular/cdk/schematics';
/** Path to the schematic collection that includes the Angular Material migrations. */
const migrationCollection = require.resolve('../../migration.json');
/** Module name suffix for data files of the `jasmine_node_test` Bazel rule. */
const bazelModuleSuffix = 'angular_material/src/lib/schematics/ng-update/test-cases';
@@ -31,9 +33,9 @@ export function resolveBazelDataFile(filePath: string) {
* Creates a test app schematic tree that will be copied over to a real filesystem location.
* This is necessary because TSLint is not able to read from the virtual filesystem tree.
*/
export function createFileSystemTestApp() {
export function createFileSystemTestApp(runner: SchematicTestRunner) {
const tempFileSystemHost = new TempScopedNodeJsSyncHost();
const appTree = createTestApp();
const appTree = createTestApp(runner);
const tempPath = getSystemPath(tempFileSystemHost.root);
// Since the TSLint fix task expects all files to be present on the real file system, we
@@ -53,7 +55,7 @@ export async function runTestCases(migrationName: string, inputs: {[name: string
let logOutput = '';
runner.logger.subscribe(entry => logOutput += entry.message);
const {appTree, tempPath} = createFileSystemTestApp();
const {appTree, tempPath} = createFileSystemTestApp(runner);
// 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.
+17 -13
View File
@@ -1,7 +1,7 @@
import {SchematicTestRunner} from '@angular-devkit/schematics/testing';
import {Schema} from './schema';
import {createTestApp} from '@angular/cdk/schematics';
import {getFileContent} from '@schematics/angular/utility/test';
import {collectionPath, createTestApp} from '../test-setup/test-app';
import {Schema} from './schema';
describe('material-table-schematic', () => {
let runner: SchematicTestRunner;
@@ -12,11 +12,11 @@ describe('material-table-schematic', () => {
};
beforeEach(() => {
runner = new SchematicTestRunner('schematics', collectionPath);
runner = new SchematicTestRunner('schematics', require.resolve('../collection.json'));
});
it('should create table files and add them to module', () => {
const tree = runner.runSchematic('table', baseOptions, createTestApp());
const tree = runner.runSchematic('table', baseOptions, createTestApp(runner));
const files = tree.files;
expect(files).toContain('/projects/material/src/app/foo/foo.component.css');
@@ -42,7 +42,7 @@ describe('material-table-schematic', () => {
});
it('should add table imports to module', () => {
const tree = runner.runSchematic('table', baseOptions, createTestApp());
const tree = runner.runSchematic('table', baseOptions, createTestApp(runner));
const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts');
expect(moduleContent).toContain('MatTableModule');
@@ -56,13 +56,14 @@ describe('material-table-schematic', () => {
describe('styleext option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'table', {styleext: 'scss', ...baseOptions}, createTestApp());
'table', {styleext: 'scss', ...baseOptions}, createTestApp(runner));
expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.scss');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic('table', baseOptions, createTestApp({style: 'less'}));
const tree = runner.runSchematic(
'table', baseOptions, createTestApp(runner, {style: 'less'}));
expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.less');
});
@@ -71,13 +72,14 @@ describe('material-table-schematic', () => {
describe('inlineStyle option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'table', {inlineStyle: true, ...baseOptions}, createTestApp());
'table', {inlineStyle: true, ...baseOptions}, createTestApp(runner));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic('table', baseOptions, createTestApp({inlineStyle: true}));
const tree = runner.runSchematic(
'table', baseOptions, createTestApp(runner, {inlineStyle: true}));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css');
});
@@ -86,14 +88,14 @@ describe('material-table-schematic', () => {
describe('inlineTemplate option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'table', {inlineTemplate: true, ...baseOptions}, createTestApp());
'table', {inlineTemplate: true, ...baseOptions}, createTestApp(runner));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic(
'table', baseOptions, createTestApp({inlineTemplate: true}));
'table', baseOptions, createTestApp(runner, {inlineTemplate: true}));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html');
});
@@ -101,13 +103,15 @@ describe('material-table-schematic', () => {
describe('spec option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic('table', {spec: false, ...baseOptions}, createTestApp());
const tree = runner.runSchematic(
'table', {spec: false, ...baseOptions}, createTestApp(runner));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic('table', baseOptions, createTestApp({skipTests: true}));
const tree = runner.runSchematic(
'table', baseOptions, createTestApp(runner, {skipTests: true}));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts');
});
@@ -1,43 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/*
* NOTE: This file will run before the actual tests start inside of Bazel.
*
* It automatically runs before all spec files because the spec files are blocked
* until Jasmine runs the `describe` blocks.
*
* We copy all needed files into the proper Bazel bin output in order to be able to test
* the schematics. Workaround for: https://github.com/bazelbuild/rules_typescript/issues/154
*/
import {sync as globSync} from 'glob';
import {dirname, join} from 'path';
import {copySync} from 'fs-extra';
// Adding the test case files to the data of the `jasmine_node_test` Bazel rule does not mean
// that the files are being copied over to the Bazel bin output. Bazel just patches the NodeJS
// resolve function and maps the module paths to the original file location. Since we want to copy
// the files to the bazel test directory because TSLint and the schematic test runner expect a real
// file system, we need to resolve the original file path through a Bazel mapped file.
const sourceDirectory = dirname(
require.resolve('angular_material/src/lib/schematics/collection.json'));
const bazelBinDir = join(__dirname, '../');
// Copy all schema files to the bazel bin directory.
globSync('**/schema.json', {cwd: sourceDirectory})
.forEach(file => copySync(join(sourceDirectory, file), join(bazelBinDir, file)));
// Copy all template files to the bazel bin directory.
globSync('**/files/**/*', {cwd: sourceDirectory})
.forEach(file => copySync(join(sourceDirectory, file), join(bazelBinDir, file)));
// Copy the collection.json and migration.json file to the bazel bin directory.
globSync('+(collection|migration).json', {cwd: sourceDirectory})
.forEach(file => copySync(join(sourceDirectory, file), join(bazelBinDir, file)));
-30
View File
@@ -1,30 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
import {join} from 'path';
/** Path to the collection file for the Material schematics */
export const collectionPath = join(__dirname, '..', 'collection.json');
/** Path to the migration file for the Material update schematics */
export const migrationCollection = join(__dirname, '..', 'migration.json');
/** Create a base app used for testing. */
export function createTestApp(appOptions = {}): UnitTestTree {
const baseRunner = new SchematicTestRunner('material-schematics', collectionPath);
const workspaceTree = baseRunner.runExternalSchematic('@schematics/angular', 'workspace', {
name: 'workspace',
version: '6.0.0',
newProjectRoot: 'projects',
});
return baseRunner.runExternalSchematic('@schematics/angular', 'application',
{...appOptions, name: 'material'}, workspaceTree);
}
+19 -12
View File
@@ -1,5 +1,5 @@
import {SchematicTestRunner} from '@angular-devkit/schematics/testing';
import {collectionPath, createTestApp} from '../test-setup/test-app';
import {createTestApp} from '@angular/cdk/schematics';
import {getFileContent} from '@schematics/angular/utility/test';
import {Schema} from './schema';
@@ -12,11 +12,11 @@ describe('Material tree schematic', () => {
};
beforeEach(() => {
runner = new SchematicTestRunner('schematics', collectionPath);
runner = new SchematicTestRunner('schematics', require.resolve('../collection.json'));
});
it('should create tree component files and add them to module', () => {
const tree = runner.runSchematic('tree', baseOptions, createTestApp());
const tree = runner.runSchematic('tree', baseOptions, createTestApp(runner));
const files = tree.files;
expect(files).toContain('/projects/material/src/app/foo/foo.component.css');
@@ -30,7 +30,7 @@ describe('Material tree schematic', () => {
});
it('should add tree imports to module', () => {
const tree = runner.runSchematic('tree', baseOptions, createTestApp());
const tree = runner.runSchematic('tree', baseOptions, createTestApp(runner));
const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts');
expect(moduleContent).toContain('MatTreeModule');
@@ -41,13 +41,14 @@ describe('Material tree schematic', () => {
describe('styleext option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'tree', {styleext: 'scss', ...baseOptions}, createTestApp());
'tree', {styleext: 'scss', ...baseOptions}, createTestApp(runner));
expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.scss');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic('tree', baseOptions, createTestApp({style: 'less'}));
const tree = runner.runSchematic(
'tree', baseOptions, createTestApp(runner, {style: 'less'}));
expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.less');
});
@@ -56,13 +57,15 @@ describe('Material tree schematic', () => {
describe('inlineStyle option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'tree', {inlineStyle: true, ...baseOptions}, createTestApp());
'tree', {inlineStyle: true, ...baseOptions}, createTestApp(runner));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic('tree', baseOptions, createTestApp({inlineStyle: true}));
const tree = runner.runSchematic(
'tree', baseOptions, createTestApp(runner, {inlineStyle: true}));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css');
});
});
@@ -70,14 +73,14 @@ describe('Material tree schematic', () => {
describe('inlineTemplate option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic(
'tree', {inlineTemplate: true, ...baseOptions}, createTestApp());
'tree', {inlineTemplate: true, ...baseOptions}, createTestApp(runner));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic(
'tree', baseOptions, createTestApp({inlineTemplate: true}));
'tree', baseOptions, createTestApp(runner, {inlineTemplate: true}));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html');
});
@@ -85,12 +88,16 @@ describe('Material tree schematic', () => {
describe('spec option', () => {
it('should respect the option value', () => {
const tree = runner.runSchematic('tree', {spec: false, ...baseOptions}, createTestApp());
const tree = runner.runSchematic(
'tree', {spec: false, ...baseOptions}, createTestApp(runner));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts');
});
it('should fallback to the @schematics/angular:component option value', () => {
const tree = runner.runSchematic('tree', baseOptions, createTestApp({skipTests: true}));
const tree = runner.runSchematic(
'tree', baseOptions, createTestApp(runner, {skipTests: true}));
expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts');
});
});