Skip to content

Commit 5edbae8

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 504e967 commit 5edbae8

File tree

9 files changed

+123
-42
lines changed

9 files changed

+123
-42
lines changed

.circleci/dynamic_config.yml

+11-5
Original file line numberDiff line numberDiff line change
@@ -235,23 +235,23 @@ jobs:
235235
- run:
236236
name: Execute CLI E2E Tests with NPM
237237
command: |
238-
node ./tests/legacy-cli/run_e2e --nb-shards=${CIRCLE_NODE_TOTAL} --shard=${CIRCLE_NODE_INDEX} <<# parameters.snapshots >>--ng-snapshots<</ parameters.snapshots >> --tmpdir=/mnt/ramdisk/e2e --ignore="tests/misc/browsers.ts"
238+
node ./tests/legacy-cli/run_e2e --nb-shards=${CIRCLE_NODE_TOTAL} --shard=${CIRCLE_NODE_INDEX} <<# parameters.snapshots >>--ng-snapshots<</ parameters.snapshots >> --tmpdir=/mnt/ramdisk/e2e --ignore="tests/misc/browsers.ts" --package ./dist/_*.tgz
239239
- when:
240240
condition:
241241
equal: ['esbuild', << parameters.subset >>]
242242
steps:
243243
- run:
244244
name: Execute CLI E2E Tests Subset with Esbuild
245245
command: |
246-
node ./tests/legacy-cli/run_e2e --nb-shards=${CIRCLE_NODE_TOTAL} --shard=${CIRCLE_NODE_INDEX} <<# parameters.snapshots >>--ng-snapshots<</ parameters.snapshots >> --esbuild --tmpdir=/mnt/ramdisk/e2e --glob="{tests/basic/**,tests/build/prod-build.ts,tests/build/relative-sourcemap.ts,tests/build/styles/scss.ts,tests/build/styles/include-paths.ts,tests/commands/add/add-pwa.ts}" --ignore="tests/basic/{environment,rebuild,serve,scripts-array}.ts"
246+
node ./tests/legacy-cli/run_e2e --nb-shards=${CIRCLE_NODE_TOTAL} --shard=${CIRCLE_NODE_INDEX} <<# parameters.snapshots >>--ng-snapshots<</ parameters.snapshots >> --esbuild --tmpdir=/mnt/ramdisk/e2e --package ./dist/_*.tgz --glob="{tests/basic/**,tests/build/prod-build.ts,tests/build/relative-sourcemap.ts,tests/build/styles/scss.ts,tests/build/styles/include-paths.ts,tests/commands/add/add-pwa.ts}" --ignore="tests/basic/{environment,rebuild,serve,scripts-array}.ts"
247247
- when:
248248
condition:
249249
equal: ['yarn', << parameters.subset >>]
250250
steps:
251251
- run:
252252
name: Execute CLI E2E Tests Subset with Yarn
253253
command: |
254-
node ./tests/legacy-cli/run_e2e --nb-shards=${CIRCLE_NODE_TOTAL} --shard=${CIRCLE_NODE_INDEX} <<# parameters.snapshots >>--ng-snapshots<</ parameters.snapshots >> --yarn --tmpdir=/mnt/ramdisk/e2e --glob="{tests/basic/**,tests/update/**,tests/commands/add/**}"
254+
node ./tests/legacy-cli/run_e2e --nb-shards=${CIRCLE_NODE_TOTAL} --shard=${CIRCLE_NODE_INDEX} <<# parameters.snapshots >>--ng-snapshots<</ parameters.snapshots >> --yarn --tmpdir=/mnt/ramdisk/e2e --package ./dist/_*.tgz --glob="{tests/basic/**,tests/update/**,tests/commands/add/**}"
255255
- fail_fast
256256

257257
test-browsers:
@@ -271,7 +271,7 @@ jobs:
271271
# Waits for the Saucelabs tunnel to be ready. This ensures that we don't run tests
272272
# too early without Saucelabs not being ready.
273273
- run: ./scripts/saucelabs/wait-for-tunnel.sh
274-
- run: node ./tests/legacy-cli/run_e2e ./tests/legacy-cli/e2e/tests/misc/browsers.ts
274+
- run: node ./tests/legacy-cli/run_e2e --package ./dist/_*.tgz --glob="tests/misc/browsers.ts"
275275
- run: ./scripts/saucelabs/stop-tunnel.sh
276276
- fail_fast
277277

