diff --git a/package.json b/package.json index c7ad0a6..a952b82 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "scripts": { "prepack": "yarn build", "prebuild": "rm -rf dist", + "cli": "$SHELL ./script/run", "lint": "rome format src tests && rome check src tests", "lint:fix": "rome format src tests --write && rome check src tests --apply", "types:check": "yarn tsc --project . --noEmit", diff --git a/script/run b/script/run new file mode 100644 index 0000000..154ffe3 --- /dev/null +++ b/script/run @@ -0,0 +1,18 @@ +#!/usr/bin/bash + +rm -rf integration-build.tgz dist +yarn pack --out integration-build.tgz + +mkdir .tmp + +echo "{}" > .tmp/package.json +touch .tmp/yarn.lock + +( + cd .tmp + yarn cache clean + yarn add ../integration-build.tgz ts-node + yarn womm "$@" +) + +rm -rf .tmp diff --git a/script/test-suite b/script/test-suite index c583fe2..0f6692c 100644 --- a/script/test-suite +++ b/script/test-suite @@ -7,6 +7,8 @@ # environment and run the test suite with it. # + +rm -rf integration-build.tgz dist yarn pack --out integration-build.tgz mkdir .tests @@ -16,6 +18,7 @@ touch .tests/yarn.lock ( cd .tests + yarn cache clean yarn add ../integration-build.tgz ts-node yarn womm ../tests --ts --workers=2 ) diff --git a/src/cli.ts b/src/cli.ts index b83f373..4abdbb7 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -3,6 +3,10 @@ import parseArgs from './argumentParser' import { getContext, redText, assertTsNodeInstall } from './utils' import run from './runner' +import createLogger from './logging' + +const logger = createLogger() + /* * Logic executed when running the test runner CLI. */ @@ -10,7 +14,7 @@ import run from './runner' const args = parseArgs(process.argv) if (args.help) { - console.log(helpText) + logger.logRaw(helpText) return } @@ -21,7 +25,7 @@ import run from './runner' try { run(args, context) } catch (e) { - console.log(redText('Test run failed')) + logger.logError('Test run failed') throw e } })().catch((e) => { diff --git a/src/logging.ts b/src/logging.ts new file mode 100644 index 0000000..777e5e1 --- /dev/null +++ b/src/logging.ts @@ -0,0 +1,46 @@ +import { redText, greenText, yellowText } from './utils' + +/* + * Standard logger for anything that needs to print messages to the user. + * + * This supports the same general functionality as the `Console` logger, + * including `group` and various levels of logging. + */ +class Logger { + indent: number = 0 + + get #indentPrefix(): string { + return ' '.repeat(this.indent) + } + + #formatMessage(text: string): string { + return `[${new Date().toLocaleString()}] ${text}` + } + + group(label: string) { + process.stdout.write(this.#formatMessage(`${label}\n`)) + this.indent += 1 + } + + groupEnd() { + if (this.indent > 0) this.indent -= 1 + } + + logError(text: string) { + process.stdout.write(this.#formatMessage(`${this.#indentPrefix}${redText(text)}\n`)) + } + + logWarning(text: string) { + process.stdout.write(`${this.#indentPrefix}${yellowText(text)}\n`) + } + + log(text: string) { + process.stdout.write(this.#formatMessage(`${this.#indentPrefix}${text}\n`)) + } + + logRaw(text: string) { + process.stdout.write(`${text}\n`) + } +} + +export default () => new Logger() diff --git a/src/runner.ts b/src/runner.ts index 454e1f4..53b10bc 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -1,10 +1,12 @@ import { forkWorker, yellowText, boldText, splitIntoBatches } from './utils' import { type Args, type Context, type WorkerReport } from './types' - +import createLogger from './logging' import { promises as fs } from 'fs' import path from 'path' import { performance } from 'perf_hooks' +const logger = createLogger() + /* * Collects test files recursively starting from the provided root * path. @@ -73,7 +75,7 @@ async function assignTestsToWorkers( const workerMessage: { results: string; failed: boolean } = JSON.parse(message) if (workerMessage.failed) workerReport.pass = false - console.log(workerMessage.results) + logger.logRaw(workerMessage.results) }, extraEnv: { TS: context.nodeRuntime === 'ts-node' ? '1' : '0' }, }) @@ -95,21 +97,21 @@ async function run(args: Args, context: Context) { const supportedTests = collectedTests.filter((testPath) => { const supported = (testPath.endsWith('.test.ts') && context.ts) || (!context.ts && !testPath.endsWith('.test.ts')) - if (!supported) console.log(yellowText(`WARN: ${testPath} is not supported without --ts and will be ignored`)) + if (!supported) logger.logWarning(`WARN: ${testPath} is not supported without --ts and will be ignored`) return supported }) performance.mark('test-collect:end') const testCollectTime = performance.measure('test-collect', 'test-collect:start', 'test-collect:end').duration - console.log(`Collected ${supportedTests.length} test files in ${boldText((testCollectTime / 1000).toFixed(3))}s`) + logger.log(`Collected ${supportedTests.length} test files in ${boldText((testCollectTime / 1000).toFixed(3))}s`) const summary = await assignTestsToWorkers(context, supportedTests, args.workers) const hasFailed = Object.values(summary).filter((workerReport) => !workerReport.pass).length > 0 performance.mark('run:end') const overallTime = performance.measure('run', 'run:start', 'run:end').duration - console.log(`Ran tests in ${boldText(overallTime / 1000)}s`) + logger.log(`Ran tests in ${boldText(overallTime / 1000)}s`) if (hasFailed) throw new Error('Test run failed') } diff --git a/src/testContext.ts b/src/testContext.ts index 828bac4..9df1b9d 100644 --- a/src/testContext.ts +++ b/src/testContext.ts @@ -2,6 +2,10 @@ import { performance } from 'perf_hooks' import { redText, greenText } from './utils' import { type TestCaseLabel, type TestCaseFunction } from './types' +import createLogger from './logging' + +const logger = createLogger() + let _testContext: TestContext | undefined | null export function getTestContext(): TestContext { @@ -42,14 +46,14 @@ export class TestContext { test() } catch (e) { hasFailed = true - console.log(redText(String(e))) + logger.logError(String(e)) } performance.mark(`test-${label}:end`) const testDuration = performance.measure(`test-${label}`, `test-${label}:start`, `test-${label}:end`).duration - if (hasFailed) console.log(redText(`❌ [FAILED] ${label} (${(testDuration / 1000).toFixed(3)}s)`)) - else console.log(greenText(`✅ [PASS] ${label} (${(testDuration / 1000).toFixed(3)}s)`)) + if (hasFailed) logger.logError(redText(`❌ [FAILED] ${label} (${(testDuration / 1000).toFixed(3)}s)`)) + else logger.log(greenText(`✅ [PASS] ${label} (${(testDuration / 1000).toFixed(3)}s)`)) } runTests() { @@ -60,9 +64,9 @@ export class TestContext { for (const child of this.children) { const [label, childContext] = child - console.group(greenText(label)) + logger.group(greenText(label)) childContext.runTests() - console.groupEnd() + logger.groupEnd() } }