forked from angular/angular-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathoptions.ts
More file actions
142 lines (127 loc) · 5.07 KB
/
options.ts
File metadata and controls
142 lines (127 loc) · 5.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import { type BuilderContext, targetFromTargetString } from '@angular-devkit/architect';
import { constants, promises as fs } from 'node:fs';
import path from 'node:path';
import { normalizeCacheOptions } from '../../utils/normalize-cache';
import { getProjectRootPaths } from '../../utils/project-metadata';
import { isTTY } from '../../utils/tty';
import { Runner, type Schema as UnitTestBuilderOptions } from './schema';
export type NormalizedUnitTestBuilderOptions = Awaited<ReturnType<typeof normalizeOptions>>;
async function exists(path: string): Promise<boolean> {
try {
await fs.access(path, constants.F_OK);
return true;
} catch {
return false;
}
}
function normalizeReporterOption(
reporters: unknown[] | undefined,
): [string, Record<string, unknown>][] | undefined {
return reporters?.map((entry) =>
typeof entry === 'string'
? ([entry, {}] as [string, Record<string, unknown>])
: (entry as [string, Record<string, unknown>]),
);
}
export async function normalizeOptions(
context: BuilderContext,
projectName: string,
options: UnitTestBuilderOptions,
) {
// Setup base paths based on workspace root and project information
const workspaceRoot = context.workspaceRoot;
const projectMetadata = await context.getProjectMetadata(projectName);
const { projectRoot, projectSourceRoot } = getProjectRootPaths(workspaceRoot, projectMetadata);
// Gather persistent caching option and provide a project specific cache location
const cacheOptions = normalizeCacheOptions(projectMetadata, workspaceRoot);
cacheOptions.path = path.join(cacheOptions.path, projectName);
// Target specifier defaults to the current project's build target using a development configuration
const buildTargetSpecifier = options.buildTarget ?? `::development`;
const buildTarget = targetFromTargetString(buildTargetSpecifier, projectName, 'build');
const { runner, browsers, progress, filter, browserViewport, ui, runnerConfig } = options;
if (ui && runner !== Runner.Vitest) {
throw new Error('The "ui" option is only available for the "vitest" runner.');
}
const [width, height] = browserViewport?.split('x').map(Number) ?? [];
let tsConfig = options.tsConfig;
if (tsConfig) {
const fullTsConfigPath = path.join(workspaceRoot, tsConfig);
if (!(await exists(fullTsConfigPath))) {
throw new Error(`The specified tsConfig file '${tsConfig}' does not exist.`);
}
} else {
const tsconfigSpecPath = path.join(projectRoot, 'tsconfig.spec.json');
if (await exists(tsconfigSpecPath)) {
// The application builder expects a path relative to the workspace root.
tsConfig = path.relative(workspaceRoot, tsconfigSpecPath);
}
}
let watch = options.watch ?? isTTY();
if (options.ui && options.watch === false) {
context.logger.warn(
`The '--ui' option requires watch mode. The '--no-watch' flag will be ignored.`,
);
watch = true;
}
return {
// Project/workspace information
workspaceRoot,
projectRoot,
projectSourceRoot,
cacheOptions,
// Target/configuration specified options
buildTarget,
include: options.include ?? ['**/*.spec.ts'],
exclude: options.exclude,
filter,
runnerName: runner ?? Runner.Vitest,
headless: options.headless,
coverage: {
enabled: options.coverage,
exclude: options.coverageExclude,
include: options.coverageInclude,
reporters: normalizeReporterOption(options.coverageReporters),
thresholds: options.coverageThresholds,
// The schema generation tool doesn't support tuple types for items, but the schema validation
// does ensure that the array has exactly two numbers.
watermarks: options.coverageWatermarks as {
statements?: [number, number];
branches?: [number, number];
functions?: [number, number];
lines?: [number, number];
},
},
tsConfig,
buildProgress: progress,
reporters: normalizeReporterOption(options.reporters),
outputFile: options.outputFile,
browsers: browsers?.length ? browsers : undefined,
browserViewport: width && height ? { width, height } : undefined,
watch,
debug: options.debug ?? false,
ui: process.env['CI'] ? false : ui,
providersFile: options.providersFile && path.join(workspaceRoot, options.providersFile),
setupFiles: options.setupFiles
? options.setupFiles.map((setupFile) => path.join(workspaceRoot, setupFile))
: [],
dumpVirtualFiles: options.dumpVirtualFiles,
listTests: options.listTests,
update: options.update ?? false,
runnerConfig:
typeof runnerConfig === 'string'
? runnerConfig.length === 0
? true
: path.resolve(workspaceRoot, runnerConfig)
: runnerConfig,
};
}
export function injectTestingPolyfills(polyfills: string[] = []): string[] {
return polyfills.includes('zone.js') ? [...polyfills, 'zone.js/testing'] : polyfills;
}