@@ -280,6 +280,10 @@ jobs:
280280
steps:
281281
- custom_attach_workspace
282282
- run: yarn build
283+
- persist_to_workspace:
284+
root: *workspace_location
285+
paths:
286+
- dist/_*.tgz
283287

284288
build-bazel-e2e:
285289
executor: action-executor
@@ -363,12 +367,14 @@ jobs:
363367
# Path where Arsenal Image Mounter files are downloaded.
364368
# Must match path in .circleci/win-ram-disk.ps1
365369
- ./aim
370+
# Build the npm packages for the e2e tests
371+
- run: yarn build
366372
# Run partial e2e suite on PRs only. Release branches will run the full e2e suite.
367373
- run:
368374
name: Execute E2E Tests
369375
command: |
370376
mkdir X:/ramdisk/e2e-main
371-
node tests\legacy-cli\run_e2e.js --nb-shards=$env:CIRCLE_NODE_TOTAL --shard=$env:CIRCLE_NODE_INDEX --tmpdir=X:/ramdisk/e2e-main --ignore="tests/misc/browsers.ts"
377+
node tests\legacy-cli\run_e2e.js --nb-shards=$env:CIRCLE_NODE_TOTAL --shard=$env:CIRCLE_NODE_INDEX --tmpdir=X:/ramdisk/e2e-main --ignore="tests/misc/browsers.ts" --package ./dist/_*.tgz
372378
- fail_fast
373379

374380
workflows:

lib/BUILD.bazel

-12
This file was deleted.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
"@types/progress": "^2.0.3",
120120
"@types/resolve": "^1.17.1",
121121
"@types/semver": "^7.3.12",
122+
"@types/tar": "^6.1.2",
122123
"@types/text-table": "^0.2.1",
123124
"@types/uuid": "^8.0.0",
124125
"@types/yargs": "^17.0.8",

tests/legacy-cli/e2e/setup/010-local-publish.ts

+21-18
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,26 @@ import { isPrereleaseCli } from '../utils/project';
44

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

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/project.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ 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';
@@ -95,15 +94,17 @@ export async function prepareProjectForE2e(name: string) {
9594
}
9695

9796
export function useBuiltPackagesVersions(): Promise<void> {
97+
const packages = getGlobalVariable('package-tars');
98+
9899
return updateJsonFile('package.json', (json) => {
99100
json['dependencies'] ??= {};
100101
json['devDependencies'] ??= {};
101102

102103
for (const packageName of Object.keys(packages)) {
103104
if (packageName in json['dependencies']) {
104-
json['dependencies'][packageName] = packages[packageName].version;
105+
json['dependencies'][packageName] = packages[packageName];
105106
} else if (packageName in json['devDependencies']) {
106-
json['devDependencies'][packageName] = packages[packageName].version;
107+
json['devDependencies'][packageName] = packages[packageName];
107108
}
108109
}
109110
});
@@ -220,7 +221,7 @@ export async function useCIChrome(projectDir: string = ''): Promise<void> {
220221
}
221222
}
222223

223-
export const NgCLIVersion = new SemVer(packages['@angular/cli'].version);
224+
export const NgCLIVersion = new SemVer(require('../../../../package.json').version);
224225

