|
6 | 6 | * found in the LICENSE file at https://angular.dev/license
|
7 | 7 | */
|
8 | 8 |
|
9 |
| -import { PathLike, constants, promises as fs } from 'node:fs'; |
10 |
| -import { basename, dirname, extname, join, relative } from 'node:path'; |
11 |
| -import { glob, isDynamicPattern } from 'tinyglobby'; |
12 |
| -import { toPosixPath } from '../../utils/path'; |
13 |
| - |
14 |
| -/* Go through all patterns and find unique list of files */ |
15 |
| -export async function findTests( |
16 |
| - include: string[], |
17 |
| - exclude: string[], |
18 |
| - workspaceRoot: string, |
19 |
| - projectSourceRoot: string, |
20 |
| -): Promise<string[]> { |
21 |
| - const matchingTestsPromises = include.map((pattern) => |
22 |
| - findMatchingTests(pattern, exclude, workspaceRoot, projectSourceRoot), |
23 |
| - ); |
24 |
| - const files = await Promise.all(matchingTestsPromises); |
25 |
| - |
26 |
| - // Unique file names |
27 |
| - return [...new Set(files.flat())]; |
28 |
| -} |
29 |
| - |
30 |
| -interface TestEntrypointsOptions { |
31 |
| - projectSourceRoot: string; |
32 |
| - workspaceRoot: string; |
33 |
| - removeTestExtension?: boolean; |
34 |
| -} |
35 |
| - |
36 |
| -/** Generate unique bundle names for a set of test files. */ |
37 |
| -export function getTestEntrypoints( |
38 |
| - testFiles: string[], |
39 |
| - { projectSourceRoot, workspaceRoot, removeTestExtension }: TestEntrypointsOptions, |
40 |
| -): Map<string, string> { |
41 |
| - const seen = new Set<string>(); |
42 |
| - |
43 |
| - return new Map( |
44 |
| - Array.from(testFiles, (testFile) => { |
45 |
| - const relativePath = removeRoots(testFile, [projectSourceRoot, workspaceRoot]) |
46 |
| - // Strip leading dots and path separators. |
47 |
| - .replace(/^[./\\]+/, '') |
48 |
| - // Replace any path separators with dashes. |
49 |
| - .replace(/[/\\]/g, '-'); |
50 |
| - |
51 |
| - let fileName = basename(relativePath, extname(relativePath)); |
52 |
| - if (removeTestExtension) { |
53 |
| - fileName = fileName.replace(/\.(spec|test)$/, ''); |
54 |
| - } |
55 |
| - |
56 |
| - const baseName = `spec-${fileName}`; |
57 |
| - let uniqueName = baseName; |
58 |
| - let suffix = 2; |
59 |
| - while (seen.has(uniqueName)) { |
60 |
| - uniqueName = `${baseName}-${suffix}`.replace(/([^\w](?:spec|test))-([\d]+)$/, '-$2$1'); |
61 |
| - ++suffix; |
62 |
| - } |
63 |
| - seen.add(uniqueName); |
64 |
| - |
65 |
| - return [uniqueName, testFile]; |
66 |
| - }), |
67 |
| - ); |
68 |
| -} |
69 |
| - |
70 |
| -const removeLeadingSlash = (pattern: string): string => { |
71 |
| - if (pattern.charAt(0) === '/') { |
72 |
| - return pattern.substring(1); |
73 |
| - } |
74 |
| - |
75 |
| - return pattern; |
76 |
| -}; |
77 |
| - |
78 |
| -const removeRelativeRoot = (path: string, root: string): string => { |
79 |
| - if (path.startsWith(root)) { |
80 |
| - return path.substring(root.length); |
81 |
| - } |
82 |
| - |
83 |
| - return path; |
84 |
| -}; |
85 |
| - |
86 |
| -function removeRoots(path: string, roots: string[]): string { |
87 |
| - for (const root of roots) { |
88 |
| - if (path.startsWith(root)) { |
89 |
| - return path.substring(root.length); |
90 |
| - } |
91 |
| - } |
92 |
| - |
93 |
| - return basename(path); |
94 |
| -} |
95 |
| - |
96 |
| -async function findMatchingTests( |
97 |
| - pattern: string, |
98 |
| - ignore: string[], |
99 |
| - workspaceRoot: string, |
100 |
| - projectSourceRoot: string, |
101 |
| -): Promise<string[]> { |
102 |
| - // normalize pattern, glob lib only accepts forward slashes |
103 |
| - let normalizedPattern = toPosixPath(pattern); |
104 |
| - normalizedPattern = removeLeadingSlash(normalizedPattern); |
105 |
| - |
106 |
| - const relativeProjectRoot = toPosixPath(relative(workspaceRoot, projectSourceRoot) + '/'); |
107 |
| - |
108 |
| - // remove relativeProjectRoot to support relative paths from root |
109 |
| - // such paths are easy to get when running scripts via IDEs |
110 |
| - normalizedPattern = removeRelativeRoot(normalizedPattern, relativeProjectRoot); |
111 |
| - |
112 |
| - // special logic when pattern does not look like a glob |
113 |
| - if (!isDynamicPattern(normalizedPattern)) { |
114 |
| - if (await isDirectory(join(projectSourceRoot, normalizedPattern))) { |
115 |
| - normalizedPattern = `${normalizedPattern}/**/*.spec.@(ts|tsx)`; |
116 |
| - } else { |
117 |
| - // see if matching spec file exists |
118 |
| - const fileExt = extname(normalizedPattern); |
119 |
| - // Replace extension to `.spec.ext`. Example: `src/app/app.component.ts`-> `src/app/app.component.spec.ts` |
120 |
| - const potentialSpec = join( |
121 |
| - projectSourceRoot, |
122 |
| - dirname(normalizedPattern), |
123 |
| - `${basename(normalizedPattern, fileExt)}.spec${fileExt}`, |
124 |
| - ); |
125 |
| - |
126 |
| - if (await exists(potentialSpec)) { |
127 |
| - return [potentialSpec]; |
128 |
| - } |
129 |
| - } |
130 |
| - } |
131 |
| - |
132 |
| - // normalize the patterns in the ignore list |
133 |
| - const normalizedIgnorePatternList = ignore.map((pattern: string) => |
134 |
| - removeRelativeRoot(removeLeadingSlash(toPosixPath(pattern)), relativeProjectRoot), |
135 |
| - ); |
136 |
| - |
137 |
| - return glob(normalizedPattern, { |
138 |
| - cwd: projectSourceRoot, |
139 |
| - absolute: true, |
140 |
| - ignore: ['**/node_modules/**', ...normalizedIgnorePatternList], |
141 |
| - }); |
142 |
| -} |
143 |
| - |
144 |
| -async function isDirectory(path: PathLike): Promise<boolean> { |
145 |
| - try { |
146 |
| - const stats = await fs.stat(path); |
147 |
| - |
148 |
| - return stats.isDirectory(); |
149 |
| - } catch { |
150 |
| - return false; |
151 |
| - } |
152 |
| -} |
153 |
| - |
154 |
| -async function exists(path: PathLike): Promise<boolean> { |
155 |
| - try { |
156 |
| - await fs.access(path, constants.F_OK); |
157 |
| - |
158 |
| - return true; |
159 |
| - } catch { |
160 |
| - return false; |
161 |
| - } |
162 |
| -} |
| 9 | +// This file is a compatibility layer that re-exports the test discovery logic from its new location. |
| 10 | +// This is necessary to avoid breaking the Karma builder, which still depends on this file. |
| 11 | +export { findTests, getTestEntrypoints } from '../unit-test/test-discovery'; |
0 commit comments