sxy-test-runner 2.5.4 → 2.5.5

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 (48) hide show
  1. package/AGENTS.md +40 -16
  2. package/dist/cli/lib/agentReport.d.ts +1 -0
  3. package/dist/cli/lib/agentReport.js +3 -0
  4. package/dist/cli/lib/agentReport.js.map +1 -1
  5. package/dist/cli/lib/esmGraphReload.d.ts +5 -0
  6. package/dist/cli/lib/esmGraphReload.js +24 -0
  7. package/dist/cli/lib/esmGraphReload.js.map +1 -0
  8. package/dist/cli/lib/esmTreeReload.d.ts +4 -0
  9. package/dist/cli/lib/esmTreeReload.js +62 -0
  10. package/dist/cli/lib/esmTreeReload.js.map +1 -0
  11. package/dist/cli/lib/handleUserInput.d.ts +2 -2
  12. package/dist/cli/lib/handleUserInput.js +13 -6
  13. package/dist/cli/lib/handleUserInput.js.map +1 -1
  14. package/dist/cli/lib/listenToUser.d.ts +2 -2
  15. package/dist/cli/lib/listenToUser.js.map +1 -1
  16. package/dist/cli/lib/loadTestFile.d.ts +0 -1
  17. package/dist/cli/lib/loadTestFile.js +23 -8
  18. package/dist/cli/lib/loadTestFile.js.map +1 -1
  19. package/dist/cli/lib/makeDependencyGraph.d.ts +1 -0
  20. package/dist/cli/lib/makeDependencyGraph.js +34 -15
  21. package/dist/cli/lib/makeDependencyGraph.js.map +1 -1
  22. package/dist/cli/lib/reRunManager.js +4 -6
  23. package/dist/cli/lib/reRunManager.js.map +1 -1
  24. package/dist/cli/lib/runTests.js +7 -2
  25. package/dist/cli/lib/runTests.js.map +1 -1
  26. package/dist/cli/lib/showTestResultsSummary.d.ts +1 -1
  27. package/dist/cli/lib/showTestResultsSummary.js +5 -1
  28. package/dist/cli/lib/showTestResultsSummary.js.map +1 -1
  29. package/dist/cli/lib/statusFile.js +20 -1
  30. package/dist/cli/lib/statusFile.js.map +1 -1
  31. package/dist/cli/lib/watchFilesAndRunTestsNodeWatch.js +3 -4
  32. package/dist/cli/lib/watchFilesAndRunTestsNodeWatch.js.map +1 -1
  33. package/dist/cli/watch.js +14 -1
  34. package/dist/cli/watch.js.map +1 -1
  35. package/dist/describe.js +5 -3
  36. package/dist/describe.js.map +1 -1
  37. package/dist/meta.js +2 -1
  38. package/dist/meta.js.map +1 -1
  39. package/dist/registration.d.ts +4 -0
  40. package/dist/registration.js +34 -0
  41. package/dist/registration.js.map +1 -0
  42. package/dist/types.d.ts +8 -0
  43. package/dist/types.js.map +1 -1
  44. package/package.json +5 -2
  45. package/readme.md +15 -122
  46. package/dist/packages/esmReload.d.ts +0 -11
  47. package/dist/packages/esmReload.js +0 -43
  48. package/dist/packages/esmReload.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"makeDependencyGraph.js","sourceRoot":"","sources":["../../../src/cli/lib/makeDependencyGraph.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport { existsSync, readFileSync, realpathSync } from 'node:fs'\nimport { isBuiltin } from 'node:module'\nimport { basename, dirname, extname, isAbsolute, join, relative, resolve } from 'node:path'\nimport cabinet from 'filing-cabinet'\nimport precinct from 'sxy-precinct'\n\nimport type { FinalConfig } from '../../types.js'\nimport slash from '../../packages/slash.js'\nimport { createTimeProfiler } from '../../packages/timeProfiler.js'\nimport { showTimeProfiling } from '../../config.js'\n\n\nconst { timeProfile } = createTimeProfiler(showTimeProfiling)\n\nexport interface DependencyGraph {\n addTest(file: string): void\n update(file: string): void\n remove(file: string): void\n testsAffectedBy(file: string): Set<string>\n}\n\n// precinct type per extension; undefined sniffs ESM vs CommonJS from the source\nconst precinctTypes: Record<string, string | undefined> = {\n '.js': undefined, '.jsx': undefined,\n '.mjs': 'es6', '.cjs': 'commonjs',\n '.ts': 'ts', '.mts': 'ts', '.cts': 'ts', '.tsx': 'tsx'\n}\n\nfor (const [extension, from] of [['.mjs', '.js'], ['.cjs', '.js'], ['.mts', '.ts'], ['.cts', '.ts']] as const) {\n const lookup = cabinet.getLookup(from)\n if (cabinet.getLookup(extension) === undefined && lookup !== undefined) cabinet.register(extension, lookup)\n}\n\nfunction tryRealpath(path: string): string | undefined {\n try {\n return realpathSync.native(path)\n } catch {\n return undefined\n }\n}\n\n// paths in and out are relative to the cwd with forward slashes; edges are kept between real paths\nexport function makeDependencyGraph(config: FinalConfig, testFiles: Iterable<string>): DependencyGraph {\n const cwd = realpathSync.native(process.cwd())\n const directory = resolve(config.watch.watchFilesBase === '' ? '.' : config.watch.watchFilesBase)\n\n const imports = new Map<string, Set<string>>()\n const importers = new Map<string, Set<string>>()\n const tests = new Set<string>()\n\n const toKey = (absolute: string): string => slash(relative(cwd, absolute))\n const toAbsolute = (key: string): string => resolve(cwd, key)\n\n // cleared by each operation, so a path created, deleted or relinked since is never served stale\n const realCache = new Map<string, string>()\n const resolveCache = new Map<string, string | null>()\n const clearCaches = (): void => {\n realCache.clear()\n resolveCache.clear()\n }\n function realAbsolute(file: string): string {\n const absolute = resolve(file)\n let real = realCache.get(absolute)\n if (real === undefined) {\n // a missing file's directory may still be reached through a link\n real = tryRealpath(absolute)\n ?? join(tryRealpath(dirname(absolute)) ?? dirname(absolute), basename(absolute))\n realCache.set(absolute, real)\n }\n return real\n }\n const realKey = (file: string): string => toKey(realAbsolute(file))\n\n // undefined when the file does not parse, so its last known imports stand\n function readImports(key: string): Set<string> | undefined {\n const filename = toAbsolute(key)\n const extension = extname(filename)\n if (!existsSync(filename) || !(extension in precinctTypes)) return new Set()\n\n let specifiers: string[]\n try {\n specifiers = precinct(readFileSync(filename, 'utf8'), {\n type: precinctTypes[extension],\n throwOnParseError: true,\n es6: { mixedImports: true },\n ts: { mixedImports: true },\n tsx: { mixedImports: true }\n })\n } catch {\n return undefined\n }\n // precinct only leaves an ast for content it sniffed; filing-cabinet otherwise parses again\n const ast = precinctTypes[extension] === undefined ? precinct.ast ?? undefined : undefined\n\n const found = new Set<string>()\n for (const specifier of specifiers) {\n if (isBuiltin(specifier)) continue\n const resolved = resolveImport(specifier, filename, ast)\n if (resolved === null || slash(resolved).includes('/node_modules/')) continue\n found.add(toKey(resolved))\n }\n return found\n }\n\n // resolution depends only on the importer's directory and kind, so its neighbours share it\n function resolveImport(specifier: string, filename: string, ast: object | undefined): string | null {\n const cacheKey = `${dirname(filename)}\\0${extname(filename)}\\0${specifier}`\n let result = resolveCache.get(cacheKey)\n if (result === undefined) {\n result = resolveUncached(specifier, filename, ast)\n resolveCache.set(cacheKey, result)\n }\n return result\n }\n\n function resolveUncached(specifier: string, filename: string, ast: object | undefined): string | null {\n let resolved = ''\n try {\n resolved = cabinet({ partial: specifier, filename, directory, ...(ast === undefined ? {} : { ast }) })\n } catch {\n // left unresolved\n }\n if (resolved !== '') return realAbsolute(resolved)\n // filing-cabinet resolves a missing file to '' - keep the edge so its creation is seen\n if (specifier.startsWith('.') || isAbsolute(specifier)) {\n return realAbsolute(resolve(dirname(filename), specifier))\n }\n return null\n }\n\n function setImports(key: string, next: Set<string>): void {\n const previous = imports.get(key) ?? new Set<string>()\n for (const dep of previous.difference(next)) importers.get(dep)?.delete(key)\n for (const dep of next.difference(previous)) {\n let set = importers.get(dep)\n if (set === undefined) importers.set(dep, set = new Set())\n set.add(key)\n }\n imports.set(key, next)\n }\n\n // re-reads `start`, then walks only into files never read before\n function walk(start: string): void {\n const queue = [start]\n const seen = new Set<string>()\n for (let key = queue.pop(); key !== undefined; key = queue.pop()) {\n if (seen.has(key)) continue\n seen.add(key)\n const found = readImports(key)\n if (found === undefined) {\n if (!imports.has(key)) imports.set(key, new Set())\n continue\n }\n setImports(key, found)\n for (const dep of found) if (!imports.has(dep)) queue.push(dep)\n }\n }\n\n timeProfile('build dependency graph', () => {\n for (const file of testFiles) {\n const key = realKey(file)\n tests.add(key)\n if (!imports.has(key)) walk(key)\n }\n })\n\n return {\n addTest(file) {\n clearCaches()\n const key = realKey(file)\n tests.add(key)\n walk(key)\n },\n update(file) {\n clearCaches()\n walk(realKey(file))\n },\n remove(file) {\n clearCaches()\n const key = realKey(file)\n tests.delete(key)\n setImports(key, new Set())\n },\n testsAffectedBy(file) {\n clearCaches()\n const start = realKey(file)\n const affected = new Set<string>()\n const queue = [start]\n const seen = new Set<string>()\n for (let key = queue.pop(); key !== undefined; key = queue.pop()) {\n if (seen.has(key)) continue\n seen.add(key)\n if (tests.has(key)) affected.add(key)\n for (const importer of importers.get(key) ?? []) queue.push(importer)\n }\n return affected\n }\n }\n}\n"]}