225226
export function isPrereleaseCli(): boolean {
226227
return (prerelease(NgCLIVersion)?.length ?? 0) > 0;

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

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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 { Parse } from 'tar';
11+
12+
/**
13+
* Extract and return the contents of a single file out of a tar file.
14+
*
15+
* @param tarball the tar file to extract from
16+
* @param filePath the path of the file to extract
17+
* @returns the Buffer of file or an error on fs/tar error or file not found
18+
*/
19+
export async function extractFile(tarball: string, filePath: string): Promise<Buffer> {
20+
return new Promise((resolve, reject) => {
21+
let chunks: Buffer[] | null = null;
22+
23+
fs.createReadStream(tarball)
24+
.pipe(
25+
new Parse({
26+
strict: true,
27+
filter: (p) => p === filePath,
28+
// TODO: @types/tar 'entry' does not have ReadEntry.on
29+
onentry: (entry: any) => {
30+
chunks = [];
31+
32+
entry.on('data', (chunk: any) => chunks!.push(chunk));
33+
entry.on('error', reject);
34+
entry.on('finish', () => resolve(Buffer.concat(chunks!)));
35+
},
36+
}),
37+
)
38+
.on('close', reject);
39+
});
40+
}

tests/legacy-cli/e2e_runner.ts

+28-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ 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';
1315

1416
Error.stackTraceLimit = Infinity;
1517

@@ -34,6 +36,8 @@ Error.stackTraceLimit = Infinity;
3436
* --shard Index of this processes' shard.
3537
* --tmpdir=path Override temporary directory to use for new projects.
3638
* --yarn Use yarn as package manager.
39+
* --package=path An npm package to be published before running tests
40+
*
3741
* If unnamed flags are passed in, the list of tests will be filtered to include only those passed.
3842
*/
3943
const argv = yargsParser(process.argv.slice(2), {
@@ -49,6 +53,7 @@ const argv = yargsParser(process.argv.slice(2), {
4953
],
5054
string: ['devkit', 'glob', 'ignore', 'reuse', 'ng-tag', 'tmpdir', 'ng-version'],
5155
number: ['nb-shards', 'shard'],
56+
array: ['package'],
5257
configuration: {
5358
'dot-notation': false,
5459
'camel-case-expansion': false,
@@ -163,10 +168,11 @@ setGlobalVariable('argv', argv);
163168
setGlobalVariable('ci', process.env['CI']?.toLowerCase() === 'true' || process.env['CI'] === '1');
164169
setGlobalVariable('package-manager', argv.yarn ? 'yarn' : 'npm');
165170

166-
Promise.all([findFreePort(), findFreePort()])
167-
.then(async ([httpPort, httpsPort]) => {
171+
Promise.all([findFreePort(), findFreePort(), findPackageTars()])
172+
.then(async ([httpPort, httpsPort, packageTars]) => {
168173
setGlobalVariable('package-registry', 'http://localhost:' + httpPort);
169174
setGlobalVariable('package-secure-registry', 'http://localhost:' + httpsPort);
175+
setGlobalVariable('package-tars', packageTars);
170176

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

yarn.lock

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

2500+
"@types/tar@^6.1.2":
2501+
version "6.1.2"
2502+
resolved "https://registry.yarnpkg.com/@types/tar/-/tar-6.1.2.tgz#e60108a7d1b08cc91bf2faf1286cc08fdad48bbe"
2503+
integrity sha512-bnX3RRm70/n1WMwmevdOAeDU4YP7f5JSubgnuU+yrO+xQQjwDboJj3u2NTJI5ngCQhXihqVVAH5h5J8YpdpEvg==
2504+
dependencies:
2505+
"@types/node" "*"
2506+
minipass "^3.3.5"
2507+
25002508
"@types/text-table@^0.2.1":
25012509
version "0.2.2"
25022510
resolved "https://registry.yarnpkg.com/@types/text-table/-/text-table-0.2.2.tgz#774c90cfcfbc8b4b0ebb00fecbe861dc8b1e8e26"
@@ -7519,6 +7527,13 @@ minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6:
75197527
dependencies:
75207528
yallist "^4.0.0"
75217529

7530+
minipass@^3.3.5:
7531+
version "3.3.5"
7532+
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.5.tgz#6da7e53a48db8a856eeb9153d85b230a2119e819"
7533+
integrity sha512-rQ/p+KfKBkeNwo04U15i+hOwoVBVmekmm/HcfTkTN2t9pbQKCMm4eN5gFeqgrrSp/kH/7BYYhTIHOxGqzbBPaA==
7534+
dependencies:
7535+
yallist "^4.0.0"
7536+
75227537
minizlib@^2.1.1, minizlib@^2.1.2:
75237538
version "2.1.2"
75247539
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"

0 commit comments

Comments
 (0)