velocious 1.0.667 → 1.0.669

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 (112) hide show
  1. package/README.md +34 -9
  2. package/build/configuration.js +23 -1
  3. package/build/environment-handlers/node/cli/commands/test/timing-manifest/merge.js +2 -48
  4. package/build/environment-handlers/node/cli/commands/test.js +17 -69
  5. package/build/frontend-models/websocket-channel.js +18 -0
  6. package/build/http-server/client/websocket-session.js +41 -4
  7. package/build/http-server/websocket-channel.js +14 -0
  8. package/build/http-server/websocket-event-log-store.js +85 -12
  9. package/build/http-server/websocket-events-host.js +14 -6
  10. package/build/src/configuration.d.ts +18 -1
  11. package/build/src/configuration.d.ts.map +1 -1
  12. package/build/src/configuration.js +22 -2
  13. package/build/src/environment-handlers/node/cli/commands/test/timing-manifest/merge.d.ts.map +1 -1
  14. package/build/src/environment-handlers/node/cli/commands/test/timing-manifest/merge.js +3 -47
  15. package/build/src/environment-handlers/node/cli/commands/test.d.ts +2 -28
  16. package/build/src/environment-handlers/node/cli/commands/test.d.ts.map +1 -1
  17. package/build/src/environment-handlers/node/cli/commands/test.js +10 -61
  18. package/build/src/frontend-models/websocket-channel.d.ts +9 -0
  19. package/build/src/frontend-models/websocket-channel.d.ts.map +1 -1
  20. package/build/src/frontend-models/websocket-channel.js +16 -1
  21. package/build/src/http-server/client/websocket-session.d.ts +25 -2
  22. package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
  23. package/build/src/http-server/client/websocket-session.js +40 -5
  24. package/build/src/http-server/websocket-channel.d.ts +11 -0
  25. package/build/src/http-server/websocket-channel.d.ts.map +1 -1
  26. package/build/src/http-server/websocket-channel.js +14 -1
  27. package/build/src/http-server/websocket-event-log-store.d.ts +60 -41
  28. package/build/src/http-server/websocket-event-log-store.d.ts.map +1 -1
  29. package/build/src/http-server/websocket-event-log-store.js +79 -12
  30. package/build/src/http-server/websocket-events-host.d.ts +10 -3
  31. package/build/src/http-server/websocket-events-host.d.ts.map +1 -1
  32. package/build/src/http-server/websocket-events-host.js +14 -7
  33. package/build/src/testing/expect-utils.d.ts +1 -51
  34. package/build/src/testing/expect-utils.d.ts.map +1 -1
  35. package/build/src/testing/expect-utils.js +2 -205
  36. package/build/src/testing/expect.d.ts +1 -171
  37. package/build/src/testing/expect.d.ts.map +1 -1
  38. package/build/src/testing/expect.js +2 -667
  39. package/build/src/testing/test-files-finder.d.ts +13 -116
  40. package/build/src/testing/test-files-finder.d.ts.map +1 -1
  41. package/build/src/testing/test-files-finder.js +26 -296
  42. package/build/src/testing/test-filter-parser.d.ts +32 -22
  43. package/build/src/testing/test-filter-parser.d.ts.map +1 -1
  44. package/build/src/testing/test-filter-parser.js +25 -217
  45. package/build/src/testing/test-profile-activity.d.ts +1 -6
  46. package/build/src/testing/test-profile-activity.d.ts.map +1 -1
  47. package/build/src/testing/test-profile-activity.js +2 -12
  48. package/build/src/testing/test-profile-output.d.ts +1 -40
  49. package/build/src/testing/test-profile-output.d.ts.map +1 -1
  50. package/build/src/testing/test-profile-output.js +2 -217
  51. package/build/src/testing/test-profiler.d.ts +15 -659
  52. package/build/src/testing/test-profiler.d.ts.map +1 -1
  53. package/build/src/testing/test-profiler.js +35 -833
  54. package/build/src/testing/test-runner.d.ts +10 -1
  55. package/build/src/testing/test-runner.d.ts.map +1 -1
  56. package/build/src/testing/test-runner.js +26 -4
  57. package/build/src/testing/test-suite-splitter.d.ts +1 -128
  58. package/build/src/testing/test-suite-splitter.d.ts.map +1 -1
  59. package/build/src/testing/test-suite-splitter.js +2 -259
  60. package/build/src/testing/test.d.ts +1 -13
  61. package/build/src/testing/test.d.ts.map +1 -1
  62. package/build/src/testing/test.js +2 -14
  63. package/build/src/testing/timing-manifest.d.ts +5 -93
  64. package/build/src/testing/timing-manifest.d.ts.map +1 -1
  65. package/build/src/testing/timing-manifest.js +4 -283
  66. package/build/src/testing/velocious-attempt-executor.d.ts.map +1 -1
  67. package/build/src/testing/velocious-attempt-executor.js +8 -2
  68. package/build/testing/expect-utils.js +1 -242
  69. package/build/testing/expect.js +1 -760
  70. package/build/testing/test-files-finder.js +29 -345
  71. package/build/testing/test-filter-parser.js +29 -248
  72. package/build/testing/test-profile-activity.js +1 -12
  73. package/build/testing/test-profile-output.js +8 -253
  74. package/build/testing/test-profiler.js +38 -895
  75. package/build/testing/test-runner.js +33 -9
  76. package/build/testing/test-suite-splitter.js +1 -301
  77. package/build/testing/test.js +3 -14
  78. package/build/testing/timing-manifest.js +10 -344
  79. package/build/testing/velocious-attempt-executor.js +7 -1
  80. package/package.json +3 -4
  81. package/scripts/test-browser.js +29 -6
  82. package/scripts/verify-docker-dev-environment.js +13 -1
  83. package/src/configuration.js +23 -1
  84. package/src/environment-handlers/node/cli/commands/test/timing-manifest/merge.js +2 -48
  85. package/src/environment-handlers/node/cli/commands/test.js +17 -69
  86. package/src/frontend-models/websocket-channel.js +18 -0
  87. package/src/http-server/client/websocket-session.js +41 -4
  88. package/src/http-server/websocket-channel.js +14 -0
  89. package/src/http-server/websocket-event-log-store.js +85 -12
  90. package/src/http-server/websocket-events-host.js +14 -6
  91. package/src/testing/expect-utils.js +1 -242
  92. package/src/testing/expect.js +1 -760
  93. package/src/testing/test-files-finder.js +29 -345
  94. package/src/testing/test-filter-parser.js +29 -248
  95. package/src/testing/test-profile-activity.js +1 -12
  96. package/src/testing/test-profile-output.js +8 -253
  97. package/src/testing/test-profiler.js +38 -895
  98. package/src/testing/test-runner.js +33 -9
  99. package/src/testing/test-suite-splitter.js +1 -301
  100. package/src/testing/test.js +3 -14
  101. package/src/testing/timing-manifest.js +10 -344
  102. package/src/testing/velocious-attempt-executor.js +7 -1
  103. package/build/src/testing/base-expect.d.ts +0 -15
  104. package/build/src/testing/base-expect.d.ts.map +0 -1
  105. package/build/src/testing/base-expect.js +0 -16
  106. package/build/src/testing/expect-to-change.d.ts +0 -32
  107. package/build/src/testing/expect-to-change.d.ts.map +0 -1
  108. package/build/src/testing/expect-to-change.js +0 -46
  109. package/build/testing/base-expect.js +0 -17
  110. package/build/testing/expect-to-change.js +0 -55
  111. package/src/testing/base-expect.js +0 -17
  112. package/src/testing/expect-to-change.js +0 -55
