Skip to content

Commit 7058737

Browse files
committed
refactor: typescript __mocks__
1 parent 213b4d6 commit 7058737

24 files changed

+323
-341
lines changed

.prettierignore

-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1 @@
11
.yarn
2-
node_modules
3-
.nyc_output
4-
coverage
5-
dist
6-
tests/fixtures/

.prettierrc.mjs

+9-2
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,19 @@
33
/** @import { Config, Plugin } from 'prettier' */
44

55
import base from '@1stg/prettier-config/semi';
6-
import svelte from 'prettier-plugin-svelte';
6+
import * as jsdoc from 'prettier-plugin-jsdoc';
7+
import * as jsdocType from 'prettier-plugin-jsdoc-type';
8+
import * as svelte from 'prettier-plugin-svelte';
79

810
/** @type {Config} */
911
const config = {
1012
...base,
11-
plugins: [.../** @type {Plugin[]} */ (base.plugins), svelte],
13+
plugins: [
14+
.../** @type {Plugin[]} */ (base.plugins),
15+
jsdoc,
16+
jsdocType,
17+
svelte,
18+
],
1219
};
1320

1421
export default config;

EXAMPLES.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ gulp.task('js:format', function () {
2323

2424
```js
2525
var through = require('through2');
26+
var PluginError = require('plugin-error');
2627
var prettierEslint = require('prettier-eslint');
2728

2829
const options = {
@@ -49,13 +50,13 @@ module.exports = function () {
4950

5051
if (file.isStream()) {
5152
return callback(
52-
new utils.PluginError('prettier-eslint', "doesn't support Streams"),
53+
new PluginError('prettier-eslint', "doesn't support Streams"),
5354
);
5455
}
5556

5657
const sourceCode = file.contents.toString();
5758
const formatted = prettierEslint({
58-
...config,
59+
...options,
5960
text: sourceCode,
6061
});
6162

__mocks__/eslint.js renamed to __mocks__/eslint.ts

+35-19
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,57 @@
1-
// this mock file is so eslint doesn't attempt to actually
2-
// search around the file system for stuff
1+
/**
2+
* This mock file is so eslint doesn't attempt to actually search around the
3+
* file system for stuff, we can not use `.ts` because it's called by
4+
* `test/fixtures/node_modules/eslint/index.js`
5+
*/
6+
7+
import eslint_, { type ESLint as ESLint_ } from 'eslint';
8+
9+
import type { ESLintLintText, MockESLint } from '../mock.js';
310

4-
/** @import { ESLintConfig } from 'prettier-eslint' */
11+
import type { ESLintConfig } from 'prettier-eslint';
12+
13+
const eslint = jest.requireActual<typeof eslint_>('eslint');
514

6-
const eslint = /** @type {typeof import('eslint')} */ (
7-
jest.requireActual('eslint')
8-
);
915
const { ESLint } = eslint;
1016

1117
const mockCalculateConfigForFileSpy = jest.fn(mockCalculateConfigForFile);
12-
mockCalculateConfigForFileSpy.overrides = {};
18+
19+
Object.assign(mockCalculateConfigForFileSpy, { overrides: {} });
20+
1321
const mockLintTextSpy = jest.fn(mockLintText);
1422

15-
module.exports = Object.assign(eslint, {
23+
export = Object.assign(eslint, {
1624
ESLint: jest.fn(MockESLint),
1725
mock: {
1826
calculateConfigForFile: mockCalculateConfigForFileSpy,
1927
lintText: mockLintTextSpy,
2028
},
2129
});
2230

23-
function MockESLint(...args) {
31+
function MockESLint(options: ESLint_.Options): MockESLint {
2432
globalThis.__PRETTIER_ESLINT_TEST_STATE__.eslintPath = __filename;
25-
const eslintInstance = new ESLint(...args);
33+
const eslintInstance = new ESLint(options) as MockESLint;
2634
eslintInstance.calculateConfigForFile = mockCalculateConfigForFileSpy;
35+
// eslint-disable-next-line @typescript-eslint/unbound-method
2736
eslintInstance._originalLintText = eslintInstance.lintText;
2837
eslintInstance.lintText = mockLintTextSpy;
2938
return eslintInstance;
3039
}
3140

32-
MockESLint.prototype = Object.create(ESLint.prototype);
41+
MockESLint.prototype = Object.create(ESLint.prototype) as ESLint_;
3342

3443
/**
35-
* @param {string} filePath
36-
* @returns {ESLintConfig} -- eslint config
37-
* @throws {Error} -- if `throwError` is specifically set on the spy, or if the
38-
* filePath is not handled
44+
* @throws If `throwError` is specifically set on the spy, or if the filePath is
45+
* not handled
3946
*/
40-
function mockCalculateConfigForFile(filePath) {
41-
if (mockCalculateConfigForFileSpy.throwError) {
47+
// eslint-disable-next-line @typescript-eslint/require-await
48+
async function mockCalculateConfigForFile(
49+
filePath: string,
50+
): Promise<ESLintConfig> {
51+
if (
52+
'throwError' in mockCalculateConfigForFileSpy &&
53+
mockCalculateConfigForFileSpy.throwError instanceof Error
54+
) {
4255
throw mockCalculateConfigForFileSpy.throwError;
4356
}
4457
if (!filePath) {
@@ -80,8 +93,11 @@ function mockCalculateConfigForFile(filePath) {
8093
);
8194
}
8295

83-
function mockLintText(...args) {
84-
if (mockLintTextSpy.throwError) {
96+
function mockLintText(this: MockESLint, ...args: Parameters<ESLintLintText>) {
97+
if (
98+
'throwError' in mockLintTextSpy &&
99+
mockLintTextSpy.throwError instanceof Error
100+
) {
85101
throw mockLintTextSpy.throwError;
86102
}
87103
return this._originalLintText(...args);

__mocks__/fs.js

-15
This file was deleted.

__mocks__/fs.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type Fs_ from 'node:fs';
2+
3+
type Fs = typeof Fs_;
4+
5+
const fs = jest.requireActual<Fs>('fs');
6+
7+
// eslint-disable-next-line prefer-object-spread -- typing issue
8+
export = Object.assign({}, fs, {
9+
readFileSync: jest.fn((filename: string) => {
10+
if (filename.endsWith('package.json')) {
11+
return '{"name": "fake", "version": "0.0.0", "prettier": {}}';
12+
}
13+
if (/\.[jt]s$/.test(filename)) {
14+
return 'var fake = true';
15+
}
16+
17+
return '';
18+
}),
19+
});

__mocks__/loglevel-colored-level-prefix.js

-36
This file was deleted.
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type {
2+
GetLogger,
3+
Logger,
4+
LogLevel,
5+
} from 'loglevel-colored-level-prefix';
6+
7+
const logger: Logger = {
8+
setLevel: jest.fn(),
9+
debug: jest.fn(getTestImplementation('debug')),
10+
error: jest.fn(getTestImplementation('error')),
11+
info: jest.fn(getTestImplementation('info')),
12+
silent: jest.fn(getTestImplementation('silent')),
13+
trace: jest.fn(getTestImplementation('trace')),
14+
warn: jest.fn(getTestImplementation('warn')),
15+
};
16+
17+
const mock: (typeof getLogger)['mock'] = { clearAll, logger, logThings: [] };
18+
19+
const getLogger = (() => logger) as unknown as GetLogger;
20+
21+
Object.assign(getLogger, { mock });
22+
23+
export = getLogger;
24+
25+
function clearAll() {
26+
for (const name of Object.keys(logger) as LogLevel[]) {
27+
logger[name].mockClear();
28+
}
29+
}
30+
31+
function getTestImplementation(level: LogLevel) {
32+
return testLogImplementation;
33+
34+
function testLogImplementation(message: string, ...args: unknown[]) {
35+
if (mock.logThings === 'all' || mock.logThings.includes(level)) {
36+
console.log(level, message, ...args); // eslint-disable-line no-console
37+
}
38+
}
39+
}

__mocks__/prettier.js

-22
This file was deleted.

__mocks__/prettier.ts

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* We can not use `.ts` because it's called by
3+
* `test/fixtures/node_modules/prettier/index.js`
4+
*/
5+
6+
import type { Options } from 'prettier';
7+
8+
import type { PrettierMock } from '../mock.js';
9+
10+
const prettier = jest.requireActual<PrettierMock>('prettier');
11+
12+
const { format } = prettier;
13+
14+
export = prettier;
15+
16+
const mockFormatSpy = jest.fn(mockFormat);
17+
18+
Object.assign(prettier, {
19+
format: mockFormatSpy,
20+
resolveConfig: jest.fn(),
21+
});
22+
23+
function mockFormat(source: string, options?: Options): Promise<string> {
24+
globalThis.__PRETTIER_ESLINT_TEST_STATE__.prettierPath = __filename;
25+
26+
if (
27+
'throwError' in mockFormatSpy &&
28+
mockFormatSpy.throwError instanceof Error
29+
) {
30+
throw mockFormatSpy.throwError;
31+
}
32+
33+
return format(source, options);
34+
}

eslint.config.mjs

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export default tseslint.config(
99
...base,
1010
...nodeDependencies.configs['flat/recommended'],
1111
{
12-
ignores: ['test/fixtures'],
12+
ignores: ['test/fixtures', '!test/fixtures/paths/node_modules/**/*.js'],
1313
},
1414
{
1515
rules: {
@@ -30,7 +30,7 @@ export default tseslint.config(
3030
},
3131
},
3232
{
33-
files: ['**/__mocks__/**/*.js', '**/*.spec.ts'],
33+
files: ['__mocks__/**/*.{js,ts}', 'test/**/*.spec.ts'],
3434
languageOptions: {
3535
globals: globals.jest,
3636
},

global.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/// <reference types="@total-typescript/ts-reset" />
2+
13
declare global {
24
declare var __PRETTIER_ESLINT_TEST_STATE__: {
35
eslintPath?: string;

jest.config.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// @ts-check
2+
3+
/** @import { Config } from 'jest' */
4+
5+
module.exports = /** @satisfies {Config} */ ({
6+
collectCoverage: true,
7+
collectCoverageFrom: ['src/**/*.ts'],
8+
coverageThreshold: {
9+
global: {
10+
branches: 96,
11+
functions: 100,
12+
lines: 100,
13+
statements: 100,
14+
},
15+
},
16+
testEnvironment: 'node',
17+
transform: {
18+
'^.+\\.ts$': '@swc-node/jest',
19+
},
20+
});

jest.config.ts

-19
This file was deleted.

0 commit comments

Comments
 (0)