1
+ {"version":3,"file":"makeDependencyGraph.js","sourceRoot":"","sources":["../../../src/cli/lib/makeDependencyGraph.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport { existsSync, readFileSync, realpathSync } from 'node:fs'\nimport { isBuiltin } from 'node:module'\nimport { basename, dirname, extname, isAbsolute, join, relative, resolve } from 'node:path'\nimport cabinet from 'filing-cabinet'\nimport precinct from 'sxy-precinct'\n\nimport type { FinalConfig } from '../../types.js'\nimport slash from '../../packages/slash.js'\nimport { createTimeProfiler } from '../../packages/timeProfiler.js'\nimport { showTimeProfiling } from '../../config.js'\n\n\nconst { timeProfile } = createTimeProfiler(showTimeProfiling)\n\nexport interface DependencyGraph {\n addTest(file: string): void\n update(file: string): void\n remove(file: string): void\n testsAffectedBy(file: string): Set<string>\n // moved on for a changed file and everything importing it, so a reload gets fresh copies\n versionOf(file: string): number | undefined\n}\n\n// precinct type per extension; undefined sniffs ESM vs CommonJS from the source\nconst precinctTypes: Record<string, string | undefined> = {\n '.js': undefined, '.jsx': undefined,\n '.mjs': 'es6', '.cjs': 'commonjs',\n '.ts': 'ts', '.mts': 'ts', '.cts': 'ts', '.tsx': 'tsx'\n}\n\nfor (const [extension, from] of [['.mjs', '.js'], ['.cjs', '.js'], ['.mts', '.ts'], ['.cts', '.ts']] as const) {\n const lookup = cabinet.getLookup(from)\n if (cabinet.getLookup(extension) === undefined && lookup !== undefined) cabinet.register(extension, lookup)\n}\n\nfunction tryRealpath(path: string): string | undefined {\n try {\n return realpathSync.native(path)\n } catch {\n return undefined\n }\n}\n\n// paths in and out are relative to the cwd with forward slashes; edges are kept between real paths\nexport function makeDependencyGraph(config: FinalConfig, testFiles: Iterable<string>): DependencyGraph {\n const cwd = realpathSync.native(process.cwd())\n const directory = resolve(config.watch.watchFilesBase === '' ? '.' : config.watch.watchFilesBase)\n\n const imports = new Map<string, Set<string>>()\n const importers = new Map<string, Set<string>>()\n const tests = new Set<string>()\n const versions = new Map<string, number>()\n\n const toKey = (absolute: string): string => slash(relative(cwd, absolute))\n const toAbsolute = (key: string): string => resolve(cwd, key)\n\n // cleared by each operation, so a path created, deleted or relinked since is never served stale\n const realCache = new Map<string, string>()\n const resolveCache = new Map<string, string | null>()\n const clearCaches = (): void => {\n realCache.clear()\n resolveCache.clear()\n }\n function realAbsolute(file: string): string {\n const absolute = resolve(file)\n let real = realCache.get(absolute)\n if (real === undefined) {\n // a missing file's directory may still be reached through a link\n real = tryRealpath(absolute)\n ?? join(tryRealpath(dirname(absolute)) ?? dirname(absolute), basename(absolute))\n realCache.set(absolute, real)\n }\n return real\n }\n const realKey = (file: string): string => toKey(realAbsolute(file))\n\n // undefined when the file does not parse, so its last known imports stand\n function readImports(key: string): Set<string> | undefined {\n const filename = toAbsolute(key)\n const extension = extname(filename)\n if (!existsSync(filename) || !(extension in precinctTypes)) return new Set()\n\n let specifiers: string[]\n try {\n specifiers = precinct(readFileSync(filename, 'utf8'), {\n type: precinctTypes[extension],\n throwOnParseError: true,\n es6: { mixedImports: true },\n ts: { mixedImports: true },\n tsx: { mixedImports: true }\n })\n } catch {\n return undefined\n }\n // precinct only leaves an ast for content it sniffed; filing-cabinet otherwise parses again\n const ast = precinctTypes[extension] === undefined ? precinct.ast ?? undefined : undefined\n\n const found = new Set<string>()\n for (const specifier of specifiers) {\n if (isBuiltin(specifier)) continue\n const resolved = resolveImport(specifier, filename, ast)\n if (resolved === null || slash(resolved).includes('/node_modules/')) continue\n found.add(toKey(resolved))\n }\n return found\n }\n\n // resolution depends only on the importer's directory and kind, so its neighbours share it\n function resolveImport(specifier: string, filename: string, ast: object | undefined): string | null {\n const cacheKey = `${dirname(filename)}\\0${extname(filename)}\\0${specifier}`\n let result = resolveCache.get(cacheKey)\n if (result === undefined) {\n result = resolveUncached(specifier, filename, ast)\n resolveCache.set(cacheKey, result)\n }\n return result\n }\n\n function resolveUncached(specifier: string, filename: string, ast: object | undefined): string | null {\n let resolved = ''\n try {\n resolved = cabinet({ partial: specifier, filename, directory, ...(ast === undefined ? {} : { ast }) })\n } catch {\n // left unresolved\n }\n if (resolved !== '') return realAbsolute(resolved)\n // filing-cabinet resolves a missing file to '' - keep the edge so its creation is seen\n if (specifier.startsWith('.') || isAbsolute(specifier)) {\n return realAbsolute(resolve(dirname(filename), specifier))\n }\n return null\n }\n\n function setImports(key: string, next: Set<string>): void {\n const previous = imports.get(key) ?? new Set<string>()\n for (const dep of previous.difference(next)) importers.get(dep)?.delete(key)\n for (const dep of next.difference(previous)) {\n let set = importers.get(dep)\n if (set === undefined) importers.set(dep, set = new Set())\n set.add(key)\n }\n imports.set(key, next)\n }\n\n // re-reads `start`, then walks only into files never read before\n function walk(start: string): void {\n const queue = [start]\n const seen = new Set<string>()\n for (let key = queue.pop(); key !== undefined; key = queue.pop()) {\n if (seen.has(key)) continue\n seen.add(key)\n const found = readImports(key)\n if (found === undefined) {\n if (!imports.has(key)) imports.set(key, new Set())\n continue\n }\n setImports(key, found)\n for (const dep of found) if (!imports.has(dep)) queue.push(dep)\n }\n }\n\n // the file and everything importing it, directly or not\n function dependents(start: string): Set<string> {\n const queue = [start]\n const seen = new Set<string>()\n for (let key = queue.pop(); key !== undefined; key = queue.pop()) {\n if (seen.has(key)) continue\n seen.add(key)\n for (const importer of importers.get(key) ?? []) queue.push(importer)\n }\n return seen\n }\n\n // Windows paths match in any case, and the loader's may differ from the real one\n const versionKey = process.platform === 'win32'\n ? (key: string): string => key.toLowerCase()\n : (key: string): string => key\n\n function bump(start: string): void {\n for (const key of dependents(start)) {\n const vKey = versionKey(key)\n versions.set(vKey, (versions.get(vKey) ?? 0) + 1)\n }\n }\n\n timeProfile('build dependency graph', () => {\n for (const file of testFiles) {\n const key = realKey(file)\n tests.add(key)\n if (!imports.has(key)) walk(key)\n }\n })\n\n return {\n addTest(file) {\n clearCaches()\n const key = realKey(file)\n tests.add(key)\n walk(key)\n bump(key)\n },\n update(file) {\n clearCaches()\n const key = realKey(file)\n walk(key)\n bump(key)\n },\n remove(file) {\n clearCaches()\n const key = realKey(file)\n tests.delete(key)\n setImports(key, new Set())\n bump(key)\n },\n testsAffectedBy(file) {\n clearCaches()\n return dependents(realKey(file)).intersection(tests)\n },\n // no realpath: called for every module load, with the real path Node resolved\n versionOf(file) {\n return versions.get(versionKey(toKey(resolve(file))))\n }\n }\n}\n"]}
@@ -77,17 +77,15 @@ customOut) {
77
77
  __test.queue = queue = queue.filter(x => x.type !== 'auto');
78
78
  queue.push({
79
79
  type,
80
- //tests,
81
80
  callback
82
81
  });
83
82
  }
84
83
  else {
85
84
  __test.queue = queue = queue.filter(x => x.type === 'auto');
86
- __test.queue = queue = [{
87
- type,
88
- //tests,
89
- callback
90
- }];
85
+ queue.push({
86
+ type,
87
+ callback
88
+ });
91
89
  }
92
90
  debug('added to queue', inspect(queue));
93
91
  // see if we can run it
@@ -1 +1 @@
1
- {"version":3,"file":"reRunManager.js","sourceRoot":"","sources":["../../../src/cli/lib/reRunManager.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport { inspect } from 'util'\n\nimport type { ReRunManageState } from './makeWatchRunState.js'\nimport type { RunTestsPromise } from './runTests.js'\nimport type { OutFn } from '../../types.js'\nimport { debug, log, out as mainOut } from '../../output.js'\nimport { agentSetRunReason } from './agentReport.js'\nimport chalk from '../../packages/chalk.js'\n\n\nexport type RunType = 'auto' | 'all' | 'last' | 'failed'\n\ntype RunReqest = {\n type: RunType\n //tests: string[] | Set<string>\n callback: () => RunTestsPromise\n}\n\n//export type ReRunManager = ReturnType<typeof makeReRunManager>\n\nexport type ReRunManager = {\n state: ReRunManageState\n requestRun: (type: RunType, callback: () => RunTestsPromise) => Promise<void>\n __test: {\n waiting: boolean\n queue: RunReqest[]\n }\n}\n\n\nexport function makeReRunManager(\n currentRun: RunTestsPromise | null,\n state: ReRunManageState,\n //callback: () => RunTestsPromise\n //concurrency: Concurrency,\n customOut?: OutFn\n): ReRunManager {\n // agent mode passes a sink - runTests reports the run instead\n const out = customOut ?? mainOut\n // state.currentRun is the one record of the run in flight - watch mode sets it directly\n // for the initial run, so a copy held here would let a change start a second run beside it\n if (currentRun !== null) state.currentRun = currentRun\n let queue: RunReqest[] = []\n let waiting = false\n\n const __test = {\n waiting,\n queue\n }\n\n async function processQueue() {\n \n debug('processing queue')\n\n if (waiting) return // do nothing if another processQueue is already waiting\n __test.waiting = waiting = true // mark that we are waiting\n await state.currentRun // wait for current run\n __test.waiting = waiting = false\n\n debug('done waitinng')\n\n // see if there is a request waiting\n debug('the queue', inspect(queue))\n const runReqest = queue.shift()\n debug('runReqest', runReqest)\n if (runReqest === undefined) return\n\n // tells the run about to start why it is running, for the agent `why=` field\n agentSetRunReason(runReqest.type)\n\n switch (runReqest.type) {\n case 'all': {\n out(chalk.brightblue`re-running all tests...`)\n break\n }\n case 'last': {\n out(chalk.brightblue`re-running last tests...`)\n break\n }\n case 'failed': {\n out(chalk.brightblue`re-running failed tests...`)\n break\n }\n case 'auto': {\n out(chalk.brightblue`running tests due to file changes`)\n break\n }\n default: throw new Error('Unrecognised run type')\n }\n\n // run it and save it\n debug('calling', runReqest.callback.toString())\n const run = runReqest.callback()\n state.currentRun = run\n // wait for the run then trigger another processQueue\n await run\n // if one has already been started by a run request\n // then this won't start a new one. This is used to\n // clear a queue if it has built it\n processQueue().catch(err => log(`processQueue threw an error: ${err}`))\n }\n\n return {\n state,\n requestRun(type: RunType, callback: () => RunTestsPromise) {\n\n // if it's type auto, we only want to keep one, but\n // we want it to include all relevant tests when it runs\n\n // if it's a human triggered type, we'll replace other\n // human requests, but leave the auto, so the user\n // can see update to changed code that would be hidden\n // otherwise\n \n if (type === 'auto') {\n __test.queue = queue = queue.filter(x => x.type !== 'auto')\n queue.push({\n type,\n //tests,\n callback\n })\n } else {\n __test.queue = queue = queue.filter(x => x.type === 'auto')\n __test.queue = queue = [{\n type,\n //tests,\n callback\n }]\n }\n debug('added to queue', inspect(queue))\n // see if we can run it\n return processQueue().catch(err => log(`processQueue threw an error: ${err}`))\n },\n __test\n }\n}\n"]}
1
+ {"version":3,"file":"reRunManager.js","sourceRoot":"","sources":["../../../src/cli/lib/reRunManager.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport { inspect } from 'util'\n\nimport type { ReRunManageState } from './makeWatchRunState.js'\nimport type { RunTestsPromise } from './runTests.js'\nimport type { OutFn } from '../../types.js'\nimport { debug, log, out as mainOut } from '../../output.js'\nimport { agentSetRunReason } from './agentReport.js'\nimport chalk from '../../packages/chalk.js'\n\n\nexport type RunType = 'auto' | 'all' | 'last' | 'failed'\n\ntype RunReqest = {\n type: RunType\n //tests: string[] | Set<string>\n callback: () => RunTestsPromise\n}\n\n//export type ReRunManager = ReturnType<typeof makeReRunManager>\n\nexport type ReRunManager = {\n state: ReRunManageState\n requestRun: (type: RunType, callback: () => RunTestsPromise) => Promise<void>\n __test: {\n waiting: boolean\n queue: RunReqest[]\n }\n}\n\n\nexport function makeReRunManager(\n currentRun: RunTestsPromise | null,\n state: ReRunManageState,\n //callback: () => RunTestsPromise\n //concurrency: Concurrency,\n customOut?: OutFn\n): ReRunManager {\n // agent mode passes a sink - runTests reports the run instead\n const out = customOut ?? mainOut\n // state.currentRun is the one record of the run in flight - watch mode sets it directly\n // for the initial run, so a copy held here would let a change start a second run beside it\n if (currentRun !== null) state.currentRun = currentRun\n let queue: RunReqest[] = []\n let waiting = false\n\n const __test = {\n waiting,\n queue\n }\n\n async function processQueue() {\n \n debug('processing queue')\n\n if (waiting) return // do nothing if another processQueue is already waiting\n __test.waiting = waiting = true // mark that we are waiting\n await state.currentRun // wait for current run\n __test.waiting = waiting = false\n\n debug('done waitinng')\n\n // see if there is a request waiting\n debug('the queue', inspect(queue))\n const runReqest = queue.shift()\n debug('runReqest', runReqest)\n if (runReqest === undefined) return\n\n // tells the run about to start why it is running, for the agent `why=` field\n agentSetRunReason(runReqest.type)\n\n switch (runReqest.type) {\n case 'all': {\n out(chalk.brightblue`re-running all tests...`)\n break\n }\n case 'last': {\n out(chalk.brightblue`re-running last tests...`)\n break\n }\n case 'failed': {\n out(chalk.brightblue`re-running failed tests...`)\n break\n }\n case 'auto': {\n out(chalk.brightblue`running tests due to file changes`)\n break\n }\n default: throw new Error('Unrecognised run type')\n }\n\n // run it and save it\n debug('calling', runReqest.callback.toString())\n const run = runReqest.callback()\n state.currentRun = run\n // wait for the run then trigger another processQueue\n await run\n // if one has already been started by a run request\n // then this won't start a new one. This is used to\n // clear a queue if it has built it\n processQueue().catch(err => log(`processQueue threw an error: ${err}`))\n }\n\n return {\n state,\n requestRun(type: RunType, callback: () => RunTestsPromise) {\n\n // if it's type auto, we only want to keep one, but\n // we want it to include all relevant tests when it runs\n\n // if it's a human triggered type, we'll replace other\n // human requests, but leave the auto, so the user\n // can see update to changed code that would be hidden\n // otherwise\n \n if (type === 'auto') {\n __test.queue = queue = queue.filter(x => x.type !== 'auto')\n queue.push({\n type,\n callback\n })\n } else {\n __test.queue = queue = queue.filter(x => x.type === 'auto')\n queue.push({\n type,\n callback\n })\n }\n debug('added to queue', inspect(queue))\n // see if we can run it\n return processQueue().catch(err => log(`processQueue threw an error: ${err}`))\n },\n __test\n }\n}\n"]}
@@ -5,11 +5,15 @@ import { loadTestFile } from './loadTestFile.js';
5
5
  import { runTest } from './runTest.js';
