Last active
August 20, 2024 13:05
-
-
Save prmichaelsen/c31888161ebd916736e026bf1b3f1df7 to your computer and use it in GitHub Desktop.
This script generates test output files for each jest test so you can view the diffs in an external diff viewer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ts-node | |
import { spawn } from 'node:child_process'; | |
import * as fs from "node:fs"; | |
// This script generates test output | |
// files for each jest test so | |
// you can view the diffs in an external | |
// diff viewer | |
function sortByKey(obj: any): any { | |
if (Array.isArray(obj)) { | |
return obj.map(sortByKey); | |
} else if (typeof obj !== 'object') { | |
return obj; | |
} | |
const keys = Object.keys(obj).sort(); | |
const result: any = {}; | |
for (const key of keys) { | |
result[key] = sortByKey(obj[key]) | |
} | |
return result; | |
} | |
interface MatcherResult { | |
expected: any; | |
actual: any; | |
} | |
interface FailureDetails { | |
matcherResult: MatcherResult; | |
} | |
interface AssertionResult { | |
fullName: string; | |
failureDetails?: Array<FailureDetails>; | |
} | |
interface TestResult { | |
assertionResults: AssertionResult[]; | |
} | |
interface JestOutput { | |
testResults: Array<TestResult>; | |
} | |
const main = async () => { | |
const cmd = `jest`; | |
const reporterArgs = [...process.argv.slice(2)]; | |
const args = [`--json`, ...process.argv.slice(2)]; | |
console.log([cmd, ...args].join(' ')); | |
// If I try and pipe stdout, jest no longer reports progress as its running. | |
// As a hack, I run two jest processes in parallel: a reporter that updates the terminal | |
// as it runs and a collector that captures the test results. | |
// Maybe I could make this work better if I understood streams better. | |
const reporter = spawn(cmd, reporterArgs, { env: process.env, stdio: ['inherit', 'inherit', 'inherit'] }); | |
const collector = spawn(cmd, args, { env: process.env, stdio: ['inherit', 'pipe', 'ignore'] }); | |
let out = ''; | |
await Promise.all([ | |
new Promise((resolve, reject) => { | |
collector.stdout?.on('data', (data) => { | |
out += data; | |
}); | |
collector.on('close', (code) => { | |
if (code === 0) { | |
resolve(code); | |
} else { | |
resolve(code); | |
} | |
}); | |
}), | |
new Promise((resolve, reject) => { | |
reporter.on('close', (code) => { | |
if (code === 0) { | |
resolve(code); | |
} else { | |
resolve(code); | |
} | |
}); | |
}) | |
]); | |
const jestOutput = JSON.parse(out) as JestOutput; | |
interface SummaryResult { | |
fullName: string; | |
assertions: Array<{ | |
expected: any; | |
actual: any; | |
}>; | |
} | |
const summaryResults: SummaryResult[] = []; | |
let numFailures = 0; | |
for (const testResult of jestOutput.testResults) { | |
for (const assertionResult of testResult.assertionResults) { | |
numFailures += assertionResult.failureDetails?.length || 0; | |
if (assertionResult.failureDetails) { | |
const summaryResult = { | |
fullName: assertionResult.fullName, | |
assertions: assertionResult.failureDetails.map((failureDetail) => ({ | |
expected: failureDetail.matcherResult.expected, | |
actual: failureDetail.matcherResult.actual, | |
})), | |
}; | |
summaryResults.push(summaryResult); | |
} | |
} | |
} | |
for (const summaryResult of summaryResults) { | |
const { fullName, assertions } = summaryResult; | |
for (const idx in assertions) { | |
const assertion = assertions[idx]; | |
const { actual, expected } = assertion; | |
const name = fullName.split(' ').join('_') + `-${idx}`; | |
const actualFilename = `${name}.actual.json`; | |
const expectedFilename = `${name}.expected.json`; | |
fs.writeFileSync(actualFilename, JSON.stringify(sortByKey(actual), null, 2)); | |
fs.writeFileSync(expectedFilename, JSON.stringify(sortByKey(expected), null, 2)); | |
} | |
} | |
if (numFailures > 0) { | |
console.log(`\nFound ${numFailures} failures and wrote outputs to files.`); | |
} else { | |
console.log(`\nFound ${numFailures} failures and did not write any output to files.`); | |
} | |
}; | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment