Skip to content

added Snapshot methods to parse and getContent separately #94

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 commits into from
Nov 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {Config as JestConfig} from '@jest/types';
import { CoverageMapData } from 'istanbul-lib-coverage';
import ProjectWorkspace, {ProjectWorkspaceConfig, createProjectWorkspace, LoginShell } from './build/project_workspace';
export {createProjectWorkspace, ProjectWorkspaceConfig, ProjectWorkspace, LoginShell};

import {SourceLocation} from '@babel/types';
export interface RunArgs {
args: string[];
replace?: boolean; // default is false
Expand Down Expand Up @@ -224,10 +224,20 @@ export interface SnapshotMetadata {
content?: string;
}

export interface SnapshotNode{
name: string;
loc: SourceLocation;
}
export interface SnapshotBlock{
node: SnapshotNode;
parents: SnapshotNode[];
}
export class Snapshot {
constructor(parser?: any, customMatchers?: string[]);
getMetadata(filepath: string, verbose?: boolean): SnapshotMetadata[];
getMetadataAsync(filePath: string, verbose?: boolean): Promise<Array<SnapshotMetadata>>;
parse(filePath: string, verbose?: boolean): SnapshotBlock[];
getSnapshotContent(filePath: string, testFullName: string): Promise<string | undefined>;
}

type FormattedTestResults = {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jest-editor-support",
"version": "30.2.1",
"version": "30.3.0",
"repository": {
"type": "git",
"url": "https://github.com/jest-community/jest-editor-support"
Expand Down
82 changes: 60 additions & 22 deletions src/Snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ const buildName: (snapshotNode: Node, parents: Array<Node>, position: number) =>
return utils.testNameToKey(fullName, position);
};

export interface SnapshotNode {
node: Node;
parents: Node[];
}

export default class Snapshot {
_parser: Function;

Expand All @@ -100,19 +105,7 @@ export default class Snapshot {
);
}

async getMetadataAsync(filePath: string, verbose: boolean = false): Promise<Array<SnapshotMetadata>> {
if (!this.snapshotResolver) {
await this._resolverPromise;
}
return this.getMetadata(filePath, verbose);
}

getMetadata(filePath: string, verbose: boolean = false): Array<SnapshotMetadata> {
if (!this.snapshotResolver) {
throw new Error('snapshotResolver is not ready yet, consider migrating to "getMetadataAsync" instead');
}
const snapshotPath = this.snapshotResolver.resolveSnapshotPath(filePath);

parse(filePath: string, verbose: boolean = false): SnapshotNode[] {
let fileNode;
try {
fileNode = this._parser(filePath);
Expand All @@ -123,37 +116,82 @@ export default class Snapshot {
}
return [];
}
const state = {
found: [],
};

const Visitors = {
Identifier(path, _state, matchers) {
Identifier(path, found, matchers) {
if (matchers.indexOf(path.node.name) >= 0) {
_state.found.push({
found.push({
node: path.node,
parents: getArrayOfParents(path),
});
}
},
};

const found = [];

traverse(fileNode, {
enter: (path) => {
const visitor = Visitors[path.node.type];
if (visitor != null) {
visitor(path, state, this._matchers);
visitor(path, found, this._matchers);
}
},
});

// NOTE if no projectConfig is given the default resolver will be used
return found.map((f) => ({
node: f.node,
parents: f.parents.filter(isValidParent),
}));
}

async _getSnapshotResolver(): Promise<SnapshotResolver> {
if (!this.snapshotResolver) {
await this._resolverPromise;
}
return this.snapshotResolver;
}

/**
* look for snapshot content for the given test.
* @param {*} filePath
* @param {*} testFullName
* @param autoPosition if true (the default), it will append position ("1") to the testFullName,
* otherwise, the testFullName should include the position in it.
* @returns the content of the snapshot, if exist. otherwise undefined.
* @throws throws exception if the snapshot version mismatched or any other unexpected error.
*/
async getSnapshotContent(
filePath: string,
testFullName: string,
autoPosition: boolean = true
): Promise<string | null> {
const snapshotResolver = await this._getSnapshotResolver();

const snapshotPath = snapshotResolver.resolveSnapshotPath(filePath);
const snapshots = utils.getSnapshotData(snapshotPath, 'none').data;
const name = autoPosition ? `${testFullName} 1` : testFullName;
return snapshots[name];
}

async getMetadataAsync(filePath: string, verbose: boolean = false): Promise<Array<SnapshotMetadata>> {
await this._getSnapshotResolver();
return this.getMetadata(filePath, verbose);
}

getMetadata(filePath: string, verbose: boolean = false): Array<SnapshotMetadata> {
if (!this.snapshotResolver) {
throw new Error('snapshotResolver is not ready yet, consider migrating to "getMetadataAsync" instead');
}
const snapshotPath = this.snapshotResolver.resolveSnapshotPath(filePath);
const snapshotNodes = this.parse(filePath, verbose);
const snapshots = utils.getSnapshotData(snapshotPath, 'none').data;

let lastParent = null;
let count = 1;

return state.found.map((snapshotNode) => {
const parents = snapshotNode.parents.filter(isValidParent);
return snapshotNodes.map((snapshotNode) => {
const {parents} = snapshotNode;
const innerAssertion = parents[parents.length - 1];

if (lastParent !== innerAssertion) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`test.each a 1`] = `a`;
exports[`test.each b 1`] = `b`;
exports[`test.each c 1`] = `c`;
exports[`1 describe with each test.each a 1`] = `1.a`;
exports[`1 describe with each test.each b 1`] = `1.b`;
exports[`1 describe with each test.each c 1`] = `1.c`;
exports[`2 describe with each test.each a 1`] = `2.a`;
exports[`2 describe with each test.each b 1`] = `2.b`;
exports[`2 describe with each test.each c 1`] = `2.c`;
exports[`3 describe with each test.each a 1`] = `3.a`;
exports[`3 describe with each test.each b 1`] = `3.b`;
exports[`3 describe with each test.each c 1`] = `3.c`;
exports[`tests with each case 1 test 1-D array each 1`] = `1 1-D`;
exports[`tests with each case 2 test 1-D array each 1`] = `2 1-D`;
exports[`tests with each case 3 test 1-D array each 1`] = `3 1-D`;
exports[`literal test 1`] = `literal test 1 content`;
exports[`literal test 2`] = `literal test 2 content`;
35 changes: 35 additions & 0 deletions src/__tests__/fixtures/snapshots/inline-and-each.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
describe('tests with each', () => {
it.each`
case|whatever
${1}|${'a'}
$(2)|${'b'}
`('case $case: test tabled each', ({whatever}) => {
expect(whatever).toMatchSnapshot();
expect(whatever).toMatchInlineSnapshot();
});
it.each([1,2,3])('case %d test 1-D array each', (n) => {
expect(n).toThrowErrorMatchingSnapshot();
expect(n).toMatchInlineSnapshot();

});
});

describe.each([1,2,3])('%d describe with each', (n) => {
it.each(['a', 'b', 'c'])('test.each %s', (char) => {
expect({n, char}).toMatchSnapshot();
});
it('a regular test', () => {
expect(n).toMatchInlineSnapshot();
});
});

it.each(['a', 'b', 'c'])('inline test.each %s', (char) => {
expect(char).toThrowErrorMatchingInlineSnapshot();
});
it.each(['a', 'b', 'c'])('test.each %s', (char) => {
expect(char).toMatchSnapshot();
});
it('regular inline test', () => {
expect(whatever).toMatchInlineSnapshot();
});

56 changes: 56 additions & 0 deletions src/__tests__/snapshot.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,59 @@ describe('when metadata parse error', () => {
expect(console.warn).toHaveBeenCalled();
});
});

describe('parse', () => {
it('can parse and return matched nodes', () => {
const filePath = path.join(snapshotFixturePath, 'nested.example');
const snapshotNodes = snapshotHelper.parse(filePath);
expect(snapshotNodes).toHaveLength(2);
snapshotNodes.forEach((n) => expect(n.node.name).toEqual('toMatchSnapshot'));
snapshotNodes.forEach((n) => expect(n.parents).toHaveLength(4));
expect(snapshotNodes[0].node.loc.start).toEqual({column: 21, line: 5});
expect(snapshotNodes[0].node.loc.end).toEqual({column: 36, line: 5});
expect(snapshotNodes[1].node.loc.start).toEqual({column: 21, line: 6});
expect(snapshotNodes[1].node.loc.end).toEqual({column: 36, line: 6});
});
it('can parse inline snapshots', () => {
const filePath = path.join(snapshotFixturePath, 'inline-and-each.example');

let snapshot = new Snapshot();
let snapshotNodes = snapshot.parse(filePath);
let inlineSnapshotNodes = snapshotNodes.filter((sn) => sn.node.name === 'toMatchInlineSnapshot');
expect(inlineSnapshotNodes).toHaveLength(0);
let inlineThrowSnapshotNodes = snapshotNodes.filter((sn) => sn.node.name === 'toThrowErrorMatchingInlineSnapshot');
expect(inlineThrowSnapshotNodes).toHaveLength(0);

snapshot = new Snapshot(undefined, ['toMatchInlineSnapshot', 'toThrowErrorMatchingInlineSnapshot']);
snapshotNodes = snapshot.parse(filePath);
inlineSnapshotNodes = snapshotNodes.filter((sn) => sn.node.name === 'toMatchInlineSnapshot');
expect(inlineSnapshotNodes).toHaveLength(4);
inlineThrowSnapshotNodes = snapshotNodes.filter((sn) => sn.node.name === 'toThrowErrorMatchingInlineSnapshot');
expect(inlineThrowSnapshotNodes).toHaveLength(1);
});
});
describe('getSnapshotContent', () => {
it.each`
testName | expected
${'regular inline test'} | ${undefined}
${'test.each %s'} | ${undefined}
${'test.each a'} | ${'a'}
${'1 describe with each test.each a'} | ${'1.a'}
${'2 describe with each test.each b'} | ${'2.b'}
${'tests with each case %d test 1-D array each'} | ${undefined}
${'tests with each case 3 test 1-D array each'} | ${'3 1-D'}
`('', async ({testName, expected}) => {
const filePath = path.join(snapshotFixturePath, 'inline-and-each.example');
const snapshot = new Snapshot(undefined, ['toMatchInlineSnapshot', 'toThrowErrorMatchingInlineSnapshot']);
const content = await snapshot.getSnapshotContent(filePath, testName);
expect(content).toEqual(expected);
});
it('can take literal snapshot name', async () => {
const filePath = path.join(snapshotFixturePath, 'inline-and-each.example');
const snapshot = new Snapshot(undefined, ['toMatchInlineSnapshot', 'toThrowErrorMatchingInlineSnapshot']);
let content = await snapshot.getSnapshotContent(filePath, `literal test 2`);
expect(content).toBeUndefined();
content = await snapshot.getSnapshotContent(filePath, `literal test 2`, false);
expect(content).toEqual('literal test 2 content');
});
});
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1731,9 +1731,9 @@ camelcase@^6.2.0:
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==

caniuse-lite@^1.0.30001254:
version "1.0.30001258"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001258.tgz#b604eed80cc54a578e4bf5a02ae3ed49f869d252"
integrity sha512-RBByOG6xWXUp0CR2/WU2amXz3stjKpSl5J1xU49F1n2OxD//uBZO4wCKUiG+QMGf7CHGfDDcqoKriomoGVxTeA==
version "1.0.30001431"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz"
integrity sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ==

chalk@^2.0.0:
version "2.4.2"
Expand Down