@@ -1,369 +1,53 @@
1
1
  // @ts-check
2
2
 
3
- import fs from "fs/promises"
4
- import path from "path"
3
+ import {
4
+ discoverTestFiles,
5
+ lineFiltersFromCandidates
6
+ } from "@velocious/testing/node"
5
7
 
6
- import fileExists from "../utils/file-exists.js"
7
- import Logger from "../logger.js"
8
8
  import restArgsError from "../utils/rest-args-error.js"
9
9
 
10
- // Incredibly complex class to find files in multiple simultanious running promises to do it as fast as possible.
11
- export default class TestFilesFinder {
12
- static IGNORED_NAMES = [".git", "node_modules"]
10
+ const VELOCIOUS_NODE_TEST_PATTERN = /(?<!\.browser)-(?:spec|test)\.(?:m)?js$/u
13
11
 
12
+ /** Compatibility facade for Velocious's published test-file finder path. */
13
+ export default class TestFilesFinder {
14
14
  /**
15
- * Runs constructor.
16
- * @param {object} args - Options object.
17
- * @param {string} args.directory - Directory path.
18
- * @param {string[]} [args.directories] - Directories.
15
+ * Creates a Velocious-compatible package discovery adapter.
16
+ * @param {object} args - Discovery options.
17
+ * @param {string} args.directory - Discovery base directory.
18
+ * @param {string[]} [args.directories] - Default directories.
19
19
  * @param {RegExp} [args.filePattern] - Test-file pattern override.
20
- * @param {string[]} args.processArgs - Process args.
20
+ * @param {string[]} args.processArgs - Process arguments including the command name.
21
21
  */
22
22
  constructor({directory, directories, filePattern, processArgs, ...restArgs}) {
23
23
  restArgsError(restArgs)
24
-
25
- this.directory = path.resolve(directory)
26
- this.logger = new Logger(this)
27
-
28
- if (directories) {
29
- this.directories = directories.map((entry) => path.resolve(entry))
30
- } else {
31
- this.directories = [
32
- `${this.directory}/__tests__`,
33
- `${this.directory}/tests`,
34
- `${this.directory}/spec`
35
- ]
36
- }
37
-
38
- this.findingCount = 0
24
+ this.directory = directory
25
+ this.directories = directories
39
26
  this.filePattern = filePattern
40
- this.processArgs = processArgs
41
-
42
- /**
43
- * Narrows the runtime value to the documented type.
44
- * @type {string[]} */
45
- this.foundFiles = []
46
-
47
- /**
48
- * Narrows the runtime value to the documented type.
49
- * @type {Record<number, Promise<void>>} */
50
- this.findingPromises = {}
51
-
52
- /**
53
- * Narrows the runtime value to the documented type.
54
- * @type {string[]} */
55
- this.testArgs = this.processArgs.filter((processArg, index) => index != 0)
56
-
57
- /**
58
- * Narrows the runtime value to the documented type.
59
- * @type {string[]} */
60
- this.directoryArgs = []
61
-
62
- /**
63
- * Narrows the runtime value to the documented type.
64
- * @type {string[]} */
65
- this.directoryFullPaths = []
66
-
67
- /**
68
- * Narrows the runtime value to the documented type.
69
- * @type {string[]} */
70
- this.fileArgs = []
71
-
72
- /**
73
- * Narrows the runtime value to the documented type.
74
- * @type {string[]} */
75
- this.explicitFiles = []
76
-
77
- /**
78
- * Narrows the runtime value to the documented type.
79
- * @type {Record<string, number[]>} */
80
- this.lineFiltersByFile = {}
81
-
82
- this._argsPrepared = false
27
+ this.candidates = processArgs.slice(1).filter((argument) => argument !== "--")
28
+ /** @type {Record<string, number[]>} */
29
+ this.lineFiltersByFile = lineFiltersFromCandidates({
30
+ cwd: directory,
31
+ candidates: this.candidates
32
+ })
83
33
  }
84
34
 
85
35
  /**
86
- * Runs find test files.
87
- * @returns {Promise<string[]>} - Resolves with the test files.
36
+ * Discovers test files through the package implementation.
37
+ * @returns {Promise<string[]>} - Discovered absolute file paths.
88
38
  */
89
39
  async findTestFiles() {
90
- await this.prepareArgs()
91
-
92
- if (this.explicitFiles.length > 0 && this.directoryArgs.length === 0) {
93
- return Array.from(new Set(this.explicitFiles))
94
- }
95
-
96
- await this.withFindingCount(async () => {
97
- const hasExplicitArgs = this.directoryFullPaths.length > 0 || this.fileArgs.length > 0
98
- const directoriesToScan = hasExplicitArgs ? this.directoryFullPaths : this.directories
99
-
100
- for (const directory of directoriesToScan) {
101
- if (await fileExists(directory)) {
102
- await this.findTestFilesInDir(directory)
103
- }
104
- }
40
+ return await discoverTestFiles({
41
+ cwd: this.directory,
42
+ candidates: this.candidates,
43
+ directories: this.directories,
44
+ filePattern: this.filePattern || VELOCIOUS_NODE_TEST_PATTERN
105
45
  })
106
-
107
- await this.waitForFindingPromises()
108
-
109
- if (this.explicitFiles.length > 0) {
110
- this.foundFiles.push(...this.explicitFiles)
111
- }
112
-
113
- return Array.from(new Set(this.foundFiles))
114
46
  }
115
47
 
116
48
  /**
117
- * Runs get line filters by file.
118
- * @returns {Record<string, number[]>} - Line filters by file.
49
+ * Gets candidate-derived line filters.
50
+ * @returns {Record<string, number[]>} - Line filters keyed by absolute file path.
119
51
  */
120
52
  getLineFiltersByFile() { return this.lineFiltersByFile }
121
-
122
- /**
123
- * Runs finding promises length.
124
- * @returns {number} - The ing promises length.
125
- */
126
- findingPromisesLength() { return Object.keys(this.findingPromises).length }
127
-
128
- async waitForFindingPromises() {
129
- while (this.findingPromisesLength() > 0) {
130
- await this.waitForFindingPromisesIteration()
131
- }
132
- }
133
-
134
- /**
135
- * Runs wait for finding promises iteration.
136
- * @returns {Promise<void>} - Resolves when complete.
137
- */
138
- async waitForFindingPromisesIteration() {
139
- const unfinishedPromises = []
140
-
141
- for (const findingPromiseId in this.findingPromises) {
142
- const findingPromise = this.findingPromises[findingPromiseId]
143
-
144
- unfinishedPromises.push(findingPromise)
145
- }
146
-
147
- await Promise.all(unfinishedPromises)
148
- }
149
-
150
- /**
151
- * Runs with finding count.
152
- * @param {() => Promise<void>} callback - Callback function.
153
- * @returns {Promise<void>} - Resolves when complete.
154
- */
155
- withFindingCount(callback) {
156
- return new Promise((resolve) => {
157
- const findingPromise = callback()
158
- const findingCount = this.findingCount
159
-
160
- this.findingCount += 1
161
- this.findingPromises[findingCount] = findingPromise
162
-
163
- findingPromise.finally(() => {
164
- delete this.findingPromises[findingCount]
165
-
166
- resolve(undefined)
167
- })
168
- })
169
- }
170
-
171
- /**
172
- * Runs find test files in dir.
173
- * @param {string} dir - Dir.
174
- * @returns {Promise<void>} - Resolves when complete.
175
- */
176
- async findTestFilesInDir(dir) {
177
- await this.withFindingCount(async () => {
178
- const files = await fs.readdir(dir)
179
-
180
- for (const file of files) {
181
- if (TestFilesFinder.IGNORED_NAMES.includes(file)) {
182
- continue
183
- }
184
-
185
- const fullPath = `${dir}/${file}`
186
- const localPath = fullPath.replace(`${this.directory}/`, "")
187
- const isDir = (await fs.stat(fullPath)).isDirectory()
188
-
189
- if (isDir) {
190
- this.findTestFilesInDir(fullPath)
191
- } else {
192
- if (this.isFileMatchingRequirements(file, localPath)) {
193
- this.foundFiles.push(fullPath)
194
- }
195
- }
196
- }
197
- })
198
- }
199
-
200
- /**
201
- * Runs is file matching requirements.
202
- * @param {string} file - File.
203
- * @param {string} localPath - Local path.
204
- * @returns {boolean} - Whether file matching requirements.
205
- */
206
- isFileMatchingRequirements(file, localPath) {
207
- if (this.directoryArgs.length > 0) {
208
- for (const directoryArg of this.directoryArgs) {
209
- if (localPath.startsWith(directoryArg) && this.looksLikeTestFile(file)) {
210
- this.logger.debug("Found test file because matching dir and looks like this file:", file)
211
- return true
212
- }
213
- }
214
- }
215
-
216
- if (this.fileArgs.length > 0) {
217
- for (const fileArg of this.fileArgs) {
218
- if (fileArg == localPath) {
219
- this.logger.debug("Found test file because matching file arg:", file)
220
- return true
221
- }
222
- }
223
- }
224
-
225
- if (this.fileArgs.length == 0 && this.directoryArgs.length == 0 && this.looksLikeTestFile(file)) {
226
- this.logger.debug("Found test file because looks like this file:", file)
227
- return true
228
- }
229
-
230
- return false
231
- }
232
-
233
- /**
234
- * Runs looks like test file.
235
- * @param {string} file - File.
236
- * @returns {boolean} - Whether looks like test file.
237
- */
238
- looksLikeTestFile(file) {
239
- if (this.filePattern) return Boolean(file.match(this.filePattern))
240
-
241
- return Boolean(file.match(/-(spec|test)\.(m|)js$/))
242
- && !file.match(/\.browser-(spec|test)\.(m|)js$/)
243
- }
244
-
245
- /**
246
- * Runs prepare args.
247
- * @returns {Promise<void>} - Resolves when test args are prepared.
248
- */
249
- async prepareArgs() {
250
- if (this._argsPrepared) return
251
-
252
- for (const testArg of this.testArgs) {
253
- if (testArg === "--") continue
254
-
255
- const {cleanArg, line} = this.splitLineArg(testArg)
256
- const forceDirectory = testArg.endsWith("/") || testArg.endsWith(path.sep)
257
- const fullPath = path.isAbsolute(cleanArg) ? cleanArg : path.resolve(this.directory, cleanArg)
258
- const baseName = path.basename(this.directory)
259
- const hasBasePrefix = cleanArg === baseName || cleanArg.startsWith(`${baseName}/`) || cleanArg.startsWith(`${baseName}${path.sep}`)
260
- const basePrefixedFullPath = (!path.isAbsolute(cleanArg) && hasBasePrefix) ? path.resolve(path.dirname(this.directory), cleanArg) : null
261
- const fullPathCandidates = basePrefixedFullPath ? [basePrefixedFullPath] : [fullPath]
262
-
263
- if (forceDirectory) {
264
- const preferredLocalPath = this.toLocalPath(basePrefixedFullPath || fullPath)
265
- this.directoryArgs.push(this.ensureTrailingSlash(preferredLocalPath))
266
- this.directoryFullPaths.push(path.resolve(basePrefixedFullPath || fullPath))
267
- continue
268
- }
269
-
270
- try {
271
- let stats
272
- let resolvedFullPath
273
-
274
- for (const candidatePath of fullPathCandidates) {
275
- try {
276
- stats = await fs.stat(candidatePath)
277
- resolvedFullPath = candidatePath
278
- break
279
- } catch {
280
- // Keep searching
281
- }
282
- }
283
-
284
- if (!stats || !resolvedFullPath) throw new Error("Path not found")
285
- const localPath = this.toLocalPath(resolvedFullPath)
286
-
287
- if (stats.isDirectory()) {
288
- this.directoryArgs.push(this.ensureTrailingSlash(localPath))
289
- this.directoryFullPaths.push(resolvedFullPath)
290
- } else if (stats.isFile()) {
291
- this.fileArgs.push(localPath)
292
- this.explicitFiles.push(resolvedFullPath)
293
-
294
- if (line) {
295
- this.addLineFilter(resolvedFullPath, line)
296
- }
297
- }
298
- } catch {
299
- const fallbackLocalPath = this.toLocalPath(basePrefixedFullPath || fullPath)
300
- this.fileArgs.push(fallbackLocalPath)
301
-
302
- if (line) {
303
- this.addLineFilter(basePrefixedFullPath || fullPath, line)
304
- }
305
- }
306
- }
307
-
308
- this._argsPrepared = true
309
- }
310
-
311
- /**
312
- * Runs add line filter.
313
- * @param {string} filePath - File path.
314
- * @param {number} line - Line number.
315
- * @returns {void} - No return value.
316
- */
317
- addLineFilter(filePath, line) {
318
- const fullPath = path.resolve(filePath)
319
-
320
- if (!this.lineFiltersByFile[fullPath]) {
321
- this.lineFiltersByFile[fullPath] = []
322
- }
323
-
324
- if (!this.lineFiltersByFile[fullPath].includes(line)) {
325
- this.lineFiltersByFile[fullPath].push(line)
326
- }
327
- }
328
-
329
- /**
330
- * Runs split line arg.
331
- * @param {string} testArg - Test arg.
332
- * @returns {{cleanArg: string, line?: number}} - Cleaned arg and line.
333
- */
334
- splitLineArg(testArg) {
335
- const match = testArg.match(/^(.*):(\d+)$/)
336
-
337
- if (!match) {
338
- return {cleanArg: testArg}
339
- }
340
-
341
- const line = Number(match[2])
342
-
343
- if (!Number.isFinite(line)) {
344
- return {cleanArg: testArg}
345
- }
346
-
347
- return {cleanArg: match[1], line}
348
- }
349
-
350
- /**
351
- * Runs ensure trailing slash.
352
- * @param {string} localPath - Local path.
353
- * @returns {string} - Normalized local path with trailing slash.
354
- */
355
- ensureTrailingSlash(localPath) {
356
- if (localPath === "") return localPath
357
- return localPath.endsWith("/") ? localPath : `${localPath}/`
358
- }
359
-
360
- /**
361
- * Runs to local path.
362
- * @param {string} fullPath - Full path.
363
- * @returns {string} - Local path relative to the base directory.
364
- */
365
- toLocalPath(fullPath) {
366
- const relativePath = path.relative(this.directory, fullPath)
367
- return relativePath.split(path.sep).join("/")
368
- }
369
53
  }
@@ -1,269 +1,50 @@
1
+ // @ts-check
2
+
3
+ import {
4
+ extractTestCliArguments,
5
+ normalizeExamplePatterns
6
+ } from "@velocious/testing/node"
7
+
1
8
  /**
2
- * ParseFiltersResult type.
3
9
  * @typedef {object} ParseFiltersResult
4
10
  * @property {string[]} includeTags - Tags to include.
5
11
  * @property {string[]} excludeTags - Tags to exclude.
6
12
  * @property {string[]} examplePatterns - Example name patterns.
7
- * @property {string[]} filteredProcessArgs - Remaining process args with filter flags removed.
13
+ * @property {string[]} filteredProcessArgs - Remaining process args with package-owned flags removed.
8
14
  * @property {number | undefined} groups - Total number of groups for test splitting.
9
15
  * @property {number | undefined} groupNumber - Which group to run (1-indexed).
10
16
  * @property {boolean} profile - Whether test profiling is enabled.
11
17
  * @property {string | undefined} profileJsonPath - Rich profile output path.
12
18
  * @property {string | undefined} timingManifestPath - JSON timing manifest path.
13
19
  * @property {string | undefined} timingManifestOutputPath - Timing manifest output path.
20
+ * @property {number | undefined} retries - Default retry count.
21
+ * @property {string[]} setupFiles - Setup files imported before test files.
22
+ * @property {number | undefined} timeoutMs - Default lifecycle timeout.
14
23
  */
15
- // @ts-check
16
-
17
- const INCLUDE_TAG_FLAGS = new Set(["--tag", "--include-tag", "-t"])
18
- const EXCLUDE_TAG_FLAGS = new Set(["--exclude-tag", "--skip-tag", "-x"])
19
- const EXAMPLE_FLAGS = new Set(["--example", "--name", "-e"])
20
- const GROUPS_FLAGS = new Set(["--groups"])
21
- const GROUP_NUMBER_FLAGS = new Set(["--group-number"])
22
- const TIMING_MANIFEST_FLAGS = new Set(["--timing-manifest"])
23
- const PROFILE_JSON_FLAGS = new Set(["--profile-json"])
24
- const TIMING_MANIFEST_OUTPUT_FLAGS = new Set(["--timing-manifest-output"])
25
-
26
- /**
27
- * Runs split tags.
28
- * @param {string | undefined} value - Tag argument value.
29
- * @returns {string[]} - Tags list.
30
- */
31
- function splitTags(value) {
32
- if (!value) return []
33
-
34
- return value
35
- .split(",")
36
- .map((tag) => tag.trim())
37
- .filter(Boolean)
38
- }
39
-
40
- /**
41
- * Runs escape reg exp.
42
- * @param {string} value - Value.
43
- * @returns {string} - Escaped value for regex.
44
- */
45
- function escapeRegExp(value) {
46
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
47
- }
48
-
49
- /**
50
- * Runs the normalizeExamplePatterns helper.
51
- * @param {string[]} patterns - Patterns.
52
- * @returns {RegExp[]} - Normalized patterns.
53
- */
54
- export function normalizeExamplePatterns(patterns) {
55
- const normalized = []
56
-
57
- for (const pattern of patterns) {
58
- const regexMatch = pattern.match(/^\/(.+)\/([gimsuy]*)$/)
59
24
 
60
- if (regexMatch) {
61
- normalized.push(new RegExp(regexMatch[1], regexMatch[2]))
62
- } else {
63
- normalized.push(new RegExp(escapeRegExp(pattern)))
64
- }
65
- }
66
-
67
- return normalized
68
- }
25
+ export { normalizeExamplePatterns }
69
26
 
70
27
  /**
71
- * Runs the parseFilters helper.
72
- * @param {string[]} processArgs - Process args.
73
- * @returns {ParseFiltersResult} - Parsed tags, group options, and process args.
28
+ * Preserves the Velocious parser result while delegating package-owned option parsing.
29
+ * @param {string[]} processArgs - Raw process arguments including the command name.
30
+ * @returns {ParseFiltersResult} - Package options and downstream process arguments.
74
31
  */
75
32
  export function parseFilters(processArgs) {
76
- const includeTags = []
77
- const excludeTags = []
78
- const filteredProcessArgs = processArgs.length > 0 ? [processArgs[0]] : []
79
- const examplePatterns = []
80
-
81
- /**
82
- * Defines groups.
83
- * @type {number | undefined} */
84
- let groups
85
- /**
86
- * Defines groupNumber.
87
- * @type {number | undefined} */
88
- let groupNumber
89
- let profile = false
90
- /** @type {string | undefined} */
91
- let profileJsonPath
92
- /** @type {string | undefined} */
93
- let timingManifestPath
94
- /** @type {string | undefined} */
95
- let timingManifestOutputPath
96
-
97
- let inRestArgs = false
98
-
99
- for (let i = 1; i < processArgs.length; i++) {
100
- const arg = processArgs[i]
101
-
102
- if (arg === "--") {
103
- inRestArgs = true
104
- filteredProcessArgs.push(arg)
105
- continue
106
- }
107
-
108
- if (!inRestArgs) {
109
- if (INCLUDE_TAG_FLAGS.has(arg)) {
110
- const nextValue = processArgs[i + 1]
111
-
112
- if (nextValue && !nextValue.startsWith("-")) {
113
- includeTags.push(...splitTags(nextValue))
114
- i++
115
- }
116
- continue
117
- }
118
-
119
- if (EXCLUDE_TAG_FLAGS.has(arg)) {
120
- const nextValue = processArgs[i + 1]
121
-
122
- if (nextValue && !nextValue.startsWith("-")) {
123
- excludeTags.push(...splitTags(nextValue))
124
- i++
125
- }
126
- continue
127
- }
128
-
129
- if (arg.startsWith("--tag=")) {
130
- includeTags.push(...splitTags(arg.slice("--tag=".length)))
131
- continue
132
- }
133
-
134
- if (arg.startsWith("--include-tag=")) {
135
- includeTags.push(...splitTags(arg.slice("--include-tag=".length)))
136
- continue
137
- }
138
-
139
- if (arg.startsWith("--exclude-tag=")) {
140
- excludeTags.push(...splitTags(arg.slice("--exclude-tag=".length)))
141
- continue
142
- }
143
-
144
- if (arg.startsWith("--skip-tag=")) {
145
- excludeTags.push(...splitTags(arg.slice("--skip-tag=".length)))
146
- continue
147
- }
148
-
149
- if (EXAMPLE_FLAGS.has(arg)) {
150
- const nextValue = processArgs[i + 1]
151
-
152
- if (nextValue && !nextValue.startsWith("-")) {
153
- examplePatterns.push(nextValue)
154
- i++
155
- }
156
- continue
157
- }
158
-
159
- if (arg.startsWith("--example=")) {
160
- examplePatterns.push(arg.slice("--example=".length))
161
- continue
162
- }
163
-
164
- if (arg.startsWith("--name=")) {
165
- examplePatterns.push(arg.slice("--name=".length))
166
- continue
167
- }
168
-
169
- if (GROUPS_FLAGS.has(arg)) {
170
- const nextValue = processArgs[i + 1]
171
-
172
- if (nextValue && !nextValue.startsWith("-")) {
173
- groups = parseInt(nextValue, 10)
174
- i++
175
- }
176
- continue
177
- }
178
-
179
- if (arg.startsWith("--groups=")) {
180
- groups = parseInt(arg.slice("--groups=".length), 10)
181
- continue
182
- }
183
-
184
- if (GROUP_NUMBER_FLAGS.has(arg)) {
185
- const nextValue = processArgs[i + 1]
186
-
187
- if (nextValue && !nextValue.startsWith("-")) {
188
- groupNumber = parseInt(nextValue, 10)
189
- i++
190
- }
191
- continue
192
- }
193
-
194
- if (arg.startsWith("--group-number=")) {
195
- groupNumber = parseInt(arg.slice("--group-number=".length), 10)
196
- continue
197
- }
198
-
199
- if (TIMING_MANIFEST_FLAGS.has(arg)) {
200
- const nextValue = processArgs[i + 1]
201
-
202
- if (!nextValue || nextValue.startsWith("-")) throw new Error("--timing-manifest requires a path")
203
- timingManifestPath = nextValue
204
- i++
205
- continue
206
- }
207
-
208
- if (arg.startsWith("--timing-manifest=")) {
209
- timingManifestPath = arg.slice("--timing-manifest=".length)
210
- if (!timingManifestPath) throw new Error("--timing-manifest requires a path")
211
- continue
212
- }
213
-
214
- if (arg === "--profile") {
215
- profile = true
216
- continue
217
- }
218
-
219
- if (PROFILE_JSON_FLAGS.has(arg)) {
220
- const nextValue = processArgs[i + 1]
221
-
222
- if (!nextValue || nextValue.startsWith("-")) throw new Error("--profile-json requires a path")
223
- profileJsonPath = nextValue
224
- profile = true
225
- i++
226
- continue
227
- }
228
-
229
- if (arg.startsWith("--profile-json=")) {
230
- profileJsonPath = arg.slice("--profile-json=".length)
231
- if (!profileJsonPath) throw new Error("--profile-json requires a path")
232
- profile = true
233
- continue
234
- }
235
-
236
- if (TIMING_MANIFEST_OUTPUT_FLAGS.has(arg)) {
237
- const nextValue = processArgs[i + 1]
238
-
239
- if (!nextValue || nextValue.startsWith("-")) throw new Error("--timing-manifest-output requires a path")
240
- timingManifestOutputPath = nextValue
241
- profile = true
242
- i++
243
- continue
244
- }
245
-
246
- if (arg.startsWith("--timing-manifest-output=")) {
247
- timingManifestOutputPath = arg.slice("--timing-manifest-output=".length)
248
- if (!timingManifestOutputPath) throw new Error("--timing-manifest-output requires a path")
249
- profile = true
250
- continue
251
- }
252
- }
253
-
254
- filteredProcessArgs.push(arg)
255
- }
33
+ const {options, remainingArguments} = extractTestCliArguments(processArgs)
256
34
 
257
35
  return {
258
- includeTags: Array.from(new Set(includeTags)),
259
- excludeTags: Array.from(new Set(excludeTags)),
260
- examplePatterns,
261
- filteredProcessArgs,
262
- groups,
263
- groupNumber,
264
- profile,
265
- profileJsonPath,
266
- timingManifestPath,
267
- timingManifestOutputPath
36
+ includeTags: options.includeTags,
37
+ excludeTags: options.excludeTags,
38
+ examplePatterns: options.examples,
39
+ filteredProcessArgs: remainingArguments,
40
+ groups: options.groups,
41
+ groupNumber: options.groupNumber,
42
+ profile: options.profile === true,
43
+ profileJsonPath: options.profileJsonPath,
44
+ timingManifestPath: options.timingManifestPath,
45
+ timingManifestOutputPath: options.timingManifestOutputPath,
46
+ retries: options.retries,
47
+ setupFiles: options.setupFiles,
48
+ timeoutMs: options.timeoutMs
268
49
  }
269
50
  }