diff --git a/test/setup.ts b/test/setup.ts index c02df22be..0af5a6299 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -2,58 +2,9 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -function sanitizeWindowsCIOutput(text: string): string { - return text - .replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "?") - .replace(/[\uD800-\uDFFF]/g, "?"); -} +import { installWindowsCIOutputSanitizer } from "./windows-ci-output-sanitizer"; -if (process.platform === "win32" && process.env.GITHUB_ACTIONS === "true") { - const decodeUtf8Text = (chunk: unknown): string | null => { - if (typeof chunk === "string") return chunk; - if (Buffer.isBuffer(chunk)) return chunk.toString("utf-8"); - if (chunk instanceof Uint8Array) - return Buffer.from(chunk).toString("utf-8"); - if (chunk instanceof ArrayBuffer) - return Buffer.from(chunk).toString("utf-8"); - if (ArrayBuffer.isView(chunk)) { - return Buffer.from( - chunk.buffer, - chunk.byteOffset, - chunk.byteLength, - ).toString("utf-8"); - } - return null; - }; - - const originalStdoutWrite = process.stdout.write.bind(process.stdout); - const originalStderrWrite = process.stderr.write.bind(process.stderr); - - process.stdout.write = ((chunk: unknown, ...args: unknown[]) => { - const text = decodeUtf8Text(chunk); - if (text !== null) - return originalStdoutWrite(sanitizeWindowsCIOutput(text), ...args); - return originalStdoutWrite(chunk as never, ...args); // passthrough - }) as typeof process.stdout.write; - - process.stderr.write = ((chunk: unknown, ...args: unknown[]) => { - const text = decodeUtf8Text(chunk); - if (text !== null) - return originalStderrWrite(sanitizeWindowsCIOutput(text), ...args); - return originalStderrWrite(chunk as never, ...args); // passthrough - }) as typeof process.stderr.write; - - const originalWriteSync = fs.writeSync.bind(fs); - fs.writeSync = ((fd: number, data: unknown, ...args: unknown[]) => { - if (fd === 1 || fd === 2) { - const text = decodeUtf8Text(data); - if (text !== null) { - return originalWriteSync(fd, sanitizeWindowsCIOutput(text), ...args); - } - } - return originalWriteSync(fd, data as never, ...(args as never[])); - }) as typeof fs.writeSync; -} +installWindowsCIOutputSanitizer(); const originalHome = process.env.HOME; const originalUserProfile = process.env.USERPROFILE; diff --git a/test/vitest-global-setup.ts b/test/vitest-global-setup.ts new file mode 100644 index 000000000..3a05d7661 --- /dev/null +++ b/test/vitest-global-setup.ts @@ -0,0 +1,5 @@ +import { installWindowsCIOutputSanitizer } from "./windows-ci-output-sanitizer"; + +export default function globalSetup() { + installWindowsCIOutputSanitizer(); +} diff --git a/test/windows-ci-output-sanitizer.ts b/test/windows-ci-output-sanitizer.ts new file mode 100644 index 000000000..37c777d4f --- /dev/null +++ b/test/windows-ci-output-sanitizer.ts @@ -0,0 +1,59 @@ +import fs from "node:fs"; + +function sanitizeWindowsCIOutput(text: string): string { + return text + .replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "?") + .replace(/[\uD800-\uDFFF]/g, "?"); +} + +function decodeUtf8Text(chunk: unknown): string | null { + if (typeof chunk === "string") return chunk; + if (Buffer.isBuffer(chunk)) return chunk.toString("utf-8"); + if (chunk instanceof Uint8Array) return Buffer.from(chunk).toString("utf-8"); + if (chunk instanceof ArrayBuffer) return Buffer.from(chunk).toString("utf-8"); + if (ArrayBuffer.isView(chunk)) { + return Buffer.from( + chunk.buffer, + chunk.byteOffset, + chunk.byteLength, + ).toString("utf-8"); + } + return null; +} + +export function installWindowsCIOutputSanitizer(): void { + if (process.platform !== "win32") return; + if (process.env.GITHUB_ACTIONS !== "true") return; + + const globalKey = "__clawdbotWindowsCIOutputSanitizerInstalled"; + if ((globalThis as Record)[globalKey] === true) return; + (globalThis as Record)[globalKey] = true; + + const originalStdoutWrite = process.stdout.write.bind(process.stdout); + const originalStderrWrite = process.stderr.write.bind(process.stderr); + + process.stdout.write = ((chunk: unknown, ...args: unknown[]) => { + const text = decodeUtf8Text(chunk); + if (text !== null) + return originalStdoutWrite(sanitizeWindowsCIOutput(text), ...args); + return originalStdoutWrite(chunk as never, ...args); // passthrough + }) as typeof process.stdout.write; + + process.stderr.write = ((chunk: unknown, ...args: unknown[]) => { + const text = decodeUtf8Text(chunk); + if (text !== null) + return originalStderrWrite(sanitizeWindowsCIOutput(text), ...args); + return originalStderrWrite(chunk as never, ...args); // passthrough + }) as typeof process.stderr.write; + + const originalWriteSync = fs.writeSync.bind(fs); + fs.writeSync = ((fd: number, data: unknown, ...args: unknown[]) => { + if (fd === 1 || fd === 2) { + const text = decodeUtf8Text(data); + if (text !== null) { + return originalWriteSync(fd, sanitizeWindowsCIOutput(text), ...args); + } + } + return originalWriteSync(fd, data as never, ...(args as never[])); + }) as typeof fs.writeSync; +} diff --git a/vitest.config.ts b/vitest.config.ts index 87f83935f..f2d9ca1a2 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -4,6 +4,7 @@ export default defineConfig({ test: { include: ["src/**/*.test.ts", "test/format-error.test.ts"], setupFiles: ["test/setup.ts"], + globalSetup: ["test/vitest-global-setup.ts"], exclude: [ "dist/**", "apps/macos/**",