Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
comments on the pr
  • Loading branch information
Cristian Carlesso authored and vvo committed Sep 30, 2017
commit b78c0ee1dd0abcb3e90724538d9fa04c7e6347ea
131 changes: 59 additions & 72 deletions packages/jest-editor-support/src/Snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

'use strict';

const fs = require('fs');
const traverse = require('babel-traverse').default;
const {getASTfor} = require('./parsers/BabylonParser');
const {utils} = require('jest-snapshot');

type Node = any;
Expand All @@ -23,47 +23,48 @@ type SnapshotMetadata = {
content?: string,
};

// create a lookup table from an array.
const lookupFromArray = array => array.reduce(
(table: any, prop) => {
table[prop] = true;
return table;
const describeVariants = Object.assign(
(Object.create(null): {[string]: boolean}),
{
describe: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like it would be good to put all this in a common package with eslint-plugin-jest, because we are duplicating all of this in multiple places.

fdescribe: true,
xdescribe: true,
},
Object.create(null)
);

const describeVariants = lookupFromArray([
'describe',
'fdescribe',
'xdescribe',
]);
const base = lookupFromArray(['describe', 'it', 'test']);
const decorators = lookupFromArray(['only', 'skip']);
const base = Object.assign((Object.create(null): {[string]: boolean}), {
describe: true,
it: true,
test: true,
});
const decorators = Object.assign((Object.create(null): {[string]: boolean}), {
only: true,
skip: true,
});

const validParents = Object.assign(
(Object.create(null): any),
base,
describeVariants,
lookupFromArray(['xtest', 'xit', 'fit'])
Object.assign((Object.create(null): {[string]: boolean}), {
fit: true,
xit: true,
xtest: true,
}),
);

const isValidMemberExpression = node => (
node.object && base[node.object.name] &&
node.property && decorators[node.property.name]
);
const isValidMemberExpression = node =>
node.object &&
base[node.object.name] &&
node.property &&
decorators[node.property.name];

const isDescribe = node => (
describeVariants[node.name] || (
isValidMemberExpression(node) && node.object.name === 'describe'
));
const isDescribe = node =>
describeVariants[node.name] ||
(isValidMemberExpression(node) && node.object.name === 'describe');

const isValidParent = parent => (
const isValidParent = parent =>
parent.callee &&
(
validParents[parent.callee.name] ||
isValidMemberExpression(parent.callee)
)
);
(validParents[parent.callee.name] || isValidMemberExpression(parent.callee));

const getArrayOfParents = path => {
const result = [];
Expand All @@ -73,20 +74,19 @@ const getArrayOfParents = path => {
parent = parent.parentPath;
}
return result;
}
};

const buildname: (
toMatchSnapshot: Node,
const buildName: (
snapshotNode: Node,
parents: Array<Node>,
position: number,
) => string = (toMatchSnapshot, parents, position) => {

) => string = (snapshotNode, parents, position) => {
const fullName = parents.map(parent => parent.arguments[0].value).join(' ');

let describeLess = '';
if (!isDescribe(parents[0].callee)) {
// if `it` or `test` exists without a surrounding `describe`
// then `test ` is prepended to the snapshot fullName
// If `it` or `test` exists without a surrounding `describe`
// then `test ` is prepended to the snapshot fullName.
describeLess = 'test ';
}

Expand All @@ -95,56 +95,43 @@ const buildname: (

module.exports = class Snapshot {
_parser: Function;
_parserOptions: any;

constructor(parser: any, parserOptions: any) {
this._parser = parser || require('babylon').parse;
this._parserOptions = parserOptions || {
plugins: [
'jsx', 'flow', 'objectRestSpread', 'classProperties',
],
sourceType: 'module',
};
_matchers: Array<string>;
constructor(parser: any, customMatchers?: Array<string>) {
this._parser = parser || getASTfor;
this._matchers = ['toMatchSnapshot', 'toThrowErrorMatchingSnapshot'].concat(
customMatchers || [],
);
}

getMetadata(filePath: string): Array<SnapshotMetadata> {
const fileContent = fs.readFileSync(filePath, 'utf8');

const fileNode = this._parser(fileContent, this._parserOptions);

const fileNode = this._parser(filePath);
const state = {
found: [],
};
const tocheck = {
found: []
};

const Visitors = {
Identifier(path, state) {
if (path.node.name === 'toMatchSnapshot') {
state.found.push({node: path.node, parents: getArrayOfParents(path)})
Identifier(path, state, matchers) {
if (matchers.includes(path.node.name)) {
state.found.push({node: path.node, parents: getArrayOfParents(path)});
}
}
}
},
};

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

let lastParent = null;
let count = 1;

const snapshotPath = utils.getSnapshotPath(filePath);
const snapshots = utils.getSnapshotData(snapshotPath, false).data;
let lastParent = null;
let count = 1;

return state.found.map((toMatchSnapshot, index) => {

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

if (lastParent !== innerAssertion) {
Expand All @@ -157,15 +144,15 @@ module.exports = class Snapshot {
count: count++,
exists: false,
name: '',
node: toMatchSnapshot.node,
node: snapshotNode.node,
};

if (!innerAssertion || isDescribe(innerAssertion.callee)) {
// an expectation inside a describe never get executed.
// An expectation inside describe never gets executed.
return result;
}

result.name = buildname(toMatchSnapshot, parents, result.count);
result.name = buildName(snapshotNode, parents, result.count);

if (snapshots[result.name]) {
result.exists = true;
Expand Down
59 changes: 37 additions & 22 deletions packages/jest-editor-support/src/__tests__/Snapshot-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,15 @@ test('nodescribe.example', () => {
const filePath = path.join(snapshotFixturePath, 'nodescribe.example');
const results = snapshotHelper.getMetadata(filePath);
const allAssertion = [
'fit', 'it', 'it.only', 'it.skip', 'test',
'test.only', 'test.skip', 'xit', 'xtest',
'fit',
'it',
'it.only',
'it.skip',
'test',
'test.only',
'test.skip',
'xit',
'xtest',
];

const expectations = Object.create(null);
Expand All @@ -41,28 +48,38 @@ test('nodescribe.example', () => {
});

expect(
Object.keys(expectations).map(key => expectations[key]).filter(
expectation => !expectation.checked
).length
Object.keys(expectations)
.map(key => expectations[key])
.filter(expectation => !expectation.checked).length,
).toBe(0);
});

test('describe.example', () => {
const filePath = path.join(snapshotFixturePath, 'describe.example');
const results = snapshotHelper.getMetadata(filePath);
const allDescribe = [
'describe', 'describe.only', 'describe.skip', 'fdescribe', 'xdescribe',
'describe',
'describe.only',
'describe.skip',
'fdescribe',
'xdescribe',
];
const allAssertion = [
'fit', 'it', 'it.only', 'it.skip', 'test',
'test.only', 'test.skip', 'xit', 'xtest',
'fit',
'it',
'it.only',
'it.skip',
'test',
'test.only',
'test.skip',
'xit',
'xtest',
];

const expectations = Object.create(null);

allDescribe.forEach(describe => {
allAssertion.forEach(assertion => {

expectations[describe.toUpperCase() + ' ' + assertion + ' 1'] = {
assertion,
checked: false,
Expand All @@ -81,15 +98,13 @@ test('describe.example', () => {

results.forEach(result => {
const check = expectations[result.name];
if(!check) console.log(result)
check.checked = (
result.content === `${check.number} ${check.assertion} ${check.describe}`
);
check.checked = result.content ===
`${check.number} ${check.assertion} ${check.describe}`;
});
expect(
Object.keys(expectations).map(key => expectations[key]).filter(
expectation => !expectation.checked
).length
Object.keys(expectations)
.map(key => expectations[key])
.filter(expectation => !expectation.checked).length,
).toBe(0);
});

Expand All @@ -100,14 +115,14 @@ test('nested.example', () => {
expect(results[1].content).toBe('second nested');

expect(results[0].name).toBe(
'outer describe outer it inner describe inner it 1'
'outer describe outer it inner describe inner it 1',
);
expect(results[1].name).toBe(
'outer describe outer it inner describe inner it 2'
'outer describe outer it inner describe inner it 2',
);

expect(results[0].node.loc.start).toEqual({column:21, line: 5});
expect(results[0].node.loc.end).toEqual({column:36, line:5});
expect(results[1].node.loc.start).toEqual({column:21, line: 6});
expect(results[1].node.loc.end).toEqual({column:36, line:6});
expect(results[0].node.loc.start).toEqual({column: 21, line: 5});
expect(results[0].node.loc.end).toEqual({column: 36, line: 5});
expect(results[1].node.loc.start).toEqual({column: 21, line: 6});
expect(results[1].node.loc.end).toEqual({column: 36, line: 6});
});
6 changes: 4 additions & 2 deletions packages/jest-editor-support/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import * as Process from './Process';
import ProjectWorkspace from './project_workspace';
import Runner from './Runner';
import Settings from './Settings';
import {Expect, ItBlock, Node} from './parsers/parser_nodes';
import {parse} from './parsers/babylon_parser';
import Snapshot from './Snapshot';
import { Expect, ItBlock, Node } from './parsers/parser_nodes';
import { parse } from './parsers/babylon_parser';
import TestReconciler from './test_reconciler';

module.exports = {
Expand All @@ -24,6 +25,7 @@ module.exports = {
ProjectWorkspace,
Runner,
Settings,
Snapshot,
TestReconciler,
parse,
};
17 changes: 10 additions & 7 deletions packages/jest-editor-support/src/parsers/babylon_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,27 @@
* @flow
*/

import {readFileSync} from 'fs';
import { readFileSync } from 'fs';

import {parse as babylonParse} from 'babylon';
import {Expect, ItBlock} from './parser_nodes';
import { parse as babylonParse } from 'babylon';
import { Expect, ItBlock } from './parser_nodes';

export type BabylonParserResult = {
expects: Array<Expect>,
itBlocks: Array<ItBlock>,
};

export const getASTfor = (file: string): BabylonParserResult => {
const data = readFileSync(file).toString();
const config = { plugins: ['*'], sourceType: 'module' };
return babylonParse(data, config);
};

export const parse = (file: string): BabylonParserResult => {
const itBlocks: ItBlock[] = [];
const expects: Expect[] = [];

const data = readFileSync(file).toString();

const config = {plugins: ['*'], sourceType: 'module'};
const ast = babylonParse(data, config);
const ast = getASTfor(file);

// An `it`/`test` was found in the AST
// So take the AST node and create an object for us
Expand Down