chore(ng-schematics): refactor away user-side complexity (#10750)

This commit is contained in:
Nikolay Vitkov 2023-08-18 11:09:26 +02:00 committed by GitHub
parent de719dbb86
commit 35dc2d8840
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 294 additions and 309 deletions

View File

@ -14,7 +14,7 @@ ng add @puppeteer/ng-schematics
Or you can use the same command followed by the [options](#options) below. Or you can use the same command followed by the [options](#options) below.
Currently, this schematic supports the following test frameworks: Currently, this schematic supports the following test runners:
- [**Jasmine**](https://jasmine.github.io/) - [**Jasmine**](https://jasmine.github.io/)
- [**Jest**](https://jestjs.io/) - [**Jest**](https://jestjs.io/)
@ -32,11 +32,8 @@ ng e2e
When adding schematics to your project you can to provide following options: When adding schematics to your project you can to provide following options:
| Option | Description | Value | Required | | Option | Description | Value | Required |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | -------- | | -------------- | ------------------------------------------------------ | ------------------------------------------ | -------- |
| `--isDefaultTester` | When true, replaces default `ng e2e` command. | `boolean` | `true` | | `--testRunner` | The testing framework to install along side Puppeteer. | `"jasmine"`, `"jest"`, `"mocha"`, `"node"` | `true` |
| `--exportConfig` | When true, creates an empty [Puppeteer configuration](https://pptr.dev/guides/configuration) file. (`.puppeteerrc.cjs`) | `boolean` | `true` |
| `--testingFramework` | The testing framework to install along side Puppeteer. | `"jasmine"`, `"jest"`, `"mocha"`, `"node"` | `true` |
| `--port` | The port to spawn server for E2E. If default is used `ng serve` and `ng e2e` will not run side-by-side. | `number` | `4200` |
## Creating a single test file ## Creating a single test file
@ -59,7 +56,7 @@ Update either `e2e` or `puppeteer` (depending on the initial setup) to:
"options": { "options": {
"commands": [...], "commands": [...],
"devServerTarget": "sandbox:serve", "devServerTarget": "sandbox:serve",
"testingFramework": "<TestingFramework>", "testRunner": "<TestRunner>",
"port": 8080 "port": 8080
}, },
... ...

View File

@ -14,7 +14,7 @@ ng add @puppeteer/ng-schematics
Or you can use the same command followed by the [options](#options) below. Or you can use the same command followed by the [options](#options) below.
Currently, this schematic supports the following test frameworks: Currently, this schematic supports the following test runners:
- [**Jasmine**](https://jasmine.github.io/) - [**Jasmine**](https://jasmine.github.io/)
- [**Jest**](https://jestjs.io/) - [**Jest**](https://jestjs.io/)
@ -32,11 +32,8 @@ ng e2e
When adding schematics to your project you can to provide following options: When adding schematics to your project you can to provide following options:
| Option | Description | Value | Required | | Option | Description | Value | Required |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | -------- | | -------------- | ------------------------------------------------------ | ------------------------------------------ | -------- |
| `--isDefaultTester` | When true, replaces default `ng e2e` command. | `boolean` | `true` | | `--testRunner` | The testing framework to install along side Puppeteer. | `"jasmine"`, `"jest"`, `"mocha"`, `"node"` | `true` |
| `--exportConfig` | When true, creates an empty [Puppeteer configuration](https://pptr.dev/guides/configuration) file. (`.puppeteerrc.cjs`) | `boolean` | `true` |
| `--testingFramework` | The testing framework to install along side Puppeteer. | `"jasmine"`, `"jest"`, `"mocha"`, `"node"` | `true` |
| `--port` | The port to spawn server for E2E. If default is used `ng serve` and `ng e2e` will not run side-by-side. | `number` | `4200` |
## Creating a single test file ## Creating a single test file
@ -59,7 +56,7 @@ Update either `e2e` or `puppeteer` (depending on the initial setup) to:
"options": { "options": {
"commands": [...], "commands": [...],
"devServerTarget": "sandbox:serve", "devServerTarget": "sandbox:serve",
"testingFramework": "<TestingFramework>", "testRunner": "<TestRunner>",
"port": 8080 "port": 8080
}, },
... ...

View File

@ -9,6 +9,8 @@ import {
} from '@angular-devkit/architect'; } from '@angular-devkit/architect';
import {JsonObject} from '@angular-devkit/core'; import {JsonObject} from '@angular-devkit/core';
import {TestRunner} from '../../schematics/utils/types.js';
import {PuppeteerBuilderOptions} from './types.js'; import {PuppeteerBuilderOptions} from './types.js';
const terminalStyles = { const terminalStyles = {
@ -39,13 +41,35 @@ function getExecutable(command: string[]) {
}; };
} }
function updateExecutablePath(command: string, root?: string) {
let path = 'node_modules/.bin/';
if (root && root !== '' && command === TestRunner.Node) {
const nested = root
.split('/')
.map(() => {
return '../';
})
.join('');
path = `${nested}${path}${command}`;
} else {
path = `./${path}${command}`;
}
return path;
}
async function executeCommand(context: BuilderContext, command: string[]) { async function executeCommand(context: BuilderContext, command: string[]) {
let project: JsonObject;
if (context.target) {
project = await context.getProjectMetadata(context.target.project);
command[0] = updateExecutablePath(command[0]!, String(project['root']));
}
await new Promise(async (resolve, reject) => { await new Promise(async (resolve, reject) => {
context.logger.debug(`Trying to execute command - ${command.join(' ')}.`); context.logger.debug(`Trying to execute command - ${command.join(' ')}.`);
const {executable, args, error} = getExecutable(command); const {executable, args, error} = getExecutable(command);
let path = context.workspaceRoot; let path = context.workspaceRoot;
if (context.target) { if (context.target) {
const project = await context.getProjectMetadata(context.target.project);
path = `${path}/${project['root']}`; path = `${path}/${project['root']}`;
} }
@ -123,8 +147,13 @@ async function executeE2ETest(
try { try {
server = await startServer(options, context); server = await startServer(options, context);
const commands = options.commands;
message('\n Building tests 🛠️ ... \n', context);
await executeCommand(context, [`tsc`, '-p', 'e2e/tsconfig.json']);
message('\n Running tests 🧪 ... \n', context); message('\n Running tests 🧪 ... \n', context);
for (const command of options.commands) { for (const command of commands) {
await executeCommand(context, command); await executeCommand(context, command);
} }

View File

@ -6,10 +6,15 @@
"factory": "./ng-add/index#ngAdd", "factory": "./ng-add/index#ngAdd",
"schema": "./ng-add/schema.json" "schema": "./ng-add/schema.json"
}, },
"test": { "e2e": {
"description": "Create a single test file", "description": "Create a single test file",
"factory": "./test/index#test", "factory": "./e2e/index#e2e",
"schema": "./test/schema.json" "schema": "./e2e/schema.json"
},
"config": {
"description": "Eject Puppeteer config file",
"factory": "./config/index#config",
"schema": "./config/schema.json"
} }
} }
} }

View File

@ -1,4 +1,4 @@
/** /**
* @type {import("puppeteer").Configuration} * @type {import("puppeteer").Configuration}
*/ */
module.exports = {}; export {};

View File

@ -0,0 +1,44 @@
/**
* Copyright 2022 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {chain, Rule, SchematicContext, Tree} from '@angular-devkit/schematics';
import {addFilesSingle} from '../utils/files.js';
import {TestRunner, AngularProject} from '../utils/types.js';
// You don't have to export the function as default. You can also have more than one rule
// factory per file.
export function config(): Rule {
return (tree: Tree, context: SchematicContext) => {
return chain([addPuppeteerConfig()])(tree, context);
};
}
function addPuppeteerConfig(): Rule {
return (tree: Tree, context: SchematicContext) => {
context.logger.debug('Adding Puppeteer config file.');
return addFilesSingle(tree, context, '', {root: ''} as AngularProject, {
// No-op here to fill types
options: {
testRunner: TestRunner.Jasmine,
port: 4200,
},
applyPath: './files',
relativeToWorkspacePath: `/`,
});
};
}

View File

@ -0,0 +1,8 @@
{
"$schema": "http://json-schema.org/schema",
"$id": "Puppeteer",
"title": "Puppeteer Config Schema",
"type": "object",
"properties": {},
"required": []
}

View File

@ -1,13 +1,17 @@
<% if(testingFramework == 'node') { %> <% if(testRunner == 'node') { %>
import * as assert from 'assert'; import * as assert from 'assert';
import {describe, it} from 'node:test'; import {describe, it} from 'node:test';
<% } %><% if(testingFramework == 'mocha') { %> <% } %><% if(testRunner == 'mocha') { %>
import * as assert from 'assert'; import * as assert from 'assert';
<% } %> <% } %>
import {setupBrowserHooks, getBrowserState} from './utils'; import {setupBrowserHooks, getBrowserState} from './utils';
describe('<%= classify(name) %>', function () { describe('<%= classify(name) %>', function () {
<% if(route) { %>
setupBrowserHooks('<%= route %>');
<% } else { %>
setupBrowserHooks(); setupBrowserHooks();
<% } %>
it('', async function () { it('', async function () {
const {page} = getBrowserState(); const {page} = getBrowserState();
}); });

View File

@ -25,19 +25,19 @@ import {
import {addCommonFiles} from '../utils/files.js'; import {addCommonFiles} from '../utils/files.js';
import {getAngularConfig} from '../utils/json.js'; import {getAngularConfig} from '../utils/json.js';
import { import {
TestingFramework, TestRunner,
SchematicsSpec, SchematicsSpec,
SchematicsOptions,
AngularProject, AngularProject,
PuppeteerSchematicsConfig,
} from '../utils/types.js'; } from '../utils/types.js';
// You don't have to export the function as default. You can also have more than one rule // You don't have to export the function as default. You can also have more than one rule
// factory per file. // factory per file.
export function test(userArgs: Record<string, string>): Rule { export function e2e(userArgs: Record<string, string>): Rule {
const options = parseUserTestArgs(userArgs); const options = parseUserTestArgs(userArgs);
return (tree: Tree, context: SchematicContext) => { return (tree: Tree, context: SchematicContext) => {
return chain([addSpecFile(options)])(tree, context); return chain([addE2EFile(options)])(tree, context);
}; };
} }
@ -51,14 +51,19 @@ function parseUserTestArgs(userArgs: Record<string, string>): SchematicsSpec {
if ('n' in userArgs) { if ('n' in userArgs) {
options['name'] = userArgs['n']; options['name'] = userArgs['n'];
} }
if ('r' in userArgs) {
options['route'] = userArgs['n'];
}
return options as SchematicsSpec; return options as SchematicsSpec;
} }
function findTestingOption<Property extends keyof SchematicsOptions>( function findTestingOption<
Property extends keyof PuppeteerSchematicsConfig['options'],
>(
[name, project]: [string, AngularProject | undefined], [name, project]: [string, AngularProject | undefined],
property: Property property: Property
): SchematicsOptions[Property] { ): PuppeteerSchematicsConfig['options'][Property] {
if (!project) { if (!project) {
throw new Error(`Project "${name}" not found.`); throw new Error(`Project "${name}" not found.`);
} }
@ -76,7 +81,7 @@ function findTestingOption<Property extends keyof SchematicsOptions>(
throw new Error(`Can't find property "${property}" for project "${name}".`); throw new Error(`Can't find property "${property}" for project "${name}".`);
} }
function addSpecFile(options: SchematicsSpec): Rule { function addE2EFile(options: SchematicsSpec): Rule {
return async (tree: Tree, context: SchematicContext) => { return async (tree: Tree, context: SchematicContext) => {
context.logger.debug('Adding Spec file.'); context.logger.debug('Adding Spec file.');
@ -96,10 +101,7 @@ function addSpecFile(options: SchematicsSpec): Rule {
); );
} }
const testingFramework = findTestingOption( const testRunner = findTestingOption(foundProject, 'testRunner');
foundProject,
'testingFramework'
);
const port = findTestingOption(foundProject, 'port'); const port = findTestingOption(foundProject, 'port');
context.logger.debug('Creating Spec file.'); context.logger.debug('Creating Spec file.');
@ -111,10 +113,11 @@ function addSpecFile(options: SchematicsSpec): Rule {
{ {
options: { options: {
name: options.name, name: options.name,
testingFramework, route: options.route,
testRunner,
// Node test runner does not support glob patterns // Node test runner does not support glob patterns
// It looks for files `*.test.js` // It looks for files `*.test.js`
ext: testingFramework === TestingFramework.Node ? 'test' : 'e2e', ext: testRunner === TestRunner.Node ? 'test' : 'e2e',
port, port,
}, },
} }

View File

@ -1,7 +1,7 @@
{ {
"$schema": "http://json-schema.org/schema", "$schema": "http://json-schema.org/schema",
"$id": "Puppeteer", "$id": "Puppeteer",
"title": "Puppeteer Spec Schema", "title": "Puppeteer E2E Schema",
"type": "object", "type": "object",
"properties": { "properties": {
"name": { "name": {
@ -20,6 +20,14 @@
"index": 1 "index": 1
}, },
"alias": "p" "alias": "p"
},
"route": {
"type": "string",
"$default": {
"$source": "argv",
"index": 1
},
"alias": "r"
} }
}, },
"required": [] "required": []

View File

@ -1,7 +1,7 @@
<% if(testingFramework == 'node') { %> <% if(testRunner == 'node') { %>
import * as assert from 'assert'; import * as assert from 'assert';
import {describe, it} from 'node:test'; import {describe, it} from 'node:test';
<% } %><% if(testingFramework == 'mocha') { %> <% } %><% if(testRunner == 'mocha') { %>
import * as assert from 'assert'; import * as assert from 'assert';
<% } %> <% } %>
import {setupBrowserHooks, getBrowserState} from './utils'; import {setupBrowserHooks, getBrowserState} from './utils';
@ -12,9 +12,9 @@ describe('App test', function () {
const {page} = getBrowserState(); const {page} = getBrowserState();
const element = await page.waitForSelector('text/<%= project %> app is running!'); const element = await page.waitForSelector('text/<%= project %> app is running!');
<% if(testingFramework == 'jasmine' || testingFramework == 'jest') { %> <% if(testRunner == 'jasmine' || testRunner == 'jest') { %>
expect(element).not.toBeNull(); expect(element).not.toBeNull();
<% } %><% if(testingFramework == 'mocha' || testingFramework == 'node') { %> <% } %><% if(testRunner == 'mocha' || testRunner == 'node') { %>
assert.ok(element); assert.ok(element);
<% } %> <% } %>
}); });

View File

@ -1,4 +1,4 @@
<% if(testingFramework == 'node') { %> <% if(testRunner == 'node') { %>
import {before, beforeEach, after, afterEach} from 'node:test'; import {before, beforeEach, after, afterEach} from 'node:test';
<% } %> <% } %>
import * as puppeteer from 'puppeteer'; import * as puppeteer from 'puppeteer';
@ -7,33 +7,35 @@ const baseUrl = '<%= baseUrl %>';
let browser: puppeteer.Browser; let browser: puppeteer.Browser;
let page: puppeteer.Page; let page: puppeteer.Page;
export function setupBrowserHooks(): void { export function setupBrowserHooks(path = '/'): void {
<% if(testingFramework == 'jasmine' || testingFramework == 'jest') { %> <% if(testRunner == 'jasmine' || testRunner == 'jest') { %>
beforeAll(async () => { beforeAll(async () => {
browser = await puppeteer.launch({ browser = await puppeteer.launch({
headless: 'new' headless: 'new'
}); });
}); });
<% } %><% if(testingFramework == 'mocha' || testingFramework == 'node') { %> <% } %><% if(testRunner == 'mocha' || testRunner == 'node') { %>
before(async () => { before(async () => {
browser = await puppeteer.launch(); browser = await puppeteer.launch({
headless: 'new'
});
}); });
<% } %> <% } %>
beforeEach(async () => { beforeEach(async () => {
page = await browser.newPage(); page = await browser.newPage();
await page.goto(baseUrl); await page.goto(`${baseUrl}${path}`);
}); });
afterEach(async () => { afterEach(async () => {
await page.close(); await page.close();
}); });
<% if(testingFramework == 'jasmine' || testingFramework == 'jest') { %> <% if(testRunner == 'jasmine' || testRunner == 'jest') { %>
afterAll(async () => { afterAll(async () => {
await browser.close(); await browser.close();
}); });
<% } %><% if(testingFramework == 'mocha' || testingFramework == 'node') { %> <% } %><% if(testRunner == 'mocha' || testRunner == 'node') { %>
after(async () => { after(async () => {
await browser.close(); await browser.close();
}); });

View File

@ -1,11 +1,10 @@
{ {
"extends": "<%= tsConfigPath %>", "extends": "<%= tsConfigPath %>",
"compilerOptions": {<% if(testingFramework == 'jest') { %> "compilerOptions": {
"esModuleInterop": true,<% } %><% if(testingFramework == 'node') { %>
"module": "CommonJS", "module": "CommonJS",
"rootDir": "tests/", "rootDir": "tests/",
"outDir": "build/",<% } %> "outDir": "build/",
"types": ["<%= testingFramework %>"] "types": ["<%= testRunner %>"]
}, },
"include": ["tests/**/*.ts"] "include": ["tests/**/*.ts"]
} }

View File

@ -1,4 +0,0 @@
require('@babel/register')({
extensions: ['.js', '.ts'],
presets: ['@babel/preset-env', '@babel/preset-typescript'],
});

View File

@ -1,8 +1,9 @@
{ {
"spec_dir": "e2e", "spec_dir": "e2e",
"spec_files": ["**/*[eE]2[eE].ts"], "spec_files": ["**/*[eE]2[eE].js"],
"helpers": ["helpers/babel.js", "helpers/**/*.{js|ts}"], "helpers": ["helpers/**/*.?(m)js"],
"env": { "env": {
"failSpecWithNoExpectations": true,
"stopSpecOnExpectationFailure": false, "stopSpecOnExpectationFailure": false,
"random": true "random": true
} }

View File

@ -3,9 +3,8 @@
* https://jestjs.io/docs/configuration * https://jestjs.io/docs/configuration
*/ */
/** @type {import('ts-jest').JestConfigWithTsJest} */ /** @type {import('jest').Config} */
module.exports = { module.exports = {
testMatch: ['<rootDir>/tests/**/?(*.)+(e2e).[tj]s?(x)'], testMatch: ['<rootDir>/tests/**/?(*.)+(e2e).js?(x)'],
preset: 'ts-jest',
testEnvironment: 'node', testEnvironment: 'node',
}; };

View File

@ -1,4 +1,3 @@
module.exports = { module.exports = {
file: ['e2e/babel.js'], spec: './e2e/tests/**/*.e2e.js',
spec: './e2e/tests/**/*.e2e.ts',
}; };

View File

@ -1,4 +0,0 @@
require('@babel/register')({
extensions: ['.js', '.ts'],
presets: ['@babel/preset-env', '@babel/preset-typescript'],
});

View File

@ -21,9 +21,9 @@ import {concatMap, map, scan} from 'rxjs/operators';
import { import {
addCommonFiles as addCommonFilesHelper, addCommonFiles as addCommonFilesHelper,
addFilesSingle,
addFrameworkFiles, addFrameworkFiles,
getNgCommandName, getNgCommandName,
hasE2ETester,
} from '../utils/files.js'; } from '../utils/files.js';
import {getAngularConfig} from '../utils/json.js'; import {getAngularConfig} from '../utils/json.js';
import { import {
@ -35,55 +35,24 @@ import {
type NodePackage, type NodePackage,
updateAngularJsonScripts, updateAngularJsonScripts,
} from '../utils/packages.js'; } from '../utils/packages.js';
import { import {TestRunner, type SchematicsOptions} from '../utils/types.js';
TestingFramework,
type SchematicsOptions, const DEFAULT_PORT = 4200;
AngularProject,
} from '../utils/types.js';
// You don't have to export the function as default. You can also have more than one rule // You don't have to export the function as default. You can also have more than one rule
// factory per file. // factory per file.
export function ngAdd(userArgs: Record<string, string>): Rule { export function ngAdd(options: SchematicsOptions): Rule {
const options = parseUserAddArgs(userArgs);
return (tree: Tree, context: SchematicContext) => { return (tree: Tree, context: SchematicContext) => {
return chain([ return chain([
addDependencies(options), addDependencies(options),
addPuppeteerConfig(options),
addCommonFiles(options), addCommonFiles(options),
addOtherFiles(options), addOtherFiles(options),
updateScripts(options), updateScripts(),
updateAngularConfig(options), updateAngularConfig(options),
])(tree, context); ])(tree, context);
}; };
} }
function parseUserAddArgs(userArgs: Record<string, string>): SchematicsOptions {
const options: Partial<SchematicsOptions> = {
...userArgs,
};
if ('p' in userArgs) {
options['port'] = Number(userArgs['p']);
}
if ('t' in userArgs) {
options['testingFramework'] = userArgs['t'] as TestingFramework;
}
if ('c' in userArgs) {
options['exportConfig'] =
typeof userArgs['c'] === 'string'
? userArgs['c'] === 'true'
: userArgs['c'];
}
if ('d' in userArgs) {
options['isDefaultTester'] =
typeof userArgs['d'] === 'string'
? userArgs['d'] === 'true'
: userArgs['d'];
}
return options as SchematicsOptions;
}
function addDependencies(options: SchematicsOptions): Rule { function addDependencies(options: SchematicsOptions): Rule {
return (tree: Tree, context: SchematicContext) => { return (tree: Tree, context: SchematicContext) => {
context.logger.debug('Adding dependencies to "package.json"'); context.logger.debug('Adding dependencies to "package.json"');
@ -108,15 +77,15 @@ function addDependencies(options: SchematicsOptions): Rule {
}; };
} }
function updateScripts(options: SchematicsOptions): Rule { function updateScripts(): Rule {
return (tree: Tree, context: SchematicContext): Tree => { return (tree: Tree, context: SchematicContext): Tree => {
context.logger.debug('Updating "package.json" scripts'); context.logger.debug('Updating "package.json" scripts');
const angularJson = getAngularConfig(tree); const {projects} = getAngularConfig(tree);
const projects = Object.keys(angularJson['projects']); const projectsKeys = Object.keys(projects);
if (projects.length === 1) { if (projectsKeys.length === 1) {
const name = getNgCommandName(options); const name = getNgCommandName(projects);
const prefix = options.isDefaultTester ? '' : `run ${projects[0]}:`; const prefix = hasE2ETester(projects) ? `run ${projectsKeys[0]}:` : '';
return addPackageJsonScripts(tree, [ return addPackageJsonScripts(tree, [
{ {
name, name,
@ -128,22 +97,6 @@ function updateScripts(options: SchematicsOptions): Rule {
}; };
} }
function addPuppeteerConfig(options: SchematicsOptions): Rule {
return (tree: Tree, context: SchematicContext) => {
context.logger.debug('Adding Puppeteer config file.');
if (options.exportConfig) {
return addFilesSingle(tree, context, '', {root: ''} as AngularProject, {
options: options,
applyPath: './files/base',
relativeToWorkspacePath: `/`,
});
}
return tree;
};
}
function addCommonFiles(options: SchematicsOptions): Rule { function addCommonFiles(options: SchematicsOptions): Rule {
return (tree: Tree, context: SchematicContext) => { return (tree: Tree, context: SchematicContext) => {
context.logger.debug('Adding Puppeteer base files.'); context.logger.debug('Adding Puppeteer base files.');
@ -152,8 +105,8 @@ function addCommonFiles(options: SchematicsOptions): Rule {
return addCommonFilesHelper(tree, context, projects, { return addCommonFilesHelper(tree, context, projects, {
options: { options: {
...options, ...options,
ext: port: DEFAULT_PORT,
options.testingFramework === TestingFramework.Node ? 'test' : 'e2e', ext: options.testRunner === TestRunner.Node ? 'test' : 'e2e',
}, },
}); });
}; };
@ -165,7 +118,10 @@ function addOtherFiles(options: SchematicsOptions): Rule {
const {projects} = getAngularConfig(tree); const {projects} = getAngularConfig(tree);
return addFrameworkFiles(tree, context, projects, { return addFrameworkFiles(tree, context, projects, {
options, options: {
...options,
port: DEFAULT_PORT,
},
}); });
}; };
} }

View File

@ -4,25 +4,13 @@
"title": "Puppeteer Install Schema", "title": "Puppeteer Install Schema",
"type": "object", "type": "object",
"properties": { "properties": {
"isDefaultTester": { "testRunner": {
"type": "boolean",
"default": true,
"alias": "d",
"x-prompt": "Use Puppeteer as default `ng e2e` command?"
},
"exportConfig": {
"type": "boolean",
"default": false,
"alias": "c",
"x-prompt": "Export default Puppeteer config file?"
},
"testingFramework": {
"type": "string", "type": "string",
"enum": ["jasmine", "jest", "mocha", "node"], "enum": ["jasmine", "jest", "mocha", "node"],
"default": "jasmine", "default": "jasmine",
"alias": "t", "alias": "t",
"x-prompt": { "x-prompt": {
"message": "With what Testing Library do you wish to integrate?", "message": "Which test runners do you wish to use?",
"type": "list", "type": "list",
"items": [ "items": [
{ {
@ -43,12 +31,6 @@
} }
] ]
} }
},
"port": {
"type": ["number"],
"default": 4200,
"alias": "p",
"x-prompt": "On which port to spawn test server on?"
} }
}, },
"required": [] "required": []

View File

@ -28,15 +28,16 @@ import {
url, url,
} from '@angular-devkit/schematics'; } from '@angular-devkit/schematics';
import {AngularProject, SchematicsOptions, TestingFramework} from './types.js'; import {AngularProject, SchematicsOptions, TestRunner} from './types.js';
export interface FilesOptions { export interface FilesOptions {
options: { options: {
testingFramework: TestingFramework; testRunner: TestRunner;
port: number; port: number;
name?: string; name?: string;
exportConfig?: boolean; exportConfig?: boolean;
ext?: string; ext?: string;
route?: string;
}; };
applyPath: string; applyPath: string;
relativeToWorkspacePath: string; relativeToWorkspacePath: string;
@ -137,50 +138,41 @@ export function addFrameworkFiles(
projects: Record<string, AngularProject>, projects: Record<string, AngularProject>,
filesOptions: Omit<FilesOptions, 'applyPath' | 'relativeToWorkspacePath'> filesOptions: Omit<FilesOptions, 'applyPath' | 'relativeToWorkspacePath'>
): any { ): any {
const testingFramework = filesOptions.options.testingFramework; const testRunner = filesOptions.options.testRunner;
const options: FilesOptions = { const options: FilesOptions = {
...filesOptions, ...filesOptions,
applyPath: `./files/${testingFramework}`, applyPath: `./files/${testRunner}`,
relativeToWorkspacePath: `/`, relativeToWorkspacePath: `/`,
}; };
return addFilesToProjects(tree, context, projects, options); return addFilesToProjects(tree, context, projects, options);
} }
export function getScriptFromOptions( export function getScriptFromOptions(options: SchematicsOptions): string[][] {
options: SchematicsOptions, switch (options.testRunner) {
root?: string case TestRunner.Jasmine:
): string[][] { return [[`jasmine`, '--config=./e2e/support/jasmine.json']];
let path = 'node_modules/.bin'; case TestRunner.Jest:
if (root && root !== '') { return [[`jest`, '-c', 'e2e/jest.config.js']];
const nested = root case TestRunner.Mocha:
.split('/') return [[`mocha`, '--config=./e2e/.mocharc.js']];
.map(() => { case TestRunner.Node:
return '../'; return [['node', '--test', '--test-reporter', 'spec', 'e2e/build/']];
})
.join('');
path = `${nested}${path}`;
} else {
path = `./${path}`;
}
switch (options.testingFramework) {
case TestingFramework.Jasmine:
return [[`${path}/jasmine`, '--config=./e2e/support/jasmine.json']];
case TestingFramework.Jest:
return [[`${path}/jest`, '-c', 'e2e/jest.config.js']];
case TestingFramework.Mocha:
return [[`${path}/mocha`, '--config=./e2e/.mocharc.js']];
case TestingFramework.Node:
return [
[`${path}/tsc`, '-p', 'e2e/tsconfig.json'],
['node', '--test', '--test-reporter', 'spec', 'e2e/build/'],
];
} }
} }
export function getNgCommandName(options: SchematicsOptions): string { export function hasE2ETester(
if (options.isDefaultTester) { projects: Record<string, AngularProject>
): boolean {
return Object.values(projects).some((project: AngularProject) => {
return Boolean(project.architect?.e2e);
});
}
export function getNgCommandName(
projects: Record<string, AngularProject>
): string {
if (!hasE2ETester(projects)) {
return 'e2e'; return 'e2e';
} }
return 'puppeteer'; return 'puppeteer';

View File

@ -24,7 +24,7 @@ import {
getJsonFileAsObject, getJsonFileAsObject,
getObjectAsJson, getObjectAsJson,
} from './json.js'; } from './json.js';
import {SchematicsOptions, TestingFramework} from './types.js'; import {SchematicsOptions, TestRunner} from './types.js';
export interface NodePackage { export interface NodePackage {
name: string; name: string;
version: string; version: string;
@ -115,24 +115,18 @@ export function getDependenciesFromOptions(
options: SchematicsOptions options: SchematicsOptions
): string[] { ): string[] {
const dependencies = ['puppeteer']; const dependencies = ['puppeteer'];
const babelPackages = [
'@babel/core',
'@babel/register',
'@babel/preset-env',
'@babel/preset-typescript',
];
switch (options.testingFramework) { switch (options.testRunner) {
case TestingFramework.Jasmine: case TestRunner.Jasmine:
dependencies.push('jasmine', ...babelPackages); dependencies.push('jasmine');
break; break;
case TestingFramework.Jest: case TestRunner.Jest:
dependencies.push('jest', '@types/jest', 'ts-jest'); dependencies.push('jest', '@types/jest');
break; break;
case TestingFramework.Mocha: case TestRunner.Mocha:
dependencies.push('mocha', '@types/mocha', ...babelPackages); dependencies.push('mocha', '@types/mocha');
break; break;
case TestingFramework.Node: case TestRunner.Node:
dependencies.push('@types/node'); dependencies.push('@types/node');
break; break;
} }
@ -168,14 +162,10 @@ export function updateAngularJsonScripts(
overwrite = true overwrite = true
): Tree { ): Tree {
const angularJson = getAngularConfig(tree); const angularJson = getAngularConfig(tree);
const name = getNgCommandName(options); const name = getNgCommandName(angularJson.projects);
const port = options.port !== 4200 ? Number(options.port) : undefined;
Object.keys(angularJson['projects']).forEach(project => { Object.keys(angularJson['projects']).forEach(project => {
const commands = getScriptFromOptions( const commands = getScriptFromOptions(options);
options,
angularJson['projects'][project]!.root
);
const e2eScript = [ const e2eScript = [
{ {
name, name,
@ -184,8 +174,7 @@ export function updateAngularJsonScripts(
options: { options: {
commands, commands,
devServerTarget: `${project}:serve`, devServerTarget: `${project}:serve`,
testingFramework: options.testingFramework, testRunner: options.testRunner,
port,
}, },
configurations: { configurations: {
production: { production: {

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
export enum TestingFramework { export enum TestRunner {
Jasmine = 'jasmine', Jasmine = 'jasmine',
Jest = 'jest', Jest = 'jest',
Mocha = 'mocha', Mocha = 'mocha',
@ -22,15 +22,15 @@ export enum TestingFramework {
} }
export interface SchematicsOptions { export interface SchematicsOptions {
isDefaultTester: boolean; testRunner: TestRunner;
exportConfig: boolean;
testingFramework: TestingFramework;
port: number;
} }
export interface PuppeteerSchematicsConfig { export interface PuppeteerSchematicsConfig {
builder: string; builder: string;
options: SchematicsOptions; options: {
port: number;
testRunner: TestRunner;
};
} }
export interface AngularProject { export interface AngularProject {
root: string; root: string;
@ -46,4 +46,5 @@ export interface AngularJson {
export interface SchematicsSpec { export interface SchematicsSpec {
name: string; name: string;
project?: string; project?: string;
route?: string;
} }

View File

@ -0,0 +1,26 @@
import expect from 'expect';
import {
buildTestingTree,
getMultiProjectFile,
setupHttpHooks,
} from './utils.js';
describe('@puppeteer/ng-schematics: config', () => {
setupHttpHooks();
describe('Single Project', () => {
it('should create default file', async () => {
const tree = await buildTestingTree('config', 'single');
expect(tree.files).toContain('/.puppeteerrc.mjs');
});
});
describe('Multi projects', () => {
it('should create default file', async () => {
const tree = await buildTestingTree('config', 'multi');
expect(tree.files).toContain('/.puppeteerrc.mjs');
expect(tree.files).not.toContain(getMultiProjectFile('.puppeteerrc.mjs'));
});
});
});

View File

@ -6,12 +6,12 @@ import {
setupHttpHooks, setupHttpHooks,
} from './utils.js'; } from './utils.js';
describe('@puppeteer/ng-schematics: test', () => { describe('@puppeteer/ng-schematics: e2e', () => {
setupHttpHooks(); setupHttpHooks();
describe('Single Project', () => { describe('Single Project', () => {
it('should create default file', async () => { it('should create default file', async () => {
const tree = await buildTestingTree('test', 'single', { const tree = await buildTestingTree('e2e', 'single', {
name: 'myTest', name: 'myTest',
}); });
expect(tree.files).toContain('/e2e/tests/my-test.e2e.ts'); expect(tree.files).toContain('/e2e/tests/my-test.e2e.ts');
@ -19,9 +19,9 @@ describe('@puppeteer/ng-schematics: test', () => {
}); });
it('should create Node file', async () => { it('should create Node file', async () => {
const tree = await buildTestingTree('test', 'single', { const tree = await buildTestingTree('e2e', 'single', {
name: 'myTest', name: 'myTest',
testingFramework: 'node', testRunner: 'node',
}); });
expect(tree.files).not.toContain('/e2e/tests/my-test.e2e.ts'); expect(tree.files).not.toContain('/e2e/tests/my-test.e2e.ts');
expect(tree.files).toContain('/e2e/tests/my-test.test.ts'); expect(tree.files).toContain('/e2e/tests/my-test.test.ts');
@ -30,7 +30,7 @@ describe('@puppeteer/ng-schematics: test', () => {
describe('Multi projects', () => { describe('Multi projects', () => {
it('should create default file', async () => { it('should create default file', async () => {
const tree = await buildTestingTree('test', 'multi', { const tree = await buildTestingTree('e2e', 'multi', {
name: 'myTest', name: 'myTest',
}); });
expect(tree.files).toContain( expect(tree.files).toContain(
@ -42,9 +42,9 @@ describe('@puppeteer/ng-schematics: test', () => {
}); });
it('should create Node file', async () => { it('should create Node file', async () => {
const tree = await buildTestingTree('test', 'multi', { const tree = await buildTestingTree('e2e', 'multi', {
name: 'myTest', name: 'myTest',
testingFramework: 'node', testRunner: 'node',
}); });
expect(tree.files).not.toContain( expect(tree.files).not.toContain(
getMultiProjectFile('e2e/tests/my-test.e2e.ts') getMultiProjectFile('e2e/tests/my-test.e2e.ts')

View File

@ -5,6 +5,7 @@ import {
getAngularJsonScripts, getAngularJsonScripts,
getMultiProjectFile, getMultiProjectFile,
getPackageJson, getPackageJson,
runSchematic,
setupHttpHooks, setupHttpHooks,
} from './utils.js'; } from './utils.js';
@ -30,49 +31,36 @@ describe('@puppeteer/ng-schematics: ng-add', () => {
}); });
}); });
it('should update create proper "ng" command for non default tester', async () => { it('should update create proper "ng" command for non default tester', async () => {
const tree = await buildTestingTree('ng-add', 'single', { let tree = await buildTestingTree('ng-add', 'single');
isDefaultTester: false, // Re-run schematic to have e2e populated
}); tree = await runSchematic(tree, 'ng-add');
const {scripts} = getPackageJson(tree); const {scripts} = getPackageJson(tree);
const {builder} = getAngularJsonScripts(tree, false); const {builder} = getAngularJsonScripts(tree, false);
expect(scripts['puppeteer']).toBe('ng run sandbox:puppeteer'); expect(scripts['puppeteer']).toBe('ng run sandbox:puppeteer');
expect(builder).toBe('@puppeteer/ng-schematics:puppeteer'); expect(builder).toBe('@puppeteer/ng-schematics:puppeteer');
}); });
it('should create Puppeteer config', async () => {
const {files} = await buildTestingTree('ng-add', 'single', {
exportConfig: true,
});
expect(files).toContain('/.puppeteerrc.cjs');
});
it('should not create Puppeteer config', async () => { it('should not create Puppeteer config', async () => {
const {files} = await buildTestingTree('ng-add', 'single', { const {files} = await buildTestingTree('ng-add', 'single');
exportConfig: false,
});
expect(files).not.toContain('/.puppeteerrc.cjs'); expect(files).not.toContain('/.puppeteerrc.cjs');
}); });
it('should create Jasmine files and update "package.json"', async () => { it('should create Jasmine files and update "package.json"', async () => {
const tree = await buildTestingTree('ng-add', 'single', { const tree = await buildTestingTree('ng-add', 'single', {
testingFramework: 'jasmine', testRunner: 'jasmine',
}); });
const {devDependencies} = getPackageJson(tree); const {devDependencies} = getPackageJson(tree);
const {options} = getAngularJsonScripts(tree); const {options} = getAngularJsonScripts(tree);
expect(tree.files).toContain('/e2e/support/jasmine.json'); expect(tree.files).toContain('/e2e/support/jasmine.json');
expect(tree.files).toContain('/e2e/helpers/babel.js');
expect(devDependencies).toContain('jasmine'); expect(devDependencies).toContain('jasmine');
expect(devDependencies).toContain('@babel/core');
expect(devDependencies).toContain('@babel/register');
expect(devDependencies).toContain('@babel/preset-typescript');
expect(options['commands']).toEqual([ expect(options['commands']).toEqual([
[`./node_modules/.bin/jasmine`, '--config=./e2e/support/jasmine.json'], [`jasmine`, '--config=./e2e/support/jasmine.json'],
]); ]);
}); });
it('should create Jest files and update "package.json"', async () => { it('should create Jest files and update "package.json"', async () => {
const tree = await buildTestingTree('ng-add', 'single', { const tree = await buildTestingTree('ng-add', 'single', {
testingFramework: 'jest', testRunner: 'jest',
}); });
const {devDependencies} = getPackageJson(tree); const {devDependencies} = getPackageJson(tree);
const {options} = getAngularJsonScripts(tree); const {options} = getAngularJsonScripts(tree);
@ -80,32 +68,27 @@ describe('@puppeteer/ng-schematics: ng-add', () => {
expect(tree.files).toContain('/e2e/jest.config.js'); expect(tree.files).toContain('/e2e/jest.config.js');
expect(devDependencies).toContain('jest'); expect(devDependencies).toContain('jest');
expect(devDependencies).toContain('@types/jest'); expect(devDependencies).toContain('@types/jest');
expect(devDependencies).toContain('ts-jest');
expect(options['commands']).toEqual([ expect(options['commands']).toEqual([
[`./node_modules/.bin/jest`, '-c', 'e2e/jest.config.js'], [`jest`, '-c', 'e2e/jest.config.js'],
]); ]);
}); });
it('should create Mocha files and update "package.json"', async () => { it('should create Mocha files and update "package.json"', async () => {
const tree = await buildTestingTree('ng-add', 'single', { const tree = await buildTestingTree('ng-add', 'single', {
testingFramework: 'mocha', testRunner: 'mocha',
}); });
const {devDependencies} = getPackageJson(tree); const {devDependencies} = getPackageJson(tree);
const {options} = getAngularJsonScripts(tree); const {options} = getAngularJsonScripts(tree);
expect(tree.files).toContain('/e2e/.mocharc.js'); expect(tree.files).toContain('/e2e/.mocharc.js');
expect(tree.files).toContain('/e2e/babel.js');
expect(devDependencies).toContain('mocha'); expect(devDependencies).toContain('mocha');
expect(devDependencies).toContain('@types/mocha'); expect(devDependencies).toContain('@types/mocha');
expect(devDependencies).toContain('@babel/core');
expect(devDependencies).toContain('@babel/register');
expect(devDependencies).toContain('@babel/preset-typescript');
expect(options['commands']).toEqual([ expect(options['commands']).toEqual([
[`./node_modules/.bin/mocha`, '--config=./e2e/.mocharc.js'], [`mocha`, '--config=./e2e/.mocharc.js'],
]); ]);
}); });
it('should create Node files', async () => { it('should create Node files', async () => {
const tree = await buildTestingTree('ng-add', 'single', { const tree = await buildTestingTree('ng-add', 'single', {
testingFramework: 'node', testRunner: 'node',
}); });
const {options} = getAngularJsonScripts(tree); const {options} = getAngularJsonScripts(tree);
@ -113,25 +96,15 @@ describe('@puppeteer/ng-schematics: ng-add', () => {
expect(tree.files).not.toContain('/e2e/tests/app.e2e.ts'); expect(tree.files).not.toContain('/e2e/tests/app.e2e.ts');
expect(tree.files).toContain('/e2e/tests/app.test.ts'); expect(tree.files).toContain('/e2e/tests/app.test.ts');
expect(options['commands']).toEqual([ expect(options['commands']).toEqual([
[`./node_modules/.bin/tsc`, '-p', 'e2e/tsconfig.json'],
['node', '--test', '--test-reporter', 'spec', 'e2e/build/'], ['node', '--test', '--test-reporter', 'spec', 'e2e/build/'],
]); ]);
}); });
it('should not create port option', async () => { it('should not create port value', async () => {
const tree = await buildTestingTree('ng-add'); const tree = await buildTestingTree('ng-add');
const {options} = getAngularJsonScripts(tree); const {options} = getAngularJsonScripts(tree);
expect(options['port']).toBeUndefined(); expect(options['port']).toBeUndefined();
}); });
it('should create port option when specified', async () => {
const port = 8080;
const tree = await buildTestingTree('ng-add', 'single', {
port,
});
const {options} = getAngularJsonScripts(tree);
expect(options['port']).toBe(port);
});
}); });
describe('Multi projects', () => { describe('Multi projects', () => {
@ -153,33 +126,24 @@ describe('@puppeteer/ng-schematics: ng-add', () => {
}); });
}); });
it('should update create proper "ng" command for non default tester', async () => { it('should update create proper "ng" command for non default tester', async () => {
const tree = await buildTestingTree('ng-add', 'multi', { let tree = await buildTestingTree('ng-add', 'multi');
isDefaultTester: false, // Re-run schematic to have e2e populated
}); tree = await runSchematic(tree, 'ng-add');
const {scripts} = getPackageJson(tree); const {scripts} = getPackageJson(tree);
const {builder} = getAngularJsonScripts(tree, false); const {builder} = getAngularJsonScripts(tree, false);
expect(scripts['puppeteer']).toBe('ng run sandbox:puppeteer'); expect(scripts['puppeteer']).toBe('ng run sandbox:puppeteer');
expect(builder).toBe('@puppeteer/ng-schematics:puppeteer'); expect(builder).toBe('@puppeteer/ng-schematics:puppeteer');
}); });
it('should create Puppeteer config', async () => {
const {files} = await buildTestingTree('ng-add', 'multi', {
exportConfig: true,
});
expect(files).toContain('/.puppeteerrc.cjs');
});
it('should not create Puppeteer config', async () => { it('should not create Puppeteer config', async () => {
const {files} = await buildTestingTree('ng-add', 'multi', { const {files} = await buildTestingTree('ng-add', 'multi');
exportConfig: false,
});
expect(files).not.toContain(getMultiProjectFile('.puppeteerrc.cjs')); expect(files).not.toContain(getMultiProjectFile('.puppeteerrc.cjs'));
expect(files).not.toContain('/.puppeteerrc.cjs'); expect(files).not.toContain('/.puppeteerrc.cjs');
}); });
it('should create Jasmine files and update "package.json"', async () => { it('should create Jasmine files and update "package.json"', async () => {
const tree = await buildTestingTree('ng-add', 'multi', { const tree = await buildTestingTree('ng-add', 'multi', {
testingFramework: 'jasmine', testRunner: 'jasmine',
}); });
const {devDependencies} = getPackageJson(tree); const {devDependencies} = getPackageJson(tree);
const {options} = getAngularJsonScripts(tree); const {options} = getAngularJsonScripts(tree);
@ -187,21 +151,14 @@ describe('@puppeteer/ng-schematics: ng-add', () => {
expect(tree.files).toContain( expect(tree.files).toContain(
getMultiProjectFile('e2e/support/jasmine.json') getMultiProjectFile('e2e/support/jasmine.json')
); );
expect(tree.files).toContain(getMultiProjectFile('e2e/helpers/babel.js'));
expect(devDependencies).toContain('jasmine'); expect(devDependencies).toContain('jasmine');
expect(devDependencies).toContain('@babel/core');
expect(devDependencies).toContain('@babel/register');
expect(devDependencies).toContain('@babel/preset-typescript');
expect(options['commands']).toEqual([ expect(options['commands']).toEqual([
[ [`jasmine`, '--config=./e2e/support/jasmine.json'],
`../../node_modules/.bin/jasmine`,
'--config=./e2e/support/jasmine.json',
],
]); ]);
}); });
it('should create Jest files and update "package.json"', async () => { it('should create Jest files and update "package.json"', async () => {
const tree = await buildTestingTree('ng-add', 'multi', { const tree = await buildTestingTree('ng-add', 'multi', {
testingFramework: 'jest', testRunner: 'jest',
}); });
const {devDependencies} = getPackageJson(tree); const {devDependencies} = getPackageJson(tree);
const {options} = getAngularJsonScripts(tree); const {options} = getAngularJsonScripts(tree);
@ -209,32 +166,27 @@ describe('@puppeteer/ng-schematics: ng-add', () => {
expect(tree.files).toContain(getMultiProjectFile('e2e/jest.config.js')); expect(tree.files).toContain(getMultiProjectFile('e2e/jest.config.js'));
expect(devDependencies).toContain('jest'); expect(devDependencies).toContain('jest');
expect(devDependencies).toContain('@types/jest'); expect(devDependencies).toContain('@types/jest');
expect(devDependencies).toContain('ts-jest');
expect(options['commands']).toEqual([ expect(options['commands']).toEqual([
[`../../node_modules/.bin/jest`, '-c', 'e2e/jest.config.js'], [`jest`, '-c', 'e2e/jest.config.js'],
]); ]);
}); });
it('should create Mocha files and update "package.json"', async () => { it('should create Mocha files and update "package.json"', async () => {
const tree = await buildTestingTree('ng-add', 'multi', { const tree = await buildTestingTree('ng-add', 'multi', {
testingFramework: 'mocha', testRunner: 'mocha',
}); });
const {devDependencies} = getPackageJson(tree); const {devDependencies} = getPackageJson(tree);
const {options} = getAngularJsonScripts(tree); const {options} = getAngularJsonScripts(tree);
expect(tree.files).toContain(getMultiProjectFile('e2e/.mocharc.js')); expect(tree.files).toContain(getMultiProjectFile('e2e/.mocharc.js'));
expect(tree.files).toContain(getMultiProjectFile('e2e/babel.js'));
expect(devDependencies).toContain('mocha'); expect(devDependencies).toContain('mocha');
expect(devDependencies).toContain('@types/mocha'); expect(devDependencies).toContain('@types/mocha');
expect(devDependencies).toContain('@babel/core');
expect(devDependencies).toContain('@babel/register');
expect(devDependencies).toContain('@babel/preset-typescript');
expect(options['commands']).toEqual([ expect(options['commands']).toEqual([
[`../../node_modules/.bin/mocha`, '--config=./e2e/.mocharc.js'], [`mocha`, '--config=./e2e/.mocharc.js'],
]); ]);
}); });
it('should create Node files', async () => { it('should create Node files', async () => {
const tree = await buildTestingTree('ng-add', 'multi', { const tree = await buildTestingTree('ng-add', 'multi', {
testingFramework: 'node', testRunner: 'node',
}); });
const {options} = getAngularJsonScripts(tree); const {options} = getAngularJsonScripts(tree);
@ -246,24 +198,14 @@ describe('@puppeteer/ng-schematics: ng-add', () => {
getMultiProjectFile('e2e/tests/app.test.ts') getMultiProjectFile('e2e/tests/app.test.ts')
); );
expect(options['commands']).toEqual([ expect(options['commands']).toEqual([
[`../../node_modules/.bin/tsc`, '-p', 'e2e/tsconfig.json'],
['node', '--test', '--test-reporter', 'spec', 'e2e/build/'], ['node', '--test', '--test-reporter', 'spec', 'e2e/build/'],
]); ]);
}); });
it('should not create port option', async () => { it('should not create port value', async () => {
const tree = await buildTestingTree('ng-add'); const tree = await buildTestingTree('ng-add');
const {options} = getAngularJsonScripts(tree); const {options} = getAngularJsonScripts(tree);
expect(options['port']).toBeUndefined(); expect(options['port']).toBeUndefined();
}); });
it('should create port option when specified', async () => {
const port = 8080;
const tree = await buildTestingTree('ng-add', 'multi', {
port,
});
const {options} = getAngularJsonScripts(tree);
expect(options['port']).toBe(port);
});
}); });
}); });

View File

@ -74,7 +74,7 @@ export function getMultiProjectFile(file: string): string {
} }
export async function buildTestingTree( export async function buildTestingTree(
command: 'ng-add' | 'test', command: 'ng-add' | 'e2e' | 'config',
type: 'single' | 'multi' = 'single', type: 'single' | 'multi' = 'single',
userOptions?: Record<string, any> userOptions?: Record<string, any>
): Promise<UnitTestTree> { ): Promise<UnitTestTree> {
@ -83,9 +83,7 @@ export async function buildTestingTree(
join(__dirname, '../../lib/schematics/collection.json') join(__dirname, '../../lib/schematics/collection.json')
); );
const options = { const options = {
isDefaultTester: true, testRunner: 'jasmine',
exportConfig: false,
testingFramework: 'jasmine',
...userOptions, ...userOptions,
}; };
let workingTree: UnitTestTree; let workingTree: UnitTestTree;
@ -121,3 +119,15 @@ export async function buildTestingTree(
return await runner.runSchematic(command, options, workingTree); return await runner.runSchematic(command, options, workingTree);
} }
export async function runSchematic(
tree: UnitTestTree,
command: 'ng-add' | 'test',
options?: Record<string, any>
): Promise<UnitTestTree> {
const runner = new SchematicTestRunner(
'schematics',
join(__dirname, '../../lib/schematics/collection.json')
);
return await runner.runSchematic(command, options, tree);
}