Skip to content

Commit

Permalink
upgrade jest-snapshot to v30 and cleanup types (#117)
Browse files Browse the repository at this point in the history
  • Loading branch information
connectdotz authored Jun 5, 2024
1 parent a03bf1c commit 9126abd
Show file tree
Hide file tree
Showing 9 changed files with 622 additions and 459 deletions.
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jest-editor-support",
"version": "32.0.0-beta.0",
"version": "32.0.0-beta.1",
"repository": {
"type": "git",
"url": "https://github.com/jest-community/jest-editor-support"
Expand All @@ -10,7 +10,7 @@
"scripts": {
"clean": "rimraf ./build ./index.d.ts",
"build": "tsc --noEmit",
"build:types": "dts-bundle-generator -o ./index.d.ts src/index.ts",
"build:types": "dts-bundle-generator -o ./index.d.ts src/index.ts --project ./tsconfig.types.json",
"build:prod": "yarn clean & tsc -p tsconfig.prod.json",
"prepublish": "yarn build:prod && yarn build:types",
"test": "jest",
Expand All @@ -24,10 +24,10 @@
"devDependencies": {
"@babel/core": "^7.20.12",
"@types/jest": "^29.2.4",
"@types/node": "^18.11.16",
"@types/node": "^20.12.7",
"@typescript-eslint/eslint-plugin": "^5.46.1",
"@typescript-eslint/parser": "^5.46.1",
"dts-bundle-generator": "^9.3.1",
"dts-bundle-generator": "^9.5.1",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jest": "^27.9.0",
Expand All @@ -42,9 +42,10 @@
"@babel/parser": "^7.20.7",
"@babel/traverse": "7.23.2",
"@babel/types": "^7.20.7",
"@jest/snapshot-utils": "^30.0.0-alpha.5",
"@jest/test-result": "^29.3.1",
"@jest/types": "^29.3.1",
"jest-snapshot": "^27.2.0"
"jest-snapshot": "^30.0.0-alpha.5"
},
"jest": {
"preset": "ts-jest",
Expand Down
44 changes: 30 additions & 14 deletions src/Runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,47 @@ import type {Options, MessageType} from './types';
import ProjectWorkspace from './project_workspace';
import {createProcess} from './Process';

export type RunnerEvent =
| 'processClose'
| 'processExit'
| 'executableJSON'
| 'executableStdErr'
| 'executableOutput'
| 'terminalError';
export type RunnerEventMap = {
processClose: [code: number | null, signal: string | null];
processExit: [code: number | null, signal: string | null];
executableJSON: [data: object, {noTestsFound: boolean}];
executableStdErr: [data: Buffer, {type: MessageType}];
executableOutput: [data: string];
terminalError: [error: string];
debuggerProcessExit: [];
};

export type AllRunnerEvent = keyof RunnerEventMap;
export type RunnerEvent = Exclude<AllRunnerEvent, 'debuggerProcessExit'>;

// This class represents the running process, and
// passes out events when it understands what data is being
// pass sent out of the process
export default class Runner extends EventEmitter {
export default class Runner extends EventEmitter<RunnerEventMap> {
/** @internal */
runProcess?: ChildProcess;

/** @internal */
outputPath: string;

/** @internal */
workspace: ProjectWorkspace;

_createProcess: (workspace: ProjectWorkspace, args: string[]) => ChildProcess;
/** @internal */
private _createProcess: (workspace: ProjectWorkspace, args: string[]) => ChildProcess;

watchMode = false;

watchAll = false;

/** @internal */
options: Options;

/** @internal */
prevMessageTypes: MessageType[];

_exited: boolean;
/** @internal */
private _exited: boolean;

constructor(workspace: ProjectWorkspace, options?: Options) {
super();
Expand All @@ -56,7 +68,8 @@ export default class Runner extends EventEmitter {
this._exited = false;
}

__convertDashedArgs(args: string[]): string[] {
/** @internal */
private __convertDashedArgs(args: string[]): string[] {
if (!this.workspace.useDashedArgs) {
return args;
}
Expand All @@ -66,7 +79,8 @@ export default class Runner extends EventEmitter {
);
}

_getArgs(): string[] {
/** @internal */
private _getArgs(): string[] {
if (this.options.args && this.options.args.replace) {
return this.options.args.skipConversion
? this.options.args.args
Expand Down Expand Up @@ -153,6 +167,7 @@ export default class Runner extends EventEmitter {
}

/**
* @internal
* parse the stdin/out stream buffer for recognized messages.
*
* note: if these messages coming in in separate chucks, we might not be able to
Expand All @@ -165,7 +180,7 @@ export default class Runner extends EventEmitter {
* @returns {MessageType}
* @memberof Runner
*/
_parseOutput(data: Buffer, isStdErr: boolean): MessageType {
private _parseOutput(data: Buffer, isStdErr: boolean): MessageType {
const msgType = this.findMessageType(data);
switch (msgType) {
case MessageTypes.testResults:
Expand Down Expand Up @@ -248,7 +263,7 @@ export default class Runner extends EventEmitter {
this.runProcess = undefined;
}

// eslint-disable-next-line class-methods-use-this
/** @internal */
findMessageType(buf: Buffer): MessageType {
const noTestRegex = /No tests found related to files changed since ((last commit)|("[a-z0-9]+"))./;
const watchUsageRegex = /^\s*Watch Usage\b/;
Expand All @@ -265,6 +280,7 @@ export default class Runner extends EventEmitter {
return match ? match.messageType : MessageTypes.unknown;
}

/** @internal */
doResultsFollowNoTestsFoundMessage(): boolean {
if (this.prevMessageTypes.length === 1) {
return this.prevMessageTypes[0] === MessageTypes.noTests;
Expand Down
32 changes: 16 additions & 16 deletions src/Snapshot.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
Expand All @@ -13,17 +9,15 @@

import traverse, {NodePath} from '@babel/traverse';
import * as t from '@babel/types';
import type {SnapshotResolver} from 'jest-snapshot';
import {buildSnapshotResolver, utils} from 'jest-snapshot';
import {SnapshotResolver, buildSnapshotResolver} from 'jest-snapshot';
import * as utils from '@jest/snapshot-utils';
import type {Config} from '@jest/types';

import {getASTfor} from './parsers/babel_parser';
import type {JESParserOptions} from './parsers';
import {shallowAttr} from './parsers/helper';
import {SnapshotData} from 'jest-snapshot/build/types';

type ParserFunc = typeof getASTfor;

export type SnapshotNode = t.Identifier;
export interface SnapshotBlock {
node: SnapshotNode;
Expand Down Expand Up @@ -106,15 +100,20 @@ export interface SnapshotParserOptions {
parserOptions?: JESParserOptions;
}
export default class Snapshot {
_parser: ParserFunc;
/** @internal */
private _parser: ParserFunc;

_matchers: string[];
/** @internal */
private _matchers: string[];

_projectConfig?: Config.ProjectConfig;
/** @internal */
private _projectConfig?: Config.ProjectConfig;

snapshotResolver?: SnapshotResolver;
/** @internal */
private snapshotResolver?: SnapshotResolver;

_resolverPromise: Promise<SnapshotResolver>;
/** @internal */
private _resolverPromise: Promise<SnapshotResolver>;

constructor(parser?: ParserFunc, customMatchers?: string[], projectConfig?: Config.ProjectConfig) {
this._parser = parser || getASTfor;
Expand Down Expand Up @@ -160,7 +159,8 @@ export default class Snapshot {
}));
}

async _getSnapshotResolver(): Promise<SnapshotResolver> {
/** @internal */
private async _getSnapshotResolver(): Promise<SnapshotResolver> {
if (!this.snapshotResolver) {
this.snapshotResolver = await this._resolverPromise;
}
Expand All @@ -175,7 +175,7 @@ export default class Snapshot {
* a SnapshotData object will be returned with all matched snapshots. If nothing matched, null will be returned.
* @throws throws exception if the snapshot version mismatched or any other unexpected error.
*/
async getSnapshotContent(filePath: string, name: string | RegExp): Promise<string | SnapshotData | null> {
async getSnapshotContent(filePath: string, name: string | RegExp): Promise<string | utils.SnapshotData | null> {
const snapshotResolver = await this._getSnapshotResolver();

const snapshotPath = snapshotResolver.resolveSnapshotPath(filePath);
Expand All @@ -184,7 +184,7 @@ export default class Snapshot {
return snapshots[name];
}
const regex = name;
const data: SnapshotData = {};
const data: utils.SnapshotData = {};
Object.entries(snapshots).forEach(([key, value]) => {
if (regex.test(key)) {
data[key] = value;
Expand Down
24 changes: 15 additions & 9 deletions src/__tests__/snapshot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,26 @@

import path from 'path';
import Snapshot from '../Snapshot';
import jestSnapshot from 'jest-snapshot';
import * as jestSnapshot from 'jest-snapshot';

jest.mock('jest-snapshot', () => {
const original = jest.requireActual('jest-snapshot');
return {
...original,
buildSnapshotResolver: jest.fn(),
};
});

const snapshotFixturePath = path.resolve(__dirname, 'fixtures/snapshots');

describe('Snapshot', () => {
let buildSnapshotResolverSpy: jest.SpyInstance;
let snapshotHelper: Snapshot;
beforeEach(() => {
jest.resetAllMocks();
jest.clearAllMocks();
(jestSnapshot.buildSnapshotResolver as jest.Mocked<any>).mockImplementation(
jest.requireActual('jest-snapshot').buildSnapshotResolver
);
snapshotHelper = new Snapshot();
buildSnapshotResolverSpy = jest.spyOn(jestSnapshot, 'buildSnapshotResolver');
});
afterEach(() => {
jest.restoreAllMocks();
});

describe('getMetadata', () => {
Expand Down Expand Up @@ -134,7 +140,7 @@ describe('Snapshot', () => {
});
it('when the resolver is not yet ready', () => {
// simulate when buildSnapshotResolver did not resolve yet
buildSnapshotResolverSpy.mockImplementation((() => {
(jestSnapshot.buildSnapshotResolver as jest.Mocked<any>).mockImplementation((() => {
// eslint-disable-next-line @typescript-eslint/no-empty-function
return new Promise(() => {});
}) as any);
Expand Down Expand Up @@ -239,7 +245,7 @@ describe('Snapshot', () => {
const snapshot = new Snapshot(undefined, undefined, customConfig);
const filePath = path.join(snapshotFixturePath, 'inline-and-each.example');
await snapshot.getSnapshotContent(filePath, /not existing test/);
expect(buildSnapshotResolverSpy).toHaveBeenCalledWith(customConfig, expect.any(Function));
expect(jestSnapshot.buildSnapshotResolver).toHaveBeenCalledWith(customConfig, expect.any(Function));
});
});
});
15 changes: 10 additions & 5 deletions src/test_reconciler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ export type TestFileAssertionStatus = Omit<JestFileResults, 'status' | 'assertio
* at a file level, generating useful error messages and providing a nice API.
*/
export default class TestReconciler {
fileStatuses: {[key: string]: TestFileAssertionStatus};
/** @internal */
private fileStatuses: {[key: string]: TestFileAssertionStatus};

constructor() {
this.fileStatuses = {};
Expand Down Expand Up @@ -93,7 +94,8 @@ export default class TestReconciler {
// we don't get this as structured data, but what we get
// is useful enough to make it for ourselves

mapAssertions(filename: string, assertions: JestAssertionResults[]): TestAssertionStatus[] {
/** @internal */
private mapAssertions(filename: string, assertions: JestAssertionResults[]): TestAssertionStatus[] {
// convert jest location (column is 0-based and line is 1-based) to all 0-based location used internally in this package
/* eslint-disable no-param-reassign */
const convertJestLocation = (jestLocation?: CodeLocation) => {
Expand Down Expand Up @@ -135,7 +137,8 @@ export default class TestReconciler {
}

// Do everything we can to try make a one-liner from the error report
sanitizeShortErrorMessage(string: string): string {
/** @internal */
private sanitizeShortErrorMessage(string: string): string {
if (string.includes('does not match stored snapshot')) {
return 'Snapshot has changed';
}
Expand All @@ -154,13 +157,15 @@ export default class TestReconciler {
}

// Pull the line out from the stack trace
lineOfError(message: string, filePath: string): number | undefined {
/** @internal */
private lineOfError(message: string, filePath: string): number | undefined {
const filename = path.basename(filePath);
const restOfTrace = message.split(filename, 2)[1];
return restOfTrace ? parseInt(restOfTrace.split(':')[1], 10) : undefined;
}

statusToReconciliationState(status: string): TestReconciliationState {
/** @internal */
private statusToReconciliationState(status: string): TestReconciliationState {
switch (status) {
case 'passed':
return 'KnownSuccess';
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
},
"include": ["./src/**/*.ts", "src/__tests__/parsers/helper.test.ts"]
"include": ["./src/**/*.ts"],
}
3 changes: 2 additions & 1 deletion tsconfig.prod.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"sourceMap": false, // Disable source maps for production
"declaration": false, // Disable declaration files generation
"outDir": "./build", // Output directory for compiled JS files
"removeComments": true // Optional: Remove comments in the production build
"removeComments": true, // Optional: Remove comments in the production build
"stripInternal": true,
},
"exclude": [
"**/__tests__/**", // Exclude all test files
Expand Down
6 changes: 6 additions & 0 deletions tsconfig.types.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "./tsconfig.prod.json",
"compilerOptions": {
"removeComments": false, // Optional: Remove comments in the production build
},
}
Loading

0 comments on commit 9126abd

Please sign in to comment.