6
6
  import { showTestLoadingError } from './showTestLoadingError.js';
7
7
  import { showTestFileResult } from './showTestFileResult.js';
8
- import { agentRunStart } from './agentReport.js';
8
+ import { agentRunStart, currentRunReason } from './agentReport.js';
9
9
  import { makeStopSignal, stopIfFailFast } from './stopSignal.js';
10
10
  import { makeRunManagerState } from './makeWatchRunState.js';
11
11
  import { showTestResultsSummary } from './showTestResultsSummary.js';
12
12
  const { timeProfileAsync } = createTimeProfiler(showTimeProfiling);
13
+ // shown after the summary's tallies, for the runs asked for from the keyboard only
14
+ const runLabels = {
15
+ startup: undefined, once: undefined, auto: undefined, all: 'all', last: 'last', failed: 'failures'
16
+ };
13
17
  function recordEntitiesWithoutTests(state, testFile, testResult) {
14
18
  const describes = testResult.describeResults
15
19
  .filter(describeResult => describeResult.success === null)
@@ -33,6 +37,7 @@ refresh = 'refresh', customOut, filters = {}) {
33
37
  }
34
38
  const state = reRunManager?.state || makeRunManagerState();
35
39
  const runStartTime = new Date().getTime();
40
+ const runLabel = runLabels[currentRunReason()];
36
41
  const runNumber = agentRunStart(config, testFiles.size);
37
42
  const stop = makeStopSignal();
38
43
  //console.log('new runTests:', watchRunState?.activeRuns)
@@ -87,7 +92,7 @@ refresh = 'refresh', customOut, filters = {}) {
87
92
  // wait for the output to complete
88
93
  await promiseAllObj(testRunOutPromises);
89
94
  });
90
- return showTestResultsSummary(runTestResults, erroringTests, runStartTime, config, runNumber, state, stop, filters, customOut);
95
+ return showTestResultsSummary(runTestResults, erroringTests, runStartTime, config, runNumber, state, stop, filters, customOut, runLabel);
91
96
  }
