|
1 | 1 | import * as matchers from 'jest-extended' |
2 | 2 | expect.extend(matchers) |
3 | 3 |
|
| 4 | +// Patch jscodeshift testUtils to normalize line endings (fixes Windows CRLF issues) |
| 5 | +// The issue: jscodeshift's printer (recast) outputs CRLF on Windows, but test fixtures use LF |
| 6 | +// We need to patch both defineTest (which uses internal closure references) and runInlineTest |
| 7 | +if (process.platform === 'win32') { |
| 8 | + try { |
| 9 | + const testUtils = require('jscodeshift/dist/testUtils') |
| 10 | + const fs = require('fs') |
| 11 | + const path = require('path') |
| 12 | + |
| 13 | + // Helper to normalize line endings |
| 14 | + // - Convert CRLF to LF |
| 15 | + // - Remove trailing whitespace from each line (not meaningful for code) |
| 16 | + // - Ensure exactly one trailing newline (POSIX convention for text files) |
| 17 | + // Using \n*$ to handle case where transform output has no trailing newline |
| 18 | + const normalizeLF = (str: string) => |
| 19 | + str |
| 20 | + .replace(/\r\n/g, '\n') |
| 21 | + .replace(/[ \t]+$/gm, '') |
| 22 | + .replace(/\n*$/, '\n') |
| 23 | + |
| 24 | + // Patch runInlineTest to normalize both transform output and expected |
| 25 | + testUtils.runInlineTest = function ( |
| 26 | + module: any, |
| 27 | + options: any, |
| 28 | + input: any, |
| 29 | + expectedOutput: string, |
| 30 | + testOptions?: any |
| 31 | + ) { |
| 32 | + // Normalize input source |
| 33 | + const normalizedInput = |
| 34 | + typeof input === 'object' && input.source |
| 35 | + ? { ...input, source: normalizeLF(input.source) } |
| 36 | + : input |
| 37 | + // Normalize expected output |
| 38 | + const normalizedExpected = normalizeLF(expectedOutput) |
| 39 | + |
| 40 | + // Run the transform and normalize its output for comparison |
| 41 | + const output = testUtils.applyTransform( |
| 42 | + module, |
| 43 | + options, |
| 44 | + normalizedInput, |
| 45 | + testOptions |
| 46 | + ) |
| 47 | + const normalizedOutput = |
| 48 | + typeof output === 'string' ? normalizeLF(output) : output |
| 49 | + |
| 50 | + // Do the comparison ourselves instead of letting the original do it |
| 51 | + // eslint-disable-next-line jest/no-standalone-expect -- called from within test blocks |
| 52 | + expect(normalizedOutput).toEqual(normalizedExpected) |
| 53 | + return normalizedOutput |
| 54 | + } |
| 55 | + |
| 56 | + // Replace defineTest entirely since it uses internal closure references |
| 57 | + // that bypass our exports patch |
| 58 | + testUtils.defineTest = function ( |
| 59 | + dirName: string, |
| 60 | + transformName: string, |
| 61 | + options: any, |
| 62 | + testFilePrefix?: string, |
| 63 | + testOptions?: { parser?: string } |
| 64 | + ) { |
| 65 | + const testName = testFilePrefix |
| 66 | + ? `transforms correctly using "${testFilePrefix}" data` |
| 67 | + : 'transforms correctly' |
| 68 | + |
| 69 | + describe(transformName, () => { |
| 70 | + it(testName, () => { |
| 71 | + const fixtureDir = path.join(dirName, '..', '__testfixtures__') |
| 72 | + const prefix = testFilePrefix || transformName |
| 73 | + const module = require(path.join(dirName, '..', transformName)) |
| 74 | + |
| 75 | + // Determine file extension based on parser option |
| 76 | + const parser = testOptions?.parser || module.parser |
| 77 | + const extension = |
| 78 | + parser === 'ts' ? 'ts' : parser === 'tsx' ? 'tsx' : 'js' |
| 79 | + |
| 80 | + const inputPath = path.join( |
| 81 | + fixtureDir, |
| 82 | + `${prefix}.input.${extension}` |
| 83 | + ) |
| 84 | + const outputPath = path.join( |
| 85 | + fixtureDir, |
| 86 | + `${prefix}.output.${extension}` |
| 87 | + ) |
| 88 | + |
| 89 | + const source = normalizeLF(fs.readFileSync(inputPath, 'utf8')) |
| 90 | + const expectedOutput = normalizeLF( |
| 91 | + fs.readFileSync(outputPath, 'utf8') |
| 92 | + ) |
| 93 | + |
| 94 | + testUtils.runInlineTest( |
| 95 | + module, |
| 96 | + options, |
| 97 | + { path: inputPath, source }, |
| 98 | + expectedOutput, |
| 99 | + testOptions |
| 100 | + ) |
| 101 | + }) |
| 102 | + }) |
| 103 | + } |
| 104 | + } catch { |
| 105 | + // jscodeshift not available, skip patching |
| 106 | + } |
| 107 | +} |
| 108 | + |
4 | 109 | // A default max-timeout of 90 seconds is allowed |
5 | 110 | // per test we should aim to bring this down though |
6 | 111 | jest.setTimeout((process.platform === 'win32' ? 180 : 60) * 1000) |
|
0 commit comments