Skip to content

Commit

Permalink
jest snapshot - TS migration (#7899)
Browse files Browse the repository at this point in the history
  • Loading branch information
doniyor2109 authored and SimenB committed Feb 16, 2019
1 parent d9d501a commit e760ec4
Show file tree
Hide file tree
Showing 29 changed files with 492 additions and 341 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -39,6 +39,7 @@
- `[babel-jest]`: Migrate to TypeScript ([#7862](https://github.com/facebook/jest/pull/7862))
- `[jest-resolve]`: Migrate to TypeScript ([#7871](https://github.com/facebook/jest/pull/7871))
- `[@jest/reporter]`: New package extracted from `jest-cli` ([#7902](https://github.com/facebook/jest/pull/7902))
- `[jest-snapshot]`: Migrate to TypeScript ([#7899](https://github.com/facebook/jest/pull/7899))

### Performance

Expand Down
7 changes: 5 additions & 2 deletions packages/jest-message-util/src/index.ts
Expand Up @@ -13,6 +13,9 @@ import micromatch from 'micromatch';
import slash from 'slash';
import {codeFrameColumns} from '@babel/code-frame';
import StackUtils from 'stack-utils';
import {Frame} from './types';

export {Frame} from './types';

type Path = Config.Path;
type AssertionResult = TestResult.AssertionResult;
Expand Down Expand Up @@ -227,7 +230,7 @@ export const getStackTraceLines = (
options: StackTraceOptions = {noStackTrace: false},
) => removeInternalStackEntries(stack.split(/\n/), options);

export const getTopFrame = (lines: string[]) => {
export const getTopFrame = (lines: string[]): Frame | null => {
for (const line of lines) {
if (line.includes(PATH_NODE_MODULES) || line.includes(PATH_JEST_PACKAGES)) {
continue;
Expand All @@ -236,7 +239,7 @@ export const getTopFrame = (lines: string[]) => {
const parsedFrame = stackUtils.parseLine(line.trim());

if (parsedFrame && parsedFrame.file) {
return parsedFrame;
return parsedFrame as Frame;
}
}

Expand Down
12 changes: 12 additions & 0 deletions packages/jest-message-util/src/types.ts
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import {StackData} from 'stack-utils';

export interface Frame extends StackData {
file: string;
}
8 changes: 8 additions & 0 deletions packages/jest-snapshot/package.json
Expand Up @@ -8,8 +8,10 @@
},
"license": "MIT",
"main": "build/index.js",
"types": "build/index.d.ts",
"dependencies": {
"@babel/types": "^7.0.0",
"@jest/types": "^24.1.0",

This comment has been minimized.

Copy link
@alloy

alloy Feb 25, 2020

@SimenB Is there a reason for this not being in devDependencies?

This comment has been minimized.

Copy link
@SimenB

SimenB Feb 25, 2020

Member

Discussed in private, but tl;dr is that dependents shouldn't have to install it to use the types

"chalk": "^2.0.1",
"jest-diff": "^24.0.0",
"jest-matcher-utils": "^24.0.0",
Expand All @@ -20,9 +22,15 @@
"pretty-format": "^24.0.0",
"semver": "^5.5.0"
},
"peerDependencies": {
"jest-haste-map": "^24.0.0"
},
"devDependencies": {
"@types/mkdirp": "^0.5.2",
"@types/natural-compare": "^1.4.0",
"@types/prettier": "^1.16.1",
"@types/semver": "^5.5.0",
"jest-haste-map": "^24.0.0",
"prettier": "^1.13.4"
},
"engines": {
Expand Down
Expand Up @@ -3,13 +3,11 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import type {Path, SnapshotUpdateState} from 'types/Config';

import fs from 'fs';
import {Config} from '@jest/types';

import {getTopFrame, getStackTraceLines} from 'jest-message-util';
import {
saveSnapshotFile,
Expand All @@ -19,41 +17,44 @@ import {
testNameToKey,
unescape,
} from './utils';
import {saveInlineSnapshots, type InlineSnapshot} from './inline_snapshots';
import {saveInlineSnapshots, InlineSnapshot} from './inline_snapshots';
import {SnapshotData} from './types';

export type SnapshotStateOptions = {|
updateSnapshot: SnapshotUpdateState,
getPrettier: () => null | any,
getBabelTraverse: () => Function,
expand?: boolean,
|};
export type SnapshotStateOptions = {
updateSnapshot: Config.SnapshotUpdateState;
getPrettier: () => null | any;
getBabelTraverse: () => Function;
expand?: boolean;
};

export type SnapshotMatchOptions = {|
testName: string,
received: any,
key?: string,
inlineSnapshot?: string,
error?: Error,
|};
export type SnapshotMatchOptions = {
testName: string;
received: any;
key?: string;
inlineSnapshot?: string;
error?: Error;
};

export default class SnapshotState {
_counters: Map<string, number>;
_dirty: boolean;
_index: number;
_updateSnapshot: SnapshotUpdateState;
_snapshotData: {[key: string]: string};
_snapshotPath: Path;
_inlineSnapshots: Array<InlineSnapshot>;
_uncheckedKeys: Set<string>;
_getBabelTraverse: () => Function;
_getPrettier: () => null | any;
private _counters: Map<string, number>;
private _dirty: boolean;
// @ts-ignore
private _index: number;
private _updateSnapshot: Config.SnapshotUpdateState;
private _snapshotData: SnapshotData;
private _snapshotPath: Config.Path;
private _inlineSnapshots: Array<InlineSnapshot>;
private _uncheckedKeys: Set<string>;
private _getBabelTraverse: () => Function;
private _getPrettier: () => null | any;

added: number;
expand: boolean;
matched: number;
unmatched: number;
updated: number;

constructor(snapshotPath: Path, options: SnapshotStateOptions) {
constructor(snapshotPath: Config.Path, options: SnapshotStateOptions) {
this._snapshotPath = snapshotPath;
const {data, dirty} = getSnapshotData(
this._snapshotPath,
Expand Down Expand Up @@ -83,15 +84,15 @@ export default class SnapshotState {
});
}

_addSnapshot(
private _addSnapshot(
key: string,
receivedSerialized: string,
options: {isInline: boolean, error?: Error},
options: {isInline: boolean; error?: Error},
) {
this._dirty = true;
if (options.isInline) {
const error = options.error || new Error();
const lines = getStackTraceLines(error.stack);
const lines = getStackTraceLines(error.stack || '');
const frame = getTopFrame(lines);
if (!frame) {
throw new Error(
Expand Down Expand Up @@ -251,7 +252,7 @@ export default class SnapshotState {
}
}

fail(testName: string, received: any, key?: string) {
fail(testName: string, _received: any, key?: string) {
this._counters.set(testName, (this._counters.get(testName) || 0) + 1);
const count = Number(this._counters.get(testName));

Expand Down
Expand Up @@ -3,65 +3,53 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

jest.mock('fs');
jest.mock('prettier');

const fs = require('fs');
const path = require('path');
const prettier = require('prettier');
const babelTraverse = require('@babel/traverse').default;
import fs from 'fs';
import path from 'path';
import prettier from 'prettier';
import babelTraverse from '@babel/traverse';
import {Frame} from 'jest-message-util';

const {saveInlineSnapshots} = require('../inline_snapshots');
import {saveInlineSnapshots} from '../inline_snapshots';

const writeFileSync = fs.writeFileSync;
const readFileSync = fs.readFileSync;
const existsSync = fs.existsSync;
const statSync = fs.statSync;
const readdirSync = fs.readdirSync;
beforeEach(() => {
// $FlowFixMe mock
fs.writeFileSync = jest.fn();
// $FlowFixMe mock
fs.readFileSync = jest.fn();
// $FlowFixMe mock
fs.existsSync = jest.fn(() => true);
// $FlowFixMe mock
fs.statSync = jest.fn(filePath => ({
(fs.statSync as jest.Mock).mockImplementation(filePath => ({
isDirectory: () => !filePath.endsWith('.js'),
}));
// $FlowFixMe mock
fs.readdirSync = jest.fn(() => []);

prettier.resolveConfig.sync.mockReset();
(prettier.resolveConfig.sync as jest.Mock).mockReset();
});
afterEach(() => {
// $FlowFixMe mock
fs.writeFileSync = writeFileSync;
// $FlowFixMe mock
fs.readFileSync = readFileSync;
// $FlowFixMe mock
fs.existsSync = existsSync;
// $FlowFixMe mock
fs.statSync = statSync;
// $FlowFixMe mock
fs.readdirSync = readdirSync;
});

test('saveInlineSnapshots() replaces empty function call with a template literal', () => {
const filename = path.join(__dirname, 'my.test.js');
// $FlowFixMe mock
fs.readFileSync = (jest.fn(
(fs.readFileSync as jest.Mock).mockImplementation(
() => `expect(1).toMatchInlineSnapshot();\n`,
): any);
);

saveInlineSnapshots(
[
{
frame: {column: 11, file: filename, line: 1},
frame: {column: 11, file: filename, line: 1} as Frame,
snapshot: `1`,
},
],
Expand All @@ -79,25 +67,26 @@ test.each([['babylon'], ['flow'], ['typescript']])(
'saveInlineSnapshots() replaces existing template literal - %s parser',
parser => {
const filename = path.join(__dirname, 'my.test.js');
// $FlowFixMe mock
fs.readFileSync = (jest.fn(
(fs.readFileSync as jest.Mock).mockImplementation(
() => 'expect(1).toMatchInlineSnapshot(`2`);\n',
): any);
);

prettier.resolveConfig.sync.mockReturnValue({parser});
(prettier.resolveConfig.sync as jest.Mock).mockReturnValue({parser});

saveInlineSnapshots(
[
{
frame: {column: 11, file: filename, line: 1},
frame: {column: 11, file: filename, line: 1} as Frame,
snapshot: `1`,
},
],
prettier,
babelTraverse,
);

expect(prettier.resolveConfig.sync.mock.results[0].value).toEqual({parser});
expect(
(prettier.resolveConfig.sync as jest.Mock).mock.results[0].value,
).toEqual({parser});

expect(fs.writeFileSync).toHaveBeenCalledWith(
filename,
Expand All @@ -108,15 +97,14 @@ test.each([['babylon'], ['flow'], ['typescript']])(

test('saveInlineSnapshots() replaces existing template literal with property matchers', () => {
const filename = path.join(__dirname, 'my.test.js');
// $FlowFixMe mock
fs.readFileSync = (jest.fn(
(fs.readFileSync as jest.Mock).mockImplementation(
() => 'expect(1).toMatchInlineSnapshot({}, `2`);\n',
): any);
);

saveInlineSnapshots(
[
{
frame: {column: 11, file: filename, line: 1},
frame: {column: 11, file: filename, line: 1} as Frame,
snapshot: `1`,
},
],
Expand All @@ -132,16 +120,15 @@ test('saveInlineSnapshots() replaces existing template literal with property mat

test('saveInlineSnapshots() throws if frame does not match', () => {
const filename = path.join(__dirname, 'my.test.js');
// $FlowFixMe mock
fs.readFileSync = (jest.fn(
(fs.readFileSync as jest.Mock).mockImplementation(
() => 'expect(1).toMatchInlineSnapshot();\n',
): any);
);

const save = () =>
saveInlineSnapshots(
[
{
frame: {column: 2 /* incorrect */, file: filename, line: 1},
frame: {column: 2 /* incorrect */, file: filename, line: 1} as Frame,
snapshot: `1`,
},
],
Expand All @@ -154,12 +141,11 @@ test('saveInlineSnapshots() throws if frame does not match', () => {

test('saveInlineSnapshots() throws if multiple calls to to the same location', () => {
const filename = path.join(__dirname, 'my.test.js');
// $FlowFixMe mock
fs.readFileSync = (jest.fn(
(fs.readFileSync as jest.Mock).mockImplementation(
() => 'expect(1).toMatchInlineSnapshot();\n',
): any);
);

const frame = {column: 11, file: filename, line: 1};
const frame = {column: 11, file: filename, line: 1} as Frame;
const save = () =>
saveInlineSnapshots(
[{frame, snapshot: `1`}, {frame, snapshot: `2`}],
Expand All @@ -174,12 +160,11 @@ test('saveInlineSnapshots() throws if multiple calls to to the same location', (

test('saveInlineSnapshots() uses escaped backticks', () => {
const filename = path.join(__dirname, 'my.test.js');
// $FlowFixMe mock
fs.readFileSync = (jest.fn(
(fs.readFileSync as jest.Mock).mockImplementation(
() => 'expect("`").toMatchInlineSnapshot();\n',
): any);
);

const frame = {column: 13, file: filename, line: 1};
const frame = {column: 13, file: filename, line: 1} as Frame;
saveInlineSnapshots([{frame, snapshot: '`'}], prettier, babelTraverse);

expect(fs.writeFileSync).toHaveBeenCalledWith(
Expand All @@ -190,19 +175,18 @@ test('saveInlineSnapshots() uses escaped backticks', () => {

test('saveInlineSnapshots() works with non-literals in expect call', () => {
const filename = path.join(__dirname, 'my.test.js');
// $FlowFixMe mock
fs.readFileSync = (jest.fn(
(fs.readFileSync as jest.Mock).mockImplementation(
() => `expect({a: 'a'}).toMatchInlineSnapshot();\n`,
): any);
prettier.resolveConfig.sync.mockReturnValue({
);
(prettier.resolveConfig.sync as jest.Mock).mockReturnValue({
bracketSpacing: false,
singleQuote: true,
});

saveInlineSnapshots(
[
{
frame: {column: 18, file: filename, line: 1},
frame: {column: 18, file: filename, line: 1} as Frame,
snapshot: `{a: 'a'}`,
},
],
Expand Down

0 comments on commit e760ec4

Please sign in to comment.