@angular-devkit/build-angular 17.1.0-next.3 → 17.1.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/builders.json +5 -0
  2. package/package.json +22 -18
  3. package/src/builders/app-shell/index.js +7 -0
  4. package/src/builders/application/execute-build.js +9 -3
  5. package/src/builders/application/options.js +3 -4
  6. package/src/builders/application/schema.d.ts +1 -1
  7. package/src/builders/application/schema.json +1 -1
  8. package/src/builders/dev-server/vite-server.js +2 -1
  9. package/src/builders/jest/index.js +2 -2
  10. package/src/builders/prerender/index.js +7 -0
  11. package/src/builders/web-test-runner/builder-status-warnings.d.ts +11 -0
  12. package/src/builders/web-test-runner/builder-status-warnings.js +46 -0
  13. package/src/builders/web-test-runner/index.d.ts +10 -0
  14. package/src/builders/web-test-runner/index.js +151 -0
  15. package/src/builders/web-test-runner/jasmine_runner.js +88 -0
  16. package/src/builders/web-test-runner/options.d.ts +24 -0
  17. package/src/builders/web-test-runner/options.js +26 -0
  18. package/src/builders/web-test-runner/schema.d.ts +188 -0
  19. package/src/builders/web-test-runner/schema.js +15 -0
  20. package/src/builders/web-test-runner/schema.json +291 -0
  21. package/src/builders/web-test-runner/test_page.html +40 -0
  22. package/src/tools/esbuild/angular/component-stylesheets.js +5 -14
  23. package/src/tools/esbuild/cache.d.ts +88 -0
  24. package/src/tools/esbuild/cache.js +92 -0
  25. package/src/tools/esbuild/javascript-transformer-worker.d.ts +2 -2
  26. package/src/tools/esbuild/javascript-transformer-worker.js +12 -5
  27. package/src/tools/esbuild/javascript-transformer.d.ts +3 -1
  28. package/src/tools/esbuild/javascript-transformer.js +42 -17
  29. package/src/tools/esbuild/stylesheets/sass-language.js +3 -12
  30. package/src/tools/sass/lexer.d.ts +0 -11
  31. package/src/tools/sass/lexer.js +1 -87
  32. package/src/{builders/jest → utils}/test-files.d.ts +1 -2
  33. package/src/{builders/jest → utils}/test-files.js +3 -3
package/builders.json CHANGED
@@ -41,6 +41,11 @@
41
41
  "schema": "./src/builders/karma/schema.json",
42
42
  "description": "Run Karma unit tests."
43
43
  },
