/
githubmirror
/
angular-cli
Обзор
Документация
Войти
/
githubmirror
/
angular-cli
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
tests/e2e/utils/project.ts
250 строк
8 KB
Charles Lyding
test: remove global Protractor/E2E setup from E2E utilities
09 фев 2026, 17:11
09 фев 2026, 17:11
c5c0ac4
Код
Авторство
О чём код?
import * as fs from 'node:fs'; import * as path from 'node:path'; import { prerelease, SemVer } from 'semver'; import { getGlobalVariable } from './env'; import { readFile, replaceInFile, writeFile } from './fs'; import { gitCommit } from './git'; import { findFreePort } from './network'; import { installWorkspacePackages, PkgInfo } from './packages'; import { execAndWaitForOutputToMatch, git, ng } from './process'; import { join } from 'node:path'; export function updateJsonFile(filePath: string, fn: (json: any) => any | void) { return readFile(filePath).then((tsConfigJson) => { // Remove single and multiline comments const tsConfig = JSON.parse(tsConfigJson.replace(/\/\*\s(.|\n|\r)*\s\*\/|\/\/.*/g, '')) as any; const result = fn(tsConfig) || tsConfig; return writeFile(filePath, JSON.stringify(result, null, 2)); }); } export function updateTsConfig(fn: (json: any) => any | void) { return updateJsonFile('tsconfig.json', fn); } export async function ngServe(...args: string[]) { const port = await findFreePort(); const esbuild = getGlobalVariable('argv')['esbuild']; const validBundleRegEx = esbuild ? /complete\./ : /Compiled successfully\./; await execAndWaitForOutputToMatch( 'ng', ['serve', '--port', String(port), ...args], validBundleRegEx, ); return port; } export async function prepareProjectForE2e(name: string) { const argv: Record<string, unknown> = getGlobalVariable('argv'); await git('config', 'user.email', 'angular-core+e2e@google.com'); await git('config', 'user.name', 'Angular CLI E2E'); await git('config', 'commit.gpgSign', 'false'); await git('config', 'core.longpaths', 'true'); if (argv['ng-snapshots'] || argv['ng-tag']) { await useSha(); } console.log(`Project ${name} created... Installing packages.`); await installWorkspacePackages(); await useCIChrome(name, ''); await useCIDefaults(name); // Force sourcemaps to be from the root of the filesystem. await updateJsonFile('tsconfig.json', (json) => { json['compilerOptions']['sourceRoot'] = '/'; }); await gitCommit('prepare-project-for-e2e'); } export function useBuiltPackagesVersions(): Promise<void> { const packages: { [name: string]: PkgInfo } = getGlobalVariable('package-tars'); return updateJsonFile('package.json', (json) => { json['dependencies'] ??= {}; json['devDependencies'] ??= {}; for (const packageName of Object.keys(packages)) { if (packageName in json['dependencies']) { json['dependencies'][packageName] = packages[packageName].version; } else if (packageName in json['devDependencies']) { json['devDependencies'][packageName] = packages[packageName].version; } } }); } export async function useSha(): Promise<void> { const argv = getGlobalVariable('argv'); if (!argv['ng-snapshots'] && !argv['ng-tag']) { return; } // We need more than the sha here, version is also needed. Examples of latest tags: // 7.0.0-beta.4+dd2a650 // 6.1.6+4a8d56a const label = argv['ng-tag'] || ''; const ngSnapshotVersions = require('../ng-snapshot/package.json'); return updateJsonFile('package.json', (json) => { // Install over the project with snapshot builds. function replaceDependencies(key: string) { const missingSnapshots: string[] = []; Object.keys(json[key] || {}) .filter((name) => name.startsWith('@angular/')) .forEach((name) => { const pkgName = name.split(/\//)[1]; if (pkgName === 'cli' || pkgName === 'ssr' || pkgName === 'build') { return; } if (label) { json[key][`@angular/${pkgName}`] = `github:angular/${pkgName}-builds${label}`; } else { const replacement = ngSnapshotVersions.dependencies[`@angular/${pkgName}`]; if (!replacement) { missingSnapshots.push(`missing @angular/${pkgName}`); } json[key][`@angular/${pkgName}`] = replacement; } }); if (missingSnapshots.length > 0) { throw new Error( 'e2e test with --ng-snapshots requires all angular packages be ' + 'listed in tests/e2e/ng-snapshot/package.json.\nErrors:\n' + missingSnapshots.join('\n '), ); } } replaceDependencies('dependencies'); replaceDependencies('devDependencies'); }); } export function useCIDefaults(projectName = 'test-project'): Promise<void> { return updateJsonFile('angular.json', (workspaceJson) => { // Disable progress reporting on CI to reduce spam. const project = workspaceJson.projects[projectName]; const appTargets = project.targets || project.architect; appTargets.build.options.progress = false; appTargets.test.options.progress = false; if (appTargets.serve) { // Use a random port in serve. appTargets.serve.options ??= {}; appTargets.serve.options.port = 0; } }); } export async function useCIChrome(projectName: string, projectDir = ''): Promise<void> { const karmaConf = path.join(projectDir, 'karma.conf.js'); if (fs.existsSync(karmaConf)) { // Ensure the headless sandboxed chrome is configured in the karma config await replaceInFile( karmaConf, `browsers: ['Chrome'],`, `browsers: ['ChromeHeadlessNoSandbox'], customLaunchers: { ChromeHeadlessNoSandbox: { base: 'ChromeHeadless', flags: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage'], }, },`, ); } // Update to use the headless sandboxed chrome return updateJsonFile('angular.json', (workspaceJson) => { const project = workspaceJson.projects[projectName]; const appTargets = project.targets || project.architect; if (appTargets.test.builder === '@angular/build:unit-test') { appTargets.test.options.browsers = ['ChromeHeadlessNoSandbox']; } else { appTargets.test.options.browsers = 'ChromeHeadlessNoSandbox'; } }); } export function getNgCLIVersion(): SemVer { const packages: { [name: string]: PkgInfo } = getGlobalVariable('package-tars'); return new SemVer(packages['@angular/cli'].version); } export function isPrereleaseCli(): boolean { return (prerelease(getNgCLIVersion())?.length ?? 0) > 0; } export function updateServerFileForEsbuild(filepath: string): Promise<void> { return writeFile( filepath, ` import { APP_BASE_HREF } from '@angular/common'; import { CommonEngine } from '@angular/ssr/node'; import express from 'express'; import { join, resolve } from 'node:path'; import bootstrap from './main.server'; // The Express app is exported so that it can be used by serverless Functions. export function app(): express.Express { const server = express(); const serverDistFolder = import.meta.dirname; const browserDistFolder = resolve(serverDistFolder, '../browser'); const indexHtml = join(serverDistFolder, 'index.server.html'); const commonEngine = new CommonEngine(); server.set('view engine', 'html'); server.set('views', browserDistFolder); server.use(express.static(browserDistFolder, { maxAge: '1y', index: false, })); // All regular routes use the Angular engine server.use((req, res, next) => { const { protocol, originalUrl, baseUrl, headers } = req; commonEngine .render({ bootstrap, documentFilePath: indexHtml, url: \`\${protocol}://\${headers.host}\${originalUrl}\`, publicPath: browserDistFolder, providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }], }) .then((html) => res.send(html)) .catch((err) => next(err)); }); return server; } function run(): void { const port = process.env['PORT'] || 4000; const server = app(); server.listen(port, (error) => { if (error) { throw error; } console.log(\`Node Express server listening on http://localhost:\${port}\`); }); } run(); `, ); } export function getTestProjectDir(): string { return join(getGlobalVariable('projects-root'), 'test-project'); }