index.ts (3426B)
1 /** 2 * @license 3 * Copyright 2023 Google Inc. 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7 import { 8 chain, 9 type Rule, 10 type SchematicContext, 11 SchematicsException, 12 type Tree, 13 } from '@angular-devkit/schematics'; 14 15 import {addCommonFiles} from '../utils/files.js'; 16 import {getApplicationProjects} from '../utils/json.js'; 17 import { 18 TestRunner, 19 type SchematicsSpec, 20 type AngularProject, 21 type PuppeteerSchematicsConfig, 22 } from '../utils/types.js'; 23 24 // You don't have to export the function as default. You can also have more than one rule 25 // factory per file. 26 export function e2e(userArgs: Record<string, string>): Rule { 27 const options = parseUserTestArgs(userArgs); 28 29 return (tree: Tree, context: SchematicContext) => { 30 return chain([addE2EFile(options)])(tree, context); 31 }; 32 } 33 34 function parseUserTestArgs(userArgs: Record<string, string>): SchematicsSpec { 35 const options: Partial<SchematicsSpec> = { 36 ...userArgs, 37 }; 38 if ('p' in userArgs) { 39 options['project'] = userArgs['p']; 40 } 41 if ('n' in userArgs) { 42 options['name'] = userArgs['n']; 43 } 44 if ('r' in userArgs) { 45 options['route'] = userArgs['r']; 46 } 47 48 if (options['route'] && options['route'].startsWith('/')) { 49 options['route'] = options['route'].substring(1); 50 } 51 52 return options as SchematicsSpec; 53 } 54 55 function findTestingOption< 56 Property extends keyof PuppeteerSchematicsConfig['options'], 57 >( 58 [name, project]: [string, AngularProject | undefined], 59 property: Property, 60 ): PuppeteerSchematicsConfig['options'][Property] { 61 if (!project) { 62 throw new Error(`Project "${name}" not found.`); 63 } 64 65 const e2e = project.architect?.e2e; 66 const puppeteer = project.architect?.puppeteer; 67 const builder = '@puppeteer/ng-schematics:puppeteer'; 68 69 if (e2e?.builder === builder) { 70 return e2e.options[property]; 71 } else if (puppeteer?.builder === builder) { 72 return puppeteer.options[property]; 73 } 74 75 throw new Error(`Can't find property "${property}" for project "${name}".`); 76 } 77 78 function addE2EFile(options: SchematicsSpec): Rule { 79 return async (tree: Tree, context: SchematicContext) => { 80 context.logger.debug('Adding Spec file.'); 81 82 const projects = getApplicationProjects(tree); 83 const projectNames = Object.keys(projects) as [string, ...string[]]; 84 const foundProject: [string, AngularProject | undefined] | undefined = 85 projectNames.length === 1 86 ? [projectNames[0], projects[projectNames[0]]] 87 : Object.entries(projects).find(([name, project]) => { 88 return options.project 89 ? options.project === name 90 : project.root === ''; 91 }); 92 if (!foundProject) { 93 throw new SchematicsException( 94 `Project not found! Please run "ng generate @puppeteer/ng-schematics:test <Test> <Project>"`, 95 ); 96 } 97 98 const testRunner = findTestingOption(foundProject, 'testRunner'); 99 const port = findTestingOption(foundProject, 'port'); 100 101 context.logger.debug('Creating Spec file.'); 102 103 return addCommonFiles( 104 {[foundProject[0]]: foundProject[1]} as Record<string, AngularProject>, 105 { 106 options: { 107 name: options.name, 108 route: options.route, 109 testRunner, 110 // Node test runner does not support glob patterns 111 // It looks for files `*.test.js` 112 ext: testRunner === TestRunner.Node ? 'test' : 'e2e', 113 port, 114 }, 115 }, 116 ); 117 }; 118 }