44
+ "web-test-runner": {
45
+ "implementation": "./src/builders/web-test-runner",
46
+ "schema": "./src/builders/web-test-runner/schema.json",
47
+ "description": "Run unit tests with Web Test Runner."
48
+ },
44
49
  "protractor": {
45
50
  "implementation": "./src/builders/protractor",
46
51
  "schema": "./src/builders/protractor/schema.json",
package/package.json CHANGED
@@ -1,26 +1,26 @@
1
1
  {
2
2
  "name": "@angular-devkit/build-angular",
3
- "version": "17.1.0-next.3",
3
+ "version": "17.1.0-rc.0",
4
4
  "description": "Angular Webpack Build Facade",
5
5
  "main": "src/index.js",
6
6
  "typings": "src/index.d.ts",
7
7
  "builders": "builders.json",
8
8
  "dependencies": {
9
9
  "@ampproject/remapping": "2.2.1",
10
- "@angular-devkit/architect": "0.1701.0-next.3",
11
- "@angular-devkit/build-webpack": "0.1701.0-next.3",
12
- "@angular-devkit/core": "17.1.0-next.3",
13
- "@babel/core": "7.23.6",
10
+ "@angular-devkit/architect": "0.1701.0-rc.0",
11
+ "@angular-devkit/build-webpack": "0.1701.0-rc.0",
12
+ "@angular-devkit/core": "17.1.0-rc.0",
13
+ "@babel/core": "7.23.7",
14
14
  "@babel/generator": "7.23.6",
15
15
  "@babel/helper-annotate-as-pure": "7.22.5",
16
16
  "@babel/helper-split-export-declaration": "7.22.6",
17
- "@babel/plugin-transform-async-generator-functions": "7.23.4",
17
+ "@babel/plugin-transform-async-generator-functions": "7.23.7",
18
18
  "@babel/plugin-transform-async-to-generator": "7.23.3",
19
- "@babel/plugin-transform-runtime": "7.23.6",
20
- "@babel/preset-env": "7.23.6",
21
- "@babel/runtime": "7.23.6",
19
+ "@babel/plugin-transform-runtime": "7.23.7",
20
+ "@babel/preset-env": "7.23.7",
21
+ "@babel/runtime": "7.23.7",
22
22
  "@discoveryjs/json-ext": "0.5.7",
23
- "@ngtools/webpack": "17.1.0-next.3",
23
+ "@ngtools/webpack": "17.1.0-rc.0",
24
24
  "@vitejs/plugin-basic-ssl": "1.0.2",
25
25
  "ansi-colors": "4.1.3",
26
26
  "autoprefixer": "10.4.16",
@@ -30,7 +30,7 @@
30
30
  "copy-webpack-plugin": "11.0.0",
31
31
  "critters": "0.0.20",
32
32
  "css-loader": "6.8.1",
33
- "esbuild-wasm": "0.19.10",
33
+ "esbuild-wasm": "0.19.11",
34
34
  "fast-glob": "3.3.2",
35
35
  "https-proxy-agent": "7.0.2",
36
36
  "http-proxy-middleware": "2.0.6",
@@ -50,19 +50,19 @@
50
50
  "picomatch": "3.0.1",
51
51
  "piscina": "4.2.1",
52
52
  "postcss": "8.4.32",
53
- "postcss-loader": "7.3.3",
53
+ "postcss-loader": "7.3.4",
54
54
  "resolve-url-loader": "5.0.0",
55
55
  "rxjs": "7.8.1",
56
- "sass": "1.69.5",
57
- "sass-loader": "13.3.2",
56
+ "sass": "1.69.7",
57
+ "sass-loader": "13.3.3",
58
58
  "semver": "7.5.4",
59
- "source-map-loader": "4.0.1",
59
+ "source-map-loader": "4.0.2",
60
60
  "source-map-support": "0.5.21",
61
61
  "terser": "5.26.0",
62
62
  "text-table": "0.2.0",
63
63
  "tree-kill": "1.2.2",
64
64
  "tslib": "2.6.2",
65
- "undici": "6.2.0",
65
+ "undici": "6.2.1",
66
66
  "vite": "5.0.10",
67
67
  "watchpack": "2.4.0",
68
68
  "webpack": "5.89.0",
@@ -72,14 +72,15 @@
72
72
  "webpack-subresource-integrity": "5.1.0"
73
73
  },
74
74
  "optionalDependencies": {
75
- "esbuild": "0.19.10"
75
+ "esbuild": "0.19.11"
76
76
  },
77
77
  "peerDependencies": {
78
78
  "@angular/compiler-cli": "^17.0.0 || ^17.1.0-next.0",
79
79
  "@angular/localize": "^17.0.0 || ^17.1.0-next.0",
80
80
  "@angular/platform-server": "^17.0.0 || ^17.1.0-next.0",
81
81
  "@angular/service-worker": "^17.0.0 || ^17.1.0-next.0",
82
- "browser-sync": "^2.29.3",
82
+ "@web/test-runner": "^0.17.3",
83
+ "browser-sync": "^3.0.2",
83
84
  "jest": "^29.5.0",
84
85
  "jest-environment-jsdom": "^29.5.0",
85
86
  "karma": "^6.3.0",
@@ -98,6 +99,9 @@
98
99
  "@angular/service-worker": {
99
100
  "optional": true
100
101
  },
102
+ "@web/test-runner": {
103
+ "optional": true
104
+ },
101
105
  "browser-sync": {
102
106
  "optional": true
103
107
  },
@@ -139,6 +139,13 @@ async function _appShellBuilder(options, context) {
139
139
  serviceWorker: false,
140
140
  optimization: optimization,
141
141
  });
142
+ if (browserTargetRun.info.builderName === '@angular-devkit/build-angular:application') {
143
+ return {
144
+ success: false,
145
+ error: '"@angular-devkit/build-angular:application" has built-in app-shell generation capabilities. ' +
146
+ 'The "appShell" option should be used instead.',
147
+ };
148
+ }
142
149
  const serverTargetRun = await context.scheduleTarget(serverTarget, {
143
150
  watch: false,
144
151
  });
@@ -93,9 +93,15 @@ async function executeBuild(options, context, rebuildState) {
93
93
  }
94
94
  // Analyze external imports if external options are enabled
95
95
  if (options.externalPackages || bundlingResult.externalConfiguration) {
96
- const { browser = new Set(), server = new Set() } = bundlingResult.externalImports;
97
- // TODO: Filter externalImports to generate third argument to support wildcard externalConfiguration values
98
- executionResult.setExternalMetadata([...browser], [...server], bundlingResult.externalConfiguration);
96
+ const { externalConfiguration, externalImports: { browser, server }, } = bundlingResult;
97
+ const implicitBrowser = browser ? [...browser] : [];
98
+ const implicitServer = server ? [...server] : [];
99
+ // TODO: Implement wildcard externalConfiguration filtering
100
+ executionResult.setExternalMetadata(externalConfiguration
101
+ ? implicitBrowser.filter((value) => !externalConfiguration.includes(value))
102
+ : implicitBrowser, externalConfiguration
103
+ ? implicitServer.filter((value) => !externalConfiguration.includes(value))
104
+ : implicitServer, externalConfiguration);
99
105
  }
100
106
  const { metafile, initialFiles, outputFiles } = bundlingResult;
101
107
  executionResult.outputFiles.push(...outputFiles);
@@ -284,10 +284,9 @@ function normalizeEntryPoints(workspaceRoot, browser, entryPoints = new Set()) {
284
284
  const entryPointName = node_path_1.default.isAbsolute(entryPoint)
285
285
  ? parsedEntryPoint.name
286
286
  : node_path_1.default.join(parsedEntryPoint.dir, parsedEntryPoint.name);
287
- // Get the full file path to the entry point input.
288
- const entryPointPath = node_path_1.default.isAbsolute(entryPoint)
289
- ? entryPoint
290
- : node_path_1.default.join(workspaceRoot, entryPoint);
287
+ // Get the full file path to a relative entry point input. Leave bare specifiers alone so they are resolved as modules.
288
+ const isRelativePath = entryPoint.startsWith('.');
289
+ const entryPointPath = isRelativePath ? node_path_1.default.join(workspaceRoot, entryPoint) : entryPoint;
291
290
  // Check for conflicts with previous entry points.
292
291
  const existingEntryPointPath = entryPointPaths[entryPointName];
293
292
  if (existingEntryPointPath) {
@@ -291,7 +291,7 @@ export interface IndexObject {
291
291
  */
292
292
  output?: string;
293
293
  /**
294
- * Generates 'preload', `modulepreload', and 'preconnect' link elements for initial
294
+ * Generates 'preload', 'modulepreload', and 'preconnect' link elements for initial
295
295
  * application files and resources.
296
296
  */
297
297
  preloadInitial?: boolean;
@@ -420,7 +420,7 @@
420
420
  "preloadInitial": {
421
421
  "type": "boolean",
422
422
  "default": true,
423
- "description": "Generates 'preload', `modulepreload', and 'preconnect' link elements for initial application files and resources."
423
+ "description": "Generates 'preload', 'modulepreload', and 'preconnect' link elements for initial application files and resources."
424
424
  }
425
425
  },
426
426
  "required": ["input"]
@@ -96,7 +96,7 @@ async function* serveWithVite(serverOptions, builderName, context, transformers,
96
96
  // Always enable JIT linking to support applications built with and without AOT.
97
97
  // In a development environment the additional scope information does not
98
98
  // have a negative effect unlike production where final output size is relevant.
99
- { sourcemap: true, jit: true, thirdPartySourcemaps }, 1, true);
99
+ { sourcemap: true, jit: true, thirdPartySourcemaps }, 1);
100
100
  // Extract output index from options
101
101
  // TODO: Provide this info from the build results
102
102
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -615,6 +615,7 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
615
615
  }
616
616
  else {
617
617
  const { default: basicSslPlugin } = await Promise.resolve().then(() => __importStar(require('@vitejs/plugin-basic-ssl')));
618
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
618
619
  configuration.plugins ??= [];
619
620
  configuration.plugins.push(basicSslPlugin());
620
621
  }
@@ -35,10 +35,10 @@ const child_process_1 = require("child_process");
35
35
  const path = __importStar(require("path"));
36
36
  const util_1 = require("util");
37
37
  const color_1 = require("../../utils/color");
38
+ const test_files_1 = require("../../utils/test-files");
38
39
  const application_1 = require("../application");
39
40
  const schema_1 = require("../browser-esbuild/schema");
40
41
  const options_1 = require("./options");
41
- const test_files_1 = require("./test-files");
42
42
  const execFile = (0, util_1.promisify)(child_process_1.execFile);
43
43
  /** Main execution function for the Jest builder. */
44
44
  exports.default = (0, architect_1.createBuilder)(async (schema, context) => {
@@ -66,7 +66,7 @@ exports.default = (0, architect_1.createBuilder)(async (schema, context) => {
66
66
  };
67
67
  }
68
68
  // Build all the test files.
69
- const testFiles = await (0, test_files_1.findTestFiles)(options, context.workspaceRoot);
69
+ const testFiles = await (0, test_files_1.findTestFiles)(options.include, options.exclude, context.workspaceRoot);
70
70
  const jestGlobal = path.join(__dirname, 'jest-global.mjs');
71
71
  const initTestBed = path.join(__dirname, 'init-test-bed.mjs');
72
72
  const buildResult = await build(context, {
@@ -93,6 +93,13 @@ async function _scheduleBuilds(options, context) {
93
93
  serviceWorker: false,
94
94
  // todo: handle service worker augmentation
95
95
  });
96
+ if (browserTargetRun.info.builderName === '@angular-devkit/build-angular:application') {
97
+ return {
98
+ success: false,
99
+ error: '"@angular-devkit/build-angular:application" has built-in prerendering capabilities. ' +
100
+ 'The "prerender" option should be used instead.',
101
+ };
102
+ }
96
103
  const serverTargetRun = await context.scheduleTarget(serverTarget, {
97
104
  watch: false,
98
105
  });
@@ -0,0 +1,11 @@
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
+ import { BuilderContext } from '@angular-devkit/architect';
9
+ import { Schema as WtrBuilderOptions } from './schema';
10
+ /** Logs a warning for any unsupported options specified. */
11
+ export declare function logBuilderStatusWarnings(options: WtrBuilderOptions, ctx: BuilderContext): void;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.io/license
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.logBuilderStatusWarnings = void 0;
11
+ const UNSUPPORTED_OPTIONS = [
12
+ 'main',
13
+ 'assets',
14
+ 'scripts',
15
+ 'styles',
16
+ 'inlineStyleLanguage',
17
+ 'stylePreprocessorOptions',
18
+ 'sourceMap',
19
+ 'progress',
20
+ 'poll',
21
+ 'preserveSymlinks',
22
+ 'browsers',
23
+ 'codeCoverage',
24
+ 'codeCoverageExclude',
25
+ 'fileReplacements',
26
+ 'webWorkerTsConfig',
27
+ 'watch',
28
+ ];
29
+ /** Logs a warning for any unsupported options specified. */
30
+ function logBuilderStatusWarnings(options, ctx) {
31
+ // Validate supported options
32
+ for (const unsupportedOption of UNSUPPORTED_OPTIONS) {
33
+ const value = options[unsupportedOption];
34
+ if (value === undefined || value === false) {
35
+ continue;
36
+ }
37
+ if (Array.isArray(value) && value.length === 0) {
38
+ continue;
39
+ }
40
+ if (typeof value === 'object' && Object.keys(value).length === 0) {
41
+ continue;
42
+ }
43
+ ctx.logger.warn(`The '${unsupportedOption}' option is not yet supported by this builder.`);
44
+ }
45
+ }
46
+ exports.logBuilderStatusWarnings = logBuilderStatusWarnings;
@@ -0,0 +1,10 @@
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
+ import { Schema } from './schema';
9
+ declare const _default: import("../../../../architect/src/internal").Builder<Schema & import("../../../../core/src").JsonObject>;
10
+ export default _default;
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.io/license
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ const architect_1 = require("@angular-devkit/architect");
14
+ const node_fs_1 = require("node:fs");
15
+ const node_module_1 = require("node:module");
16
+ const node_path_1 = __importDefault(require("node:path"));
17
+ const test_files_1 = require("../../utils/test-files");
18
+ const application_1 = require("../application");
19
+ const schema_1 = require("../browser-esbuild/schema");
20
+ const builder_status_warnings_1 = require("./builder-status-warnings");
21
+ const options_1 = require("./options");
22
+ exports.default = (0, architect_1.createBuilder)(async (schema, ctx) => {
23
+ ctx.logger.warn('NOTE: The Web Test Runner builder is currently EXPERIMENTAL and not ready for production use.');
24
+ (0, builder_status_warnings_1.logBuilderStatusWarnings)(schema, ctx);
25
+ // Dynamic import `@web/test-runner` from the user's workspace. As an optional peer dep, it may not be installed
26
+ // and may not be resolvable from `@angular-devkit/build-angular`.
27
+ const require = (0, node_module_1.createRequire)(`${ctx.workspaceRoot}/`);
28
+ let wtr;
29
+ try {
30
+ wtr = require('@web/test-runner');
31
+ }
32
+ catch {
33
+ return {
34
+ success: false,
35
+ // TODO(dgp1130): Display a more accurate message for non-NPM users.
36
+ error: 'Web Test Runner is not installed, most likely you need to run `npm install @web/test-runner --save-dev` in your project.',
37
+ };
38
+ }
39
+ const options = (0, options_1.normalizeOptions)(schema);
40
+ const testDir = 'dist/test-out';
41
+ // Parallelize startup work.
42
+ const [testFiles] = await Promise.all([
43
+ // Glob for files to test.
44
+ (0, test_files_1.findTestFiles)(options.include, options.exclude, ctx.workspaceRoot).then((files) => Array.from(files).map((file) => node_path_1.default.relative(process.cwd(), file))),
45
+ // Clean build output path.
46
+ node_fs_1.promises.rm(testDir, { recursive: true, force: true }),
47
+ ]);
48
+ // Build the tests and abort on any build failure.
49
+ const buildOutput = await buildTests(testFiles, testDir, options, ctx);
50
+ if (!buildOutput.success) {
51
+ return buildOutput;
52
+ }
53
+ // Run the built tests.
54
+ return await runTests(wtr, `${testDir}/browser`, options);
55
+ });
56
+ /** Build all the given test files and write the result to the given output path. */
57
+ async function buildTests(testFiles, outputPath, options, ctx) {
58
+ const entryPoints = new Set([
59
+ ...testFiles,
60
+ 'jasmine-core/lib/jasmine-core/jasmine.js',
61
+ '@angular-devkit/build-angular/src/builders/web-test-runner/jasmine_runner.js',
62
+ ]);
63
+ // Extract `zone.js/testing` to a separate entry point because it needs to be loaded after Jasmine.
64
+ const [polyfills, hasZoneTesting] = extractZoneTesting(options.polyfills);
65
+ if (hasZoneTesting) {
66
+ entryPoints.add('zone.js/testing');
67
+ }
68
+ // Build tests with `application` builder, using test files as entry points.
69
+ // Also bundle in Jasmine and the Jasmine runner script, which need to share chunked dependencies.
70
+ const buildOutput = await first((0, application_1.buildApplicationInternal)({
71
+ entryPoints,
72
+ tsConfig: options.tsConfig,
73
+ outputPath,
74
+ aot: false,
75
+ index: false,
76
+ outputHashing: schema_1.OutputHashing.None,
77
+ optimization: false,
78
+ externalDependencies: [
79
+ // Resolved by `@web/test-runner` at runtime with dynamically generated code.
80
+ '@web/test-runner-core',
81
+ ],
82
+ sourceMap: {
83
+ scripts: true,
84
+ styles: true,
85
+ vendor: true,
86
+ },
87
+ polyfills,
88
+ }, ctx));
89
+ return buildOutput;
90
+ }
91
+ function extractZoneTesting(polyfills) {
92
+ const polyfillsWithoutZoneTesting = polyfills.filter((polyfill) => polyfill !== 'zone.js/testing');
93
+ const hasZoneTesting = polyfills.length !== polyfillsWithoutZoneTesting.length;
94
+ return [polyfillsWithoutZoneTesting, hasZoneTesting];
95
+ }
96
+ /** Run Web Test Runner on the given directory of bundled JavaScript tests. */
97
+ async function runTests(wtr, testDir, options) {
98
+ const testPagePath = node_path_1.default.resolve(__dirname, 'test_page.html');
99
+ const testPage = await node_fs_1.promises.readFile(testPagePath, 'utf8');
100
+ const runner = await wtr.startTestRunner({
101
+ config: {
102
+ rootDir: testDir,
103
+ files: [
104
+ `${testDir}/**/*.js`,
105
+ `!${testDir}/polyfills.js`,
106
+ `!${testDir}/chunk-*.js`,
107
+ `!${testDir}/jasmine.js`,
108
+ `!${testDir}/jasmine_runner.js`,
109
+ `!${testDir}/testing.js`, // `zone.js/testing`
110
+ ],
111
+ testFramework: {
112
+ config: {
113
+ defaultTimeoutInterval: 5000,
114
+ },
115
+ },
116
+ nodeResolve: true,
117
+ port: 9876,
118
+ watch: options.watch ?? false,
119
+ testRunnerHtml: (_testFramework, _config) => testPage,
120
+ },
121
+ readCliArgs: false,
122
+ readFileConfig: false,
123
+ autoExitProcess: false,
124
+ });
125
+ if (!runner) {
126
+ throw new Error('Failed to start Web Test Runner.');
127
+ }
128
+ // Wait for the tests to complete and stop the runner.
129
+ const passed = (await once(runner, 'finished'));
130
+ await runner.stop();
131
+ // No need to return error messages because Web Test Runner already printed them to the console.
132
+ return { success: passed };
133
+ }
134
+ /** Returns the first item yielded by the given generator and cancels the execution. */
135
+ async function first(generator) {
136
+ for await (const value of generator) {
137
+ return value;
138
+ }
139
+ throw new Error('Expected generator to emit at least once.');
140
+ }
141
+ /** Listens for a single emission of an event and returns the value emitted. */
142
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
143
+ function once(emitter, event) {
144
+ return new Promise((resolve) => {
145
+ const onEmit = (arg) => {
146
+ emitter.off(event, onEmit);
147
+ resolve(arg);
148
+ };
149
+ emitter.on(event, onEmit);
150
+ });
151
+ }
@@ -0,0 +1,88 @@
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 { getTestBed } from '@angular/core/testing';
10
+ import {
11
+ BrowserDynamicTestingModule,
12
+ platformBrowserDynamicTesting,
13
+ } from '@angular/platform-browser-dynamic/testing';
14
+ import {
15
+ getConfig,
16
+ sessionFailed,
17
+ sessionFinished,
18
+ sessionStarted,
19
+ } from '@web/test-runner-core/browser/session.js';
20
+
21
+ /** Executes Angular Jasmine tests in the given environment and reports the results to Web Test Runner. */
22
+ export async function runJasmineTests(jasmineEnv) {
23
+ const allSpecs = [];
24
+ const failedSpecs = [];
25
+
26
+ jasmineEnv.addReporter({
27
+ specDone(result) {
28
+ const expectations = [...result.passedExpectations, ...result.failedExpectations];
29
+ allSpecs.push(...expectations.map((e) => ({ name: e.fullName, passed: e.passed })));
30
+
31
+ for (const e of result.failedExpectations) {
32
+ const message = `${result.fullName}\n${e.message}\n${e.stack}`;
33
+ // eslint-disable-next-line no-console
34
+ console.error(message);
35
+ failedSpecs.push({
36
+ message,
37
+ name: e.fullName,
38
+ stack: e.stack,
39
+ expected: e.expected,
40
+ actual: e.actual,
41
+ });
42
+ }
43
+ },
44
+
45
+ async jasmineDone(result) {
46
+ // eslint-disable-next-line no-console
47
+ console.log(`Tests ${result.overallStatus}!`);
48
+ await sessionFinished({
49
+ passed: result.overallStatus === 'passed',
50
+ errors: failedSpecs,
51
+ testResults: {
52
+ name: '',
53
+ suites: [],
54
+ tests: allSpecs,
55
+ },
56
+ });
57
+ },
58
+ });
59
+
60
+ await sessionStarted();
61
+
62
+ // Web Test Runner uses a different HTML page for every test, so we only get one `testFile` for the single `*.js` file we need to execute.
63
+ const { testFile, testFrameworkConfig } = await getConfig();
64
+ const config = { defaultTimeoutInterval: 60_000, ...(testFrameworkConfig ?? {}) };
65
+
66
+ // eslint-disable-next-line no-undef
67
+ jasmine.DEFAULT_TIMEOUT_INTERVAL = config.defaultTimeoutInterval;
68
+
69
+ // Initialize `TestBed` automatically for users. This assumes we already evaluated `zone.js/testing`.
70
+ getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
71
+ errorOnUnknownElements: true,
72
+ errorOnUnknownProperties: true,
73
+ });
74
+
75
+ // Load the test file and evaluate it.
76
+ try {
77
+ // eslint-disable-next-line no-undef
78
+ await import(new URL(testFile, document.baseURI).href);
79
+
80
+ // Execute the test functions.
81
+ // eslint-disable-next-line no-undef
82
+ jasmineEnv.execute();
83
+ } catch (err) {
84
+ // eslint-disable-next-line no-console
85
+ console.error(err);
86
+ await sessionFailed(err);
87
+ }
88
+ }
@@ -0,0 +1,24 @@
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
+ import { Schema as WtrBuilderSchema } from './schema';
9
+ /**
10
+ * Options supported for the Web Test Runner builder. The schema is an approximate
11
+ * representation of the options type, but this is a more precise version.
12
+ */
13
+ export type WtrBuilderOptions = Overwrite<WtrBuilderSchema, {
14
+ include: string[];
15
+ exclude: string[];
16
+ polyfills: string[];
17
+ }>;
18
+ type Overwrite<Obj extends {}, Overrides extends {}> = Omit<Obj, keyof Overrides> & Overrides;
19
+ /**
20
+ * Normalizes input options validated by the schema to a more precise and useful
21
+ * options type in {@link WtrBuilderOptions}.
22
+ */
23
+ export declare function normalizeOptions(schema: WtrBuilderSchema): WtrBuilderOptions;
24
+ export {};
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.io/license
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.normalizeOptions = void 0;
11
+ /**
12
+ * Normalizes input options validated by the schema to a more precise and useful
13
+ * options type in {@link WtrBuilderOptions}.
14
+ */
15
+ function normalizeOptions(schema) {
16
+ return {
17
+ ...schema,
18
+ // Options with default values can't actually be null, even if the types say so.
19
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
20
+ include: schema.include,
21
+ exclude: schema.exclude,
22
+ /* eslint-enable @typescript-eslint/no-non-null-assertion */
23
+ polyfills: typeof schema.polyfills === 'string' ? [schema.polyfills] : schema.polyfills ?? [],
24
+ };
25
+ }
26
+ exports.normalizeOptions = normalizeOptions;