chore: tests for multi projects angular (#10675)

This commit is contained in:
Nikolay Vitkov 2023-08-03 13:02:25 +02:00 committed by GitHub
parent 2f6870651e
commit 7ecfe150a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 507 additions and 212 deletions

View File

@ -98,6 +98,12 @@ To run the creating of single test schematic:
npm run sandbox:test npm run sandbox:test
``` ```
To create a multi project workspace use the following command
```bash
npm run sandbox -- --init --multi
```
### Unit Testing ### Unit Testing
The schematics utilize `@angular-devkit/schematics/testing` for verifying correct file creation and `package.json` updates. To execute the test suit: The schematics utilize `@angular-devkit/schematics/testing` for verifying correct file creation and `package.json` updates. To execute the test suit:

View File

@ -98,6 +98,12 @@ To run the creating of single test schematic:
npm run sandbox:test npm run sandbox:test
``` ```
To create a multi project workspace use the following command
```bash
npm run sandbox -- --init --multi
```
### Unit Testing ### Unit Testing
The schematics utilize `@angular-devkit/schematics/testing` for verifying correct file creation and `package.json` updates. To execute the test suit: The schematics utilize `@angular-devkit/schematics/testing` for verifying correct file creation and `package.json` updates. To execute the test suit:

View File

@ -20,7 +20,8 @@ import {of} from 'rxjs';
import {concatMap, map, scan} from 'rxjs/operators'; import {concatMap, map, scan} from 'rxjs/operators';
import { import {
addBaseFiles, addCommonFiles as addCommonFilesHelper,
addFilesSingle,
addFrameworkFiles, addFrameworkFiles,
getNgCommandName, getNgCommandName,
} from '../utils/files.js'; } from '../utils/files.js';
@ -34,15 +35,22 @@ import {
type NodePackage, type NodePackage,
updateAngularJsonScripts, updateAngularJsonScripts,
} from '../utils/packages.js'; } from '../utils/packages.js';
import {TestingFramework, type SchematicsOptions} from '../utils/types.js'; import {
TestingFramework,
type SchematicsOptions,
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(options: SchematicsOptions): Rule { export function ngAdd(userArgs: Record<string, string>): Rule {
const options = parseUserAddArgs(userArgs);
return (tree: Tree, context: SchematicContext) => { return (tree: Tree, context: SchematicContext) => {
return chain([ return chain([
addDependencies(options), addDependencies(options),
addPuppeteerFiles(options), addPuppeteerConfig(options),
addCommonFiles(options),
addOtherFiles(options), addOtherFiles(options),
updateScripts(options), updateScripts(options),
updateAngularConfig(options), updateAngularConfig(options),
@ -50,6 +58,32 @@ export function ngAdd(options: SchematicsOptions): Rule {
}; };
} }
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"');
@ -94,13 +128,28 @@ function updateScripts(options: SchematicsOptions): Rule {
}; };
} }
function addPuppeteerFiles(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 {
return (tree: Tree, context: SchematicContext) => { return (tree: Tree, context: SchematicContext) => {
context.logger.debug('Adding Puppeteer base files.'); context.logger.debug('Adding Puppeteer base files.');
const {projects} = getAngularConfig(tree); const {projects} = getAngularConfig(tree);
return addBaseFiles(tree, context, { return addCommonFilesHelper(tree, context, projects, {
projects,
options: { options: {
...options, ...options,
ext: ext:
@ -115,8 +164,7 @@ function addOtherFiles(options: SchematicsOptions): Rule {
context.logger.debug('Adding Puppeteer additional files.'); context.logger.debug('Adding Puppeteer additional files.');
const {projects} = getAngularConfig(tree); const {projects} = getAngularConfig(tree);
return addFrameworkFiles(tree, context, { return addFrameworkFiles(tree, context, projects, {
projects,
options, options,
}); });
}; };

View File

@ -22,7 +22,7 @@ import {
Tree, Tree,
} from '@angular-devkit/schematics'; } from '@angular-devkit/schematics';
import {addBaseFiles} 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, TestingFramework,
@ -33,12 +33,28 @@ import {
// 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(options: SchematicsSpec): Rule { export function test(userArgs: Record<string, string>): Rule {
const options = parseUserTestArgs(userArgs);
return (tree: Tree, context: SchematicContext) => { return (tree: Tree, context: SchematicContext) => {
return chain([addSpecFile(options)])(tree, context); return chain([addSpecFile(options)])(tree, context);
}; };
} }
function parseUserTestArgs(userArgs: Record<string, string>): SchematicsSpec {
const options: Partial<SchematicsSpec> = {
...userArgs,
};
if ('p' in userArgs) {
options['project'] = userArgs['p'];
}
if ('n' in userArgs) {
options['name'] = userArgs['n'];
}
return options as SchematicsSpec;
}
function findTestingOption<Property extends keyof SchematicsOptions>( function findTestingOption<Property extends keyof SchematicsOptions>(
[name, project]: [string, AngularProject | undefined], [name, project]: [string, AngularProject | undefined],
property: Property property: Property
@ -76,7 +92,7 @@ function addSpecFile(options: SchematicsSpec): Rule {
}); });
if (!foundProject) { if (!foundProject) {
throw new SchematicsException( throw new SchematicsException(
`Project not found! Please use -p to specify in which project to run.` `Project not found! Please run "ng generate @puppeteer/ng-schematics:test <Test> <Project>"`
); );
} }
@ -88,16 +104,20 @@ function addSpecFile(options: SchematicsSpec): Rule {
context.logger.debug('Creating Spec file.'); context.logger.debug('Creating Spec file.');
return addBaseFiles(tree, context, { return addCommonFiles(
projects: {[foundProject[0]]: foundProject[1]}, tree,
options: { context,
name: options.name, {[foundProject[0]]: foundProject[1]} as Record<string, AngularProject>,
testingFramework, {
// Node test runner does not support glob patterns options: {
// It looks for files `*.test.js` name: options.name,
ext: testingFramework === TestingFramework.Node ? 'test' : 'e2e', testingFramework,
port, // Node test runner does not support glob patterns
}, // It looks for files `*.test.js`
}); ext: testingFramework === TestingFramework.Node ? 'test' : 'e2e',
port,
},
}
);
}; };
} }

View File

@ -15,6 +15,10 @@
}, },
"project": { "project": {
"type": "string", "type": "string",
"$default": {
"$source": "argv",
"index": 1
},
"alias": "p" "alias": "p"
} }
}, },

View File

@ -23,7 +23,6 @@ import {
apply, apply,
applyTemplates, applyTemplates,
chain, chain,
filter,
mergeWith, mergeWith,
move, move,
url, url,
@ -32,7 +31,6 @@ import {
import {AngularProject, SchematicsOptions, TestingFramework} from './types.js'; import {AngularProject, SchematicsOptions, TestingFramework} from './types.js';
export interface FilesOptions { export interface FilesOptions {
projects: Record<string, any>;
options: { options: {
testingFramework: TestingFramework; testingFramework: TestingFramework;
port: number; port: number;
@ -43,61 +41,61 @@ export interface FilesOptions {
applyPath: string; applyPath: string;
relativeToWorkspacePath: string; relativeToWorkspacePath: string;
movePath?: string; movePath?: string;
filterPredicate?: (path: string) => boolean;
} }
const PUPPETEER_CONFIG_TEMPLATE = '.puppeteerrc.cjs.template'; export function addFilesToProjects(
export function addFiles(
tree: Tree, tree: Tree,
context: SchematicContext, context: SchematicContext,
{ projects: Record<string, AngularProject>,
projects, options: FilesOptions
options,
applyPath,
movePath,
relativeToWorkspacePath,
filterPredicate,
}: FilesOptions
): any { ): any {
return chain( return chain(
Object.keys(projects).map(name => { Object.keys(projects).map(name => {
const project = projects[name] as AngularProject; return addFilesSingle(
const projectPath = resolve(getSystemPath(normalize(project.root))); tree,
const workspacePath = resolve(getSystemPath(normalize(''))); context,
name,
const relativeToWorkspace = relative( projects[name] as AngularProject,
`${projectPath}${relativeToWorkspacePath}`, options
workspacePath
);
const baseUrl = getProjectBaseUrl(project, options.port);
const tsConfigPath = getTsConfigPath(project);
return mergeWith(
apply(url(applyPath), [
filter(
filterPredicate ??
(() => {
return true;
})
),
move(movePath ? `${project.root}${movePath}` : project.root),
applyTemplates({
...options,
...strings,
root: project.root ? `${project.root}/` : project.root,
baseUrl,
tsConfigPath,
project: name,
relativeToWorkspace,
}),
])
); );
}) })
)(tree, context); )(tree, context);
} }
export function addFilesSingle(
_tree: Tree,
_context: SchematicContext,
name: string,
project: AngularProject,
{options, applyPath, movePath, relativeToWorkspacePath}: FilesOptions
): any {
const projectPath = resolve(getSystemPath(normalize(project.root)));
const workspacePath = resolve(getSystemPath(normalize('')));
const relativeToWorkspace = relative(
`${projectPath}${relativeToWorkspacePath}`,
workspacePath
);
const baseUrl = getProjectBaseUrl(project, options.port);
const tsConfigPath = getTsConfigPath(project);
return mergeWith(
apply(url(applyPath), [
move(movePath ? `${project.root}${movePath}` : project.root),
applyTemplates({
...options,
...strings,
root: project.root ? `${project.root}/` : project.root,
baseUrl,
tsConfigPath,
project: name,
relativeToWorkspace,
}),
])
);
}
function getProjectBaseUrl(project: any, port: number): string { function getProjectBaseUrl(project: any, port: number): string {
let options = {protocol: 'http', port, host: 'localhost'}; let options = {protocol: 'http', port, host: 'localhost'};
@ -118,29 +116,25 @@ function getTsConfigPath(project: AngularProject): string {
return `../tsconfig.app.json`; return `../tsconfig.app.json`;
} }
export function addBaseFiles( export function addCommonFiles(
tree: Tree, tree: Tree,
context: SchematicContext, context: SchematicContext,
projects: Record<string, AngularProject>,
filesOptions: Omit<FilesOptions, 'applyPath' | 'relativeToWorkspacePath'> filesOptions: Omit<FilesOptions, 'applyPath' | 'relativeToWorkspacePath'>
): any { ): any {
const options: FilesOptions = { const options: FilesOptions = {
...filesOptions, ...filesOptions,
applyPath: './files/base', applyPath: './files/common',
relativeToWorkspacePath: `/`, relativeToWorkspacePath: `/`,
filterPredicate: path => {
return path.includes(PUPPETEER_CONFIG_TEMPLATE) &&
!filesOptions.options.exportConfig
? false
: true;
},
}; };
return addFiles(tree, context, options); return addFilesToProjects(tree, context, projects, options);
} }
export function addFrameworkFiles( export function addFrameworkFiles(
tree: Tree, tree: Tree,
context: SchematicContext, context: SchematicContext,
projects: Record<string, AngularProject>,
filesOptions: Omit<FilesOptions, 'applyPath' | 'relativeToWorkspacePath'> filesOptions: Omit<FilesOptions, 'applyPath' | 'relativeToWorkspacePath'>
): any { ): any {
const testingFramework = filesOptions.options.testingFramework; const testingFramework = filesOptions.options.testingFramework;
@ -150,7 +144,7 @@ export function addFrameworkFiles(
relativeToWorkspacePath: `/`, relativeToWorkspacePath: `/`,
}; };
return addFiles(tree, context, options); return addFilesToProjects(tree, context, projects, options);
} }
export function getScriptFromOptions( export function getScriptFromOptions(

View File

@ -16,7 +16,7 @@
import {SchematicsException, Tree} from '@angular-devkit/schematics'; import {SchematicsException, Tree} from '@angular-devkit/schematics';
import {AngularJson} from './types.js'; import type {AngularJson} from './types.js';
export function getJsonFileAsObject( export function getJsonFileAsObject(
tree: Tree, tree: Tree,

View File

@ -3,6 +3,7 @@ import expect from 'expect';
import { import {
buildTestingTree, buildTestingTree,
getAngularJsonScripts, getAngularJsonScripts,
getMultiProjectFile,
getPackageJson, getPackageJson,
setupHttpHooks, setupHttpHooks,
} from './utils.js'; } from './utils.js';
@ -10,132 +11,259 @@ import {
describe('@puppeteer/ng-schematics: ng-add', () => { describe('@puppeteer/ng-schematics: ng-add', () => {
setupHttpHooks(); setupHttpHooks();
it('should create base files and update to "package.json"', async () => { describe('Single Project', () => {
const tree = await buildTestingTree('ng-add'); it('should create base files and update to "package.json"', async () => {
const {devDependencies, scripts} = getPackageJson(tree); const tree = await buildTestingTree('ng-add');
const {builder, configurations} = getAngularJsonScripts(tree); const {devDependencies, scripts} = getPackageJson(tree);
const {builder, configurations} = getAngularJsonScripts(tree);
expect(tree.files).toContain('/e2e/tsconfig.json'); expect(tree.files).toContain('/e2e/tsconfig.json');
expect(tree.files).toContain('/e2e/tests/app.e2e.ts'); expect(tree.files).toContain('/e2e/tests/app.e2e.ts');
expect(tree.files).toContain('/e2e/tests/utils.ts'); expect(tree.files).toContain('/e2e/tests/utils.ts');
expect(devDependencies).toContain('puppeteer'); expect(devDependencies).toContain('puppeteer');
expect(scripts['e2e']).toBe('ng e2e'); expect(scripts['e2e']).toBe('ng e2e');
expect(builder).toBe('@puppeteer/ng-schematics:puppeteer'); expect(builder).toBe('@puppeteer/ng-schematics:puppeteer');
expect(configurations).toEqual({ expect(configurations).toEqual({
production: { production: {
devServerTarget: 'sandbox:serve:production', devServerTarget: 'sandbox:serve:production',
}, },
});
});
it('should update create proper "ng" command for non default tester', async () => {
const tree = await buildTestingTree('ng-add', 'single', {
isDefaultTester: false,
});
const {scripts} = getPackageJson(tree);
const {builder} = getAngularJsonScripts(tree, false);
expect(scripts['puppeteer']).toBe('ng run sandbox: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 () => {
const {files} = await buildTestingTree('ng-add', 'single', {
exportConfig: false,
});
expect(files).not.toContain('/.puppeteerrc.cjs');
});
it('should create Jasmine files and update "package.json"', async () => {
const tree = await buildTestingTree('ng-add', 'single', {
testingFramework: 'jasmine',
});
const {devDependencies} = getPackageJson(tree);
const {options} = getAngularJsonScripts(tree);
expect(tree.files).toContain('/e2e/support/jasmine.json');
expect(tree.files).toContain('/e2e/helpers/babel.js');
expect(devDependencies).toContain('jasmine');
expect(devDependencies).toContain('@babel/core');
expect(devDependencies).toContain('@babel/register');
expect(devDependencies).toContain('@babel/preset-typescript');
expect(options['commands']).toEqual([
[`./node_modules/.bin/jasmine`, '--config=./e2e/support/jasmine.json'],
]);
});
it('should create Jest files and update "package.json"', async () => {
const tree = await buildTestingTree('ng-add', 'single', {
testingFramework: 'jest',
});
const {devDependencies} = getPackageJson(tree);
const {options} = getAngularJsonScripts(tree);
expect(tree.files).toContain('/e2e/jest.config.js');
expect(devDependencies).toContain('jest');
expect(devDependencies).toContain('@types/jest');
expect(devDependencies).toContain('ts-jest');
expect(options['commands']).toEqual([
[`./node_modules/.bin/jest`, '-c', 'e2e/jest.config.js'],
]);
});
it('should create Mocha files and update "package.json"', async () => {
const tree = await buildTestingTree('ng-add', 'single', {
testingFramework: 'mocha',
});
const {devDependencies} = getPackageJson(tree);
const {options} = getAngularJsonScripts(tree);
expect(tree.files).toContain('/e2e/.mocharc.js');
expect(tree.files).toContain('/e2e/babel.js');
expect(devDependencies).toContain('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([
[`./node_modules/.bin/mocha`, '--config=./e2e/.mocharc.js'],
]);
});
it('should create Node files', async () => {
const tree = await buildTestingTree('ng-add', 'single', {
testingFramework: 'node',
});
const {options} = getAngularJsonScripts(tree);
expect(tree.files).toContain('/e2e/.gitignore');
expect(tree.files).not.toContain('/e2e/tests/app.e2e.ts');
expect(tree.files).toContain('/e2e/tests/app.test.ts');
expect(options['commands']).toEqual([
[`./node_modules/.bin/tsc`, '-p', 'e2e/tsconfig.json'],
['node', '--test', '--test-reporter', 'spec', 'e2e/build/'],
]);
});
it('should not create port option', async () => {
const tree = await buildTestingTree('ng-add');
const {options} = getAngularJsonScripts(tree);
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);
}); });
}); });
it('should update create proper "ng" command for non default tester', async () => { describe('Multi projects', () => {
const tree = await buildTestingTree('ng-add', { it('should create base files and update to "package.json"', async () => {
isDefaultTester: false, const tree = await buildTestingTree('ng-add', 'multi');
const {devDependencies, scripts} = getPackageJson(tree);
const {builder, configurations} = getAngularJsonScripts(tree);
expect(tree.files).toContain(getMultiProjectFile('e2e/tsconfig.json'));
expect(tree.files).toContain(getMultiProjectFile('e2e/tests/app.e2e.ts'));
expect(tree.files).toContain(getMultiProjectFile('e2e/tests/utils.ts'));
expect(devDependencies).toContain('puppeteer');
expect(scripts['e2e']).toBe('ng e2e');
expect(builder).toBe('@puppeteer/ng-schematics:puppeteer');
expect(configurations).toEqual({
production: {
devServerTarget: 'sandbox:serve:production',
},
});
}); });
const {scripts} = getPackageJson(tree); it('should update create proper "ng" command for non default tester', async () => {
const {builder} = getAngularJsonScripts(tree, false); const tree = await buildTestingTree('ng-add', 'multi', {
isDefaultTester: false,
});
const {scripts} = getPackageJson(tree);
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', {
exportConfig: true,
}); });
it('should create Puppeteer config', async () => {
const {files} = await buildTestingTree('ng-add', 'multi', {
exportConfig: true,
});
expect(files).toContain('/.puppeteerrc.cjs'); expect(files).toContain('/.puppeteerrc.cjs');
});
it('should not create Puppeteer config', async () => {
const {files} = await buildTestingTree('ng-add', {
exportConfig: false,
}); });
it('should not create Puppeteer config', async () => {
const {files} = await buildTestingTree('ng-add', 'multi', {
exportConfig: false,
});
expect(files).not.toContain('/.puppeteerrc.cjs'); expect(files).not.toContain(getMultiProjectFile('.puppeteerrc.cjs'));
}); expect(files).not.toContain('/.puppeteerrc.cjs');
it('should create Jasmine files and update "package.json"', async () => {
const tree = await buildTestingTree('ng-add', {
testingFramework: 'jasmine',
}); });
const {devDependencies} = getPackageJson(tree); it('should create Jasmine files and update "package.json"', async () => {
const {options} = getAngularJsonScripts(tree); const tree = await buildTestingTree('ng-add', 'multi', {
testingFramework: 'jasmine',
});
const {devDependencies} = getPackageJson(tree);
const {options} = getAngularJsonScripts(tree);
expect(tree.files).toContain('/e2e/support/jasmine.json'); expect(tree.files).toContain(
expect(tree.files).toContain('/e2e/helpers/babel.js'); getMultiProjectFile('e2e/support/jasmine.json')
expect(devDependencies).toContain('jasmine'); );
expect(devDependencies).toContain('@babel/core'); expect(tree.files).toContain(getMultiProjectFile('e2e/helpers/babel.js'));
expect(devDependencies).toContain('@babel/register'); expect(devDependencies).toContain('jasmine');
expect(devDependencies).toContain('@babel/preset-typescript'); expect(devDependencies).toContain('@babel/core');
expect(options['commands']).toEqual([ expect(devDependencies).toContain('@babel/register');
[`./node_modules/.bin/jasmine`, '--config=./e2e/support/jasmine.json'], expect(devDependencies).toContain('@babel/preset-typescript');
]); expect(options['commands']).toEqual([
}); [
`../../node_modules/.bin/jasmine`,
it('should create Jest files and update "package.json"', async () => { '--config=./e2e/support/jasmine.json',
const tree = await buildTestingTree('ng-add', { ],
testingFramework: 'jest', ]);
}); });
const {devDependencies} = getPackageJson(tree); it('should create Jest files and update "package.json"', async () => {
const {options} = getAngularJsonScripts(tree); const tree = await buildTestingTree('ng-add', 'multi', {
testingFramework: 'jest',
});
const {devDependencies} = getPackageJson(tree);
const {options} = getAngularJsonScripts(tree);
expect(tree.files).toContain('/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(devDependencies).toContain('ts-jest');
expect(options['commands']).toEqual([ expect(options['commands']).toEqual([
[`./node_modules/.bin/jest`, '-c', 'e2e/jest.config.js'], [`../../node_modules/.bin/jest`, '-c', 'e2e/jest.config.js'],
]); ]);
});
it('should create Mocha files and update "package.json"', async () => {
const tree = await buildTestingTree('ng-add', {
testingFramework: 'mocha',
}); });
const {devDependencies} = getPackageJson(tree); it('should create Mocha files and update "package.json"', async () => {
const {options} = getAngularJsonScripts(tree); const tree = await buildTestingTree('ng-add', 'multi', {
testingFramework: 'mocha',
});
const {devDependencies} = getPackageJson(tree);
const {options} = getAngularJsonScripts(tree);
expect(tree.files).toContain('/e2e/.mocharc.js'); expect(tree.files).toContain(getMultiProjectFile('e2e/.mocharc.js'));
expect(tree.files).toContain('/e2e/babel.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/core');
expect(devDependencies).toContain('@babel/register'); expect(devDependencies).toContain('@babel/register');
expect(devDependencies).toContain('@babel/preset-typescript'); expect(devDependencies).toContain('@babel/preset-typescript');
expect(options['commands']).toEqual([ expect(options['commands']).toEqual([
[`./node_modules/.bin/mocha`, '--config=./e2e/.mocharc.js'], [`../../node_modules/.bin/mocha`, '--config=./e2e/.mocharc.js'],
]); ]);
});
it('should create Node files', async () => {
const tree = await buildTestingTree('ng-add', {
testingFramework: 'node',
}); });
const {options} = getAngularJsonScripts(tree); it('should create Node files', async () => {
const tree = await buildTestingTree('ng-add', 'multi', {
testingFramework: 'node',
});
const {options} = getAngularJsonScripts(tree);
expect(tree.files).toContain('/e2e/.gitignore'); expect(tree.files).toContain(getMultiProjectFile('e2e/.gitignore'));
expect(tree.files).not.toContain('/e2e/tests/app.e2e.ts'); expect(tree.files).not.toContain(
expect(tree.files).toContain('/e2e/tests/app.test.ts'); getMultiProjectFile('e2e/tests/app.e2e.ts')
expect(options['commands']).toEqual([ );
[`./node_modules/.bin/tsc`, '-p', 'e2e/tsconfig.json'], expect(tree.files).toContain(
['node', '--test', '--test-reporter', 'spec', 'e2e/build/'], getMultiProjectFile('e2e/tests/app.test.ts')
]); );
}); expect(options['commands']).toEqual([
[`../../node_modules/.bin/tsc`, '-p', 'e2e/tsconfig.json'],
it('should not create port option', async () => { ['node', '--test', '--test-reporter', 'spec', 'e2e/build/'],
const tree = await buildTestingTree('ng-add'); ]);
const {options} = getAngularJsonScripts(tree);
expect(options['port']).toBeUndefined();
});
it('should create port option when specified', async () => {
const port = 8080;
const tree = await buildTestingTree('ng-add', {
port,
}); });
it('should not create port option', async () => {
const tree = await buildTestingTree('ng-add');
const {options} = getAngularJsonScripts(tree); const {options} = getAngularJsonScripts(tree);
expect(options['port']).toBe(port); 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

@ -1,24 +1,57 @@
import expect from 'expect'; import expect from 'expect';
import {buildTestingTree, setupHttpHooks} from './utils.js'; import {
buildTestingTree,
getMultiProjectFile,
setupHttpHooks,
} from './utils.js';
describe('@puppeteer/ng-schematics: test', () => { describe('@puppeteer/ng-schematics: test', () => {
setupHttpHooks(); setupHttpHooks();
it('should create default file', async () => { describe('Single Project', () => {
const tree = await buildTestingTree('test', { it('should create default file', async () => {
name: 'myTest', const tree = await buildTestingTree('test', 'single', {
name: 'myTest',
});
expect(tree.files).toContain('/e2e/tests/my-test.e2e.ts');
expect(tree.files).not.toContain('/e2e/tests/my-test.test.ts');
});
it('should create Node file', async () => {
const tree = await buildTestingTree('test', 'single', {
name: 'myTest',
testingFramework: 'node',
});
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.e2e.ts');
expect(tree.files).not.toContain('/e2e/tests/my-test.test.ts');
}); });
it('should create Node file', async () => { describe('Multi projects', () => {
const tree = await buildTestingTree('test', { it('should create default file', async () => {
name: 'myTest', const tree = await buildTestingTree('test', 'multi', {
testingFramework: 'node', name: 'myTest',
});
expect(tree.files).toContain(
getMultiProjectFile('e2e/tests/my-test.e2e.ts')
);
expect(tree.files).not.toContain(
getMultiProjectFile('e2e/tests/my-test.test.ts')
);
});
it('should create Node file', async () => {
const tree = await buildTestingTree('test', 'multi', {
name: 'myTest',
testingFramework: 'node',
});
expect(tree.files).not.toContain(
getMultiProjectFile('e2e/tests/my-test.e2e.ts')
);
expect(tree.files).toContain(
getMultiProjectFile('e2e/tests/my-test.test.ts')
);
}); });
expect(tree.files).not.toContain('/e2e/tests/my-test.e2e.ts');
expect(tree.files).toContain('/e2e/tests/my-test.test.ts');
}); });
}); });

View File

@ -8,7 +8,17 @@ import {
} from '@angular-devkit/schematics/testing'; } from '@angular-devkit/schematics/testing';
import sinon from 'sinon'; import sinon from 'sinon';
const APPLICATION_OPTIONS = { const WORKSPACE_OPTIONS = {
name: 'workspace',
newProjectRoot: 'projects',
version: '14.0.0',
};
const MULTI_APPLICATION_OPTIONS = {
name: 'sandbox',
};
const SINGLE_APPLICATION_OPTIONS = {
name: 'sandbox', name: 'sandbox',
directory: '.', directory: '.',
createApplication: true, createApplication: true,
@ -41,9 +51,9 @@ export function getAngularJsonScripts(
} { } {
const angularJson = tree.readJson('angular.json') as any; const angularJson = tree.readJson('angular.json') as any;
const e2eScript = isDefault ? 'e2e' : 'puppeteer'; const e2eScript = isDefault ? 'e2e' : 'puppeteer';
return angularJson['projects']?.[APPLICATION_OPTIONS.name]?.['architect'][ return angularJson['projects']?.[SINGLE_APPLICATION_OPTIONS.name]?.[
e2eScript 'architect'
]; ][e2eScript];
} }
export function getPackageJson(tree: UnitTestTree): { export function getPackageJson(tree: UnitTestTree): {
@ -59,8 +69,13 @@ export function getPackageJson(tree: UnitTestTree): {
}; };
} }
export function getMultiProjectFile(file: string): string {
return `/${WORKSPACE_OPTIONS.newProjectRoot}/${MULTI_APPLICATION_OPTIONS.name}/${file}`;
}
export async function buildTestingTree( export async function buildTestingTree(
command: 'ng-add' | 'test', command: 'ng-add' | 'test',
type: 'single' | 'multi' = 'single',
userOptions?: Record<string, any> userOptions?: Record<string, any>
): Promise<UnitTestTree> { ): Promise<UnitTestTree> {
const runner = new SchematicTestRunner( const runner = new SchematicTestRunner(
@ -76,11 +91,27 @@ export async function buildTestingTree(
let workingTree: UnitTestTree; let workingTree: UnitTestTree;
// Build workspace // Build workspace
workingTree = await runner.runExternalSchematic( if (type === 'single') {
'@schematics/angular', workingTree = await runner.runExternalSchematic(
'ng-new', '@schematics/angular',
APPLICATION_OPTIONS 'ng-new',
); SINGLE_APPLICATION_OPTIONS
);
} else {
// Build workspace
workingTree = await runner.runExternalSchematic(
'@schematics/angular',
'workspace',
WORKSPACE_OPTIONS
);
// Build dummy application
workingTree = await runner.runExternalSchematic(
'@schematics/angular',
'application',
MULTI_APPLICATION_OPTIONS,
workingTree
);
}
if (command !== 'ng-add') { if (command !== 'ng-add') {
// We want to create update the proper files with `ng-add` // We want to create update the proper files with `ng-add`

View File

@ -20,11 +20,29 @@ const {join} = require('path');
const {cwd} = require('process'); const {cwd} = require('process');
const isInit = process.argv.indexOf('--init') !== -1; const isInit = process.argv.indexOf('--init') !== -1;
const isMulti = process.argv.indexOf('--multi') !== -1;
const isBuild = process.argv.indexOf('--build') !== -1; const isBuild = process.argv.indexOf('--build') !== -1;
const isTest = process.argv.indexOf('--test') !== -1; const isTest = process.argv.indexOf('--test') !== -1;
const commands = { const commands = {
build: ['npm run build'], build: ['npm run build'],
createSandbox: ['npx ng new sandbox --defaults'], createSandbox: ['npx ng new sandbox --defaults'],
createMultiWorkspace: [
'ng new sandbox --create-application=false --directory=multi',
],
createMultiProjects: [
{
command: 'ng generate application core --style=css --routing=false',
options: {
cwd: join(cwd(), '/multi/'),
},
},
{
command: 'ng generate application admin --style=css --routing=false',
options: {
cwd: join(cwd(), '/multi/'),
},
},
],
runSchematics: [ runSchematics: [
{ {
command: 'npm run schematics', command: 'npm run schematics',
@ -51,7 +69,7 @@ const scripts = {
// Runs the Puppeteer Ng-Schematics against the sandbox // Runs the Puppeteer Ng-Schematics against the sandbox
schematics: schematics:
'npm run delete:file && npm run build:schematics && schematics ../:ng-add --dry-run=false', 'npm run delete:file && npm run build:schematics && schematics ../:ng-add --dry-run=false',
'schematics:spec': 'schematics:test':
'npm run build:schematics && schematics ../:test --dry-run=false', 'npm run build:schematics && schematics ../:test --dry-run=false',
}; };
/** /**
@ -79,7 +97,7 @@ async function executeCommand(commands) {
}); });
createProcess.on('error', message => { createProcess.on('error', message => {
console.error(message); console.error(`Running ${toExecute} exited with error:`, message);
reject(message); reject(message);
}); });
@ -96,9 +114,16 @@ async function executeCommand(commands) {
async function main() { async function main() {
if (isInit) { if (isInit) {
await executeCommand(commands.createSandbox); if (isMulti) {
await executeCommand(commands.createMultiWorkspace);
await executeCommand(commands.createMultiProjects);
} else {
await executeCommand(commands.createSandbox);
}
const packageJsonFile = join(cwd(), '/sandbox/package.json'); const directory = isMulti ? 'multi' : 'sandbox';
const packageJsonFile = join(cwd(), `/${directory}/package.json`);
const packageJson = JSON.parse(await readFile(packageJsonFile)); const packageJson = JSON.parse(await readFile(packageJsonFile));
packageJson['scripts'] = { packageJson['scripts'] = {
...packageJson['scripts'], ...packageJson['scripts'],

View File

@ -10,7 +10,7 @@
}, },
"packages/testserver": {}, "packages/testserver": {},
"packages/ng-schematics": { "packages/ng-schematics": {
"release-as": "0.3.0" "release-as": "0.4.0"
}, },
"packages/browsers": {} "packages/browsers": {}
}, },