Skip to content

Commit 684170c

Browse files
committed
test: run e2e tests on pre-compiled packages
The NPM packages being tested must be pre-compiled and the tar packages specified via --package. This way the real packages such as snapshots, release artifacts or cached packages can be tested. Previously the e2e tests compiled and packaged during test execution.
1 parent ca56489 commit 684170c

File tree

12 files changed

+138
-41
lines changed

12 files changed

+138
-41
lines changed

.circleci/dynamic_config.yml

+7-1
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ jobs:
275275
# Waits for the Saucelabs tunnel to be ready. This ensures that we don't run tests
276276
# too early without Saucelabs not being ready.
277277
- run: ./scripts/saucelabs/wait-for-tunnel.sh
278-
- run: node ./tests/legacy-cli/run_e2e ./tests/legacy-cli/e2e/tests/misc/browsers.ts
278+
- run: node ./tests/legacy-cli/run_e2e --glob="tests/misc/browsers.ts"
279279
- run: ./scripts/saucelabs/stop-tunnel.sh
280280
- fail_fast
281281

@@ -284,6 +284,10 @@ jobs:
284284
steps:
285285
- custom_attach_workspace
286286
- run: yarn build
287+
- persist_to_workspace:
288+
root: *workspace_location
289+
paths:
290+
- dist/_*.tgz
287291

288292
build-bazel-e2e:
289293
executor: action-executor
@@ -387,6 +391,8 @@ jobs:
387391
# Path where Arsenal Image Mounter files are downloaded.
388392
# Must match path in .circleci/win-ram-disk.ps1
389393
- ./aim
394+
# Build the npm packages for the e2e tests
395+
- run: yarn build
390396
# Run partial e2e suite on PRs only. Release branches will run the full e2e suite.
391397
- run:
392398
name: Execute E2E Tests