92
97
  finally {
93
98
  if (reRunManager !== undefined)
@@ -1 +1 @@
1
- {"version":3,"file":"runTests.js","sourceRoot":"","sources":["../../../src/cli/lib/runTests.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport { allObj as promiseAllObj } from 'sxy-lib/promises.js'\n\nimport type { FinalConfig, OutFn } from '../../types.js'\nimport type { TestExecutionFilters } from '../cliTypes.js'\nimport type { ReRunManager } from './reRunManager.js'\nimport { createTimeProfiler } from '../../packages/timeProfiler.js'\nimport { showTimeProfiling } from '../../config.js'\nimport { loadTestFile, type TestFile } from './loadTestFile.js'\nimport { runTest, type TestResult } from './runTest.js'\nimport { showTestLoadingError } from './showTestLoadingError.js'\nimport { showTestFileResult } from './showTestFileResult.js'\nimport { agentRunStart } from './agentReport.js'\nimport { makeStopSignal, stopIfFailFast } from './stopSignal.js'\nimport { makeRunManagerState, type ReRunManageState } from './makeWatchRunState.js'\nimport { showTestResultsSummary } from './showTestResultsSummary.js'\n\n\nconst { timeProfileAsync } = createTimeProfiler(showTimeProfiling)\n\nexport interface TestsResultsSummary {\n total: number\n passes: number\n invalid: number\n describesTotal: number\n describesPasses: number\n describesInvalid: number\n itsTotal: number\n itsPasses: number\n}\n\nexport type RunTestsPromise = Promise<boolean | null>\n\n\nexport type Concurrency = {\n limit: number\n}\n\nfunction recordEntitiesWithoutTests(state: ReRunManageState, testFile: string, testResult: TestResult): void {\n const describes = testResult.describeResults\n .filter(describeResult => describeResult.success === null)\n .map(describeResult => describeResult.thing)\n const fileWithoutTests = testResult.success === null\n if (fileWithoutTests || describes.length > 0) {\n state.withoutTests.set(testFile, {\n file: testResult.sourceFile ?? testResult.file, fileWithoutTests, describes\n })\n } else {\n state.withoutTests.delete(testFile)\n }\n}\n\nexport async function runTests(\n config: FinalConfig,\n testFiles: Set<string>,\n reRunManager: ReRunManager | undefined,\n concurrency: Concurrency = { limit: -1 }, // eslint-disable-line @typescript-eslint/no-unused-vars\n // -- WIP\n refresh: 'refresh' | 'cached' = 'refresh',\n customOut?: OutFn,\n filters: TestExecutionFilters = {}\n): RunTestsPromise {\n if (reRunManager !== undefined) {\n reRunManager.state.lastTestFiles = new Set(testFiles)\n reRunManager.state.activeRuns++ // whatever, obsolete. Can use it to check on things\n }\n\n const state = reRunManager?.state || makeRunManagerState()\n const runStartTime = new Date().getTime()\n const runNumber = agentRunStart(config, testFiles.size)\n const stop = makeStopSignal()\n //console.log('new runTests:', watchRunState?.activeRuns)\n //console.log(inspect(testFiles))\n\n try {\n const testRunPromises: Record<string, Promise<TestResult>> = {}\n const erroringTests: Record<string, TestFile> = {}\n const testRunOutPromises: Record<string, Promise<void>> = {}\n\n await timeProfileAsync('run tests', async () => {\n if (Array.isArray(testFiles) ? testFiles.length > 0 : testFiles.size > 0) {\n for (const testFile of testFiles) {\n //console.log('testFiles', testFiles)\n //console.log('testFile', testFile)\n\n // an earlier file failed under failFast - leave the rest unloaded\n if (stop.stopped) break\n\n const test = await loadTestFile(config, testFile, refresh)\n\n if (test.error !== undefined && test.error !== false) {\n erroringTests[testFile] = test\n state.failedTestFiles.add(testFile)\n showTestLoadingError(config, test, customOut)\n stopIfFailFast(config, stop, true)\n } else {\n const testRunPromise = runTest(config, test, filters, stop)\n testRunPromises[testFile] = testRunPromise\n\n testRunOutPromises[testFile] = testRunPromise.then(testResult => {\n if (testResult.success === true || testResult.success === null) {\n state.failedTestFiles.delete(testResult.file)\n } else {\n state.failedTestFiles.add(testFile)\n }\n const executionFilterActive = filters.describe !== undefined || filters.it !== undefined\n // a filter makes files and describes look empty when they are not\n if (!executionFilterActive) recordEntitiesWithoutTests(state, testFile, testResult)\n if (!executionFilterActive || testResult.describeResultsSummary.itsTotal > 0) {\n showTestFileResult(config, testResult, customOut)\n }\n })\n\n // wait for the test to complete before proceeding if we are in 'sequential' mode\n if (config.execution.files === 'sequential') await testRunOutPromises[testFile]\n }\n }\n }\n })\n\n let runTestResults: Record<string, TestResult> = {}\n\n await timeProfileAsync('wait for tests to finish', async () => {\n runTestResults = await promiseAllObj(testRunPromises)\n\n // wait for the output to complete\n await promiseAllObj(testRunOutPromises)\n })\n\n return showTestResultsSummary(\n runTestResults, erroringTests, runStartTime,\n config, runNumber, state, stop, filters, customOut\n )\n \n } finally {\n if (reRunManager !== undefined) reRunManager.state.activeRuns--\n }\n}\n"]}
1
+ {"version":3,"file":"runTests.js","sourceRoot":"","sources":["../../../src/cli/lib/runTests.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport { allObj as promiseAllObj } from 'sxy-lib/promises.js'\n\nimport type { FinalConfig, OutFn } from '../../types.js'\nimport type { TestExecutionFilters } from '../cliTypes.js'\nimport type { ReRunManager } from './reRunManager.js'\nimport { createTimeProfiler } from '../../packages/timeProfiler.js'\nimport { showTimeProfiling } from '../../config.js'\nimport { loadTestFile, type TestFile } from './loadTestFile.js'\nimport { runTest, type TestResult } from './runTest.js'\nimport { showTestLoadingError } from './showTestLoadingError.js'\nimport { showTestFileResult } from './showTestFileResult.js'\nimport { agentRunStart, currentRunReason, type RunReason } from './agentReport.js'\nimport { makeStopSignal, stopIfFailFast } from './stopSignal.js'\nimport { makeRunManagerState, type ReRunManageState } from './makeWatchRunState.js'\nimport { showTestResultsSummary } from './showTestResultsSummary.js'\n\n\nconst { timeProfileAsync } = createTimeProfiler(showTimeProfiling)\n\n// shown after the summary's tallies, for the runs asked for from the keyboard only\nconst runLabels: Record<RunReason, string | undefined> = {\n startup: undefined, once: undefined, auto: undefined, all: 'all', last: 'last', failed: 'failures'\n}\n\nexport interface TestsResultsSummary {\n total: number\n passes: number\n invalid: number\n describesTotal: number\n describesPasses: number\n describesInvalid: number\n itsTotal: number\n itsPasses: number\n}\n\nexport type RunTestsPromise = Promise<boolean | null>\n\n\nexport type Concurrency = {\n limit: number\n}\n\nfunction recordEntitiesWithoutTests(state: ReRunManageState, testFile: string, testResult: TestResult): void {\n const describes = testResult.describeResults\n .filter(describeResult => describeResult.success === null)\n .map(describeResult => describeResult.thing)\n const fileWithoutTests = testResult.success === null\n if (fileWithoutTests || describes.length > 0) {\n state.withoutTests.set(testFile, {\n file: testResult.sourceFile ?? testResult.file, fileWithoutTests, describes\n })\n } else {\n state.withoutTests.delete(testFile)\n }\n}\n\nexport async function runTests(\n config: FinalConfig,\n testFiles: Set<string>,\n reRunManager: ReRunManager | undefined,\n concurrency: Concurrency = { limit: -1 }, // eslint-disable-line @typescript-eslint/no-unused-vars\n // -- WIP\n refresh: 'refresh' | 'cached' = 'refresh',\n customOut?: OutFn,\n filters: TestExecutionFilters = {}\n): RunTestsPromise {\n if (reRunManager !== undefined) {\n reRunManager.state.lastTestFiles = new Set(testFiles)\n reRunManager.state.activeRuns++ // whatever, obsolete. Can use it to check on things\n }\n\n const state = reRunManager?.state || makeRunManagerState()\n const runStartTime = new Date().getTime()\n const runLabel = runLabels[currentRunReason()]\n const runNumber = agentRunStart(config, testFiles.size)\n const stop = makeStopSignal()\n //console.log('new runTests:', watchRunState?.activeRuns)\n //console.log(inspect(testFiles))\n\n try {\n const testRunPromises: Record<string, Promise<TestResult>> = {}\n const erroringTests: Record<string, TestFile> = {}\n const testRunOutPromises: Record<string, Promise<void>> = {}\n\n await timeProfileAsync('run tests', async () => {\n if (Array.isArray(testFiles) ? testFiles.length > 0 : testFiles.size > 0) {\n for (const testFile of testFiles) {\n //console.log('testFiles', testFiles)\n //console.log('testFile', testFile)\n\n // an earlier file failed under failFast - leave the rest unloaded\n if (stop.stopped) break\n\n const test = await loadTestFile(config, testFile, refresh)\n\n if (test.error !== undefined && test.error !== false) {\n erroringTests[testFile] = test\n state.failedTestFiles.add(testFile)\n showTestLoadingError(config, test, customOut)\n stopIfFailFast(config, stop, true)\n } else {\n const testRunPromise = runTest(config, test, filters, stop)\n testRunPromises[testFile] = testRunPromise\n\n testRunOutPromises[testFile] = testRunPromise.then(testResult => {\n if (testResult.success === true || testResult.success === null) {\n state.failedTestFiles.delete(testResult.file)\n } else {\n state.failedTestFiles.add(testFile)\n }\n const executionFilterActive = filters.describe !== undefined || filters.it !== undefined\n // a filter makes files and describes look empty when they are not\n if (!executionFilterActive) recordEntitiesWithoutTests(state, testFile, testResult)\n if (!executionFilterActive || testResult.describeResultsSummary.itsTotal > 0) {\n showTestFileResult(config, testResult, customOut)\n }\n })\n\n // wait for the test to complete before proceeding if we are in 'sequential' mode\n if (config.execution.files === 'sequential') await testRunOutPromises[testFile]\n }\n }\n }\n })\n\n let runTestResults: Record<string, TestResult> = {}\n\n await timeProfileAsync('wait for tests to finish', async () => {\n runTestResults = await promiseAllObj(testRunPromises)\n\n // wait for the output to complete\n await promiseAllObj(testRunOutPromises)\n })\n\n return showTestResultsSummary(\n runTestResults, erroringTests, runStartTime,\n config, runNumber, state, stop, filters, customOut, runLabel\n )\n \n } finally {\n if (reRunManager !== undefined) reRunManager.state.activeRuns--\n }\n}\n"]}
@@ -4,5 +4,5 @@ import type { TestFile } from './loadTestFile.js';
4
4
  import type { ReRunManageState } from './makeWatchRunState.js';
5
5
  import type { TestResult } from './runTest.js';
6
6
  import type { StopSignal } from './stopSignal.js';
7
- export declare function showTestResultsSummary(runTestResults: Record<string, TestResult>, erroringTests: Record<string, TestFile>, runStartTime: number, config: FinalConfig, runNumber: number, state: ReRunManageState, stop: StopSignal, filters: TestExecutionFilters, customOut?: OutFn): boolean | null;
7
+ export declare function showTestResultsSummary(runTestResults: Record<string, TestResult>, erroringTests: Record<string, TestFile>, runStartTime: number, config: FinalConfig, runNumber: number, state: ReRunManageState, stop: StopSignal, filters: TestExecutionFilters, customOut?: OutFn, runLabel?: string): boolean | null;
8
8
  //# sourceMappingURL=showTestResultsSummary.d.ts.map
@@ -19,7 +19,9 @@ function plural(count, noun, verb) {
19
19
  return '';
20
20
  return `${count} ${noun}${count === 1 ? '' : 's'} ${verb}`;
21
21
  }
22
- export function showTestResultsSummary(runTestResults, erroringTests, runStartTime, config, runNumber, state, stop, filters, customOut) {
22
+ export function showTestResultsSummary(runTestResults, erroringTests, runStartTime, config, runNumber, state, stop, filters, customOut,
23
+ // what the run covered, e.g. 'all' or 'failures', shown uncoloured after the tallies
24
+ runLabel) {
23
25
  return timeProfile('show test run result summary', () => {
24
26
  const out = customOut ?? mainOut;
25
27
  const testsResultsSummary = {
@@ -175,6 +177,8 @@ export function showTestResultsSummary(runTestResults, erroringTests, runStartTi
175
177
  : '';
176
178
  if (stoppedPlain !== '')
177
179
  out(chalk.orange(stoppedPlain));
180
+ if (runLabel !== undefined)
181
+ out(`(${runLabel})`);
178
182
  const summaryLines = [
179
183
  testsText,
180
184
  joinPlain(describesText, describesThrownPlain, invalidDescribesPlain),
@@ -1 +1 @@
1
- {"version":3,"file":"showTestResultsSummary.js","sourceRoot":"","sources":["../../../src/cli/lib/showTestResultsSummary.ts"],"names":[],"mappings":"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","sourcesContent":["\r\nimport { count } from 'sxy-lib/objects.js'\r\n\r\nimport type { FinalConfig, OutFn } from '../../types.js'\r\nimport type { TestExecutionFilters } from '../cliTypes.js'\r\nimport type { TestFile } from './loadTestFile.js'\r\nimport type { ReRunManageState } from './makeWatchRunState.js'\r\nimport type { TestResult } from './runTest.js'\r\nimport type { TestsResultsSummary } from './runTests.js'\r\nimport type { StopSignal } from './stopSignal.js'\r\nimport chalk from '../../packages/chalk.js'\r\nimport figures from '../../packages/figures.js'\r\nimport { agentDone, agentFailure } from './agentReport.js'\r\nimport { failureEntries } from './failureEntries.js'\r\nimport { failureLines } from './failureLines.js'\r\n//import { showFailures } from './showFailures.js'\r\nimport { showTestFileResult } from './showTestFileResult.js'\r\nimport { showTestLoadingError } from './showTestLoadingError.js'\r\nimport { writeStatusFile, statusCodes } from './statusFile.js'\r\nimport { out as mainOut } from '../../output.js'\r\nimport { timeProfile } from './timeProfiler.js'\r\n\r\n\r\n// the summary lines, minus the colouring, for the status file\r\nfunction joinPlain(...parts: string[]): string {\r\n return parts.filter(part => part !== '').join(' ')\r\n}\r\n\r\nfunction plural(count: number, noun: string, verb: string): string {\r\n if (count < 1) return ''\r\n return `${count} ${noun}${count === 1 ? '' : 's'} ${verb}`\r\n}\r\n\r\nexport function showTestResultsSummary(\r\n runTestResults: Record<string, TestResult>,\r\n erroringTests: Record<string, TestFile>,\r\n runStartTime: number,\r\n config: FinalConfig,\r\n runNumber: number,\r\n state: ReRunManageState,\r\n stop: StopSignal,\r\n filters: TestExecutionFilters,\r\n customOut?: OutFn,\r\n) {\r\n return timeProfile('show test run result summary', () => {\r\n \r\n const out = customOut ?? mainOut\r\n\r\n const testsResultsSummary: TestsResultsSummary = {\r\n total: 0,\r\n passes: 0,\r\n invalid: 0,\r\n describesTotal: 0,\r\n describesPasses: 0,\r\n describesInvalid: 0,\r\n itsTotal: 0,\r\n itsPasses: 0\r\n // itsInvalid: 0 // we don't check whether tests run any asserts (we don't control the assertion lib)\r\n }\r\n let describesThrownCount = 0\r\n\r\n for (const testFile in runTestResults) {\r\n const runTestResult = runTestResults[testFile]\r\n if (!runTestResult) continue\r\n\r\n testsResultsSummary.total += +(runTestResult.success !== null)\r\n testsResultsSummary.passes += +(runTestResult.success ?? false)\r\n testsResultsSummary.invalid += +(runTestResult.success === null)\r\n testsResultsSummary.describesTotal += runTestResult.describeResultsSummary.total\r\n testsResultsSummary.describesPasses += runTestResult.describeResultsSummary.passes\r\n testsResultsSummary.describesInvalid += runTestResult.describeResultsSummary.invalid\r\n testsResultsSummary.itsTotal += runTestResult.describeResultsSummary.itsTotal\r\n testsResultsSummary.itsPasses += runTestResult.describeResultsSummary.itsPasses\r\n describesThrownCount += runTestResult.describeResults\r\n .filter(describeResult => describeResult.error !== undefined)\r\n .length\r\n }\r\n\r\n const failedToLoadCount = count(erroringTests)\r\n testsResultsSummary.total += failedToLoadCount\r\n\r\n if (\r\n (filters.describe !== undefined || filters.it !== undefined)\r\n && testsResultsSummary.itsTotal === 0\r\n && failedToLoadCount === 0\r\n && describesThrownCount === 0\r\n ) {\r\n out(chalk.orange('No tests matched the describe/test filters.'))\r\n const durationMs = new Date().getTime() - runStartTime\r\n writeStatusFile(\r\n config,\r\n statusCodes.couldNotRun,\r\n 'no tests matched the describe/test filters',\r\n [],\r\n durationMs\r\n )\r\n agentDone(\r\n config, runNumber, statusCodes.couldNotRun,\r\n 'no tests matched the describe/test filters', testsResultsSummary, durationMs,\r\n state.failedTestFiles.size\r\n )\r\n return null\r\n }\r\n\r\n const allPassed = (\r\n testsResultsSummary.passes === testsResultsSummary.total\r\n && testsResultsSummary.describesPasses === testsResultsSummary.describesTotal\r\n && testsResultsSummary.itsPasses === testsResultsSummary.itsTotal\r\n )\r\n\r\n const failedTestsCount = testsResultsSummary.itsTotal - testsResultsSummary.itsPasses\r\n\r\n // a file that would not load, or a describe that threw, has no tests in itsTotal -\r\n // each counts as one unit on both sides\r\n const failuresN = failedTestsCount + describesThrownCount + failedToLoadCount\r\n const unitsN = testsResultsSummary.itsTotal + describesThrownCount + failedToLoadCount\r\n\r\n // the originals may have scrolled away on a long run, but past a point repeating\r\n // them buries the tallies - and when everything failed, they are the whole output already\r\n const showFailuresSection = config.restateFailures && failuresN > 0 && failuresN < unitsN\r\n && (failuresN <= 10 || failuresN <= 1.7 * unitsN ** 0.58)\r\n\r\n // the rule is separation enough, without the tallies' wider gap\r\n out((config.compact || showFailuresSection) ? '' : '\\n')\r\n\r\n if (allPassed) {\r\n out(chalk.mediumgreen(`${figures.tick}`))\r\n } else {\r\n //showFailures(out, Object.values(runTestResults), erroringTests)\r\n if (showFailuresSection) {\r\n \r\n const hr = '='.repeat((process.stdout.columns ?? 41) - 1)\r\n out(hr)\r\n out('Failures:')\r\n out(hr)\r\n\r\n for (const test of Object.values(erroringTests)) showTestLoadingError(config, test, out)\r\n for (const testResult of Object.values(runTestResults)) {\r\n if (testResult.success !== false) continue\r\n showTestFileResult(config, testResult, out, { failuresOnly: true })\r\n }\r\n out(config.compact ? '' : '\\n')\r\n }\r\n out(chalk.mediumred(`${figures.cross}`))\r\n }\r\n\r\n // number of tests successful\r\n const testsText = `Tests: ${testsResultsSummary.itsPasses}/${testsResultsSummary.itsTotal}`\r\n if (testsResultsSummary.itsPasses === testsResultsSummary.itsTotal) {\r\n if (testsResultsSummary.itsTotal === 0) {\r\n out(chalk.grey(`${testsText}`))\r\n } else {\r\n out(chalk.mediumgreen(`${testsText}`))\r\n }\r\n } else {\r\n out(chalk.mediumred(`${testsText}`))\r\n }\r\n\r\n // number of items successful\r\n const describesText = `Items: ${testsResultsSummary.describesPasses}/${testsResultsSummary.describesTotal}`\r\n const invalidDescribesPlain = (testsResultsSummary.describesInvalid >= 1)\r\n ? (testsResultsSummary.describesInvalid === 1)\r\n ? '(+1 describe without tests)'\r\n : `(+${testsResultsSummary.describesInvalid} describes without tests)`\r\n : ''\r\n const describesThrownPlain = (describesThrownCount >= 1)\r\n ? (describesThrownCount === 1)\r\n ? '(1 describe errored)'\r\n : `(${describesThrownCount} describes errored)`\r\n : ''\r\n const describesDetailsText = [\r\n chalk.mediumred(describesThrownPlain), chalk.grey(invalidDescribesPlain)\r\n ]\r\n .filter(text => text !== '')\r\n .join(' ')\r\n const describesDetailsSuffix = describesDetailsText === '' ? '' : ` ${describesDetailsText}`\r\n if (testsResultsSummary.describesPasses === testsResultsSummary.describesTotal) {\r\n if (testsResultsSummary.describesTotal === 0) {\r\n out(chalk.grey(`${describesText}`) + describesDetailsSuffix)\r\n } else {\r\n out(chalk.mediumgreen(`${describesText}`) + describesDetailsSuffix)\r\n }\r\n } else {\r\n out(chalk.mediumred(`${describesText}`) + describesDetailsSuffix)\r\n }\r\n\r\n // number of test files successful\r\n const testFilesText = `Test Files: ${testsResultsSummary.passes}/${testsResultsSummary.total}`\r\n const invalidTestFilesPlain = (testsResultsSummary.invalid >= 1)\r\n ? (testsResultsSummary.invalid === 1)\r\n ? '(+1 file without tests)'\r\n : `(+${testsResultsSummary.invalid} files without tests)`\r\n : ''\r\n const failedToLoadPlain = (failedToLoadCount >= 1)\r\n ? (failedToLoadCount === 1)\r\n ? '(1 file failed to load, or errored)'\r\n : `(${failedToLoadCount} files failed to load, or errored)`\r\n : ''\r\n const testFilesDetailsText = [\r\n chalk.mediumred(failedToLoadPlain), chalk.grey(invalidTestFilesPlain)\r\n ]\r\n .filter(text => text !== '')\r\n .join(' ')\r\n const testFilesDetailsSuffix = testFilesDetailsText === '' ? '' : ` ${testFilesDetailsText}`\r\n if (testsResultsSummary.passes === testsResultsSummary.total) {\r\n if (testsResultsSummary.describesTotal === 0) {\r\n out(chalk.grey(`${testFilesText}`) + testFilesDetailsSuffix)\r\n } else {\r\n out(chalk.mediumgreen(`${testFilesText}`) + testFilesDetailsSuffix)\r\n }\r\n } else {\r\n out(chalk.mediumred(`${testFilesText}`) + testFilesDetailsSuffix)\r\n }\r\n\r\n // the tallies only cover what ran, so say the run was cut short - otherwise\r\n // \"Tests: 0/1\" reads as a one test suite\r\n const stoppedPlain = (stop.stopped)\r\n ? 'stopped at the first failure (failFast)'\r\n : ''\r\n if (stoppedPlain !== '') out(chalk.orange(stoppedPlain))\r\n\r\n const summaryLines = [\r\n testsText,\r\n joinPlain(describesText, describesThrownPlain, invalidDescribesPlain),\r\n joinPlain(testFilesText, failedToLoadPlain, invalidTestFilesPlain),\r\n ...(stoppedPlain === '') ? [] : [stoppedPlain]\r\n ]\r\n\r\n const durationMs = new Date().getTime() - runStartTime\r\n\r\n if (allPassed) {\r\n writeStatusFile(\r\n config, statusCodes.passed, 'all tests passed', summaryLines, durationMs\r\n )\r\n agentDone(\r\n config, runNumber, statusCodes.passed, 'all tests passed',\r\n testsResultsSummary, durationMs, state.failedTestFiles.size\r\n )\r\n } else {\r\n const abortedFiles = Object.keys(erroringTests)\r\n\r\n // a file that would not load, or a describe that threw, means the run died\r\n // partway - a different problem from tests that ran and failed\r\n const aborted = abortedFiles.length > 0 || describesThrownCount > 0\r\n\r\n const description = ([\r\n plural(abortedFiles.length, 'test file', 'failed to load or errored'),\r\n plural(describesThrownCount, 'describe', 'errored'),\r\n plural(failedTestsCount, 'test', 'failed')\r\n ].filter(part => part !== '').join(', ') || 'test run failed')\r\n + ((stop.stopped) ? ', stopped at the first failure' : '')\r\n\r\n const code = (aborted) ? statusCodes.aborted : statusCodes.failed\r\n const failures = failureEntries(\r\n Object.values(runTestResults), erroringTests, config.agentLineLengthLimit\r\n )\r\n\r\n writeStatusFile(\r\n config,\r\n code,\r\n description,\r\n [...summaryLines, ...failureLines(Object.values(runTestResults), erroringTests)],\r\n durationMs\r\n )\r\n\r\n // failures first, so the done line is a complete end of run marker\r\n for (const failure of failures) agentFailure(config, runNumber, failure)\r\n agentDone(\r\n config, runNumber, code, description, testsResultsSummary, durationMs,\r\n state.failedTestFiles.size\r\n )\r\n }\r\n\r\n // full success?\r\n // true/false gets returned to the caller of runTests()\r\n return allPassed\r\n })\r\n}\r\n"]}
1
+ {"version":3,"file":"showTestResultsSummary.js","sourceRoot":"","sources":["../../../src/cli/lib/showTestResultsSummary.ts"],"names":[],"mappings":"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","sourcesContent":["\r\nimport { count } from 'sxy-lib/objects.js'\r\n\r\nimport type { FinalConfig, OutFn } from '../../types.js'\r\nimport type { TestExecutionFilters } from '../cliTypes.js'\r\nimport type { TestFile } from './loadTestFile.js'\r\nimport type { ReRunManageState } from './makeWatchRunState.js'\r\nimport type { TestResult } from './runTest.js'\r\nimport type { TestsResultsSummary } from './runTests.js'\r\nimport type { StopSignal } from './stopSignal.js'\r\nimport chalk from '../../packages/chalk.js'\r\nimport figures from '../../packages/figures.js'\r\nimport { agentDone, agentFailure } from './agentReport.js'\r\nimport { failureEntries } from './failureEntries.js'\r\nimport { failureLines } from './failureLines.js'\r\n//import { showFailures } from './showFailures.js'\r\nimport { showTestFileResult } from './showTestFileResult.js'\r\nimport { showTestLoadingError } from './showTestLoadingError.js'\r\nimport { writeStatusFile, statusCodes } from './statusFile.js'\r\nimport { out as mainOut } from '../../output.js'\r\nimport { timeProfile } from './timeProfiler.js'\r\n\r\n\r\n// the summary lines, minus the colouring, for the status file\r\nfunction joinPlain(...parts: string[]): string {\r\n return parts.filter(part => part !== '').join(' ')\r\n}\r\n\r\nfunction plural(count: number, noun: string, verb: string): string {\r\n if (count < 1) return ''\r\n return `${count} ${noun}${count === 1 ? '' : 's'} ${verb}`\r\n}\r\n\r\nexport function showTestResultsSummary(\r\n runTestResults: Record<string, TestResult>,\r\n erroringTests: Record<string, TestFile>,\r\n runStartTime: number,\r\n config: FinalConfig,\r\n runNumber: number,\r\n state: ReRunManageState,\r\n stop: StopSignal,\r\n filters: TestExecutionFilters,\r\n customOut?: OutFn,\r\n // what the run covered, e.g. 'all' or 'failures', shown uncoloured after the tallies\r\n runLabel?: string\r\n) {\r\n return timeProfile('show test run result summary', () => {\r\n \r\n const out = customOut ?? mainOut\r\n\r\n const testsResultsSummary: TestsResultsSummary = {\r\n total: 0,\r\n passes: 0,\r\n invalid: 0,\r\n describesTotal: 0,\r\n describesPasses: 0,\r\n describesInvalid: 0,\r\n itsTotal: 0,\r\n itsPasses: 0\r\n // itsInvalid: 0 // we don't check whether tests run any asserts (we don't control the assertion lib)\r\n }\r\n let describesThrownCount = 0\r\n\r\n for (const testFile in runTestResults) {\r\n const runTestResult = runTestResults[testFile]\r\n if (!runTestResult) continue\r\n\r\n testsResultsSummary.total += +(runTestResult.success !== null)\r\n testsResultsSummary.passes += +(runTestResult.success ?? false)\r\n testsResultsSummary.invalid += +(runTestResult.success === null)\r\n testsResultsSummary.describesTotal += runTestResult.describeResultsSummary.total\r\n testsResultsSummary.describesPasses += runTestResult.describeResultsSummary.passes\r\n testsResultsSummary.describesInvalid += runTestResult.describeResultsSummary.invalid\r\n testsResultsSummary.itsTotal += runTestResult.describeResultsSummary.itsTotal\r\n testsResultsSummary.itsPasses += runTestResult.describeResultsSummary.itsPasses\r\n describesThrownCount += runTestResult.describeResults\r\n .filter(describeResult => describeResult.error !== undefined)\r\n .length\r\n }\r\n\r\n const failedToLoadCount = count(erroringTests)\r\n testsResultsSummary.total += failedToLoadCount\r\n\r\n if (\r\n (filters.describe !== undefined || filters.it !== undefined)\r\n && testsResultsSummary.itsTotal === 0\r\n && failedToLoadCount === 0\r\n && describesThrownCount === 0\r\n ) {\r\n out(chalk.orange('No tests matched the describe/test filters.'))\r\n const durationMs = new Date().getTime() - runStartTime\r\n writeStatusFile(\r\n config,\r\n statusCodes.couldNotRun,\r\n 'no tests matched the describe/test filters',\r\n [],\r\n durationMs\r\n )\r\n agentDone(\r\n config, runNumber, statusCodes.couldNotRun,\r\n 'no tests matched the describe/test filters', testsResultsSummary, durationMs,\r\n state.failedTestFiles.size\r\n )\r\n return null\r\n }\r\n\r\n const allPassed = (\r\n testsResultsSummary.passes === testsResultsSummary.total\r\n && testsResultsSummary.describesPasses === testsResultsSummary.describesTotal\r\n && testsResultsSummary.itsPasses === testsResultsSummary.itsTotal\r\n )\r\n\r\n const failedTestsCount = testsResultsSummary.itsTotal - testsResultsSummary.itsPasses\r\n\r\n // a file that would not load, or a describe that threw, has no tests in itsTotal -\r\n // each counts as one unit on both sides\r\n const failuresN = failedTestsCount + describesThrownCount + failedToLoadCount\r\n const unitsN = testsResultsSummary.itsTotal + describesThrownCount + failedToLoadCount\r\n\r\n // the originals may have scrolled away on a long run, but past a point repeating\r\n // them buries the tallies - and when everything failed, they are the whole output already\r\n const showFailuresSection = config.restateFailures && failuresN > 0 && failuresN < unitsN\r\n && (failuresN <= 10 || failuresN <= 1.7 * unitsN ** 0.58)\r\n\r\n // the rule is separation enough, without the tallies' wider gap\r\n out((config.compact || showFailuresSection) ? '' : '\\n')\r\n\r\n if (allPassed) {\r\n out(chalk.mediumgreen(`${figures.tick}`))\r\n } else {\r\n //showFailures(out, Object.values(runTestResults), erroringTests)\r\n if (showFailuresSection) {\r\n \r\n const hr = '='.repeat((process.stdout.columns ?? 41) - 1)\r\n out(hr)\r\n out('Failures:')\r\n out(hr)\r\n\r\n for (const test of Object.values(erroringTests)) showTestLoadingError(config, test, out)\r\n for (const testResult of Object.values(runTestResults)) {\r\n if (testResult.success !== false) continue\r\n showTestFileResult(config, testResult, out, { failuresOnly: true })\r\n }\r\n out(config.compact ? '' : '\\n')\r\n }\r\n out(chalk.mediumred(`${figures.cross}`))\r\n }\r\n\r\n // number of tests successful\r\n const testsText = `Tests: ${testsResultsSummary.itsPasses}/${testsResultsSummary.itsTotal}`\r\n if (testsResultsSummary.itsPasses === testsResultsSummary.itsTotal) {\r\n if (testsResultsSummary.itsTotal === 0) {\r\n out(chalk.grey(`${testsText}`))\r\n } else {\r\n out(chalk.mediumgreen(`${testsText}`))\r\n }\r\n } else {\r\n out(chalk.mediumred(`${testsText}`))\r\n }\r\n\r\n // number of items successful\r\n const describesText = `Items: ${testsResultsSummary.describesPasses}/${testsResultsSummary.describesTotal}`\r\n const invalidDescribesPlain = (testsResultsSummary.describesInvalid >= 1)\r\n ? (testsResultsSummary.describesInvalid === 1)\r\n ? '(+1 describe without tests)'\r\n : `(+${testsResultsSummary.describesInvalid} describes without tests)`\r\n : ''\r\n const describesThrownPlain = (describesThrownCount >= 1)\r\n ? (describesThrownCount === 1)\r\n ? '(1 describe errored)'\r\n : `(${describesThrownCount} describes errored)`\r\n : ''\r\n const describesDetailsText = [\r\n chalk.mediumred(describesThrownPlain), chalk.grey(invalidDescribesPlain)\r\n ]\r\n .filter(text => text !== '')\r\n .join(' ')\r\n const describesDetailsSuffix = describesDetailsText === '' ? '' : ` ${describesDetailsText}`\r\n if (testsResultsSummary.describesPasses === testsResultsSummary.describesTotal) {\r\n if (testsResultsSummary.describesTotal === 0) {\r\n out(chalk.grey(`${describesText}`) + describesDetailsSuffix)\r\n } else {\r\n out(chalk.mediumgreen(`${describesText}`) + describesDetailsSuffix)\r\n }\r\n } else {\r\n out(chalk.mediumred(`${describesText}`) + describesDetailsSuffix)\r\n }\r\n\r\n // number of test files successful\r\n const testFilesText = `Test Files: ${testsResultsSummary.passes}/${testsResultsSummary.total}`\r\n const invalidTestFilesPlain = (testsResultsSummary.invalid >= 1)\r\n ? (testsResultsSummary.invalid === 1)\r\n ? '(+1 file without tests)'\r\n : `(+${testsResultsSummary.invalid} files without tests)`\r\n : ''\r\n const failedToLoadPlain = (failedToLoadCount >= 1)\r\n ? (failedToLoadCount === 1)\r\n ? '(1 file failed to load, or errored)'\r\n : `(${failedToLoadCount} files failed to load, or errored)`\r\n : ''\r\n const testFilesDetailsText = [\r\n chalk.mediumred(failedToLoadPlain), chalk.grey(invalidTestFilesPlain)\r\n ]\r\n .filter(text => text !== '')\r\n .join(' ')\r\n const testFilesDetailsSuffix = testFilesDetailsText === '' ? '' : ` ${testFilesDetailsText}`\r\n if (testsResultsSummary.passes === testsResultsSummary.total) {\r\n if (testsResultsSummary.describesTotal === 0) {\r\n out(chalk.grey(`${testFilesText}`) + testFilesDetailsSuffix)\r\n } else {\r\n out(chalk.mediumgreen(`${testFilesText}`) + testFilesDetailsSuffix)\r\n }\r\n } else {\r\n out(chalk.mediumred(`${testFilesText}`) + testFilesDetailsSuffix)\r\n }\r\n\r\n // the tallies only cover what ran, so say the run was cut short - otherwise\r\n // \"Tests: 0/1\" reads as a one test suite\r\n const stoppedPlain = (stop.stopped)\r\n ? 'stopped at the first failure (failFast)'\r\n : ''\r\n if (stoppedPlain !== '') out(chalk.orange(stoppedPlain))\r\n\r\n if (runLabel !== undefined) out(`(${runLabel})`)\r\n\r\n const summaryLines = [\r\n testsText,\r\n joinPlain(describesText, describesThrownPlain, invalidDescribesPlain),\r\n joinPlain(testFilesText, failedToLoadPlain, invalidTestFilesPlain),\r\n ...(stoppedPlain === '') ? [] : [stoppedPlain]\r\n ]\r\n\r\n const durationMs = new Date().getTime() - runStartTime\r\n\r\n if (allPassed) {\r\n writeStatusFile(\r\n config, statusCodes.passed, 'all tests passed', summaryLines, durationMs\r\n )\r\n agentDone(\r\n config, runNumber, statusCodes.passed, 'all tests passed',\r\n testsResultsSummary, durationMs, state.failedTestFiles.size\r\n )\r\n } else {\r\n const abortedFiles = Object.keys(erroringTests)\r\n\r\n // a file that would not load, or a describe that threw, means the run died\r\n // partway - a different problem from tests that ran and failed\r\n const aborted = abortedFiles.length > 0 || describesThrownCount > 0\r\n\r\n const description = ([\r\n plural(abortedFiles.length, 'test file', 'failed to load or errored'),\r\n plural(describesThrownCount, 'describe', 'errored'),\r\n plural(failedTestsCount, 'test', 'failed')\r\n ].filter(part => part !== '').join(', ') || 'test run failed')\r\n + ((stop.stopped) ? ', stopped at the first failure' : '')\r\n\r\n const code = (aborted) ? statusCodes.aborted : statusCodes.failed\r\n const failures = failureEntries(\r\n Object.values(runTestResults), erroringTests, config.agentLineLengthLimit\r\n )\r\n\r\n writeStatusFile(\r\n config,\r\n code,\r\n description,\r\n [...summaryLines, ...failureLines(Object.values(runTestResults), erroringTests)],\r\n durationMs\r\n )\r\n\r\n // failures first, so the done line is a complete end of run marker\r\n for (const failure of failures) agentFailure(config, runNumber, failure)\r\n agentDone(\r\n config, runNumber, code, description, testsResultsSummary, durationMs,\r\n state.failedTestFiles.size\r\n )\r\n }\r\n\r\n // full success?\r\n // true/false gets returned to the caller of runTests()\r\n return allPassed\r\n })\r\n}\r\n"]}
@@ -46,6 +46,25 @@ function localTime(date) {
46
46
  + ` ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
47
47
  + ` ${offset}`;
48
48
  }
49
+ const renameAttempts = 10;
50
+ const renameRetryMs = 10;
51
+ const transientRenameCodes = new Set(['EPERM', 'EBUSY', 'EACCES']);
52
+ // Windows refuses a rename over a file something else briefly holds open - antivirus or the
53
+ // indexer, typically just after it was written - so a refusal is waited out for a moment
54
+ function renameRetrying(from, to) {
55
+ for (let attempt = 1;; attempt++) {
56
+ try {
57
+ fs.renameSync(from, to);
58
+ return;
59
+ }
60
+ catch (error) {
61
+ const code = (error instanceof Error && 'code' in error) ? String(error.code) : '';
62
+ if (attempt >= renameAttempts || !transientRenameCodes.has(code))
63
+ throw error;
64
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, renameRetryMs);
65
+ }
66
+ }
67
+ }
49
68
  function write(config, contents) {
50
69
  const statusFile = config.watch.statusFile;
51
70
  if (typeof statusFile !== 'string' || statusFile === '')
@@ -56,7 +75,7 @@ function write(config, contents) {
56
75
  try {
57
76
  fs.mkdirSync(path.dirname(location), { recursive: true });
58
77
  fs.writeFileSync(tempLocation, contents);
59
- fs.renameSync(tempLocation, location);
78
+ renameRetrying(tempLocation, location);
60
79
  }
61
80
  catch (error) {
62
81
  // never let the status file take the run down with it
@@ -1 +1 @@
1
- {"version":3,"file":"statusFile.js","sourceRoot":"","sources":["../../../src/cli/lib/statusFile.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport fs from 'fs'\nimport path from 'path'\n\nimport type { FinalConfig } from '../../types.js'\nimport chalk from '../../packages/chalk.js'\nimport { packageName } from '../../config.js'\nimport { log } from '../../output.js'\n\n\n// The status file holds the result of the latest run: `<code> - <description>` on the first\n// line, then any detail. Watch mode resets it to -1 on startup so a stale result is never\n// mistaken for the current one.\nexport const statusCodes = {\n notRunYet: -1, // watch mode has started, but no run has finished\n passed: 0,\n failed: 1, // tests ran and failed\n aborted: 2, // the run died partway - a test file failed to load, or a describe threw\n couldNotRun: 3 // nothing ran at all - no test files, or no match for the filters\n} as const\n\nexport type StatusCode = typeof statusCodes[keyof typeof statusCodes]\n\n// The same states as words, for the agent stream. A number needs the table above to mean\n// anything; a reader of one line should not have to go and find it. Defined here so the two\n// cannot drift apart.\nexport const statusNames: Record<StatusCode, string> = {\n [statusCodes.notRunYet]: 'not-run',\n [statusCodes.passed]: 'passed',\n [statusCodes.failed]: 'failed',\n [statusCodes.aborted]: 'aborted',\n [statusCodes.couldNotRun]: 'could-not-run'\n}\n\nexport function resetStatusFile(config: FinalConfig): void {\n writeStatusFile(config, statusCodes.notRunYet, 'no run has finished yet')\n}\n\nexport function writeStatusFile(\n config: FinalConfig,\n code: StatusCode,\n description: string,\n details: string[] = [],\n durationMs?: number\n): void {\n write(config, [\n `${code} - ${description}`,\n localTime(new Date()),\n ...details,\n ...(durationMs === undefined) ? [] : [`${durationMs.toLocaleString()}ms`]\n ].join('\\n') + '\\n')\n}\n\n// local time, so the file reads the way the terminal beside it does, with the offset so a\n// reader elsewhere can still place it\nfunction localTime(date: Date): string {\n const pad = (value: number): string => String(value).padStart(2, '0')\n // getTimezoneOffset() counts minutes behind UTC, so the sign is the other way round\n const offsetMinutes = -date.getTimezoneOffset()\n const offset = `${(offsetMinutes < 0) ? '-' : '+'}`\n + `${pad(Math.floor(Math.abs(offsetMinutes) / 60))}:${pad(Math.abs(offsetMinutes) % 60)}`\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`\n + ` ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`\n + ` ${offset}`\n}\n\nfunction write(config: FinalConfig, contents: string): void {\n const statusFile = config.watch.statusFile\n if (typeof statusFile !== 'string' || statusFile === '') return\n\n const location = path.resolve(process.cwd(), statusFile)\n // written via a temp file and renamed, so a poller never reads a half written status\n const tempLocation = `${location}.${process.pid}.tmp`\n try {\n fs.mkdirSync(path.dirname(location), { recursive: true })\n fs.writeFileSync(tempLocation, contents)\n fs.renameSync(tempLocation, location)\n } catch (error) {\n // never let the status file take the run down with it\n log(chalk.mediumred(\n `[${packageName}] Could not write status file ${location}: ${String(error)}`\n ))\n try { fs.rmSync(tempLocation, { force: true }) } catch { /* nothing more to do */ }\n }\n}\n"]}
1
+ {"version":3,"file":"statusFile.js","sourceRoot":"","sources":["../../../src/cli/lib/statusFile.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport fs from 'fs'\nimport path from 'path'\n\nimport type { FinalConfig } from '../../types.js'\nimport chalk from '../../packages/chalk.js'\nimport { packageName } from '../../config.js'\nimport { log } from '../../output.js'\n\n\n// The status file holds the result of the latest run: `<code> - <description>` on the first\n// line, then any detail. Watch mode resets it to -1 on startup so a stale result is never\n// mistaken for the current one.\nexport const statusCodes = {\n notRunYet: -1, // watch mode has started, but no run has finished\n passed: 0,\n failed: 1, // tests ran and failed\n aborted: 2, // the run died partway - a test file failed to load, or a describe threw\n couldNotRun: 3 // nothing ran at all - no test files, or no match for the filters\n} as const\n\nexport type StatusCode = typeof statusCodes[keyof typeof statusCodes]\n\n// The same states as words, for the agent stream. A number needs the table above to mean\n// anything; a reader of one line should not have to go and find it. Defined here so the two\n// cannot drift apart.\nexport const statusNames: Record<StatusCode, string> = {\n [statusCodes.notRunYet]: 'not-run',\n [statusCodes.passed]: 'passed',\n [statusCodes.failed]: 'failed',\n [statusCodes.aborted]: 'aborted',\n [statusCodes.couldNotRun]: 'could-not-run'\n}\n\nexport function resetStatusFile(config: FinalConfig): void {\n writeStatusFile(config, statusCodes.notRunYet, 'no run has finished yet')\n}\n\nexport function writeStatusFile(\n config: FinalConfig,\n code: StatusCode,\n description: string,\n details: string[] = [],\n durationMs?: number\n): void {\n write(config, [\n `${code} - ${description}`,\n localTime(new Date()),\n ...details,\n ...(durationMs === undefined) ? [] : [`${durationMs.toLocaleString()}ms`]\n ].join('\\n') + '\\n')\n}\n\n// local time, so the file reads the way the terminal beside it does, with the offset so a\n// reader elsewhere can still place it\nfunction localTime(date: Date): string {\n const pad = (value: number): string => String(value).padStart(2, '0')\n // getTimezoneOffset() counts minutes behind UTC, so the sign is the other way round\n const offsetMinutes = -date.getTimezoneOffset()\n const offset = `${(offsetMinutes < 0) ? '-' : '+'}`\n + `${pad(Math.floor(Math.abs(offsetMinutes) / 60))}:${pad(Math.abs(offsetMinutes) % 60)}`\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`\n + ` ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`\n + ` ${offset}`\n}\n\nconst renameAttempts = 10\nconst renameRetryMs = 10\nconst transientRenameCodes = new Set(['EPERM', 'EBUSY', 'EACCES'])\n\n// Windows refuses a rename over a file something else briefly holds open - antivirus or the\n// indexer, typically just after it was written - so a refusal is waited out for a moment\nfunction renameRetrying(from: string, to: string): void {\n for (let attempt = 1; ; attempt++) {\n try {\n fs.renameSync(from, to)\n return\n } catch (error) {\n const code = (error instanceof Error && 'code' in error) ? String(error.code) : ''\n if (attempt >= renameAttempts || !transientRenameCodes.has(code)) throw error\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, renameRetryMs)\n }\n }\n}\n\nfunction write(config: FinalConfig, contents: string): void {\n const statusFile = config.watch.statusFile\n if (typeof statusFile !== 'string' || statusFile === '') return\n\n const location = path.resolve(process.cwd(), statusFile)\n // written via a temp file and renamed, so a poller never reads a half written status\n const tempLocation = `${location}.${process.pid}.tmp`\n try {\n fs.mkdirSync(path.dirname(location), { recursive: true })\n fs.writeFileSync(tempLocation, contents)\n renameRetrying(tempLocation, location)\n } catch (error) {\n // never let the status file take the run down with it\n log(chalk.mediumred(\n `[${packageName}] Could not write status file ${location}: ${String(error)}`\n ))\n try { fs.rmSync(tempLocation, { force: true }) } catch { /* nothing more to do */ }\n }\n}\n"]}
@@ -110,14 +110,13 @@ customOut, executionFilters = {}) {
110
110
  // changes accumulate here because an 'auto' request made while a
111
111
  // run is in flight is coalesced into the queued one - everything
112
112
  // edited in the meantime still has to be covered
113
- reRunManager.state.untestedFiles
114
- = reRunManager.state.untestedFiles.union(tests);
113
+ for (const test of tests)
114
+ reRunManager.state.untestedFiles.add(test);
115
115
  void reRunManager.requestRun('auto', () => {
116
116
  // resolved when the run actually starts, not when it is
117
117
  // queued, so a coalesced request picks up the later edits
118
118
  const toTest = (config.watch.reRunFailingTests)
119
- ? reRunManager.state.untestedFiles
120
- .union(reRunManager.state.failedTestFiles)
119
+ ? reRunManager.state.untestedFiles.union(reRunManager.state.failedTestFiles)
121
120
  : new Set(reRunManager.state.untestedFiles);
122
121
  // consumed by this run - without this they are retested
123
122
  // on every later run for the rest of the session
@@ -1 +1 @@
1
- {"version":3,"file":"watchFilesAndRunTestsNodeWatch.js","sourceRoot":"","sources":["../../../src/cli/lib/watchFilesAndRunTestsNodeWatch.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport { matchesGlob } from 'node:path'\n\nimport type { FinalConfig, OutFn } from '../../types.js'\nimport type { TestExecutionFilters } from '../cliTypes.js'\nimport type { ReRunManager } from './reRunManager.js'\nimport type { DependencyGraph } from './makeDependencyGraph.js'\nimport type { StopFn } from '../watch.js'\nimport chalk from '../../packages/chalk.js'\nimport slash from '../../packages/slash.js'\nimport { createTimeProfiler } from '../../packages/timeProfiler.js'\nimport { showTimeProfiling } from '../../config.js'\nimport { out as mainOut, log, debug } from '../../output.js'\nimport { agentChange } from './agentReport.js'\nimport { runTests } from './runTests.js'\nimport { globArgCombine } from './globArgCombine.js'\nimport { addBasePath } from './addBasePath.js'\nimport { watchFilesNodeWatch } from './watchFilesNodeWatch.js'\nimport { filter } from './filter.js'\n\n\nconst { timeProfile, timeProfileAsync } = createTimeProfiler(showTimeProfiling)\n\nexport async function watchFilesAndRunTestsNodeWatch(\n config: FinalConfig,\n //testFiles: Set<string>,\n // null when watch.dependencyEvaluation is off\n dependencyGraph: DependencyGraph | null,\n failingTests: string[],\n reRunManager: ReRunManager,\n //watchRunState: WatchRunState,\n customOut?: OutFn,\n executionFilters: TestExecutionFilters = {}\n): Promise<StopFn> {\n // agent mode passes a sink here, so its own change lines are the only ones printed\n const out = customOut ?? mainOut\n\n return timeProfileAsync('init watching', async () => {\n debug('failingTests', failingTests)\n\n const testsGlobs = addBasePath(config.testsBase, config.tests)\n const testsIgnoreGlobs = addBasePath(config.testsBase, config.testsIgnore)\n\n // indentify a test file\n const testFilter = (file: string): boolean => {\n return matchesGlob(file, globArgCombine(testsGlobs))\n && !matchesGlob(file, globArgCombine(testsIgnoreGlobs))\n && (\n config.testsFilter === undefined\n || config.testsFilter === ''\n || filter(config.testsBase, config.testsFilter, file)\n )\n }\n\n let changedFiles: Array<[string, string]> = []\n let timeout: ReturnType<typeof setTimeout> | null = null\n\n if (config.watch.watchFilesBase === '') {\n out('No config.watch.watchFilesBase provided, defaulting to \\'.\\'')\n config.watch.watchFilesBase = '.'\n }\n\n const watcher = await watchFilesNodeWatch(config, watchEvent)\n return () => {\n watcher.close()\n // a change seen just before stopping would otherwise still run its tests\n if (timeout) clearTimeout(timeout)\n }\n\n function watchEvent(filename: string, event: string): void {\n debug('handle watch event ', filename, event)\n\n filename = slash(filename)\n\n // forget a deleted test, avoiding trying to run it later\n if (event === 'remove') {\n debug('removing test', filename)\n reRunManager.state.withoutTests.delete(filename)\n reRunManager.state.allTestFiles.delete(filename)\n }\n\n // info on the event found\n out(chalk.grey`change: ${filename} (${event})`)\n agentChange(config, filename, event)\n\n // add the file to changed files\n changedFiles.push([filename, event])\n\n // 33ms debounce\n const debounceMs = 33\n if (timeout) {\n clearTimeout(timeout)\n }\n timeout = setTimeout(() => {\n timeProfile('handle changed files', () => {\n debug('changedFiles', changedFiles)\n try {\n // the whole batch is applied before any lookup, so an import added in one\n // file can reach a file created in the same batch\n if (dependencyGraph !== null) {\n for (const [file, fileEvent] of changedFiles) {\n if (fileEvent === 'remove') dependencyGraph.remove(file)\n else if (testFilter(file)) dependencyGraph.addTest(file)\n else dependencyGraph.update(file)\n }\n }\n\n // build related test list\n const tests = new Set<string>\n for (const [file, fileEvent] of changedFiles) {\n debug('check', file)\n if (fileEvent !== 'remove') { // only check if it's a test if it exists (has not just been removed)\n if (testFilter(file)) { // it is a test file itself, add to run list\n debug('its a test', file)\n tests.add(file)\n // makes a newly created test part of what `a` runs\n reRunManager.state.allTestFiles.add(file)\n }\n }\n\n // with evaluation off there is nothing to map the change through:\n // any watched file could affect any test, so run the lot\n if (dependencyGraph === null) {\n for (const f of reRunManager.state.allTestFiles) tests.add(f)\n continue\n }\n\n for (const f of dependencyGraph.testsAffectedBy(file)) tests.add(f)\n }\n\n changedFiles = []\n\n debug('tests', tests)\n\n if (tests.size > 0) {\n timeProfile('build list of files to test and request a run',\n () => {\n debug('tests', tests)\n debug('reRunFailingTests', config.watch.reRunFailingTests)\n\n // changes accumulate here because an 'auto' request made while a\n // run is in flight is coalesced into the queued one - everything\n // edited in the meantime still has to be covered\n reRunManager.state.untestedFiles\n = reRunManager.state.untestedFiles.union(tests)\n\n void reRunManager.requestRun(\n 'auto',\n () => {\n // resolved when the run actually starts, not when it is\n // queued, so a coalesced request picks up the later edits\n const toTest = (config.watch.reRunFailingTests)\n ? reRunManager.state.untestedFiles\n .union(reRunManager.state.failedTestFiles)\n : new Set(reRunManager.state.untestedFiles)\n\n // consumed by this run - without this they are retested\n // on every later run for the rest of the session\n reRunManager.state.untestedFiles = new Set()\n\n debug('run tests on change')\n debug('toTest', toTest)\n\n return runTests(\n config,\n toTest,\n reRunManager,\n { limit: -1 },\n 'refresh',\n customOut,\n executionFilters\n )\n }\n )\n }\n )\n }\n } catch (e) {\n log('watch handler error', e)\n }\n })\n }, debounceMs)\n } // end of watch event\n })\n\n // NOTE: \"watching...\" is intentionally NOT printed here. watch() already emits it,\n // tied to a real milestone (the initial test run finishing, or no tests found).\n // Printing it again here produced a duplicate \"watching...\" on every startup.\n // out(chalk.brightblue`watching...`)\n}\n"]}
1
+ {"version":3,"file":"watchFilesAndRunTestsNodeWatch.js","sourceRoot":"","sources":["../../../src/cli/lib/watchFilesAndRunTestsNodeWatch.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport { matchesGlob } from 'node:path'\n\nimport type { FinalConfig, OutFn } from '../../types.js'\nimport type { TestExecutionFilters } from '../cliTypes.js'\nimport type { ReRunManager } from './reRunManager.js'\nimport type { DependencyGraph } from './makeDependencyGraph.js'\nimport type { StopFn } from '../watch.js'\nimport chalk from '../../packages/chalk.js'\nimport slash from '../../packages/slash.js'\nimport { createTimeProfiler } from '../../packages/timeProfiler.js'\nimport { showTimeProfiling } from '../../config.js'\nimport { out as mainOut, log, debug } from '../../output.js'\nimport { agentChange } from './agentReport.js'\nimport { runTests } from './runTests.js'\nimport { globArgCombine } from './globArgCombine.js'\nimport { addBasePath } from './addBasePath.js'\nimport { watchFilesNodeWatch } from './watchFilesNodeWatch.js'\nimport { filter } from './filter.js'\n\n\nconst { timeProfile, timeProfileAsync } = createTimeProfiler(showTimeProfiling)\n\nexport async function watchFilesAndRunTestsNodeWatch(\n config: FinalConfig,\n //testFiles: Set<string>,\n // null when watch.dependencyEvaluation is off\n dependencyGraph: DependencyGraph | null,\n failingTests: string[],\n reRunManager: ReRunManager,\n //watchRunState: WatchRunState,\n customOut?: OutFn,\n executionFilters: TestExecutionFilters = {}\n): Promise<StopFn> {\n // agent mode passes a sink here, so its own change lines are the only ones printed\n const out = customOut ?? mainOut\n\n return timeProfileAsync('init watching', async () => {\n debug('failingTests', failingTests)\n\n const testsGlobs = addBasePath(config.testsBase, config.tests)\n const testsIgnoreGlobs = addBasePath(config.testsBase, config.testsIgnore)\n\n // indentify a test file\n const testFilter = (file: string): boolean => {\n return matchesGlob(file, globArgCombine(testsGlobs))\n && !matchesGlob(file, globArgCombine(testsIgnoreGlobs))\n && (\n config.testsFilter === undefined\n || config.testsFilter === ''\n || filter(config.testsBase, config.testsFilter, file)\n )\n }\n\n let changedFiles: Array<[string, string]> = []\n let timeout: ReturnType<typeof setTimeout> | null = null\n\n if (config.watch.watchFilesBase === '') {\n out('No config.watch.watchFilesBase provided, defaulting to \\'.\\'')\n config.watch.watchFilesBase = '.'\n }\n\n const watcher = await watchFilesNodeWatch(config, watchEvent)\n return () => {\n watcher.close()\n // a change seen just before stopping would otherwise still run its tests\n if (timeout) clearTimeout(timeout)\n }\n\n function watchEvent(filename: string, event: string): void {\n debug('handle watch event ', filename, event)\n\n filename = slash(filename)\n\n // forget a deleted test, avoiding trying to run it later\n if (event === 'remove') {\n debug('removing test', filename)\n reRunManager.state.withoutTests.delete(filename)\n reRunManager.state.allTestFiles.delete(filename)\n }\n\n // info on the event found\n out(chalk.grey`change: ${filename} (${event})`)\n agentChange(config, filename, event)\n\n // add the file to changed files\n changedFiles.push([filename, event])\n\n // 33ms debounce\n const debounceMs = 33\n if (timeout) {\n clearTimeout(timeout)\n }\n timeout = setTimeout(() => {\n timeProfile('handle changed files', () => {\n debug('changedFiles', changedFiles)\n try {\n // the whole batch is applied before any lookup, so an import added in one\n // file can reach a file created in the same batch\n if (dependencyGraph !== null) {\n for (const [file, fileEvent] of changedFiles) {\n if (fileEvent === 'remove') dependencyGraph.remove(file)\n else if (testFilter(file)) dependencyGraph.addTest(file)\n else dependencyGraph.update(file)\n }\n }\n\n // build related test list\n const tests = new Set<string>\n for (const [file, fileEvent] of changedFiles) {\n debug('check', file)\n if (fileEvent !== 'remove') { // only check if it's a test if it exists (has not just been removed)\n if (testFilter(file)) { // it is a test file itself, add to run list\n debug('its a test', file)\n tests.add(file)\n // makes a newly created test part of what `a` runs\n reRunManager.state.allTestFiles.add(file)\n }\n }\n\n // with evaluation off there is nothing to map the change through:\n // any watched file could affect any test, so run the lot\n if (dependencyGraph === null) {\n for (const f of reRunManager.state.allTestFiles) tests.add(f)\n continue\n }\n\n for (const f of dependencyGraph.testsAffectedBy(file)) tests.add(f)\n }\n\n changedFiles = []\n\n debug('tests', tests)\n\n if (tests.size > 0) {\n timeProfile('build list of files to test and request a run', () => {\n debug('tests', tests)\n debug('reRunFailingTests', config.watch.reRunFailingTests)\n\n // changes accumulate here because an 'auto' request made while a\n // run is in flight is coalesced into the queued one - everything\n // edited in the meantime still has to be covered\n for (const test of tests) reRunManager.state.untestedFiles.add(test)\n\n void reRunManager.requestRun('auto', () => {\n // resolved when the run actually starts, not when it is\n // queued, so a coalesced request picks up the later edits\n const toTest = (config.watch.reRunFailingTests)\n ? reRunManager.state.untestedFiles.union(reRunManager.state.failedTestFiles)\n : new Set(reRunManager.state.untestedFiles)\n\n // consumed by this run - without this they are retested\n // on every later run for the rest of the session\n reRunManager.state.untestedFiles = new Set()\n\n debug('run tests on change')\n debug('toTest', toTest)\n\n return runTests(\n config,\n toTest,\n reRunManager,\n { limit: -1 },\n 'refresh',\n customOut,\n executionFilters\n )\n })\n })\n }\n } catch (e) {\n log('watch handler error', e)\n }\n })\n }, debounceMs)\n } // end of watch event\n })\n\n // NOTE: \"watching...\" is intentionally NOT printed here. watch() already emits it,\n // tied to a real milestone (the initial test run finishing, or no tests found).\n // Printing it again here produced a duplicate \"watching...\" on every startup.\n // out(chalk.brightblue`watching...`)\n}\n"]}
package/dist/cli/watch.js CHANGED
@@ -155,6 +155,19 @@ export async function doWatch(input = process.stdin) {
155
155
  agentOut(chalk.brightblue `finding initial dependencies...`);
156
156
  dependencyGraph = makeDependencyGraph(config, testFiles);
157
157
  }
158
+ // registered before anything loads, so a rerun reloads only what the graph says changed
159
+ if (dependencyGraph !== null) {
160
+ const graph = dependencyGraph;
161
+ const { registerGraphReload } = await import('./lib/esmGraphReload.js');
162
+ const { handle } = registerGraphReload(file => graph.versionOf(file));
163
+ stopFns.push(() => handle.deregister());
164
+ }
165
+ else {
166
+ // todo: one instance per run; until then each test file reloads its whole graph
167
+ const { registerTreeReload } = await import('./lib/esmTreeReload.js');
168
+ const handle = registerTreeReload();
169
+ stopFns.push(() => handle.deregister());
170
+ }
158
171
  // live before the first test runs, for two reasons. A change landing during the initial
159
172
  // run (a compiler emit, typically) is seen rather than lost. And the watcher starts on a
160
173
  // quiet loop: node-watch probes for native recursive support with a 200ms timeout, and a
@@ -196,7 +209,7 @@ export async function doWatch(input = process.stdin) {
196
209
  agentOut(chalk.brightblue `watching...`);
197
210
  agentWatching(config, testFiles.size);
198
211
  const { listenToUser } = await import('./lib/listenToUser.js');
199
- const disposeTty = listenToUser(reRunManager, files => () => {
212
+ const disposeTty = listenToUser(reRunManager, files => {
200
213
  return runTests(config, files, reRunManager, concurrency, 'refresh', runsOut, executionFilters);
201
214
  }, config.watch.ttyActionsOnKeypress, initialTestRun, input, runsOut);
202
215
  stopFns.push(disposeTty);