Skip to content

chore: fix failing integration-tests on Windows #10999

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
2 changes: 0 additions & 2 deletions knip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ export default {
'glob',
'jest-specific-snapshot',
'make-dir',
'ncp',
'tmp',
// imported for type purposes only
'website',
],
Expand Down
3 changes: 0 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@
"@types/jest": "29.5.13",
"@types/jest-specific-snapshot": "^0.5.9",
"@types/natural-compare": "^1.4.3",
"@types/ncp": "^2.0.8",
"@types/node": "^20.12.5",
"@types/semver": "^7.5.8",
"@types/tmp": "^0.2.6",
Expand Down Expand Up @@ -117,13 +116,11 @@
"lint-staged": "^15.2.2",
"make-dir": "^4.0.0",
"markdownlint-cli": "^0.44.0",
"ncp": "^2.0.0",
"nx": "20.7.2",
"prettier": "3.5.0",
"pretty-format": "^29.7.0",
"rimraf": "^5.0.5",
"semver": "7.7.0",
"tmp": "^0.2.1",
"tsx": "*",
"typescript": ">=4.8.4 <5.9.0",
"typescript-eslint": "workspace:^",
Expand Down
16 changes: 6 additions & 10 deletions packages/integration-tests/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,16 @@

// pack the packages ahead of time and create a mapping for use in the tests
require('tsx/cjs');
const { tseslintPackages } = require('./tools/pack-packages');
const { setup } = require('./tools/pack-packages');

// @ts-check
/** @type {import('@jest/types').Config.InitialOptions} */
module.exports = {
/** @type {() => Promise<import('@jest/types').Config.InitialOptions>} */
module.exports = async () => ({
...require('../../jest.config.base.js'),
globals: {
tseslintPackages,
tseslintPackages: await setup(),
},
globalTeardown: './tools/pack-packages.ts',
testRegex: ['/tests/[^/]+.test.ts$'],
rootDir: __dirname,

// TODO(Brad Zacher) - for some reason if we run more than 1 test at a time
// yarn will error saying the tarballs are corrupt on just
// the first test.
maxWorkers: 1,
};
});
2 changes: 0 additions & 2 deletions packages/integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
"devDependencies": {
"@jest/types": "29.6.3",
"jest": "29.7.0",
"ncp": "*",
"tmp": "*",
"tsx": "*"
}
}
132 changes: 20 additions & 112 deletions packages/integration-tests/tools/integration-test-base.ts
Original file line number Diff line number Diff line change
@@ -1,126 +1,25 @@
import type { DirOptions } from 'tmp';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';

import ncp from 'ncp';
import childProcess from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import { promisify } from 'node:util';
import tmp from 'tmp';

interface PackageJSON {
devDependencies: Record<string, string>;
name: string;
private?: boolean;
}

const rootPackageJson: PackageJSON = require('../../../package.json');

tmp.setGracefulCleanup();

const copyDir = promisify(ncp.ncp);
const execFile = promisify(childProcess.execFile);
const readFile = promisify(fs.readFile);
const tmpDir = promisify(tmp.dir) as (opts?: DirOptions) => Promise<string>;
const tmpFile = promisify(tmp.file);
const writeFile = promisify(fs.writeFile);

const BASE_DEPENDENCIES: PackageJSON['devDependencies'] = {
...global.tseslintPackages,
eslint: rootPackageJson.devDependencies.eslint,
jest: rootPackageJson.devDependencies.jest,
typescript: rootPackageJson.devDependencies.typescript,
};

const FIXTURES_DIR = path.join(__dirname, '..', 'fixtures');
// an env var to persist the temp folder so that it can be inspected for debugging purposes
const KEEP_INTEGRATION_TEST_DIR =
process.env.KEEP_INTEGRATION_TEST_DIR === 'true';
import { execFile, FIXTURES_DESTINATION_DIR } from './pack-packages';

// make sure that jest doesn't timeout the test
jest.setTimeout(60000);
jest.setTimeout(60_000);

