puppeteer/packages/ng-schematics/tools/projects.mjs
2024-01-15 10:11:04 +00:00

160 lines
4.2 KiB
JavaScript

/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import {spawn} from 'child_process';
import {randomUUID} from 'crypto';
import {readFile, writeFile} from 'fs/promises';
import {join} from 'path';
import {cwd} from 'process';
class AngularProject {
static ports = new Set();
static randomPort() {
const min = 4000;
const max = 9876;
return Math.floor(Math.random() * (max - min + 1) + min);
}
static port() {
const port = AngularProject.randomPort();
if (AngularProject.ports.has(port)) {
return AngularProject.port();
}
return port;
}
static #scripts = testRunner => {
return {
// Builds the ng-schematics before running them
'build:schematics': 'npm run --prefix ../../ build',
// Deletes all files created by Puppeteer Ng-Schematics to avoid errors
'delete:file':
'rm -f .puppeteerrc.cjs && rm -f tsconfig.e2e.json && rm -R -f e2e/',
// Runs the Puppeteer Ng-Schematics against the sandbox
schematics: 'schematics ../../:ng-add --dry-run=false',
'schematics:e2e': 'schematics ../../:e2e --dry-run=false',
'schematics:config': 'schematics ../../:config --dry-run=false',
'schematics:smoke': `schematics ../../:ng-add --dry-run=false --test-runner="${testRunner}" && ng e2e`,
};
};
/** Folder name */
#name;
/** E2E test runner to use */
#runner;
constructor(runner, name) {
this.#runner = runner ?? 'node';
this.#name = name ?? randomUUID();
}
get runner() {
return this.#runner;
}
get name() {
return this.#name;
}
async executeCommand(command, options) {
const [executable, ...args] = command.split(' ');
await new Promise((resolve, reject) => {
const createProcess = spawn(executable, args, {
shell: true,
...options,
});
createProcess.stdout.on('data', data => {
data = data
.toString()
// Replace new lines with a prefix including the test runner
.replace(/(?:\r\n?|\n)(?=.*[\r\n])/g, `\n${this.#runner} - `);
console.log(`${this.#runner} - ${data}`);
});
createProcess.on('error', message => {
console.error(`Running ${command} exited with error:`, message);
reject(message);
});
createProcess.on('exit', code => {
if (code === 0) {
resolve(true);
} else {
reject();
}
});
});
}
async create() {
await this.createProject();
await this.updatePackageJson();
}
async updatePackageJson() {
const packageJsonFile = join(cwd(), `/sandbox/${this.#name}/package.json`);
const packageJson = JSON.parse(await readFile(packageJsonFile));
packageJson['scripts'] = {
...packageJson['scripts'],
...AngularProject.#scripts(this.#runner),
};
await writeFile(packageJsonFile, JSON.stringify(packageJson, null, 2));
}
get commandOptions() {
return {
...process.env,
cwd: join(cwd(), `/sandbox/${this.#name}/`),
};
}
async runNpmScripts(command) {
await this.executeCommand(`npm run ${command}`, this.commandOptions);
}
async runSchematics() {
await this.runNpmScripts('schematics');
}
async runSchematicsE2E() {
await this.runNpmScripts('schematics:e2e');
}
async runSchematicsConfig() {
await this.runNpmScripts('schematics:config');
}
async runSmoke() {
await this.runNpmScripts(
`schematics:smoke -- --port=${AngularProject.port()}`
);
}
}
export class AngularProjectSingle extends AngularProject {
async createProject() {
await this.executeCommand(
`ng new ${this.name} --directory=sandbox/${this.name} --defaults --skip-git`
);
}
}
export class AngularProjectMulti extends AngularProject {
async createProject() {
await this.executeCommand(
`ng new ${this.name} --create-application=false --directory=sandbox/${this.name} --defaults --skip-git`
);
await this.executeCommand(
`ng generate application core --style=css --routing=true`,
this.commandOptions
);
await this.executeCommand(
`ng generate application admin --style=css --routing=false`,
this.commandOptions
);
}
}