Skip to content

Commit

Permalink
added Snapshot methods to parse and getContent separately (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
connectdotz authored Nov 18, 2022
1 parent 6eed147 commit 710fd66
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 27 deletions.
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

0 comments on commit 710fd66

Please sign in to comment.