docs/DEVELOPER.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,10 @@ You can find more info about debugging [tests with Bazel in the docs.](https://g
8282

8383
### End to end tests
8484

85-
- Run: `node tests/legacy-cli/run_e2e.js`
85+
- Compile the packages being tested: `yarn build`
86+
- Run all tests: `node tests/legacy-cli/run_e2e.js`
8687
- Run a subset of the tests: `node tests/legacy-cli/run_e2e.js tests/legacy-cli/e2e/tests/i18n/ivy-localize-*`
88+
- Run on a custom set of npm packages (tar files): `node tests/legacy-cli/run_e2e.js --package _angular_cli.tgz _angular_create.tgz dist/*.tgz ...`
8789

8890
When running the debug commands, Node will stop and wait for a debugger to attach.
8991
You can attach your IDE to the debugger to stop on breakpoints and step through the code. Also, see [IDE Specific Usage](#ide-specific-usage) for a

lib/BUILD.bazel

-12
This file was deleted.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@
118118
"@types/progress": "^2.0.3",
119119
"@types/resolve": "^1.17.1",
120120
"@types/semver": "^7.3.12",
121+
"@types/tar": "^6.1.2",
121122
"@types/text-table": "^0.2.1",
122123
"@types/uuid": "^8.0.0",
123124
"@types/yargs": "^17.0.8",
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,30 @@
11
import { getGlobalVariable } from '../utils/env';
2+
import { PkgInfo } from '../utils/packages';
23
import { globalNpm, extractNpmEnv } from '../utils/process';
34
import { isPrereleaseCli } from '../utils/project';
45

56
export default async function () {
67
const testRegistry: string = getGlobalVariable('package-registry');
7-
await globalNpm(
8-
[
9-
'run',
10-
'admin',
11-
'--',
12-
'publish',
13-
'--no-versionCheck',
14-
'--no-branchCheck',
15-
`--registry=${testRegistry}`,
16-
'--tag',
17-
isPrereleaseCli() ? 'next' : 'latest',
18-
],
19-
{
20-
...extractNpmEnv(),
21-
// Also set an auth token value for the local test registry which is required by npm 7+
22-
// even though it is never actually used.
23-
'NPM_CONFIG__AUTH': 'e2e-testing',
24-
},
8+
const packageTars: PkgInfo[] = Object.values(getGlobalVariable('package-tars'));
9+
10+
// Publish packages specified with --package
11+
await Promise.all(
12+
packageTars.map(({ path: p }) =>
13+
globalNpm(
14+
[
15+
'publish',
16+
`--registry=${testRegistry}`,
17+
'--tag',
18+
isPrereleaseCli() ? 'next' : 'latest',
19+
p,
20+
],
21+
{
22+
...extractNpmEnv(),
23+
// Also set an auth token value for the local test registry which is required by npm 7+
24+
// even though it is never actually used.
25+
'NPM_CONFIG__AUTH': 'e2e-testing',
26+
},
27+
),
28+
),
2529
);
2630
}

tests/legacy-cli/e2e/tests/update/update.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { createProjectFromAsset } from '../../utils/assets';
44
import { expectFileMatchToExist, readFile } from '../../utils/fs';
55
import { getActivePackageManager } from '../../utils/packages';
66
import { ng, noSilentNg } from '../../utils/process';
7-
import { isPrereleaseCli, useCIChrome, useCIDefaults, NgCLIVersion } from '../../utils/project';
7+
import { isPrereleaseCli, useCIChrome, useCIDefaults, getNgCLIVersion } from '../../utils/project';
88

99
export default async function () {
1010
let restoreRegistry: (() => Promise<void>) | undefined;
@@ -32,7 +32,7 @@ export default async function () {
3232
const cliMajorProjectVersion = new SemVer(cliVersion).major;
3333

3434
// CLI current version.
35-
const cliMajorVersion = NgCLIVersion.major;
35+
const cliMajorVersion = getNgCLIVersion().major;
3636

3737
for (let version = cliMajorProjectVersion + 1; version < cliMajorVersion; version++) {
3838
// Run all the migrations until the current build major version - 1.

tests/legacy-cli/e2e/utils/BUILD.bazel

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ ts_library(
66
srcs = glob(["**/*.ts"]),
77
visibility = ["//visibility:public"],
88
deps = [
9-
"//lib",
109
"//tests/legacy-cli/e2e/ng-snapshot",
1110
"@npm//@types/glob",
1211
"@npm//@types/node-fetch",
1312
"@npm//@types/semver",
13+
"@npm//@types/tar",
1414
"@npm//@types/yargs-parser",
1515
"@npm//ansi-colors",
1616
"@npm//glob",
@@ -19,6 +19,7 @@ ts_library(
1919
"@npm//puppeteer",
2020
"@npm//rxjs",
2121
"@npm//semver",
22+
"@npm//tar",
2223
"@npm//tree-kill",
2324
"@npm//verdaccio",
2425
"@npm//verdaccio-auth-memory",

tests/legacy-cli/e2e/utils/packages.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { getGlobalVariable } from './env';
22
import { ProcessOutput, silentNpm, silentYarn } from './process';
33

4+
export interface PkgInfo {
5+
readonly name: string;
6+
readonly version: string;
7+
readonly path: string;
8+
}
9+
410
export function getActivePackageManager(): 'npm' | 'yarn' {
511
const value = getGlobalVariable('package-manager');
612
if (value && value !== 'npm' && value !== 'yarn') {

tests/legacy-cli/e2e/utils/project.ts

+9-4
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@ import * as fs from 'fs';
22
import * as path from 'path';
33
import { prerelease, SemVer } from 'semver';
44
import yargsParser from 'yargs-parser';
5-
import { packages } from '../../../../lib/packages';
65
import { getGlobalVariable } from './env';
76
import { prependToFile, readFile, replaceInFile, writeFile } from './fs';
87
import { gitCommit } from './git';
98
import { findFreePort } from './network';
10-
import { installWorkspacePackages } from './packages';
9+
import { installWorkspacePackages, PkgInfo } from './packages';
1110
import { exec, execAndWaitForOutputToMatch, git, ng } from './process';
1211

1312
export function updateJsonFile(filePath: string, fn: (json: any) => any | void) {
@@ -95,6 +94,8 @@ export async function prepareProjectForE2e(name: string) {
9594
}
9695

9796
export function useBuiltPackagesVersions(): Promise<void> {
97+
const packages: { [name: string]: PkgInfo } = getGlobalVariable('package-tars');
98+
9899
return updateJsonFile('package.json', (json) => {
99100
json['dependencies'] ??= {};
100101
json['devDependencies'] ??= {};
@@ -220,8 +221,12 @@ export async function useCIChrome(projectDir: string = ''): Promise<void> {
220221
}
221222
}
222223

223-
export const NgCLIVersion = new SemVer(packages['@angular/cli'].version);
224+
export function getNgCLIVersion(): SemVer {
225+
const packages: { [name: string]: PkgInfo } = getGlobalVariable('package-tars');
226+
227+
return new SemVer(packages['@angular/cli'].version);
228+
}
224229

225230
export function isPrereleaseCli(): boolean {
226-
return (prerelease(NgCLIVersion)?.length ?? 0) > 0;
231+
return (prerelease(getNgCLIVersion())?.length ?? 0) > 0;
227232
}

tests/legacy-cli/e2e/utils/tar.ts

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import fs from 'fs';
10+
import { normalize } from 'path';
11+
import { Parse } from 'tar';
12+
13+
/**
14+
* Extract and return the contents of a single file out of a tar file.
15+
*
16+
* @param tarball the tar file to extract from
17+
* @param filePath the path of the file to extract
18+
* @returns the Buffer of file or an error on fs/tar error or file not found
19+
*/
20+
export async function extractFile(tarball: string, filePath: string): Promise<Buffer> {
21+
return new Promise((resolve, reject) => {
22+
fs.createReadStream(tarball)
23+
.pipe(
24+
new Parse({
25+
strict: true,
26+
filter: (p) => normalize(p) === normalize(filePath),
27+
// TODO: @types/tar 'entry' does not have ReadEntry.on
28+
onentry: (entry: any) => {
29+
const chunks: Buffer[] = [];
30+
31+
entry.on('data', (chunk: any) => chunks!.push(chunk));
32+
entry.on('error', reject);
33+
entry.on('finish', () => resolve(Buffer.concat(chunks!)));
34+
},
35+
}),
36+
)
37+
.on('close', () => reject(`${tarball} does not contain ${filePath}`));
38+
});
39+
}

tests/legacy-cli/e2e_runner.ts

+32-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import { createNpmRegistry } from './e2e/utils/registry';
1010
import { launchTestProcess } from './e2e/utils/process';
1111
import { join } from 'path';
1212
import { findFreePort } from './e2e/utils/network';
13+
import { extractFile } from './e2e/utils/tar';
14+
import { realpathSync } from 'fs';
15+
import { PkgInfo } from './e2e/utils/packages';
1316

1417
Error.stackTraceLimit = Infinity;
1518

@@ -34,6 +37,8 @@ Error.stackTraceLimit = Infinity;
3437
* --shard Index of this processes' shard.
3538
* --tmpdir=path Override temporary directory to use for new projects.
3639
* --yarn Use yarn as package manager.
40+
* --package=path An npm package to be published before running tests
41+
*
3742
* If unnamed flags are passed in, the list of tests will be filtered to include only those passed.
3843
*/
3944
const argv = yargsParser(process.argv.slice(2), {
@@ -49,10 +54,14 @@ const argv = yargsParser(process.argv.slice(2), {
4954
],
5055
string: ['devkit', 'glob', 'ignore', 'reuse', 'ng-tag', 'tmpdir', 'ng-version'],
5156
number: ['nb-shards', 'shard'],
57+
array: ['package'],
5258
configuration: {
5359
'dot-notation': false,
5460
'camel-case-expansion': false,
5561
},
62+
default: {
63+
'package': ['./dist/_*.tgz'],
64+
},
5665
});
5766

5867
/**
@@ -162,10 +171,11 @@ console.log(['Tests:', ...testsToRun].join('\n '));
162171
setGlobalVariable('argv', argv);
163172
setGlobalVariable('package-manager', argv.yarn ? 'yarn' : 'npm');
164173

165-
Promise.all([findFreePort(), findFreePort()])
166-
.then(async ([httpPort, httpsPort]) => {
174+
Promise.all([findFreePort(), findFreePort(), findPackageTars()])
175+
.then(async ([httpPort, httpsPort, packageTars]) => {
167176
setGlobalVariable('package-registry', 'http://localhost:' + httpPort);
168177
setGlobalVariable('package-secure-registry', 'http://localhost:' + httpsPort);
178+
setGlobalVariable('package-tars', packageTars);
169179

170180
// NPM registries for the lifetime of the test execution
171181
const registryProcess = await createNpmRegistry(httpPort, httpPort);
@@ -306,3 +316,23 @@ function printFooter(testName: string, type: 'setup' | 'initializer' | 'test', s
306316
);
307317
console.log('');
308318
}
319+
320+
// Collect the packages passed as arguments and return as {package-name => pkg-path}
321+
async function findPackageTars(): Promise<{ [pkg: string]: PkgInfo }> {
322+
const pkgs: string[] = (getGlobalVariable('argv').package as string[]).flatMap((p) =>
323+
glob.sync(p, { realpath: true }),
324+
);
325+
326+
const pkgJsons = await Promise.all(pkgs.map((pkg) => extractFile(pkg, './package/package.json')));
327+
328+
return pkgs.reduce((all, pkg, i) => {
329+
const json = pkgJsons[i].toString('utf8');
330+
const { name, version } = JSON.parse(json);
331+
if (!name) {
332+
throw new Error(`Package ${pkg} - package.json name/version not found`);
333+
}
334+
335+
all[name] = { path: realpathSync(pkg), name, version };
336+
return all;
337+
}, {} as { [pkg: string]: PkgInfo });
338+
}

yarn.lock

+15
Original file line numberDiff line numberDiff line change
@@ -2538,6 +2538,14 @@
25382538
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310"
25392539
integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==
25402540

2541+
"@types/tar@^6.1.2":
2542+
version "6.1.2"
2543+
resolved "https://registry.yarnpkg.com/@types/tar/-/tar-6.1.2.tgz#e60108a7d1b08cc91bf2faf1286cc08fdad48bbe"
2544+
integrity sha512-bnX3RRm70/n1WMwmevdOAeDU4YP7f5JSubgnuU+yrO+xQQjwDboJj3u2NTJI5ngCQhXihqVVAH5h5J8YpdpEvg==
2545+
dependencies:
2546+
"@types/node" "*"
2547+
minipass "^3.3.5"
2548+
25412549
"@types/text-table@^0.2.1":
25422550
version "0.2.2"
25432551
resolved "https://registry.yarnpkg.com/@types/text-table/-/text-table-0.2.2.tgz#774c90cfcfbc8b4b0ebb00fecbe861dc8b1e8e26"
@@ -7710,6 +7718,13 @@ minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6:
77107718
dependencies:
77117719
yallist "^4.0.0"
77127720

7721+
minipass@^3.3.5:
7722+
version "3.3.5"
7723+
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.5.tgz#6da7e53a48db8a856eeb9153d85b230a2119e819"
7724+
integrity sha512-rQ/p+KfKBkeNwo04U15i+hOwoVBVmekmm/HcfTkTN2t9pbQKCMm4eN5gFeqgrrSp/kH/7BYYhTIHOxGqzbBPaA==
7725+
dependencies:
7726+
yallist "^4.0.0"
7727+
77137728
minizlib@^2.1.1, minizlib@^2.1.2:
77147729
version "2.1.2"
77157730
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"

0 commit comments

Comments
 (0)