function integrationTest(
testName: string,
testFilename: string,
executeTest: (testFolder: string) => Promise<void>,
): void {
const fixture = path.parse(testFilename).name.replace('.test', '');
describe(fixture, () => {
const fixtureDir = path.join(FIXTURES_DIR, fixture);

const testFolder = path.join(FIXTURES_DESTINATION_DIR, fixture);

describe(fixture, () => {
describe(testName, () => {
it('should work successfully', async () => {
const testFolder = await tmpDir({
keep: KEEP_INTEGRATION_TEST_DIR,
});
if (KEEP_INTEGRATION_TEST_DIR) {
console.error(testFolder);
}

// copy the fixture files to the temp folder
await copyDir(fixtureDir, testFolder);

// build and write the package.json for the test
const fixturePackageJson: PackageJSON = await import(
path.join(fixtureDir, 'package.json')
);
await writeFile(
path.join(testFolder, 'package.json'),
JSON.stringify({
private: true,
...fixturePackageJson,
devDependencies: {
...BASE_DEPENDENCIES,
...fixturePackageJson.devDependencies,
},
// ensure everything uses the locally packed versions instead of the NPM versions
resolutions: {
...global.tseslintPackages,
},
}),
);
// console.log('package.json written.');

// Ensure yarn uses the node-modules linker and not PnP
await writeFile(
path.join(testFolder, '.yarnrc.yml'),
`nodeLinker: node-modules`,
);

await new Promise<void>((resolve, reject) => {
// we use the non-promise version so we can log everything on error
childProcess.execFile(
// we use yarn instead of npm as it will cache the remote packages and
// make installing things faster
'yarn',
// We call explicitly with --no-immutable to prevent errors related to missing lock files in CI
['install', '--no-immutable'],
{
cwd: testFolder,
},
(err, stdout, stderr) => {
if (err) {
if (stdout.length > 0) {
console.warn(stdout);
}
if (stderr.length > 0) {
console.error(stderr);
}
// childProcess.ExecFileException is an extension of Error
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
reject(err);
} else {
resolve();
}
},
);
});
// console.log('Install complete.');

await executeTest(testFolder);
});

afterAll(() => {});
});
});
}
Expand All @@ -131,7 +30,8 @@ export function eslintIntegrationTest(
): void {
integrationTest('eslint', testFilename, async testFolder => {
// lint, outputting to a JSON file
const outFile = await tmpFile();
const outFile = path.join(testFolder, 'eslint.json');

let stderr = '';
try {
await execFile(
Expand All @@ -147,6 +47,7 @@ export function eslintIntegrationTest(
],
{
cwd: testFolder,
shell: true,
},
);
} catch (ex) {
Expand All @@ -161,12 +62,18 @@ export function eslintIntegrationTest(
expect(stderr).toHaveLength(0);

// assert the linting state is consistent
const lintOutputRAW = (await readFile(outFile, 'utf8'))
const lintOutputRAW = (await fs.readFile(outFile, { encoding: 'utf-8' }))
// clean the output to remove any changing facets so tests are stable
.replaceAll(
new RegExp(`"filePath": ?"(/private)?${testFolder}`, 'g'),
'"filePath": "<root>',
);
)
.replaceAll(
/"filePath":"([^"]*)"/g,
(_, testFile: string) =>
`"filePath": "<root>/${path.relative(testFolder, testFile)}"`,
)
.replaceAll(/C:\\\\(usr)\\\\(linked)\\\\(tsconfig.json)/g, '/$1/$2/$3');
try {
const lintOutput = JSON.parse(lintOutputRAW);
expect(lintOutput).toMatchSnapshot();
Expand All @@ -186,8 +93,9 @@ export function typescriptIntegrationTest(
): void {
integrationTest(testName, testFilename, async testFolder => {
const [result] = await Promise.allSettled([
execFile('yarn', ['tsc', '--noEmit', ...tscArgs], {
execFile('yarn', ['tsc', '--noEmit', '--skipLibCheck', ...tscArgs], {
cwd: testFolder,
shell: true,
}),
]);

Expand Down
Loading
Loading