Skip to content

Commit

Permalink
refactor: move detectJestVersion into its own file
Browse files Browse the repository at this point in the history
  • Loading branch information
G-Rath committed Sep 17, 2021
1 parent 580ad23 commit 1158b43
Show file tree
Hide file tree
Showing 5 changed files with 297 additions and 170 deletions.
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const importDefault = (moduleName: string) =>
interopRequireDefault(require(moduleName)).default;

const rulesDir = join(__dirname, 'rules');
const excludedFiles = ['__tests__', 'utils'];
const excludedFiles = ['__tests__', 'detectJestVersion', 'utils'];

const rules = readdirSync(rulesDir)
.map(rule => parse(rule).name)
Expand Down
229 changes: 229 additions & 0 deletions src/rules/__tests__/detectJestVersion.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import { spawnSync } from 'child_process';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { JSONSchemaForNPMPackageJsonFiles } from '@schemastore/package';
import { create } from 'ts-node';
import { detectJestVersion } from '../detectJestVersion';

const compileFnCode = (pathToFn: string) => {
const fnContents = fs.readFileSync(pathToFn, 'utf-8');

return create({
transpileOnly: true,
compilerOptions: { sourceMap: false },
}).compile(fnContents, pathToFn);
};
const compiledFn = compileFnCode(require.resolve('../detectJestVersion.ts'));
const relativePathToFn = 'eslint-plugin-jest/lib/rules/detectJestVersion.js';

const runNodeScript = (cwd: string, script: string) => {
return spawnSync('node', ['-e', script], { cwd, encoding: 'utf-8' });
};

const runDetectJestVersion = (cwd: string) => {
return runNodeScript(
cwd,
`
try {
console.log(require('${relativePathToFn}').detectJestVersion());
} catch (error) {
console.error(error.message);
}
`,
);
};

/**
* Makes a new temp directory, prefixed with `eslint-plugin-jest-`
*
* @return {Promise<string>}
*/
const makeTempDir = () =>
fs.mkdtempSync(path.join(os.tmpdir(), 'eslint-plugin-jest-'));

interface ProjectStructure {
[key: `${string}/package.json`]: JSONSchemaForNPMPackageJsonFiles;
[key: `${string}/${typeof relativePathToFn}`]: string;
[key: `${string}/`]: null;
'package.json'?: JSONSchemaForNPMPackageJsonFiles;
}

const setupFakeProject = (structure: ProjectStructure): string => {
const tempDir = makeTempDir();

for (const [filePath, contents] of Object.entries(structure)) {
if (contents === null) {
fs.mkdirSync(path.join(tempDir, filePath), { recursive: true });

continue;
}

const folderPath = path.dirname(filePath);

// make the directory (recursively)
fs.mkdirSync(path.join(tempDir, folderPath), { recursive: true });

const finalContents =
typeof contents === 'string' ? contents : JSON.stringify(contents);

fs.writeFileSync(path.join(tempDir, filePath), finalContents);
}

return tempDir;
};

// pin the original cwd so that we can restore it after each test
// const projectDir = process.cwd();

// afterEach(() => process.chdir(projectDir));

describe('detectJestVersion', () => {
describe('basic tests', () => {
const packageJsonFactory = jest.fn<JSONSchemaForNPMPackageJsonFiles, []>();

beforeEach(() => {
jest.resetModules();
jest.doMock(require.resolve('jest/package.json'), packageJsonFactory);
});

describe('when the package.json is missing the version property', () => {
it('throws an error', () => {
packageJsonFactory.mockReturnValue({});

expect(() => detectJestVersion()).toThrow(
/Unable to detect Jest version/iu,
);
});
});

it('caches versions', () => {
packageJsonFactory.mockReturnValue({ version: '1.2.3' });

const version = detectJestVersion();

jest.resetModules();

expect(detectJestVersion).not.toThrow();
expect(detectJestVersion()).toBe(version);
});
});

describe('when in a simple project', () => {
it('finds the correct version', () => {
const projectDir = setupFakeProject({
'package.json': { name: 'simple-project' },
[`node_modules/${relativePathToFn}` as const]: compiledFn,
'node_modules/jest/package.json': {
name: 'jest',
version: '21.0.0',
},
});

const { stdout, stderr } = runDetectJestVersion(projectDir);

expect(stdout.trim()).toBe('21');
expect(stderr.trim()).toBe('');
});
});

describe('when in a hoisted mono-repo', () => {
it('finds the correct version', () => {
const projectDir = setupFakeProject({
'package.json': { name: 'mono-repo' },
[`node_modules/${relativePathToFn}` as const]: compiledFn,
'node_modules/jest/package.json': {
name: 'jest',
version: '19.0.0',
},
'packages/a/package.json': { name: 'package-a' },
'packages/b/package.json': { name: 'package-b' },
});

const { stdout, stderr } = runDetectJestVersion(projectDir);

expect(stdout.trim()).toBe('19');
expect(stderr.trim()).toBe('');
});
});

describe('when in a subproject', () => {
it('finds the correct versions', () => {
const projectDir = setupFakeProject({
'backend/package.json': { name: 'package-a' },
[`backend/node_modules/${relativePathToFn}` as const]: compiledFn,
'backend/node_modules/jest/package.json': {
name: 'jest',
version: '24.0.0',
},
'frontend/package.json': { name: 'package-b' },
[`frontend/node_modules/${relativePathToFn}` as const]: compiledFn,
'frontend/node_modules/jest/package.json': {
name: 'jest',
version: '15.0.0',
},
});

const { stdout: stdoutBackend, stderr: stderrBackend } =
runDetectJestVersion(path.join(projectDir, 'backend'));

expect(stdoutBackend.trim()).toBe('24');
expect(stderrBackend.trim()).toBe('');

const { stdout: stdoutFrontend, stderr: stderrFrontend } =
runDetectJestVersion(path.join(projectDir, 'frontend'));

expect(stdoutFrontend.trim()).toBe('15');
expect(stderrFrontend.trim()).toBe('');
});
});

describe('when jest is not installed', () => {
it('throws an error', () => {
const projectDir = setupFakeProject({
'package.json': { name: 'no-jest' },
[`node_modules/${relativePathToFn}` as const]: compiledFn,
'node_modules/pack/package.json': { name: 'pack' },
});

const { stdout, stderr } = runDetectJestVersion(projectDir);

expect(stdout.trim()).toBe('');
expect(stderr.trim()).toContain('Unable to detect Jest version');
});
});

describe('when jest is changed on disk', () => {
it('uses the cached version', () => {
const projectDir = setupFakeProject({
'package.json': { name: 'no-jest' },
[`node_modules/${relativePathToFn}` as const]: compiledFn,
'node_modules/jest/package.json': { name: 'jest', version: '26.0.0' },
});

const { stdout, stderr } = runNodeScript(
projectDir,
`
const { detectJestVersion } = require('${relativePathToFn}');
const fs = require('fs');
console.log(detectJestVersion());
fs.writeFileSync(
'node_modules/jest/package.json',
JSON.stringify({
name: 'jest',
version: '25.0.0',
}),
);
console.log(detectJestVersion());
`,
);

const [firstCall, secondCall] = stdout.split('\n');

expect(firstCall).toBe('26');
expect(secondCall).toBe('26');
expect(stderr.trim()).toBe('');
});
});
});

0 comments on commit 1158b43

Please sign in to comment.