Skip to content

Commit 710fd66

Browse files
authored
added Snapshot methods to parse and getContent separately (#94)
1 parent 6eed147 commit 710fd66

File tree

7 files changed

+184
-27
lines changed

7 files changed

+184
-27
lines changed

index.d.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {Config as JestConfig} from '@jest/types';
1111
import { CoverageMapData } from 'istanbul-lib-coverage';
1212
import ProjectWorkspace, {ProjectWorkspaceConfig, createProjectWorkspace, LoginShell } from './build/project_workspace';
1313
export {createProjectWorkspace, ProjectWorkspaceConfig, ProjectWorkspace, LoginShell};
14-
14+
import {SourceLocation} from '@babel/types';
1515
export interface RunArgs {
1616
args: string[];
1717
replace?: boolean; // default is false
@@ -224,10 +224,20 @@ export interface SnapshotMetadata {
224224
content?: string;
225225
}
226226

227+
export interface SnapshotNode{
228+
name: string;
229+
loc: SourceLocation;
230+
}
231+
export interface SnapshotBlock{
232+
node: SnapshotNode;
233+
parents: SnapshotNode[];
234+
}
227235
export class Snapshot {
228236
constructor(parser?: any, customMatchers?: string[]);
229237
getMetadata(filepath: string, verbose?: boolean): SnapshotMetadata[];
230238
getMetadataAsync(filePath: string, verbose?: boolean): Promise<Array<SnapshotMetadata>>;
239+
parse(filePath: string, verbose?: boolean): SnapshotBlock[];
240+
getSnapshotContent(filePath: string, testFullName: string): Promise<string | undefined>;
231241
}
232242

233243
type FormattedTestResults = {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jest-editor-support",
3-
"version": "30.2.1",
3+
"version": "30.3.0",
44
"repository": {
55
"type": "git",
66
"url": "https://github.com/jest-community/jest-editor-support"

src/Snapshot.js

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ const buildName: (snapshotNode: Node, parents: Array<Node>, position: number) =>
7878
return utils.testNameToKey(fullName, position);
7979
};
8080

81+
export interface SnapshotNode {
82+
node: Node;
83+
parents: Node[];
84+
}
85+
8186
export default class Snapshot {
8287
_parser: Function;
8388

@@ -100,19 +105,7 @@ export default class Snapshot {
100105
);
101106
}
102107

103-
async getMetadataAsync(filePath: string, verbose: boolean = false): Promise<Array<SnapshotMetadata>> {
104-
if (!this.snapshotResolver) {
105-
await this._resolverPromise;
106-
}
107-
return this.getMetadata(filePath, verbose);
108-
}
109-
110-
getMetadata(filePath: string, verbose: boolean = false): Array<SnapshotMetadata> {
111-
if (!this.snapshotResolver) {
112-
throw new Error('snapshotResolver is not ready yet, consider migrating to "getMetadataAsync" instead');
113-
}
114-
const snapshotPath = this.snapshotResolver.resolveSnapshotPath(filePath);
115-
108+
parse(filePath: string, verbose: boolean = false): SnapshotNode[] {
116109
let fileNode;
117110
try {
118111
fileNode = this._parser(filePath);
@@ -123,37 +116,82 @@ export default class Snapshot {
123116
}
124117
return [];
125118
}
126-
const state = {
127-
found: [],
128-
};
119+
129120
const Visitors = {
130-
Identifier(path, _state, matchers) {
121+
Identifier(path, found, matchers) {
131122
if (matchers.indexOf(path.node.name) >= 0) {
132-
_state.found.push({
123+
found.push({
133124
node: path.node,
134125
parents: getArrayOfParents(path),
135126
});
136127
}
137128
},
138129
};
139130

131+
const found = [];
132+
140133
traverse(fileNode, {
141134
enter: (path) => {
142135
const visitor = Visitors[path.node.type];
143136
if (visitor != null) {
144-
visitor(path, state, this._matchers);
137+
visitor(path, found, this._matchers);
145138
}
146139
},
147140
});
148141

149-
// NOTE if no projectConfig is given the default resolver will be used
142+
return found.map((f) => ({
143+
node: f.node,
144+
parents: f.parents.filter(isValidParent),
145+
}));
146+
}
150147

148+
async _getSnapshotResolver(): Promise<SnapshotResolver> {
149+
if (!this.snapshotResolver) {
150+
await this._resolverPromise;
151+
}
152+
return this.snapshotResolver;
153+
}
154+
155+
/**
156+
* look for snapshot content for the given test.
157+
* @param {*} filePath
158+
* @param {*} testFullName
159+
* @param autoPosition if true (the default), it will append position ("1") to the testFullName,
160+
* otherwise, the testFullName should include the position in it.
161+
* @returns the content of the snapshot, if exist. otherwise undefined.
162+
* @throws throws exception if the snapshot version mismatched or any other unexpected error.
163+
*/
164+
async getSnapshotContent(
165+
filePath: string,
166+
testFullName: string,
167+
autoPosition: boolean = true
168+
): Promise<string | null> {
169+
const snapshotResolver = await this._getSnapshotResolver();
170+
171+
const snapshotPath = snapshotResolver.resolveSnapshotPath(filePath);
172+
const snapshots = utils.getSnapshotData(snapshotPath, 'none').data;
173+
const name = autoPosition ? `${testFullName} 1` : testFullName;
174+
return snapshots[name];
175+
}
176+
177+
async getMetadataAsync(filePath: string, verbose: boolean = false): Promise<Array<SnapshotMetadata>> {
178+
await this._getSnapshotResolver();
179+
return this.getMetadata(filePath, verbose);
180+
}
181+
182+
getMetadata(filePath: string, verbose: boolean = false): Array<SnapshotMetadata> {
183+
if (!this.snapshotResolver) {
184+
throw new Error('snapshotResolver is not ready yet, consider migrating to "getMetadataAsync" instead');
185+
}
186+
const snapshotPath = this.snapshotResolver.resolveSnapshotPath(filePath);
187+
const snapshotNodes = this.parse(filePath, verbose);
151188
const snapshots = utils.getSnapshotData(snapshotPath, 'none').data;
189+
152190
let lastParent = null;
153191
let count = 1;
154192

155-
return state.found.map((snapshotNode) => {
156-
const parents = snapshotNode.parents.filter(isValidParent);
193+
return snapshotNodes.map((snapshotNode) => {
194+
const {parents} = snapshotNode;
157195
const innerAssertion = parents[parents.length - 1];
158196

159197
if (lastParent !== innerAssertion) {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
exports[`test.each a 1`] = `a`;
3+
exports[`test.each b 1`] = `b`;
4+
exports[`test.each c 1`] = `c`;
5+
exports[`1 describe with each test.each a 1`] = `1.a`;
6+
exports[`1 describe with each test.each b 1`] = `1.b`;
7+
exports[`1 describe with each test.each c 1`] = `1.c`;
8+
exports[`2 describe with each test.each a 1`] = `2.a`;
9+
exports[`2 describe with each test.each b 1`] = `2.b`;
10+
exports[`2 describe with each test.each c 1`] = `2.c`;
11+
exports[`3 describe with each test.each a 1`] = `3.a`;
12+
exports[`3 describe with each test.each b 1`] = `3.b`;
13+
exports[`3 describe with each test.each c 1`] = `3.c`;
14+
exports[`tests with each case 1 test 1-D array each 1`] = `1 1-D`;
15+
exports[`tests with each case 2 test 1-D array each 1`] = `2 1-D`;
16+
exports[`tests with each case 3 test 1-D array each 1`] = `3 1-D`;
17+
exports[`literal test 1`] = `literal test 1 content`;
18+
exports[`literal test 2`] = `literal test 2 content`;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
describe('tests with each', () => {
2+
it.each`
3+
case|whatever
4+
${1}|${'a'}
5+
$(2)|${'b'}
6+
`('case $case: test tabled each', ({whatever}) => {
7+
expect(whatever).toMatchSnapshot();
8+
expect(whatever).toMatchInlineSnapshot();
9+
});
10+
it.each([1,2,3])('case %d test 1-D array each', (n) => {
11+
expect(n).toThrowErrorMatchingSnapshot();
12+
expect(n).toMatchInlineSnapshot();
13+
14+
});
15+
});
16+
17+
describe.each([1,2,3])('%d describe with each', (n) => {
18+
it.each(['a', 'b', 'c'])('test.each %s', (char) => {
19+
expect({n, char}).toMatchSnapshot();
20+
});
21+
it('a regular test', () => {
22+
expect(n).toMatchInlineSnapshot();
23+
});
24+
});
25+
26+
it.each(['a', 'b', 'c'])('inline test.each %s', (char) => {
27+
expect(char).toThrowErrorMatchingInlineSnapshot();
28+
});
29+
it.each(['a', 'b', 'c'])('test.each %s', (char) => {
30+
expect(char).toMatchSnapshot();
31+
});
32+
it('regular inline test', () => {
33+
expect(whatever).toMatchInlineSnapshot();
34+
});
35+

src/__tests__/snapshot.test.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,59 @@ describe('when metadata parse error', () => {
118118
expect(console.warn).toHaveBeenCalled();
119119
});
120120
});
121+
122+
describe('parse', () => {
123+
it('can parse and return matched nodes', () => {
124+
const filePath = path.join(snapshotFixturePath, 'nested.example');
125+
const snapshotNodes = snapshotHelper.parse(filePath);
126+
expect(snapshotNodes).toHaveLength(2);
127+
snapshotNodes.forEach((n) => expect(n.node.name).toEqual('toMatchSnapshot'));
128+
snapshotNodes.forEach((n) => expect(n.parents).toHaveLength(4));
129+
expect(snapshotNodes[0].node.loc.start).toEqual({column: 21, line: 5});
130+
expect(snapshotNodes[0].node.loc.end).toEqual({column: 36, line: 5});
131+
expect(snapshotNodes[1].node.loc.start).toEqual({column: 21, line: 6});
132+
expect(snapshotNodes[1].node.loc.end).toEqual({column: 36, line: 6});
133+
});
134+
it('can parse inline snapshots', () => {
135+
const filePath = path.join(snapshotFixturePath, 'inline-and-each.example');
136+
137+
let snapshot = new Snapshot();
138+
let snapshotNodes = snapshot.parse(filePath);
139+
let inlineSnapshotNodes = snapshotNodes.filter((sn) => sn.node.name === 'toMatchInlineSnapshot');
140+
expect(inlineSnapshotNodes).toHaveLength(0);
141+
let inlineThrowSnapshotNodes = snapshotNodes.filter((sn) => sn.node.name === 'toThrowErrorMatchingInlineSnapshot');
142+
expect(inlineThrowSnapshotNodes).toHaveLength(0);
143+
144+
snapshot = new Snapshot(undefined, ['toMatchInlineSnapshot', 'toThrowErrorMatchingInlineSnapshot']);
145+
snapshotNodes = snapshot.parse(filePath);
146+
inlineSnapshotNodes = snapshotNodes.filter((sn) => sn.node.name === 'toMatchInlineSnapshot');
147+
expect(inlineSnapshotNodes).toHaveLength(4);
148+
inlineThrowSnapshotNodes = snapshotNodes.filter((sn) => sn.node.name === 'toThrowErrorMatchingInlineSnapshot');
149+
expect(inlineThrowSnapshotNodes).toHaveLength(1);
150+
});
151+
});
152+
describe('getSnapshotContent', () => {
153+
it.each`
154+
testName | expected
155+
${'regular inline test'} | ${undefined}
156+
${'test.each %s'} | ${undefined}
157+
${'test.each a'} | ${'a'}
158+
${'1 describe with each test.each a'} | ${'1.a'}
159+
${'2 describe with each test.each b'} | ${'2.b'}
160+
${'tests with each case %d test 1-D array each'} | ${undefined}
161+
${'tests with each case 3 test 1-D array each'} | ${'3 1-D'}
162+
`('', async ({testName, expected}) => {
163+
const filePath = path.join(snapshotFixturePath, 'inline-and-each.example');
164+
const snapshot = new Snapshot(undefined, ['toMatchInlineSnapshot', 'toThrowErrorMatchingInlineSnapshot']);
165+
const content = await snapshot.getSnapshotContent(filePath, testName);
166+
expect(content).toEqual(expected);
167+
});
168+
it('can take literal snapshot name', async () => {
169+
const filePath = path.join(snapshotFixturePath, 'inline-and-each.example');
170+
const snapshot = new Snapshot(undefined, ['toMatchInlineSnapshot', 'toThrowErrorMatchingInlineSnapshot']);
171+
let content = await snapshot.getSnapshotContent(filePath, `literal test 2`);
172+
expect(content).toBeUndefined();
173+
content = await snapshot.getSnapshotContent(filePath, `literal test 2`, false);
174+
expect(content).toEqual('literal test 2 content');
175+
});
176+
});

yarn.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1731,9 +1731,9 @@ camelcase@^6.2.0:
17311731
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
17321732

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

17381738
chalk@^2.0.0:
17391739
version "2.4.2"

0 commit comments

Comments
 (0)