From ff469e09c46bd86f80b3c9c5d0520cc306c7089b Mon Sep 17 00:00:00 2001 From: Recca Tsai Date: Thu, 28 Sep 2017 19:56:24 +0800 Subject: [PATCH 01/16] --showConfig change to show all config, jest-editor-support fails (#4494) * --showConfig to show all project configs #4078 * return configs * new method getConfigs * new method getConfigs * call completed * eslint --- packages/jest-editor-support/src/Settings.js | 13 +++++-- .../src/__tests__/settings.test.js | 35 +++++++++++++++++-- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/packages/jest-editor-support/src/Settings.js b/packages/jest-editor-support/src/Settings.js index 5b7a3c46bd0b..36f93e98d283 100644 --- a/packages/jest-editor-support/src/Settings.js +++ b/packages/jest-editor-support/src/Settings.js @@ -54,19 +54,19 @@ export default class Settings extends EventEmitter { }; } - getConfig(completed: any) { + getConfigs(completed: any) { this.getConfigProcess = this._createProcess(this.workspace, [ '--showConfig', ]); this.getConfigProcess.stdout.on('data', (data: Buffer) => { - const {config, version} = JSON.parse(data.toString()); + const {configs, version} = JSON.parse(data.toString()); // We can give warnings to versions under 17 now // See https://github.com/facebook/jest/issues/2343 for moving this into // the config object this.jestVersionMajor = parseInt(version.split('.').shift(), 10); - this.settings = config; + this.settings = configs; }); // They could have an older build of Jest which @@ -75,4 +75,11 @@ export default class Settings extends EventEmitter { completed(); }); } + + getConfig(completed: any) { + this.getConfigs(() => { + this.settings = this.settings[0]; + completed(); + }); + } } diff --git a/packages/jest-editor-support/src/__tests__/settings.test.js b/packages/jest-editor-support/src/__tests__/settings.test.js index e0372357c29c..90ad492eea94 100644 --- a/packages/jest-editor-support/src/__tests__/settings.test.js +++ b/packages/jest-editor-support/src/__tests__/settings.test.js @@ -26,6 +26,35 @@ describe('Settings', () => { expect(settings.settings).toEqual(expect.any(Object)); }); + it('reads and parses the configs', () => { + const workspace = new ProjectWorkspace( + 'root_path', + 'path_to_jest', + 'test', + 1000, + ); + const completed = jest.fn(); + const configs = [{cacheDirectory: '/tmp/jest', name: '[md5 hash]'}]; + const json = { + configs, + version: '19.0.0', + }; + + const mockProcess: any = new EventEmitter(); + mockProcess.stdout = new EventEmitter(); + const createProcess = () => mockProcess; + const buffer = makeBuffer(JSON.stringify(json)); + const settings = new Settings(workspace, {createProcess}); + + settings.getConfigs(completed); + settings.getConfigProcess.stdout.emit('data', buffer); + settings.getConfigProcess.emit('close'); + + expect(completed).toHaveBeenCalled(); + expect(settings.jestVersionMajor).toBe(19); + expect(settings.settings).toEqual(configs); + }); + it('reads and parses the config', () => { const workspace = new ProjectWorkspace( 'root_path', @@ -34,9 +63,9 @@ describe('Settings', () => { 1000, ); const completed = jest.fn(); - const config = {cacheDirectory: '/tmp/jest', name: '[md5 hash]'}; + const configs = [{cacheDirectory: '/tmp/jest', name: '[md5 hash]'}]; const json = { - config, + configs, version: '19.0.0', }; @@ -52,7 +81,7 @@ describe('Settings', () => { expect(completed).toHaveBeenCalled(); expect(settings.jestVersionMajor).toBe(19); - expect(settings.settings).toEqual(config); + expect(settings.settings).toEqual(configs[0]); }); it('calls callback even if no data is sent', () => { From 80bd4cd4b6100d493290293645095506c968f6cf Mon Sep 17 00:00:00 2001 From: Lucas Azzola Date: Thu, 28 Sep 2017 23:53:09 +1000 Subject: [PATCH 02/16] [jest-docblock] add docblock.print() (#4517) * [jest-docblock]: add docblock.print() * Add missing flow type, clean up * Fix docs typo * Fix doc typo * [WIP] add docblock.parseWithComments() * Detect newline character, consolidate API * Fixup types * Remove default export from jest-docblock BREAKING CHANGE: Changes jest-docblock to an ES module without a default export, requires `import * as docblock from 'jest-docbloc'` --- packages/jest-docblock/README.md | 14 +- packages/jest-docblock/package.json | 5 +- .../jest-docblock/src/__tests__/index.test.js | 182 +++++++++++++++++- packages/jest-docblock/src/index.js | 69 ++++++- packages/jest-haste-map/src/worker.js | 2 +- packages/jest-runner/src/run_test.js | 2 +- yarn.lock | 4 + 7 files changed, 266 insertions(+), 12 deletions(-) diff --git a/packages/jest-docblock/README.md b/packages/jest-docblock/README.md index 41f021a912c2..85a740b8e6aa 100644 --- a/packages/jest-docblock/README.md +++ b/packages/jest-docblock/README.md @@ -30,6 +30,7 @@ Pragmas can also take arguments: `jest-docblock` can: * extract the docblock from some code as a string * parse a docblock string's pragmas into an object +* print an object and some comments back to a string ## Installation ```sh @@ -56,13 +57,18 @@ const code = ` } `; -const { extract, parse } = require("jest-docblock"); +const { extract, parse, parseWithComments, print } = require("jest-docblock"); const docblock = extract(code); console.log(docblock); // "/**\n * Everything is awesome!\n * \n * @everything is:awesome\n * @flow\n */" const pragmas = parse(docblock); console.log(pragmas); // { everything: "is:awesome", flow: "" } + +const parsed = parseWithComments(docblock); +console.log(parsed); // { comments: "Everything is awesome!", pragmas: { everything: "is:awesome", flow: "" } } + +console.log(print({pragmas, comments: "hi!"})) // /**\n * hi!\n *\n * @everything is:awesome\n * @flow\n */; ``` ## API Documentation @@ -72,3 +78,9 @@ Extracts a docblock from some file contents. Returns the docblock contained in ` ### `parse(docblock: string): {[key: string]: string}` Parses the pragmas in a docblock string into an object whose keys are the pragma tags and whose values are the arguments to those pragmas. + +### `parseWithComments(docblock: string): { comments: string, pragmas: {[key: string]: string} }` +Similar to `parse` except this method also returns the comments from the docblock. Useful when used with `print()`. + +### `print({ comments?: string, pragmas?: {[key: string]: string} }): string` +Prints an object of key-value pairs back into a docblock. If `comments` are provided, they will be positioned on the top of the docblock. \ No newline at end of file diff --git a/packages/jest-docblock/package.json b/packages/jest-docblock/package.json index 1c8921a08ade..6739455f2511 100644 --- a/packages/jest-docblock/package.json +++ b/packages/jest-docblock/package.json @@ -6,5 +6,8 @@ "url": "https://github.com/facebook/jest.git" }, "license": "MIT", - "main": "build/index.js" + "main": "build/index.js", + "dependencies": { + "detect-newline": "^2.1.0" + } } diff --git a/packages/jest-docblock/src/__tests__/index.test.js b/packages/jest-docblock/src/__tests__/index.test.js index c44de6118221..b2097b3ba484 100644 --- a/packages/jest-docblock/src/__tests__/index.test.js +++ b/packages/jest-docblock/src/__tests__/index.test.js @@ -10,8 +10,8 @@ 'use strict'; -const os = require('os'); -const docblock = require('../'); +import os from 'os'; +import * as docblock from '..'; describe('docblock', () => { it('extracts valid docblock with line comment', () => { @@ -212,4 +212,182 @@ describe('docblock', () => { providesModule: 'apple/banana', }); }); + + it('extracts comments from docblock', () => { + const code = + '/**' + + os.EOL + + ' * hello world' + + os.EOL + + ' * @flow yes' + + os.EOL + + ' */'; + expect(docblock.parseWithComments(code)).toEqual({ + comments: 'hello world', + pragmas: {flow: 'yes'}, + }); + }); + + it('extracts multiline comments from docblock', () => { + const code = + '/**' + + os.EOL + + ' * hello' + + os.EOL + + ' * world' + + os.EOL + + ' * @flow yes' + + os.EOL + + ' */'; + expect(docblock.parseWithComments(code)).toEqual({ + comments: 'hello' + os.EOL + 'world', + pragmas: {flow: 'yes'}, + }); + }); + + it('extracts comments from beginning and end of docblock', () => { + const code = + '/**' + + os.EOL + + ' * hello' + + os.EOL + + ' * @flow yes' + + os.EOL + + ' * ' + + os.EOL + + ' * world' + + os.EOL + + ' */'; + expect(docblock.parseWithComments(code)).toEqual({ + comments: 'hello' + os.EOL + os.EOL + 'world', + pragmas: {flow: 'yes'}, + }); + }); + + it('extracts docblock comments as CRLF when docblock contains CRLF', () => { + const code = '/**\r\n * foo\r\n * bar\r\n*/'; + expect(docblock.parseWithComments(code)).toEqual({ + comments: 'foo\r\nbar', + pragmas: {}, + }); + }); + + it('extracts docblock comments as LF when docblock contains LF', () => { + const code = '/**\n * foo\n * bar\n*/'; + expect(docblock.parseWithComments(code)).toEqual({ + comments: 'foo\nbar', + pragmas: {}, + }); + }); + + it('prints docblocks with no pragmas as empty string', () => { + const pragmas = {}; + expect(docblock.print({pragmas})).toEqual(''); + }); + + it('prints docblocks with one pragma on one line', () => { + const pragmas = {flow: ''}; + expect(docblock.print({pragmas})).toEqual('/** @flow */'); + }); + + it('prints docblocks with multiple pragmas on multiple lines', () => { + const pragmas = { + flow: '', + format: '', + }; + expect(docblock.print({pragmas})).toEqual( + '/**' + os.EOL + ' * @flow' + os.EOL + ' * @format' + os.EOL + ' */', + ); + }); + + it('prints docblocks with pragmas', () => { + const pragmas = { + flow: 'foo', + providesModule: 'x/y/z', + }; + expect(docblock.print({pragmas})).toEqual( + '/**' + + os.EOL + + ' * @flow foo' + + os.EOL + + ' * @providesModule x/y/z' + + os.EOL + + ' */', + ); + }); + + it('prints docblocks with comments', () => { + const pragmas = {flow: 'foo'}; + const comments = 'hello'; + expect(docblock.print({comments, pragmas})).toEqual( + '/**' + + os.EOL + + ' * hello' + + os.EOL + + ' *' + + os.EOL + + ' * @flow foo' + + os.EOL + + ' */', + ); + }); + + it('prints docblocks with comments and no keys', () => { + const pragmas = {}; + const comments = 'Copyright 2004-present Facebook. All Rights Reserved.'; + expect(docblock.print({comments, pragmas})).toEqual( + '/**' + os.EOL + ' * ' + comments + os.EOL + ' */', + ); + }); + + it('prints docblocks with multiline comments', () => { + const pragmas = {}; + const comments = 'hello' + os.EOL + 'world'; + expect(docblock.print({comments, pragmas})).toEqual( + '/**' + os.EOL + ' * hello' + os.EOL + ' * world' + os.EOL + ' */', + ); + }); + + it('prints docblocks that are parseable', () => { + const pragmas = {a: 'b', c: ''}; + const comments = 'hello world!'; + const formatted = docblock.print({comments, pragmas}); + const parsed = docblock.parse(formatted); + expect(parsed).toEqual(pragmas); + }); + + it('can augment existing docblocks with comments', () => { + const before = + '/**' + os.EOL + ' * Legalese' + os.EOL + ' * @flow' + os.EOL + ' */'; + const {comments, pragmas} = docblock.parseWithComments(before); + pragmas.format = ''; + const after = docblock.print({comments, pragmas}); + expect(after).toEqual( + '/**' + + os.EOL + + ' * Legalese' + + os.EOL + + ' *' + + os.EOL + + ' * @flow' + + os.EOL + + ' * @format' + + os.EOL + + ' */', + ); + }); + + it('prints docblocks using CRLF if comments contains CRLF', () => { + const pragmas = {}; + const comments = 'hello\r\nworld'; + const formatted = docblock.print({comments, pragmas}); + expect(formatted).toEqual('/**\r\n * hello\r\n * world\r\n */'); + }); + + it('prints docblocks using LF if comments contains LF', () => { + const pragmas = {}; + const comments = 'hello\nworld'; + const formatted = docblock.print({comments, pragmas}); + expect(formatted).toEqual('/**\n * hello\n * world\n */'); + }); }); diff --git a/packages/jest-docblock/src/index.js b/packages/jest-docblock/src/index.js index 714c20032fdf..7281a6941f77 100644 --- a/packages/jest-docblock/src/index.js +++ b/packages/jest-docblock/src/index.js @@ -7,6 +7,9 @@ * @flow */ +import detectNewline from 'detect-newline'; +import {EOL} from 'os'; + const commentEndRe = /\*\/$/; const commentStartRe = /^\/\*\*/; const docblockRe = /^\s*(\/\*\*?(.|\r?\n)*?\*\/)/; @@ -15,14 +18,23 @@ const ltrimRe = /^\s*/; const multilineRe = /(?:^|\r?\n) *(@[^\r\n]*?) *\r?\n *([^@\r\n\s][^@\r\n]+?) *\r?\n/g; const propertyRe = /(?:^|\r?\n) *@(\S+) *([^\r\n]*)/g; const stringStartRe = /(\r?\n|^) *\*/g; +const lineStartRe = /(\r?\n|^) */g; const wsRe = /[\t ]+/g; -function extract(contents: string): string { +export function extract(contents: string): string { const match = contents.match(docblockRe); return match ? match[0].replace(ltrimRe, '') || '' : ''; } -function parse(docblock: string): {[key: string]: string} { +export function parse(docblock: string): {[key: string]: string} { + return parseWithComments(docblock).pragmas; +} + +export function parseWithComments( + docblock: string, +): {comments: string, pragmas: {[key: string]: string}} { + const line = detectNewline(docblock) || EOL; + docblock = docblock .replace(commentStartRe, '') .replace(commentEndRe, '') @@ -34,17 +46,62 @@ function parse(docblock: string): {[key: string]: string} { let prev = ''; while (prev !== docblock) { prev = docblock; - docblock = docblock.replace(multilineRe, '\n$1 $2\n'); + docblock = docblock.replace(multilineRe, `${line}$1 $2${line}`); } docblock = docblock.trim(); const result = Object.create(null); + const comments = docblock.replace(propertyRe, '').replace(lineStartRe, line); let match; while ((match = propertyRe.exec(docblock))) { result[match[1]] = match[2]; } - return result; + return {comments: comments.trim(), pragmas: result}; } -exports.extract = extract; -exports.parse = parse; +export function print({ + comments = '', + pragmas = {}, +}: { + comments?: string, + pragmas?: {[key: string]: string}, +}): string { + const line = detectNewline(comments) || EOL; + const head = '/**'; + const start = ' *'; + const tail = ' */'; + + const keys = Object.keys(pragmas); + + const printedObject = keys + .map(key => start + ' ' + printKeyValue(key, pragmas[key]) + line) + .join(''); + + if (!comments) { + if (keys.length === 0) { + return ''; + } + if (keys.length === 1) { + return `${head} ${printKeyValue(keys[0], pragmas[keys[0]])}${tail}`; + } + } + + const printedComments = + comments + .split(line) + .map(textLine => `${start} ${textLine}`) + .join(line) + line; + + return ( + head + + line + + (comments ? printedComments : '') + + (comments && keys.length ? start + line : '') + + printedObject + + tail + ); +} + +function printKeyValue(key, value) { + return `@${key} ${value}`.trim(); +} diff --git a/packages/jest-haste-map/src/worker.js b/packages/jest-haste-map/src/worker.js index 1b6edcf963a3..8e39d12cfa32 100644 --- a/packages/jest-haste-map/src/worker.js +++ b/packages/jest-haste-map/src/worker.js @@ -11,7 +11,7 @@ import type {SerializableError} from 'types/TestResult'; import type {HasteImpl, WorkerMessage, WorkerCallback} from './types'; import path from 'path'; -import docblock from 'jest-docblock'; +import * as docblock from 'jest-docblock'; import fs from 'graceful-fs'; import H from './constants'; import extractRequires from './lib/extract_requires'; diff --git a/packages/jest-runner/src/run_test.js b/packages/jest-runner/src/run_test.js index 5bb9e878e023..eb0a50fcd7fe 100644 --- a/packages/jest-runner/src/run_test.js +++ b/packages/jest-runner/src/run_test.js @@ -24,7 +24,7 @@ import { } from 'jest-util'; import jasmine2 from 'jest-jasmine2'; import {getTestEnvironment} from 'jest-config'; -import docblock from 'jest-docblock'; +import * as docblock from 'jest-docblock'; // The default jest-runner is required because it is the default test runner // and required implicitly through the `testRunner` ProjectConfig option. diff --git a/yarn.lock b/yarn.lock index f37aaa12e644..59fcae6047ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1971,6 +1971,10 @@ detect-indent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" +detect-newline@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" + detective@^4.0.0: version "4.5.0" resolved "https://registry.yarnpkg.com/detective/-/detective-4.5.0.tgz#6e5a8c6b26e6c7a254b1c6b6d7490d98ec91edd1" From 1fcffab0b86e16e84ce66d2321e03a4744268f74 Mon Sep 17 00:00:00 2001 From: Vlad Zhukov Date: Thu, 28 Sep 2017 17:13:25 +0300 Subject: [PATCH 03/16] jest-runtime: move babel-core to peer dependenies (#4557) --- packages/jest-runtime/package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index 01a5d47218ce..5f9e653132f3 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -8,7 +8,6 @@ "license": "MIT", "main": "build/index.js", "dependencies": { - "babel-core": "^6.0.0", "babel-jest": "^21.2.0", "babel-plugin-istanbul": "^4.0.0", "chalk": "^2.0.1", @@ -27,9 +26,13 @@ "yargs": "^9.0.0" }, "devDependencies": { + "babel-core": "^6.0.0", "jest-environment-jsdom": "^21.2.1", "jest-environment-node": "^21.2.1" }, + "peerDependencies": { + "babel-core": "^6.0.0 || ^7.0.0-alpha || ^7.0.0-beta || ^7.0.0" + }, "bin": { "jest-runtime": "./bin/jest-runtime.js" } From 1e3ee68e1dbdefc456f9b8f59e7d960c73a7fd64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Bene=C5=A1?= Date: Thu, 28 Sep 2017 16:13:48 +0200 Subject: [PATCH 04/16] docs: mention about optional `done` argument in test fn (#4556) * docs(testAPI): mention about optional `done` argument I've run into an issue that my test hangs because I've defined the parameter for the test callback. Could be handy to mention that. * Update GlobalAPI.md --- docs/en/GlobalAPI.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/en/GlobalAPI.md b/docs/en/GlobalAPI.md index 8e9128735a6e..7fa2785f45b9 100644 --- a/docs/en/GlobalAPI.md +++ b/docs/en/GlobalAPI.md @@ -288,7 +288,8 @@ test('did not rain', () => { The first argument is the test name; the second argument is a function that contains the expectations to test. -If a promise is returned from `test`, Jest will wait for the promise to resolve before letting the test complete. +> Note: If a **promise is returned** from `test`, Jest will wait for the promise to resolve before letting the test complete. +Jest will also wait if you **provide an argument to the test function**, usually called `done`. This could be handy when you want to test callbacks. See how to test async code [here](http://facebook.github.io/jest/docs/en/asynchronous.html#callbacks). For example, let's say `fetchBeverageList()` returns a promise that is supposed to resolve to a list that has `lemon` in it. You can test this with: From 9af7bef8b8e66e89c5bba75d1ec96075fd4c3dd9 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Thu, 28 Sep 2017 11:34:19 -0400 Subject: [PATCH 05/16] Highlight only last of odd length leading spaces in jest-diff (#4558) --- .../__tests__/__snapshots__/diff.test.js.snap | 15 ++++++++ packages/jest-diff/src/__tests__/diff.test.js | 34 +++++++++++++++++++ packages/jest-diff/src/diff_strings.js | 7 +++- 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/packages/jest-diff/src/__tests__/__snapshots__/diff.test.js.snap b/packages/jest-diff/src/__tests__/__snapshots__/diff.test.js.snap index ad01ffbebaa7..d26f62585094 100644 --- a/packages/jest-diff/src/__tests__/__snapshots__/diff.test.js.snap +++ b/packages/jest-diff/src/__tests__/__snapshots__/diff.test.js.snap @@ -183,4 +183,19 @@ exports[`falls back to not call toJSON if objects look identical 1`] = ` }" `; +exports[`highlight only the last in odd length of leading spaces (expanded) 1`] = ` +"- Expected ++ Received + +
+-   attributes.reduce(function (props, attribute) {
+-    props[attribute.name] = attribute.value;
++   attributes.reduce((props, {name, value}) => {
++   props[name] = value;
+    return props;
+-  }, {});
++ }, {});
+  
" +`; + exports[`prints a fallback message if two objects truly look identical 1`] = `"Compared values have no visual difference."`; diff --git a/packages/jest-diff/src/__tests__/diff.test.js b/packages/jest-diff/src/__tests__/diff.test.js index e33c4b32be33..32753279a37b 100644 --- a/packages/jest-diff/src/__tests__/diff.test.js +++ b/packages/jest-diff/src/__tests__/diff.test.js @@ -761,6 +761,40 @@ describe('background color of spaces', () => { }); }); +describe('highlight only the last in odd length of leading spaces', () => { + const pre5 = { + $$typeof: elementSymbol, + props: { + children: [ + 'attributes.reduce(function (props, attribute) {', + ' props[attribute.name] = attribute.value;', // 3 leading spaces + ' return props;', // 2 leading spaces + ' }, {});', // 1 leading space + ].join('\n'), + }, + type: 'pre', + }; + const pre6 = { + $$typeof: elementSymbol, + props: { + children: [ + 'attributes.reduce((props, {name, value}) => {', + ' props[name] = value;', // from 3 to 2 leading spaces + ' return props;', // unchanged 2 leading spaces + '}, {});', // from 1 to 0 leading spaces + ].join('\n'), + }, + type: 'pre', + }; + const received = diff(pre5, pre6, expanded); + test('(expanded)', () => { + expect(received).toMatchSnapshot(); + }); + test('(unexpanded)', () => { + expect(diff(pre5, pre6, unexpanded)).toBe(received); + }); +}); + test('collapses big diffs to patch format', () => { const result = diff( {test: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}, diff --git a/packages/jest-diff/src/diff_strings.js b/packages/jest-diff/src/diff_strings.js index e10574117086..1afc95ecbbb4 100644 --- a/packages/jest-diff/src/diff_strings.js +++ b/packages/jest-diff/src/diff_strings.js @@ -76,7 +76,12 @@ const highlightLeadingTrailingSpaces = ( line: string, bgColor: Function, ): string => - highlightTrailingSpaces(line.replace(/^\s+/, bgColor('$&')), bgColor); + // If line consists of ALL spaces: highlight all of them. + highlightTrailingSpaces(line, bgColor).replace( + // If line has an ODD length of leading spaces: highlight only the LAST. + /^(\s\s)*(\s)(?=[^\s])/, + '$1' + bgColor('$2'), + ); const getAnnotation = (options: ?DiffOptions): string => chalk.green('- ' + ((options && options.aAnnotation) || 'Expected')) + From e81f1fb77f412816da221862d77b337f695ecef5 Mon Sep 17 00:00:00 2001 From: Vincent Voyer Date: Sat, 30 Sep 2017 20:15:06 +0200 Subject: [PATCH 06/16] feat(jest-editor-snapshot): Add Snapshots metadata (#4570) * first iteration * Use babel-traverse * comments on the pr * feat(jest-editor-support): Add Snapshot metadata Following #2629, I rebased the code on current master. I still have to actually test the feature with orta/vscode-jest#73. What i would like to add ultimately is the ability to: - preview the snapshot - have the snapshot diff - have a button to accept diff per snapshot (would update it) WDYT? --- .eslintrc.js | 2 + packages/jest-editor-support/package.json | 4 +- packages/jest-editor-support/src/Snapshot.js | 167 +++++++++++++++ .../src/__tests__/Snapshot-test.js | 128 ++++++++++++ .../__snapshots__/describe.example.snap | 91 ++++++++ .../__snapshots__/nested.example.snap | 3 + .../__snapshots__/nodescribe.example.snap | 19 ++ .../fixtures/snapshots/describe.example | 194 ++++++++++++++++++ .../fixtures/snapshots/nested.example | 10 + .../fixtures/snapshots/nodescribe.example | 36 ++++ packages/jest-editor-support/src/index.js | 2 + .../src/parsers/babylon_parser.js | 12 +- packages/jest-snapshot/src/index.js | 9 +- yarn.lock | 2 +- 14 files changed, 669 insertions(+), 10 deletions(-) create mode 100644 packages/jest-editor-support/src/Snapshot.js create mode 100644 packages/jest-editor-support/src/__tests__/Snapshot-test.js create mode 100644 packages/jest-editor-support/src/__tests__/fixtures/snapshots/__snapshots__/describe.example.snap create mode 100644 packages/jest-editor-support/src/__tests__/fixtures/snapshots/__snapshots__/nested.example.snap create mode 100644 packages/jest-editor-support/src/__tests__/fixtures/snapshots/__snapshots__/nodescribe.example.snap create mode 100644 packages/jest-editor-support/src/__tests__/fixtures/snapshots/describe.example create mode 100644 packages/jest-editor-support/src/__tests__/fixtures/snapshots/nested.example create mode 100644 packages/jest-editor-support/src/__tests__/fixtures/snapshots/nodescribe.example diff --git a/.eslintrc.js b/.eslintrc.js index 053fc383039e..fc737bf0a8f2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -63,6 +63,8 @@ module.exports = { 'packages/jest-editor-support/src/Process.js', 'packages/jest-editor-support/src/Runner.js', 'packages/jest-editor-support/src/Settings.js', + 'packages/jest-editor-support/src/Snapshot.js', + 'packages/jest-editor-support/src/__tests__/Snapshot-test.js', 'packages/jest-jasmine2/src/jasmine/Env.js', 'packages/jest-jasmine2/src/jasmine/Spec.js', 'packages/jest-jasmine2/src/jasmine/Suite.js', diff --git a/packages/jest-editor-support/package.json b/packages/jest-editor-support/package.json index 3af7167b2330..79415dfbdd73 100644 --- a/packages/jest-editor-support/package.json +++ b/packages/jest-editor-support/package.json @@ -8,7 +8,9 @@ "license": "MIT", "main": "build/index.js", "dependencies": { - "babylon": "^6.14.1" + "babylon": "^6.14.1", + "babel-traverse": "^6.14.1", + "jest-snapshot": "^21.1.0" }, "typings": "index.d.ts" } diff --git a/packages/jest-editor-support/src/Snapshot.js b/packages/jest-editor-support/src/Snapshot.js new file mode 100644 index 000000000000..8b445a88a028 --- /dev/null +++ b/packages/jest-editor-support/src/Snapshot.js @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + */ + +'use strict'; + +import traverse from 'babel-traverse'; +import {getASTfor} from './parsers/babylon_parser'; +import {utils} from 'jest-snapshot'; + +type Node = any; + +type SnapshotMetadata = { + exists: true | false, + name: string, + node: Node, + content?: string, +}; + +const describeVariants = Object.assign( + (Object.create(null): {[string]: boolean}), + { + describe: true, + fdescribe: true, + xdescribe: true, + }, +); +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, + 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 isDescribe = node => + describeVariants[node.name] || + (isValidMemberExpression(node) && node.object.name === 'describe'); + +const isValidParent = parent => + parent.callee && + (validParents[parent.callee.name] || isValidMemberExpression(parent.callee)); + +const getArrayOfParents = path => { + const result = []; + let parent = path.parentPath; + while (parent) { + result.unshift(parent.node); + parent = parent.parentPath; + } + return result; +}; + +const buildName: ( + snapshotNode: Node, + parents: Array, + position: number, +) => 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. + describeLess = 'test '; + } + + return utils.testNameToKey(describeLess + fullName, position); +}; + +export default class Snapshot { + _parser: Function; + _matchers: Array; + constructor(parser: any, customMatchers?: Array) { + this._parser = parser || getASTfor; + this._matchers = ['toMatchSnapshot', 'toThrowErrorMatchingSnapshot'].concat( + customMatchers || [], + ); + } + + getMetadata(filePath: string): Array { + const fileNode = this._parser(filePath); + const state = { + found: [], + }; + const Visitors = { + Identifier(path, state, matchers) { + if (matchers.includes(path.node.name)) { + state.found.push({ + node: path.node, + parents: getArrayOfParents(path), + }); + } + }, + }; + + traverse(fileNode, { + enter: path => { + const visitor = Visitors[path.node.type]; + if (visitor != null) { + visitor(path, state, this._matchers); + } + }, + }); + + const snapshotPath = utils.getSnapshotPath(filePath); + const snapshots = utils.getSnapshotData(snapshotPath, 'none').data; + let lastParent = null; + let count = 1; + + return state.found.map((snapshotNode, index) => { + const parents = snapshotNode.parents.filter(isValidParent); + const innerAssertion = parents[parents.length - 1]; + + if (lastParent !== innerAssertion) { + lastParent = innerAssertion; + count = 1; + } + + const result = { + content: undefined, + count: count++, + exists: false, + name: '', + node: snapshotNode.node, + }; + + if (!innerAssertion || isDescribe(innerAssertion.callee)) { + // An expectation inside describe never gets executed. + return result; + } + + result.name = buildName(snapshotNode, parents, result.count); + + if (snapshots[result.name]) { + result.exists = true; + result.content = snapshots[result.name]; + } + return result; + }); + } +} diff --git a/packages/jest-editor-support/src/__tests__/Snapshot-test.js b/packages/jest-editor-support/src/__tests__/Snapshot-test.js new file mode 100644 index 000000000000..ca076a7ae800 --- /dev/null +++ b/packages/jest-editor-support/src/__tests__/Snapshot-test.js @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + +const path = require('path'); +const Snapshot = require('../Snapshot'); +const snapshotHelper = new Snapshot(); +const snapshotFixturePath = path.resolve(__dirname, 'fixtures/snapshots'); + +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', + ]; + + const expectations = Object.create(null); + allAssertion.forEach(assertion => { + expectations['test ' + assertion + ' 1'] = { + assertion, + checked: false, + number: 1, + }; + expectations['test ' + assertion + ' 2'] = { + assertion, + checked: false, + number: 2, + }; + }); + + results.forEach(result => { + const check = expectations[result.name]; + check.checked = result.content === `${check.assertion} ${check.number}`; + }); + + expect( + 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', + ]; + const allAssertion = [ + '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, + describe, + number: 1, + }; + + expectations[describe.toUpperCase() + ' ' + assertion + ' 2'] = { + assertion, + checked: false, + describe, + number: 2, + }; + }); + }); + + results.forEach(result => { + const check = expectations[result.name]; + check.checked = + result.content === `${check.number} ${check.assertion} ${check.describe}`; + }); + expect( + Object.keys(expectations) + .map(key => expectations[key]) + .filter(expectation => !expectation.checked).length, + ).toBe(0); +}); + +test('nested.example', () => { + const filePath = path.join(snapshotFixturePath, 'nested.example'); + const results = snapshotHelper.getMetadata(filePath); + expect(results[0].content).toBe('first nested'); + expect(results[1].content).toBe('second nested'); + + expect(results[0].name).toBe( + 'outer describe outer it inner describe inner it 1', + ); + expect(results[1].name).toBe( + '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}); +}); diff --git a/packages/jest-editor-support/src/__tests__/fixtures/snapshots/__snapshots__/describe.example.snap b/packages/jest-editor-support/src/__tests__/fixtures/snapshots/__snapshots__/describe.example.snap new file mode 100644 index 000000000000..d60e6dc0998d --- /dev/null +++ b/packages/jest-editor-support/src/__tests__/fixtures/snapshots/__snapshots__/describe.example.snap @@ -0,0 +1,91 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`DESCRIBE fit 1`] = `1 fit describe`; +exports[`DESCRIBE fit 2`] = `2 fit describe`; +exports[`DESCRIBE it 1`] = `1 it describe`; +exports[`DESCRIBE it 2`] = `2 it describe`; +exports[`DESCRIBE it.only 1`] = `1 it.only describe`; +exports[`DESCRIBE it.only 2`] = `2 it.only describe`; +exports[`DESCRIBE it.skip 1`] = `1 it.skip describe`; +exports[`DESCRIBE it.skip 2`] = `2 it.skip describe`; +exports[`DESCRIBE test 1`] = `1 test describe`; +exports[`DESCRIBE test 2`] = `2 test describe`; +exports[`DESCRIBE test.only 1`] = `1 test.only describe`; +exports[`DESCRIBE test.only 2`] = `2 test.only describe`; +exports[`DESCRIBE test.skip 1`] = `1 test.skip describe`; +exports[`DESCRIBE test.skip 2`] = `2 test.skip describe`; +exports[`DESCRIBE xit 1`] = `1 xit describe`; +exports[`DESCRIBE xit 2`] = `2 xit describe`; +exports[`DESCRIBE xtest 1`] = `1 xtest describe`; +exports[`DESCRIBE xtest 2`] = `2 xtest describe`; +exports[`DESCRIBE.ONLY fit 1`] = `1 fit describe.only`; +exports[`DESCRIBE.ONLY fit 2`] = `2 fit describe.only`; +exports[`DESCRIBE.ONLY it 1`] = `1 it describe.only`; +exports[`DESCRIBE.ONLY it 2`] = `2 it describe.only`; +exports[`DESCRIBE.ONLY it.only 1`] = `1 it.only describe.only`; +exports[`DESCRIBE.ONLY it.only 2`] = `2 it.only describe.only`; +exports[`DESCRIBE.ONLY it.skip 1`] = `1 it.skip describe.only`; +exports[`DESCRIBE.ONLY it.skip 2`] = `2 it.skip describe.only`; +exports[`DESCRIBE.ONLY test 1`] = `1 test describe.only`; +exports[`DESCRIBE.ONLY test 2`] = `2 test describe.only`; +exports[`DESCRIBE.ONLY test.only 1`] = `1 test.only describe.only`; +exports[`DESCRIBE.ONLY test.only 2`] = `2 test.only describe.only`; +exports[`DESCRIBE.ONLY test.skip 1`] = `1 test.skip describe.only`; +exports[`DESCRIBE.ONLY test.skip 2`] = `2 test.skip describe.only`; +exports[`DESCRIBE.ONLY xit 1`] = `1 xit describe.only`; +exports[`DESCRIBE.ONLY xit 2`] = `2 xit describe.only`; +exports[`DESCRIBE.ONLY xtest 1`] = `1 xtest describe.only`; +exports[`DESCRIBE.ONLY xtest 2`] = `2 xtest describe.only`; +exports[`DESCRIBE.SKIP fit 1`] = `1 fit describe.skip`; +exports[`DESCRIBE.SKIP fit 2`] = `2 fit describe.skip`; +exports[`DESCRIBE.SKIP it 1`] = `1 it describe.skip`; +exports[`DESCRIBE.SKIP it 2`] = `2 it describe.skip`; +exports[`DESCRIBE.SKIP it.only 1`] = `1 it.only describe.skip`; +exports[`DESCRIBE.SKIP it.only 2`] = `2 it.only describe.skip`; +exports[`DESCRIBE.SKIP it.skip 1`] = `1 it.skip describe.skip`; +exports[`DESCRIBE.SKIP it.skip 2`] = `2 it.skip describe.skip`; +exports[`DESCRIBE.SKIP test 1`] = `1 test describe.skip`; +exports[`DESCRIBE.SKIP test 2`] = `2 test describe.skip`; +exports[`DESCRIBE.SKIP test.only 1`] = `1 test.only describe.skip`; +exports[`DESCRIBE.SKIP test.only 2`] = `2 test.only describe.skip`; +exports[`DESCRIBE.SKIP test.skip 1`] = `1 test.skip describe.skip`; +exports[`DESCRIBE.SKIP test.skip 2`] = `2 test.skip describe.skip`; +exports[`DESCRIBE.SKIP xit 1`] = `1 xit describe.skip`; +exports[`DESCRIBE.SKIP xit 2`] = `2 xit describe.skip`; +exports[`DESCRIBE.SKIP xtest 1`] = `1 xtest describe.skip`; +exports[`DESCRIBE.SKIP xtest 2`] = `2 xtest describe.skip`; +exports[`FDESCRIBE fit 1`] = `1 fit fdescribe`; +exports[`FDESCRIBE fit 2`] = `2 fit fdescribe`; +exports[`FDESCRIBE it 1`] = `1 it fdescribe`; +exports[`FDESCRIBE it 2`] = `2 it fdescribe`; +exports[`FDESCRIBE it.only 1`] = `1 it.only fdescribe`; +exports[`FDESCRIBE it.only 2`] = `2 it.only fdescribe`; +exports[`FDESCRIBE it.skip 1`] = `1 it.skip fdescribe`; +exports[`FDESCRIBE it.skip 2`] = `2 it.skip fdescribe`; +exports[`FDESCRIBE test 1`] = `1 test fdescribe`; +exports[`FDESCRIBE test 2`] = `2 test fdescribe`; +exports[`FDESCRIBE test.only 1`] = `1 test.only fdescribe`; +exports[`FDESCRIBE test.only 2`] = `2 test.only fdescribe`; +exports[`FDESCRIBE test.skip 1`] = `1 test.skip fdescribe`; +exports[`FDESCRIBE test.skip 2`] = `2 test.skip fdescribe`; +exports[`FDESCRIBE xit 1`] = `1 xit fdescribe`; +exports[`FDESCRIBE xit 2`] = `2 xit fdescribe`; +exports[`FDESCRIBE xtest 1`] = `1 xtest fdescribe`; +exports[`FDESCRIBE xtest 2`] = `2 xtest fdescribe`; +exports[`XDESCRIBE fit 1`] = `1 fit xdescribe`; +exports[`XDESCRIBE fit 2`] = `2 fit xdescribe`; +exports[`XDESCRIBE it 1`] = `1 it xdescribe`; +exports[`XDESCRIBE it 2`] = `2 it xdescribe`; +exports[`XDESCRIBE it.only 1`] = `1 it.only xdescribe`; +exports[`XDESCRIBE it.only 2`] = `2 it.only xdescribe`; +exports[`XDESCRIBE it.skip 1`] = `1 it.skip xdescribe`; +exports[`XDESCRIBE it.skip 2`] = `2 it.skip xdescribe`; +exports[`XDESCRIBE test 1`] = `1 test xdescribe`; +exports[`XDESCRIBE test 2`] = `2 test xdescribe`; +exports[`XDESCRIBE test.only 1`] = `1 test.only xdescribe`; +exports[`XDESCRIBE test.only 2`] = `2 test.only xdescribe`; +exports[`XDESCRIBE test.skip 1`] = `1 test.skip xdescribe`; +exports[`XDESCRIBE test.skip 2`] = `2 test.skip xdescribe`; +exports[`XDESCRIBE xit 1`] = `1 xit xdescribe`; +exports[`XDESCRIBE xit 2`] = `2 xit xdescribe`; +exports[`XDESCRIBE xtest 1`] = `1 xtest xdescribe`; +exports[`XDESCRIBE xtest 2`] = `2 xtest xdescribe`; diff --git a/packages/jest-editor-support/src/__tests__/fixtures/snapshots/__snapshots__/nested.example.snap b/packages/jest-editor-support/src/__tests__/fixtures/snapshots/__snapshots__/nested.example.snap new file mode 100644 index 000000000000..ee8a6ffce3a2 --- /dev/null +++ b/packages/jest-editor-support/src/__tests__/fixtures/snapshots/__snapshots__/nested.example.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`outer describe outer it inner describe inner it 1`] = `first nested`; +exports[`outer describe outer it inner describe inner it 2`] = `second nested`; diff --git a/packages/jest-editor-support/src/__tests__/fixtures/snapshots/__snapshots__/nodescribe.example.snap b/packages/jest-editor-support/src/__tests__/fixtures/snapshots/__snapshots__/nodescribe.example.snap new file mode 100644 index 000000000000..36680c31ce0c --- /dev/null +++ b/packages/jest-editor-support/src/__tests__/fixtures/snapshots/__snapshots__/nodescribe.example.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`test fit 1`]=`fit 1`; +exports[`test fit 2`]=`fit 2`; +exports[`test it 1`]=`it 1`; +exports[`test it 2`]=`it 2`; +exports[`test it.only 1`]=`it.only 1`; +exports[`test it.only 2`]=`it.only 2`; +exports[`test it.skip 1`]=`it.skip 1`; +exports[`test it.skip 2`]=`it.skip 2`; +exports[`test test 1`]=`test 1`; +exports[`test test 2`]=`test 2`; +exports[`test test.only 1`]=`test.only 1`; +exports[`test test.only 2`]=`test.only 2`; +exports[`test test.skip 1`]=`test.skip 1`; +exports[`test test.skip 2`]=`test.skip 2`; +exports[`test xit 1`]=`xit 1`; +exports[`test xit 2`]=`xit 2`; +exports[`test xtest 1`]=`xtest 1`; +exports[`test xtest 2`]=`xtest 2`; diff --git a/packages/jest-editor-support/src/__tests__/fixtures/snapshots/describe.example b/packages/jest-editor-support/src/__tests__/fixtures/snapshots/describe.example new file mode 100644 index 000000000000..5380757ef2e8 --- /dev/null +++ b/packages/jest-editor-support/src/__tests__/fixtures/snapshots/describe.example @@ -0,0 +1,194 @@ +describe('DESCRIBE', () => { + fit('fit', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + it('it', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + it.only('it.only', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + it.skip('it.skip', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + test('test', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + test.only('test.only', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + test.skip('test.skip', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + xit('xit', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + xtest('xtest', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) +}) + +describe.only('DESCRIBE.ONLY', () => { + fit('fit', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + it('it', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + it.only('it.only', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + it.skip('it.skip', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + test('test', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + test.only('test.only', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + test.skip('test.skip', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + xit('xit', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + xtest('xtest', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) +}) + +describe.skip('DESCRIBE.SKIP', () => { + fit('fit', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + it('it', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + it.only('it.only', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + it.skip('it.skip', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + test('test', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + test.only('test.only', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + test.skip('test.skip', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + xit('xit', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + xtest('xtest', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) +}) + +fdescribe('FDESCRIBE', () => { + fit('fit', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + it('it', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + it.only('it.only', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + it.skip('it.skip', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + test('test', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + test.only('test.only', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + test.skip('test.skip', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + xit('xit', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + xtest('xtest', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) +}) + +xdescribe('XDESCRIBE', () => { + fit('fit', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + it('it', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + it.only('it.only', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + it.skip('it.skip', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + test('test', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + test.only('test.only', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + test.skip('test.skip', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + xit('xit', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + xtest('xtest', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) +}) diff --git a/packages/jest-editor-support/src/__tests__/fixtures/snapshots/nested.example b/packages/jest-editor-support/src/__tests__/fixtures/snapshots/nested.example new file mode 100644 index 000000000000..260bd5a556a4 --- /dev/null +++ b/packages/jest-editor-support/src/__tests__/fixtures/snapshots/nested.example @@ -0,0 +1,10 @@ +describe('outer describe', () => { + it('outer it', () => { + describe('inner describe', () => { + it('inner it', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); + }) + }) + }) +}) diff --git a/packages/jest-editor-support/src/__tests__/fixtures/snapshots/nodescribe.example b/packages/jest-editor-support/src/__tests__/fixtures/snapshots/nodescribe.example new file mode 100644 index 000000000000..90634fdc7560 --- /dev/null +++ b/packages/jest-editor-support/src/__tests__/fixtures/snapshots/nodescribe.example @@ -0,0 +1,36 @@ +fit('fit', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); +}) +it('it', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); +}) +it.only('it.only', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); +}) +it.skip('it.skip', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); +}) +test('test', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); +}) +test.only('test.only', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); +}) +test.skip('test.skip', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); +}) +xit('xit', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); +}) +xtest('xtest', () => { + expect(fake).toMatchSnapshot(); + expect(fake).toMatchSnapshot(); +}) diff --git a/packages/jest-editor-support/src/index.js b/packages/jest-editor-support/src/index.js index db2b32dd2c33..146f57f90b6e 100644 --- a/packages/jest-editor-support/src/index.js +++ b/packages/jest-editor-support/src/index.js @@ -12,6 +12,7 @@ import * as Process from './Process'; import ProjectWorkspace from './project_workspace'; import Runner from './Runner'; import Settings from './Settings'; +import Snapshot from './Snapshot'; import {Expect, ItBlock, Node} from './parsers/parser_nodes'; import {parse} from './parsers/babylon_parser'; import TestReconciler from './test_reconciler'; @@ -24,6 +25,7 @@ module.exports = { ProjectWorkspace, Runner, Settings, + Snapshot, TestReconciler, parse, }; diff --git a/packages/jest-editor-support/src/parsers/babylon_parser.js b/packages/jest-editor-support/src/parsers/babylon_parser.js index 314d9b28335b..9e3c38fe982d 100644 --- a/packages/jest-editor-support/src/parsers/babylon_parser.js +++ b/packages/jest-editor-support/src/parsers/babylon_parser.js @@ -11,20 +11,24 @@ import {readFileSync} from 'fs'; import {parse as babylonParse} from 'babylon'; import {Expect, ItBlock} from './parser_nodes'; +import type {File as BabylonFile} from 'babylon'; export type BabylonParserResult = { expects: Array, itBlocks: Array, }; +export const getASTfor = (file: string): BabylonFile => { + 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 diff --git a/packages/jest-snapshot/src/index.js b/packages/jest-snapshot/src/index.js index 3d723f365128..bd59c992baa5 100644 --- a/packages/jest-snapshot/src/index.js +++ b/packages/jest-snapshot/src/index.js @@ -22,13 +22,13 @@ import { } from 'jest-matcher-utils'; import SnapshotState from './State'; import {addSerializer, getSerializers} from './plugins'; -import {SNAPSHOT_EXTENSION} from './utils'; +import * as utils from './utils'; const fileExists = (filePath: Path, hasteFS: HasteFS): boolean => hasteFS.exists(filePath) || fs.existsSync(filePath); const cleanup = (hasteFS: HasteFS, update: SnapshotUpdateState) => { - const pattern = '\\.' + SNAPSHOT_EXTENSION + '$'; + const pattern = '\\.' + utils.SNAPSHOT_EXTENSION + '$'; const files = hasteFS.matchFiles(pattern); const filesRemoved = files .filter( @@ -37,7 +37,7 @@ const cleanup = (hasteFS: HasteFS, update: SnapshotUpdateState) => { path.resolve( path.dirname(snapshotFile), '..', - path.basename(snapshotFile, '.' + SNAPSHOT_EXTENSION), + path.basename(snapshotFile, '.' + utils.SNAPSHOT_EXTENSION), ), hasteFS, ), @@ -148,11 +148,12 @@ const toThrowErrorMatchingSnapshot = function(received: any, expected: void) { }; module.exports = { - EXTENSION: SNAPSHOT_EXTENSION, + EXTENSION: utils.SNAPSHOT_EXTENSION, SnapshotState, addSerializer, cleanup, getSerializers, toMatchSnapshot, toThrowErrorMatchingSnapshot, + utils, }; diff --git a/yarn.lock b/yarn.lock index 59fcae6047ef..1f68c5565c1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -908,7 +908,7 @@ babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0: babylon "^6.18.0" lodash "^4.17.4" -babel-traverse@^6.18.0, babel-traverse@^6.23.1, babel-traverse@^6.24.1, babel-traverse@^6.26.0: +babel-traverse@^6.14.1, babel-traverse@^6.18.0, babel-traverse@^6.23.1, babel-traverse@^6.24.1, babel-traverse@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" dependencies: From cfe1a9a84b93ca44523c01379162b547717abf77 Mon Sep 17 00:00:00 2001 From: Vincent Voyer Date: Sat, 30 Sep 2017 20:17:26 +0200 Subject: [PATCH 07/16] chore(vscode): ease flow usage (#4569) * chore(vscode): ease flow usage Following https://github.com/flowtype/flow-for-vscode#setup they recommend setting `"javascript.validate.enable": false` in workspace settings, otherwise I get errors like "... can only be used in a .ts file". As I am new to flow and vscode I might be wrong, let me know vscode users contributing to Jest how it works for you today? * Update settings.json --- .vscode/settings.json | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f25eef6ad02f..d0188f92d5cb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,16 +1,18 @@ { - "jest.pathToJest": "yarn jest --", "editor.rulers": [80], "files.exclude": { "**/.git": true, "**/node_modules": true, "**/build": true }, + "editor.formatOnSave": true, + "flow.useNPMPackagedFlow": true, + "javascript.validate.enable": false, + "jest.pathToJest": "yarn jest --", + "prettier.eslintIntegration": true, "prettier.parser": "flow", "prettier.printWidth": 80, - "prettier.singleQuote": true, - "prettier.trailingComma": "all", "prettier.semi": true, - "editor.formatOnSave": true, - "prettier.eslintIntegration": true + "prettier.singleQuote": true, + "prettier.trailingComma": "all" } From 216e8edbe8ca60b34688d03e8ef3cb7262104b51 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sat, 30 Sep 2017 22:39:23 +0200 Subject: [PATCH 08/16] Add simple rAF polyfill in jsdom environment (#4568) * Add simple rAF polyfill in jsdom environment Closes #4545 * Fix flow error * Tweak test * Try to log out stderr on CI * Use snake case naming for test file * Update to newest yarn on ci * Revert "Try to log out stderr on CI" This reverts commit 08d58c54824e0f3b4b13c8354e13a2f27aac5be7. * Remove extra -- from appveyor to avoid warning on newer yarn * Include time since window initialised in rAF implementation --- appveyor.yml | 4 ++-- .../__tests__/request_animation_frame.test.js | 19 +++++++++++++++++ .../__tests__/request_animation_frame.test.js | 21 +++++++++++++++++++ .../request_animation_frame/package.json | 5 +++++ packages/jest-environment-jsdom/src/index.js | 11 ++++++++++ 5 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 integration_tests/__tests__/request_animation_frame.test.js create mode 100644 integration_tests/request_animation_frame/__tests__/request_animation_frame.test.js create mode 100644 integration_tests/request_animation_frame/package.json diff --git a/appveyor.yml b/appveyor.yml index 6c905cf67bab..7e1d847a2f78 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -10,7 +10,7 @@ init: install: - ps: Install-Product node $env:nodejs_version x64 - node --version - - curl -fsSL -o yarn.js https://github.com/yarnpkg/yarn/releases/download/v0.28.4/yarn-0.28.4.js + - curl -fsSL -o yarn.js https://github.com/yarnpkg/yarn/releases/download/v1.1.0/yarn-1.1.0.js - node ./yarn.js --version - node ./yarn.js install - node ./yarn.js run build @@ -20,7 +20,7 @@ cache: - .eslintcache test_script: - - node ./yarn.js run jest -- --color + - node ./yarn.js run jest --color # Don't actually build. build: off diff --git a/integration_tests/__tests__/request_animation_frame.test.js b/integration_tests/__tests__/request_animation_frame.test.js new file mode 100644 index 000000000000..383291d1f604 --- /dev/null +++ b/integration_tests/__tests__/request_animation_frame.test.js @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. 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. + * + * @flow + */ +'use strict'; + +const runJest = require('../runJest'); + +test('requestAnimationFrame', () => { + const result = runJest('request_animation_frame', ['--verbose']); + const stderr = result.stderr.toString(); + + expect(stderr).toMatch('requestAnimationFrame test'); + expect(result.status).toBe(0); +}); diff --git a/integration_tests/request_animation_frame/__tests__/request_animation_frame.test.js b/integration_tests/request_animation_frame/__tests__/request_animation_frame.test.js new file mode 100644 index 000000000000..8f1ff5cbfabf --- /dev/null +++ b/integration_tests/request_animation_frame/__tests__/request_animation_frame.test.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. 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. + */ + +/* eslint-env browser */ + +'use strict'; + +test('requestAnimationFrame test', done => { + expect.hasAssertions(); + + requestAnimationFrame(timestamp => { + expect(true).toBe(true); + expect(timestamp).toBeGreaterThan(0); + + done(); + }); +}); diff --git a/integration_tests/request_animation_frame/package.json b/integration_tests/request_animation_frame/package.json new file mode 100644 index 000000000000..0ded940b7cb7 --- /dev/null +++ b/integration_tests/request_animation_frame/package.json @@ -0,0 +1,5 @@ +{ + "jest": { + "testEnvironment": "jsdom" + } +} diff --git a/packages/jest-environment-jsdom/src/index.js b/packages/jest-environment-jsdom/src/index.js index 2f957d2e9ac9..f03b6543c92b 100644 --- a/packages/jest-environment-jsdom/src/index.js +++ b/packages/jest-environment-jsdom/src/index.js @@ -22,6 +22,7 @@ class JSDOMEnvironment { moduleMocker: ?ModuleMocker; constructor(config: ProjectConfig): void { + const jsdomInitialized = process.hrtime(); // lazy require this.document = JSDom.jsdom('', { url: config.testURL, @@ -32,6 +33,16 @@ class JSDOMEnvironment { this.global.Error.stackTraceLimit = 100; installCommonGlobals(global, config.globals); + if (!global.requestAnimationFrame) { + global.requestAnimationFrame = callback => { + const hr = process.hrtime(jsdomInitialized); + const hrInNano = hr[0] * 1e9 + hr[1]; + const hrInMicro = hrInNano / 1e6; + + return global.setTimeout(callback, 0, hrInMicro); + }; + } + this.moduleMocker = new mock.ModuleMocker(global); this.fakeTimers = new FakeTimers(global, this.moduleMocker, config); } From 4a3ab53155cd5e6fa3d044c20b4fcfa93a2e452c Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 30 Sep 2017 17:44:38 -0400 Subject: [PATCH 09/16] [jest-docblock] add strip (#4571) * [jest-docblock] add strip * add a couple tests * off by one error * improve code as per @cpojers suggestion --- packages/jest-docblock/README.md | 10 ++++++++-- packages/jest-docblock/src/__tests__/index.test.js | 10 ++++++++++ packages/jest-docblock/src/index.js | 5 +++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/jest-docblock/README.md b/packages/jest-docblock/README.md index 85a740b8e6aa..4ffee198f817 100644 --- a/packages/jest-docblock/README.md +++ b/packages/jest-docblock/README.md @@ -57,11 +57,14 @@ const code = ` } `; -const { extract, parse, parseWithComments, print } = require("jest-docblock"); +const { extract, strip, parse, parseWithComments, print } = require("jest-docblock"); const docblock = extract(code); console.log(docblock); // "/**\n * Everything is awesome!\n * \n * @everything is:awesome\n * @flow\n */" +const stripped = strip(code); +console.log(stripped); // "export const everything = Object.create(null);\n export default function isAwesome(something) {\n return something === everything;\n }" + const pragmas = parse(docblock); console.log(pragmas); // { everything: "is:awesome", flow: "" } @@ -76,6 +79,9 @@ console.log(print({pragmas, comments: "hi!"})) // /**\n * hi!\n *\n * @everythin ### `extract(contents: string): string` Extracts a docblock from some file contents. Returns the docblock contained in `contents`. If `contents` did not contain a docblock, it will return the empty string (`""`). +### `strip(contents: string): string` +Strips the top docblock from a file and return the result. If a file does not have a docblock at the top, then return the file unchanged. + ### `parse(docblock: string): {[key: string]: string}` Parses the pragmas in a docblock string into an object whose keys are the pragma tags and whose values are the arguments to those pragmas. @@ -83,4 +89,4 @@ Parses the pragmas in a docblock string into an object whose keys are the pragma Similar to `parse` except this method also returns the comments from the docblock. Useful when used with `print()`. ### `print({ comments?: string, pragmas?: {[key: string]: string} }): string` -Prints an object of key-value pairs back into a docblock. If `comments` are provided, they will be positioned on the top of the docblock. \ No newline at end of file +Prints an object of key-value pairs back into a docblock. If `comments` are provided, they will be positioned on the top of the docblock. diff --git a/packages/jest-docblock/src/__tests__/index.test.js b/packages/jest-docblock/src/__tests__/index.test.js index b2097b3ba484..579560a3de63 100644 --- a/packages/jest-docblock/src/__tests__/index.test.js +++ b/packages/jest-docblock/src/__tests__/index.test.js @@ -280,6 +280,16 @@ describe('docblock', () => { }); }); + it('strips the docblock out of a file that contains a top docblock', () => { + const code = '/**\n * foo\n * bar\n*/\nthe rest'; + expect(docblock.strip(code)).toEqual('\nthe rest'); + }); + + it('returns a file unchanged if there is no top docblock to strip', () => { + const code = 'someCodeAtTheTop();\n/** docblock */'; + expect(docblock.strip(code)).toEqual(code); + }); + it('prints docblocks with no pragmas as empty string', () => { const pragmas = {}; expect(docblock.print({pragmas})).toEqual(''); diff --git a/packages/jest-docblock/src/index.js b/packages/jest-docblock/src/index.js index 7281a6941f77..7eddad6f94b7 100644 --- a/packages/jest-docblock/src/index.js +++ b/packages/jest-docblock/src/index.js @@ -26,6 +26,11 @@ export function extract(contents: string): string { return match ? match[0].replace(ltrimRe, '') || '' : ''; } +export function strip(contents: string) { + const match = contents.match(docblockRe); + return match && match[0] ? contents.substring(match[0].length) : contents; +} + export function parse(docblock: string): {[key: string]: string} { return parseWithComments(docblock).pragmas; } From ec87e1f722b7c169bcad8c77bad270ff457a03ca Mon Sep 17 00:00:00 2001 From: Orta Date: Sat, 30 Sep 2017 19:00:04 -0400 Subject: [PATCH 10/16] Update CODEOWNERS (#4573) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a59916191d85..00d138b99214 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,3 @@ # Pings orta when PRs are to this module -packages/jest-editor-support/* @orta +packages/jest-editor-support/src/* @orta From c89ee8c5226e1fbd39a9f58a83f16425e595898d Mon Sep 17 00:00:00 2001 From: ConnectDotz Date: Sun, 1 Oct 2017 05:12:54 -0400 Subject: [PATCH 11/16] Vscode jest monorepo support (#4572) * added watch mode and test_reconciler changes + upgrade typescript version * upgrade typescript (jest-test-typescript-parser) * clean up --- fixtures/failing_jsons/monorepo_root_1.json | 474 ++++++++++++++++++ fixtures/failing_jsons/monorepo_root_2.json | 246 +++++++++ packages/jest-editor-support/index.d.ts | 29 +- packages/jest-editor-support/src/Runner.js | 15 +- .../src/__tests__/test_reconciler.test.js | 118 ++++- .../src/test_reconciler.js | 47 +- .../jest-test-typescript-parser/package.json | 2 +- yarn.lock | 6 +- 8 files changed, 874 insertions(+), 63 deletions(-) create mode 100644 fixtures/failing_jsons/monorepo_root_1.json create mode 100644 fixtures/failing_jsons/monorepo_root_2.json diff --git a/fixtures/failing_jsons/monorepo_root_1.json b/fixtures/failing_jsons/monorepo_root_1.json new file mode 100644 index 000000000000..bb381772295f --- /dev/null +++ b/fixtures/failing_jsons/monorepo_root_1.json @@ -0,0 +1,474 @@ +{ + "numFailedTestSuites": 4, + "numFailedTests": 2, + "numPassedTestSuites": 4, + "numPassedTests": 46, + "numPendingTestSuites": 0, + "numPendingTests": 0, + "numRuntimeErrorTestSuites": 2, + "numTotalTestSuites": 8, + "numTotalTests": 48, + "snapshot": { + "added": 0, + "didUpdate": false, + "failure": false, + "filesAdded": 0, + "filesRemoved": 0, + "filesUnmatched": 0, + "filesUpdated": 0, + "matched": 0, + "total": 0, + "unchecked": 0, + "unmatched": 0, + "updated": 0 + }, + "startTime": 1506696062972, + "success": false, + "testResults": [ + { + "assertionResults": [ + { + "ancestorTitles": ["testing EthNumber..."], + "failureMessages": [], + "fullName": + "testing EthNumber... can take various format bigNumber can take", + "status": "passed", + "title": "can take various format bigNumber can take" + }, + { + "ancestorTitles": ["testing EthNumber..."], + "failureMessages": [ + "Error: Failed: that's right\n at stackFormatter (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/expectation_result_factory.js:49:427)\n at expectationResultFactory (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/expectation_result_factory.js:49:591)\n at Spec.Object..Spec.addExpectationResult (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine/Spec.js:73:70)\n at Env.fail (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine/Env.js:510:25)\n at fail (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine/jasmine_light.js:116:23)\n at Object. (/Z/Y/packages/Y-core/src/eth/__tests__/types.test.ts:19:9)\n at Object.asyncFn (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine_async.js:124:345)\n at resolve (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/queue_runner.js:46:12)\n at Promise ()\n at mapper (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/queue_runner.js:34:499)" + ], + "fullName": "testing EthNumber... should fail", + "status": "failed", + "title": "should fail" + }, + { + "ancestorTitles": ["testing EthNumber..."], + "failureMessages": [], + "fullName": + "testing EthNumber... can handle empty and null as expected", + "status": "passed", + "title": "can handle empty and null as expected" + }, + { + "ancestorTitles": ["testing EthNumber..."], + "failureMessages": [], + "fullName": "testing EthNumber... should prevent toNumber for money", + "status": "passed", + "title": "should prevent toNumber for money" + }, + { + "ancestorTitles": ["testing EthNumber..."], + "failureMessages": [], + "fullName": "testing EthNumber... can understand unit", + "status": "passed", + "title": "can understand unit" + }, + { + "ancestorTitles": ["testing EthNumber..."], + "failureMessages": [], + "fullName": "testing EthNumber... can compare across unit", + "status": "passed", + "title": "can compare across unit" + }, + { + "ancestorTitles": ["testing EthNumber..."], + "failureMessages": [], + "fullName": "testing EthNumber... can operate across unit", + "status": "passed", + "title": "can operate across unit" + }, + { + "ancestorTitles": ["testing Address"], + "failureMessages": [], + "fullName": "testing Address can prevent invalid address", + "status": "passed", + "title": "can prevent invalid address" + }, + { + "ancestorTitles": ["testing Address"], + "failureMessages": [], + "fullName": + "testing Address can accept valid address with or without prefix", + "status": "passed", + "title": "can accept valid address with or without prefix" + }, + { + "ancestorTitles": ["testing Address"], + "failureMessages": [], + "fullName": "testing Address can detect if address is empty", + "status": "passed", + "title": "can detect if address is empty" + } + ], + "endTime": 1506696066220, + "message": + " ● testing EthNumber... › should fail\n\n Failed: that's right\n \n at stackFormatter (node_modules/jest/node_modules/jest-jasmine2/build/expectation_result_factory.js:49:427)\n at Object. (packages/Y-core/src/eth/__tests__/types.test.ts:19:9)\n at Promise ()\n", + "name": "/X/packages/Y-core/src/eth/__tests__/types.test.ts", + "startTime": 1506696063929, + "status": "failed", + "summary": "" + }, + { + "assertionResults": [ + { + "ancestorTitles": ["curry"], + "failureMessages": [], + "fullName": "curry simple curry", + "status": "passed", + "title": "simple curry" + }, + { + "ancestorTitles": ["curry"], + "failureMessages": [], + "fullName": "curry can parse middleware", + "status": "passed", + "title": "can parse middleware" + }, + { + "ancestorTitles": ["curry"], + "failureMessages": [], + "fullName": "curry can be used many times", + "status": "passed", + "title": "can be used many times" + }, + { + "ancestorTitles": ["curry"], + "failureMessages": [], + "fullName": "curry can be used with deferred functions many times", + "status": "passed", + "title": "can be used with deferred functions many times" + }, + { + "ancestorTitles": ["curry"], + "failureMessages": [ + "Error: Failed: intentionally failed\n at stackFormatter (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/expectation_result_factory.js:49:427)\n at expectationResultFactory (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/expectation_result_factory.js:49:591)\n at Spec.Object..Spec.addExpectationResult (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine/Spec.js:73:70)\n at Env.fail (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine/Env.js:510:25)\n at fail (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine/jasmine_light.js:116:23)\n at Object. (/Z/Y/packages/Y-keeper/src/redux/doable/__tests__/learn-ramda.test.ts:75:9)\n at Object.asyncFn (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine_async.js:124:345)\n at resolve (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/queue_runner.js:46:12)\n at Promise ()\n at mapper (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/queue_runner.js:34:499)" + ], + "fullName": "curry keeper jest test", + "status": "failed", + "title": "keeper jest test" + } + ], + "endTime": 1506696066416, + "message": + " ● curry › keeper jest test\n\n Failed: intentionally failed\n \n at stackFormatter (node_modules/jest/node_modules/jest-jasmine2/build/expectation_result_factory.js:49:427)\n at Object. (packages/Y-keeper/src/redux/doable/__tests__/learn-ramda.test.ts:75:9)\n at Promise ()\n", + "name": + "/X/packages/Y-keeper/src/redux/doable/__tests__/learn-ramda.test.ts", + "startTime": 1506696063883, + "status": "failed", + "summary": "" + }, + { + "assertionResults": [ + { + "ancestorTitles": ["testing HDKeyMaker"], + "failureMessages": [], + "fullName": + "testing HDKeyMaker can create account and generate mnemonic phrase", + "status": "passed", + "title": "can create account and generate mnemonic phrase" + }, + { + "ancestorTitles": ["testing HDKeyMaker"], + "failureMessages": [], + "fullName": + "testing HDKeyMaker same mnemonic will generate same keys and same order", + "status": "passed", + "title": "same mnemonic will generate same keys and same order" + }, + { + "ancestorTitles": ["testing HDKeyMaker"], + "failureMessages": [], + "fullName": + "testing HDKeyMaker different mnemonic will generate different keys", + "status": "passed", + "title": "different mnemonic will generate different keys" + }, + { + "ancestorTitles": ["testing HDKeyMaker"], + "failureMessages": [], + "fullName": + "testing HDKeyMaker can add more accounts, while to be restorable", + "status": "passed", + "title": "can add more accounts, while to be restorable" + }, + { + "ancestorTitles": ["testing HDKeyMaker"], + "failureMessages": [], + "fullName": "testing HDKeyMaker has the right type", + "status": "passed", + "title": "has the right type" + }, + { + "ancestorTitles": ["testing HDKeyMaker"], + "failureMessages": [], + "fullName": + "testing HDKeyMaker can serialize then deserialize correctly", + "status": "passed", + "title": "can serialize then deserialize correctly" + }, + { + "ancestorTitles": ["testing HDKeyMaker"], + "failureMessages": [], + "fullName": "testing HDKeyMaker satisfy KeyMakerCreatable interface", + "status": "passed", + "title": "satisfy KeyMakerCreatable interface" + } + ], + "endTime": 1506696066829, + "message": "", + "name": "/X/packages/Y-key-hd/src/__tests__/HDKeyMaker.test.ts", + "startTime": 1506696066442, + "status": "passed", + "summary": "" + }, + { + "assertionResults": [ + { + "ancestorTitles": ["createGateMonitor"], + "failureMessages": [], + "fullName": "createGateMonitor can log/profile all actions", + "status": "passed", + "title": "can log/profile all actions" + }, + { + "ancestorTitles": ["createGateMonitor"], + "failureMessages": [], + "fullName": "createGateMonitor can log/profile doable async actions", + "status": "passed", + "title": "can log/profile doable async actions" + } + ], + "endTime": 1506696066914, + "message": "", + "name": + "/X/packages/Y-keeper/src/redux/middlewares/__tests__/createGateMonitor.test.ts", + "startTime": 1506696066251, + "status": "passed", + "summary": "" + }, + { + "assertionResults": [ + { + "ancestorTitles": ["test protocols"], + "failureMessages": [], + "fullName": "test protocols can create KeyMaker", + "status": "passed", + "title": "can create KeyMaker" + }, + { + "ancestorTitles": ["test protocols"], + "failureMessages": [], + "fullName": + "test protocols can pass a keyMakerCreatible class for creation", + "status": "passed", + "title": "can pass a keyMakerCreatible class for creation" + }, + { + "ancestorTitles": ["test protocols"], + "failureMessages": [], + "fullName": + "test protocols can pass a keyMakerCreatible class for deserialization", + "status": "passed", + "title": "can pass a keyMakerCreatible class for deserialization" + } + ], + "endTime": 1506696066958, + "message": "", + "name": "/X/packages/Y-core/src/protocols/__tests__/Keys.test.ts", + "startTime": 1506696066853, + "status": "passed", + "summary": "" + }, + { + "assertionResults": [], + "coverage": {}, + "endTime": 1506696067888, + "message": + " ● Test suite failed to run\n\n Cannot find module 'StyleSheet' from 'react-native-implementation.js'\n \n at Resolver.resolveModule (node_modules/jest-runtime/node_modules/jest-resolve/build/index.js:191:17)\n at Object.get StyleSheet [as StyleSheet] (packages/Y-app-vault/native/node_modules/react-native/Libraries/react-native/react-native-implementation.js:95:25)\n at Object. (packages/Y-app-vault/native/app/App.js:56:24)\n", + "name": "/X/packages/Y-app-vault/native/__tests__/index.ios.js", + "startTime": 1506696067888, + "status": "failed", + "summary": "" + }, + { + "assertionResults": [], + "coverage": {}, + "endTime": 1506696067888, + "message": + " ● Test suite failed to run\n\n Cannot find module 'StyleSheet' from 'react-native-implementation.js'\n \n at Resolver.resolveModule (node_modules/jest-runtime/node_modules/jest-resolve/build/index.js:191:17)\n at Object.get StyleSheet [as StyleSheet] (packages/Y-app-vault/native/node_modules/react-native/Libraries/react-native/react-native-implementation.js:95:25)\n at Object. (packages/Y-app-vault/native/app/App.js:56:24)\n", + "name": "/X/packages/Y-app-vault/native/__tests__/index.android.js", + "startTime": 1506696067888, + "status": "failed", + "summary": "" + }, + { + "assertionResults": [ + { + "ancestorTitles": ["DoableHelper"], + "failureMessages": [], + "fullName": "DoableHelper can build Doable with simple do", + "status": "passed", + "title": "can build Doable with simple do" + }, + { + "ancestorTitles": ["DoableHelper"], + "failureMessages": [], + "fullName": "DoableHelper can build async Doable", + "status": "passed", + "title": "can build async Doable" + }, + { + "ancestorTitles": ["DoableHelper"], + "failureMessages": [], + "fullName": "DoableHelper can build doable with static result", + "status": "passed", + "title": "can build doable with static result" + }, + { + "ancestorTitles": ["DoableHelper"], + "failureMessages": [], + "fullName": "DoableHelper can build doable with reducer", + "status": "passed", + "title": "can build doable with reducer" + }, + { + "ancestorTitles": ["DoableHelper"], + "failureMessages": [], + "fullName": "DoableHelper can build with both do and reducer", + "status": "passed", + "title": "can build with both do and reducer" + }, + { + "ancestorTitles": ["DoableHelper"], + "failureMessages": [], + "fullName": "DoableHelper can test if action is a doableAction", + "status": "passed", + "title": "can test if action is a doableAction" + }, + { + "ancestorTitles": ["DoableHelper"], + "failureMessages": [], + "fullName": "DoableHelper an empty doable doesn't make action doable", + "status": "passed", + "title": "an empty doable doesn't make action doable" + }, + { + "ancestorTitles": ["DoableAction"], + "failureMessages": [], + "fullName": "DoableAction can be bind with store dispatch", + "status": "passed", + "title": "can be bind with store dispatch" + }, + { + "ancestorTitles": ["DoableActionInvoker"], + "failureMessages": [], + "fullName": "DoableActionInvoker will skip non-DoableAction", + "status": "passed", + "title": "will skip non-DoableAction" + }, + { + "ancestorTitles": ["DoableActionInvoker"], + "failureMessages": [], + "fullName": "DoableActionInvoker can invoke DoableAction", + "status": "passed", + "title": "can invoke DoableAction" + }, + { + "ancestorTitles": ["DoableActionInvoker"], + "failureMessages": [], + "fullName": "DoableActionInvoker can chain with other middlewares", + "status": "passed", + "title": "can chain with other middlewares" + }, + { + "ancestorTitles": ["DoableActionInvoker"], + "failureMessages": [], + "fullName": + "DoableActionInvoker won't execute the action already executed", + "status": "passed", + "title": "won't execute the action already executed" + }, + { + "ancestorTitles": ["DoableActionInvoker"], + "failureMessages": [], + "fullName": "DoableActionInvoker can handle action exception", + "status": "passed", + "title": "can handle action exception" + }, + { + "ancestorTitles": ["DoableActionInvoker"], + "failureMessages": [], + "fullName": "DoableActionInvoker can invoke asyn action", + "status": "passed", + "title": "can invoke asyn action" + }, + { + "ancestorTitles": ["DoableActionLogging"], + "failureMessages": [], + "fullName": "DoableActionLogging can log sync actions", + "status": "passed", + "title": "can log sync actions" + }, + { + "ancestorTitles": ["DoableActionLogging"], + "failureMessages": [], + "fullName": + "DoableActionLogging can log async actions upon actual completion", + "status": "passed", + "title": "can log async actions upon actual completion" + }, + { + "ancestorTitles": ["DoableActionReducer"], + "failureMessages": [], + "fullName": "DoableActionReducer can invoke DoableAction's reducer", + "status": "passed", + "title": "can invoke DoableAction's reducer" + }, + { + "ancestorTitles": ["DoableActionReducer"], + "failureMessages": [], + "fullName": + "DoableActionReducer can merge from DoableAction's doResult", + "status": "passed", + "title": "can merge from DoableAction's doResult" + }, + { + "ancestorTitles": ["DoableActionReducer"], + "failureMessages": [], + "fullName": + "DoableActionReducer will return original state for actions without reducer property", + "status": "passed", + "title": + "will return original state for actions without reducer property" + }, + { + "ancestorTitles": ["more complex cases"], + "failureMessages": [], + "fullName": + "more complex cases can handle nested dispatches in sync operation", + "status": "passed", + "title": "can handle nested dispatches in sync operation" + }, + { + "ancestorTitles": ["more complex cases"], + "failureMessages": [], + "fullName": + "more complex cases can handle nested dispatches in async operation", + "status": "passed", + "title": "can handle nested dispatches in async operation" + } + ], + "endTime": 1506696067828, + "message": "", + "name": + "/X/packages/Y-keeper/src/redux/doable/__tests__/ReduxDoable.test.ts", + "startTime": 1506696063895, + "status": "passed", + "summary": "" + } + ], + "wasInterrupted": false +} diff --git a/fixtures/failing_jsons/monorepo_root_2.json b/fixtures/failing_jsons/monorepo_root_2.json new file mode 100644 index 000000000000..457eb1e6462e --- /dev/null +++ b/fixtures/failing_jsons/monorepo_root_2.json @@ -0,0 +1,246 @@ +{ + "numFailedTestSuites": 2, + "numFailedTests": 2, + "numPassedTestSuites": 2, + "numPassedTests": 21, + "numPendingTestSuites": 0, + "numPendingTests": 0, + "numRuntimeErrorTestSuites": 0, + "numTotalTestSuites": 4, + "numTotalTests": 23, + "snapshot": { + "added": 0, + "didUpdate": false, + "failure": false, + "filesAdded": 0, + "filesRemoved": 0, + "filesUnmatched": 0, + "filesUpdated": 0, + "matched": 0, + "total": 0, + "unchecked": 0, + "unmatched": 0, + "updated": 0 + }, + "startTime": 1506716960031, + "success": false, + "testResults": [ + { + "assertionResults": [ + { + "ancestorTitles": ["curry"], + "failureMessages": [], + "fullName": "curry simple curry", + "status": "passed", + "title": "simple curry" + }, + { + "ancestorTitles": ["curry"], + "failureMessages": [], + "fullName": "curry can parse middleware", + "status": "passed", + "title": "can parse middleware" + }, + { + "ancestorTitles": ["curry"], + "failureMessages": [], + "fullName": "curry can be used many times", + "status": "passed", + "title": "can be used many times" + }, + { + "ancestorTitles": ["curry"], + "failureMessages": [], + "fullName": "curry can be used with deferred functions many times", + "status": "passed", + "title": "can be used with deferred functions many times" + }, + { + "ancestorTitles": ["curry"], + "failureMessages": [ + "Error: Failed: intentionally failed\n at stackFormatter (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/expectation_result_factory.js:49:427)\n at expectationResultFactory (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/expectation_result_factory.js:49:591)\n at Spec.Object..Spec.addExpectationResult (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine/Spec.js:73:70)\n at Env.fail (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine/Env.js:510:25)\n at fail (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine/jasmine_light.js:116:23)\n at Object. (/Z/Y/packages/Y-keeper/src/redux/doable/__tests__/learn-ramda.test.ts:75:9)\n at Object.asyncFn (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine_async.js:124:345)\n at resolve (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/queue_runner.js:46:12)\n at tryCallTwo (/Z/Y/node_modules/promise/lib/core.js:45:5)\n at doResolve (/Z/Y/node_modules/promise/lib/core.js:200:13)" + ], + "fullName": "curry keeper jest test", + "status": "failed", + "title": "keeper jest test" + } + ], + "endTime": 1506716960706, + "message": + " ● curry › keeper jest test\n\n Failed: intentionally failed\n \n at stackFormatter (node_modules/jest/node_modules/jest-jasmine2/build/expectation_result_factory.js:49:427)\n at Object. (packages/Y-keeper/src/redux/doable/__tests__/learn-ramda.test.ts:75:9)\n at tryCallTwo (node_modules/promise/lib/core.js:45:5)\n at doResolve (node_modules/promise/lib/core.js:200:13)\n", + "name": + "/X/packages/Y-keeper/src/redux/doable/__tests__/learn-ramda.test.ts", + "startTime": 1506716960103, + "status": "failed", + "summary": "" + }, + { + "assertionResults": [ + { + "ancestorTitles": ["testing EthNumber..."], + "failureMessages": [], + "fullName": + "testing EthNumber... can take various format bigNumber can take", + "status": "passed", + "title": "can take various format bigNumber can take" + }, + { + "ancestorTitles": ["testing EthNumber..."], + "failureMessages": [], + "fullName": + "testing EthNumber... can handle empty and null as expected", + "status": "passed", + "title": "can handle empty and null as expected" + }, + { + "ancestorTitles": ["testing EthNumber..."], + "failureMessages": [], + "fullName": "testing EthNumber... should prevent toNumber for money", + "status": "passed", + "title": "should prevent toNumber for money" + }, + { + "ancestorTitles": ["testing EthNumber..."], + "failureMessages": [], + "fullName": "testing EthNumber... can understand unit", + "status": "passed", + "title": "can understand unit" + }, + { + "ancestorTitles": ["testing EthNumber..."], + "failureMessages": [], + "fullName": "testing EthNumber... can compare across unit", + "status": "passed", + "title": "can compare across unit" + }, + { + "ancestorTitles": ["testing EthNumber..."], + "failureMessages": [], + "fullName": "testing EthNumber... can operate across unit", + "status": "passed", + "title": "can operate across unit" + }, + { + "ancestorTitles": ["testing Address"], + "failureMessages": [], + "fullName": "testing Address can prevent invalid address", + "status": "passed", + "title": "can prevent invalid address" + }, + { + "ancestorTitles": ["testing Address"], + "failureMessages": [], + "fullName": + "testing Address can accept valid address with or without prefix", + "status": "passed", + "title": "can accept valid address with or without prefix" + }, + { + "ancestorTitles": ["testing Address"], + "failureMessages": [], + "fullName": "testing Address can detect if address is empty", + "status": "passed", + "title": "can detect if address is empty" + } + ], + "endTime": 1506716961514, + "message": "", + "name": "/X/packages/Y-core/src/eth/__tests__/types.test.ts", + "startTime": 1506716960759, + "status": "passed", + "summary": "" + }, + { + "assertionResults": [ + { + "ancestorTitles": [], + "failureMessages": [], + "fullName": "renders correctly", + "status": "passed", + "title": "renders correctly" + }, + { + "ancestorTitles": [], + "failureMessages": [ + "Error: Failed: make jest fail for react-native\n at stackFormatter (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/expectation_result_factory.js:49:427)\n at expectationResultFactory (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/expectation_result_factory.js:49:591)\n at Spec.Object..Spec.addExpectationResult (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine/Spec.js:73:70)\n at Env.fail (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine/Env.js:510:25)\n at fail (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine/jasmine_light.js:116:23)\n at Object. (/Z/Y/packages/Y-app-vault/native/__tests__/index.ios.js:14:1)\n at Object.asyncFn (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/jasmine_async.js:124:345)\n at resolve (/Z/Y/node_modules/jest/node_modules/jest-jasmine2/build/queue_runner.js:46:12)\n at tryCallTwo (/Z/Y/node_modules/promise/lib/core.js:45:5)\n at doResolve (/Z/Y/node_modules/promise/lib/core.js:200:13)" + ], + "fullName": "testing jest with react-native", + "status": "failed", + "title": "testing jest with react-native" + } + ], + "endTime": 1506716961734, + "message": + " ● testing jest with react-native\n\n Failed: make jest fail for react-native\n \n at stackFormatter (node_modules/jest/node_modules/jest-jasmine2/build/expectation_result_factory.js:49:427)\n at Object. (packages/Y-app-vault/native/__tests__/index.ios.js:14:1)\n at tryCallTwo (node_modules/promise/lib/core.js:45:5)\n at doResolve (node_modules/promise/lib/core.js:200:13)\n", + "name": "/X/packages/Y-app-vault/native/__tests__/index.ios.js", + "startTime": 1506716961553, + "status": "failed", + "summary": "" + }, + { + "assertionResults": [ + { + "ancestorTitles": ["testing HDKeyMaker"], + "failureMessages": [], + "fullName": + "testing HDKeyMaker can create account and generate mnemonic phrase", + "status": "passed", + "title": "can create account and generate mnemonic phrase" + }, + { + "ancestorTitles": ["testing HDKeyMaker"], + "failureMessages": [], + "fullName": + "testing HDKeyMaker same mnemonic will generate same keys and same order", + "status": "passed", + "title": "same mnemonic will generate same keys and same order" + }, + { + "ancestorTitles": ["testing HDKeyMaker"], + "failureMessages": [], + "fullName": + "testing HDKeyMaker different mnemonic will generate different keys", + "status": "passed", + "title": "different mnemonic will generate different keys" + }, + { + "ancestorTitles": ["testing HDKeyMaker"], + "failureMessages": [], + "fullName": + "testing HDKeyMaker can add more accounts, while to be restorable", + "status": "passed", + "title": "can add more accounts, while to be restorable" + }, + { + "ancestorTitles": ["testing HDKeyMaker"], + "failureMessages": [], + "fullName": "testing HDKeyMaker has the right type", + "status": "passed", + "title": "has the right type" + }, + { + "ancestorTitles": ["testing HDKeyMaker"], + "failureMessages": [], + "fullName": + "testing HDKeyMaker can serialize then deserialize correctly", + "status": "passed", + "title": "can serialize then deserialize correctly" + }, + { + "ancestorTitles": ["testing HDKeyMaker"], + "failureMessages": [], + "fullName": "testing HDKeyMaker satisfy KeyMakerCreatable interface", + "status": "passed", + "title": "satisfy KeyMakerCreatable interface" + } + ], + "endTime": 1506716961992, + "message": "", + "name": "/X/packages/Y-key-hd/src/__tests__/HDKeyMaker.test.ts", + "startTime": 1506716961763, + "status": "passed", + "summary": "" + } + ], + "wasInterrupted": false +} diff --git a/packages/jest-editor-support/index.d.ts b/packages/jest-editor-support/index.d.ts index 20692498c733..dd6ea3c589f9 100644 --- a/packages/jest-editor-support/index.d.ts +++ b/packages/jest-editor-support/index.d.ts @@ -5,11 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import {EventEmitter} from 'events'; +import { EventEmitter } from 'events'; export class Runner extends EventEmitter { constructor(workspace: ProjectWorkspace); - start(): void; + watchMode: boolean; + start(watchMode?: boolean): void; closeProcess(): void; runJestWithUpdateForSnapshots(completion: any): void; } @@ -28,7 +29,7 @@ export class ProjectWorkspace { rootPath: string, pathToJest: string, pathToConfig: string, - localJestMajorVersin: number + localJestMajorVersin: number, ); pathToJest: string; rootPath: string; @@ -61,15 +62,19 @@ export class Expect extends Node {} export class TestReconciler { stateForTestFile(file: string): TestReconcilationState; - stateForTestAssertion(file: string, name: string): TestFileAssertionStatus | null; - failedStatuses(): Array; - updateFileWithJestStatus(data): void; + assertionsForTestFile(file: string): TestAssertionStatus[] | null; + stateForTestAssertion( + file: string, + name: string, + ): TestFileAssertionStatus | null; + updateFileWithJestStatus(data): TestFileAssertionStatus[]; } -export type TestReconcilationState = "Unknown" | - "KnownSuccess" | - "KnownFail" | - "KnownSkip"; +export type TestReconcilationState = + | 'Unknown' + | 'KnownSuccess' + | 'KnownFail' + | 'KnownSkip'; export interface TestFileAssertionStatus { file: string; @@ -91,7 +96,7 @@ export interface JestFileResults { name: string; summary: string; message: string; - status: "failed" | "passed"; + status: 'failed' | 'passed'; startTime: number; endTime: number; assertionResults: Array; @@ -100,7 +105,7 @@ export interface JestFileResults { export interface JestAssertionResults { name: string; title: string; - status: "failed" | "passed"; + status: 'failed' | 'passed'; failureMessages: string[]; } diff --git a/packages/jest-editor-support/src/Runner.js b/packages/jest-editor-support/src/Runner.js index 9bbee12b2864..048950e28850 100644 --- a/packages/jest-editor-support/src/Runner.js +++ b/packages/jest-editor-support/src/Runner.js @@ -27,6 +27,7 @@ export default class Runner extends EventEmitter { workspace: ProjectWorkspace, args: Array, ) => ChildProcess; + watchMode: boolean; constructor(workspace: ProjectWorkspace, options?: Options) { super(); @@ -35,21 +36,19 @@ export default class Runner extends EventEmitter { this.outputPath = tmpdir() + '/jest_runner.json'; } - start() { + start(watchMode: boolean = true) { if (this.debugprocess) { return; } + + this.watchMode = watchMode; + // Handle the arg change on v18 const belowEighteen = this.workspace.localJestMajorVersion < 18; const outputArg = belowEighteen ? '--jsonOutputFile' : '--outputFile'; - const args = [ - '--json', - '--useStderr', - '--watch', - outputArg, - this.outputPath, - ]; + const args = ['--json', '--useStderr', outputArg, this.outputPath]; + if (this.watchMode) args.push('--watch'); this.debugprocess = this._createProcess(this.workspace, args); this.debugprocess.stdout.on('data', (data: Buffer) => { diff --git a/packages/jest-editor-support/src/__tests__/test_reconciler.test.js b/packages/jest-editor-support/src/__tests__/test_reconciler.test.js index e06e6966424e..b979aa0a50b7 100644 --- a/packages/jest-editor-support/src/__tests__/test_reconciler.test.js +++ b/packages/jest-editor-support/src/__tests__/test_reconciler.test.js @@ -10,26 +10,38 @@ import fs from 'fs'; import path from 'path'; import TestReconciler from '../test_reconciler'; +import type {TestFileAssertionStatus, TestAssertionStatus} from '../types'; const fixtures = path.resolve(__dirname, '../../../../fixtures'); -const reconcilerWithFile = (file: string): TestReconciler => { - const parser = new TestReconciler(); +function reconcilerWithFile( + parser: TestReconciler, + file: string, +): TestFileAssertionStatus[] { const exampleJSON = fs.readFileSync(`${fixtures}/failing_jsons/${file}`); const json = JSON.parse(exampleJSON.toString()); - parser.updateFileWithJestStatus(json); - return parser; -}; + if (!parser) console.error('no parser for ', file); + return parser.updateFileWithJestStatus(json); +} describe('Test Reconciler', () => { let parser: TestReconciler; + let results: TestFileAssertionStatus[]; + const dangerFilePath = '/Users/orta/dev/projects/danger/' + 'danger-js/source/ci_source/_tests/_travis.test.js'; describe('for a simple project', () => { + beforeAll(() => { + parser = new TestReconciler(); + results = reconcilerWithFile(parser, 'failing_jest_json.json'); + }); + + it('returns expected result for all test suites', () => { + expect(results.length).toEqual(5); + }); it('passes a passing method', () => { - parser = reconcilerWithFile('failing_jest_json.json'); const testName = 'does not validate without josh'; const status: any = parser.stateForTestAssertion( dangerFilePath, @@ -40,7 +52,6 @@ describe('Test Reconciler', () => { }); it('fails a failing method in the same file', () => { - parser = reconcilerWithFile('failing_jest_json.json'); const testName = 'validates when all Travis environment' + ' vars are set and Josh K says so'; @@ -60,7 +71,6 @@ Expected value to be falsy, instead received }); it('skips a skipped method', () => { - parser = reconcilerWithFile('failing_jest_json.json'); const testName = 'does not pull it out of the env'; const status: any = parser.stateForTestAssertion( dangerFilePath, @@ -70,11 +80,101 @@ Expected value to be falsy, instead received expect(status.line).toBeNull(); }); }); + + describe('in a monorepo project', () => { + beforeEach(() => { + parser = new TestReconciler(); + results = reconcilerWithFile(parser, 'monorepo_root_1.json'); + }); + + it('did processed all test suits including the suites failed to run', () => { + expect(results.length).toEqual(8); + const failed = results.filter(r => r.status === 'KnownFail'); + expect(failed.length).toEqual(4); + //2 of them is failed suite, i.e. no assertions + expect( + failed.filter(r => !r.assertions || r.assertions.length === 0).length, + ).toEqual(2); + }); + it('did catch the passed tests', () => { + const succeededSuites = results.filter(r => r.status === 'KnownSuccess'); + expect(succeededSuites.length).toEqual(4); + + const succeededTests = results + .map(r => r.assertions || []) + .reduce((sum: number, assertions: TestAssertionStatus[]) => { + const success = assertions.filter(a => a.status === 'KnownSuccess'); + return sum + success.length; + }, 0); + expect(succeededTests).toEqual(46); + }); + describe('when test updated', () => { + const targetTests = { + failedThenRemoved: [ + '/X/packages/Y-core/src/eth/__tests__/types.test.ts', + 'should fail', + ], + missingThenFailed: [ + '/X/packages/Y-app-vault/native/__tests__/index.ios.js', + 'testing jest with react-native', + ], + missingThenFixed: [ + '/X/packages/Y-app-vault/native/__tests__/index.ios.js', + 'renders correctly', + ], + passed: [ + '/X/packages/Y-keeper/src/redux/middlewares/__tests__/createGateMonitor.test.ts', + 'can log/profile doable async actions', + ], + }; + + function verifyTest(key: string, expectedStatus?: string) { + const test = parser.stateForTestAssertion( + targetTests[key][0], + targetTests[key][1], + ); + if (!test && !expectedStatus) { + return; + } + if (expectedStatus && test) { + expect(test.status).toEqual(expectedStatus); + return; + } + expect(key + ': ' + JSON.stringify(test)).toEqual(expectedStatus); // failed! + } + + it('verify before update occurred', () => { + verifyTest('missingThenFixed', undefined); + verifyTest('missingThenFailed', undefined); + verifyTest('failedThenRemoved', 'KnownFail'); + verifyTest('passed', 'KnownSuccess'); + }); + + it('new file can update existing result', () => { + //in file 2 we fixed 2 failed suites and removed 1 failed test + //let's check the failed tests are now passed, while the previously + //passed test should still be accessible + const results2 = reconcilerWithFile(parser, 'monorepo_root_2.json'); + expect(results2.length).toEqual(4); + + verifyTest('missingThenFixed', 'KnownSuccess'); + verifyTest('missingThenFailed', 'KnownFail'); + verifyTest('failedThenRemoved', undefined); + verifyTest('passed', 'KnownSuccess'); + }); + }); + }); }); describe('Terse Messages', () => { + let parser: TestReconciler; + + beforeEach(() => { + parser = new TestReconciler(); + const _ = reconcilerWithFile(parser, 'failing_expects.json'); + }); + it('handles shrinking a snapshot message', () => { - const parser = reconcilerWithFile('failing_expects.json'); const file = '/Users/orta/dev/projects/artsy/js/' + 'libs/jest-snapshots-svg/src/_tests/example.test.ts'; diff --git a/packages/jest-editor-support/src/test_reconciler.js b/packages/jest-editor-support/src/test_reconciler.js index 1f019323b9f4..cdfad3ba37ac 100644 --- a/packages/jest-editor-support/src/test_reconciler.js +++ b/packages/jest-editor-support/src/test_reconciler.js @@ -28,21 +28,21 @@ import path from 'path'; * at a file level, generating useful error messages and providing a nice API. */ export default class TestReconciler { - fileStatuses: any; - fails: Array; - passes: Array; - skips: Array; + fileStatuses: {[key: string]: TestFileAssertionStatus}; constructor() { this.fileStatuses = {}; } - updateFileWithJestStatus(results: FormattedTestResults) { - this.fails = []; - this.passes = []; - this.skips = []; - + // the processed test results will be returned immediately instead of saved in + // instance properties. This is 1) to prevent race condition 2) the data is already + // stored in the this.fileStatuses, no dup is better 3) client will most likely need to process + // all the results anyway. + updateFileWithJestStatus( + results: FormattedTestResults, + ): TestFileAssertionStatus[] { // Loop through all files inside the report from Jest + const statusList: TestFileAssertionStatus[] = []; results.testResults.forEach(file => { // Did the file pass/fail? const status = this.statusToReconcilationState(file.status); @@ -54,27 +54,9 @@ export default class TestReconciler { status, }; this.fileStatuses[file.name] = fileStatus; - - if (status === 'KnownFail') { - this.fails.push(fileStatus); - } else if (status === 'KnownSuccess') { - this.passes.push(fileStatus); - } else if (status === 'KnownSkip') { - this.skips.push(fileStatus); - } + statusList.push(fileStatus); }); - } - - failedStatuses(): Array { - return this.fails || []; - } - - passedStatuses(): Array { - return this.passes || []; - } - - skipedStatuses(): Array { - return this.skips || []; + return statusList; } // A failed test also contains the stack trace for an `expect` @@ -162,12 +144,17 @@ export default class TestReconciler { return results.status; } + assertionsForTestFile(file: string): TestAssertionStatus[] | null { + const results = this.fileStatuses[file]; + return results ? results.assertions : null; + } + stateForTestAssertion( file: string, name: string, ): TestAssertionStatus | null { const results = this.fileStatuses[file]; - if (!results) { + if (!results || !results.assertions) { return null; } const assertion = results.assertions.find(a => a.title === name); diff --git a/packages/jest-test-typescript-parser/package.json b/packages/jest-test-typescript-parser/package.json index 8ccc4ca289a1..bd1128d967e3 100644 --- a/packages/jest-test-typescript-parser/package.json +++ b/packages/jest-test-typescript-parser/package.json @@ -9,6 +9,6 @@ "main": "build/index.js", "dependencies": { "jest-editor-support": "^21.2.0", - "typescript": "^2.2.1" + "typescript": "^2.5.3" } } diff --git a/yarn.lock b/yarn.lock index 1f68c5565c1f..42f4d5265093 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5837,9 +5837,9 @@ typedarray@^0.0.6, typedarray@~0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -typescript@^2.2.1, typescript@^2.2.2: - version "2.5.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.2.tgz#038a95f7d9bbb420b1bf35ba31d4c5c1dd3ffe34" +typescript@^2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.3.tgz#df3dcdc38f3beb800d4bc322646b04a3f6ca7f0d" ua-parser-js@^0.7.9: version "0.7.14" From 6e5d149e15027e6f19a87f36b6cc6c92bae82b74 Mon Sep 17 00:00:00 2001 From: Lucas Azzola Date: Sun, 1 Oct 2017 20:14:45 +1100 Subject: [PATCH 12/16] [jest-docblock] Preserve leading whitespace in docblock comments (#4576) --- packages/jest-docblock/src/__tests__/index.test.js | 9 +++++++++ packages/jest-docblock/src/index.js | 14 +++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/jest-docblock/src/__tests__/index.test.js b/packages/jest-docblock/src/__tests__/index.test.js index 579560a3de63..0a00ac6b2107 100644 --- a/packages/jest-docblock/src/__tests__/index.test.js +++ b/packages/jest-docblock/src/__tests__/index.test.js @@ -245,6 +245,15 @@ describe('docblock', () => { }); }); + it('preserves leading whitespace in multiline comments from docblock', () => { + const code = + '/**' + os.EOL + ' * hello' + os.EOL + ' * world' + os.EOL + ' */'; + + expect(docblock.parseWithComments(code).comments).toEqual( + ' hello' + os.EOL + ' world', + ); + }); + it('extracts comments from beginning and end of docblock', () => { const code = '/**' + diff --git a/packages/jest-docblock/src/index.js b/packages/jest-docblock/src/index.js index 7eddad6f94b7..771693acd332 100644 --- a/packages/jest-docblock/src/index.js +++ b/packages/jest-docblock/src/index.js @@ -15,11 +15,11 @@ const commentStartRe = /^\/\*\*/; const docblockRe = /^\s*(\/\*\*?(.|\r?\n)*?\*\/)/; const lineCommentRe = /\/\/([^\r\n]*)/g; const ltrimRe = /^\s*/; +const rtrimRe = /\s*$/; +const ltrimNewlineRe = /^(\r?\n)+/; const multilineRe = /(?:^|\r?\n) *(@[^\r\n]*?) *\r?\n *([^@\r\n\s][^@\r\n]+?) *\r?\n/g; const propertyRe = /(?:^|\r?\n) *@(\S+) *([^\r\n]*)/g; -const stringStartRe = /(\r?\n|^) *\*/g; -const lineStartRe = /(\r?\n|^) */g; -const wsRe = /[\t ]+/g; +const stringStartRe = /(\r?\n|^) *\* ?/g; export function extract(contents: string): string { const match = contents.match(docblockRe); @@ -43,7 +43,6 @@ export function parseWithComments( docblock = docblock .replace(commentStartRe, '') .replace(commentEndRe, '') - .replace(wsRe, ' ') .replace(lineCommentRe, '') .replace(stringStartRe, '$1'); @@ -53,15 +52,16 @@ export function parseWithComments( prev = docblock; docblock = docblock.replace(multilineRe, `${line}$1 $2${line}`); } - docblock = docblock.trim(); + docblock = docblock.replace(ltrimNewlineRe, '').replace(rtrimRe, ''); const result = Object.create(null); - const comments = docblock.replace(propertyRe, '').replace(lineStartRe, line); + const comments = docblock.replace(propertyRe, ''); + let match; while ((match = propertyRe.exec(docblock))) { result[match[1]] = match[2]; } - return {comments: comments.trim(), pragmas: result}; + return {comments, pragmas: result}; } export function print({ From eff7a1cfbdffa2111deaa020378c9395385a72a0 Mon Sep 17 00:00:00 2001 From: Recca Tsai Date: Sun, 1 Oct 2017 19:25:21 +0800 Subject: [PATCH 13/16] --showConfig support jest20 and jest21 (#4575) * --showConfig to show all project configs #4078 * return configs * new method getConfigs * new method getConfigs * call completed * eslint * support jest 20 * lint * version * getConfig for jest21 * lint * fix jest20 bug * fix bug * lint * add type ConfigRepresentations * remove ConfigRepresentations * update --- packages/jest-editor-support/src/Settings.js | 20 +++++---- .../src/__tests__/settings.test.js | 45 +++++++++++++++---- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/packages/jest-editor-support/src/Settings.js b/packages/jest-editor-support/src/Settings.js index 36f93e98d283..9ad13b186346 100644 --- a/packages/jest-editor-support/src/Settings.js +++ b/packages/jest-editor-support/src/Settings.js @@ -32,6 +32,8 @@ type ConfigRepresentation = { testMatch: Array, }; +type ConfigRepresentations = Array; + export default class Settings extends EventEmitter { getConfigProcess: ChildProcess; jestVersionMajor: number | null; @@ -39,6 +41,7 @@ export default class Settings extends EventEmitter { workspace: ProjectWorkspace, args: Array, ) => ChildProcess; + configs: ConfigRepresentations; settings: ConfigRepresentation; workspace: ProjectWorkspace; @@ -52,6 +55,8 @@ export default class Settings extends EventEmitter { testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)(spec|test).js?(x)'], testRegex: '(/__tests__/.*|\\.(test|spec))\\.jsx?$', }; + + this.configs = [this.settings]; } getConfigs(completed: any) { @@ -60,13 +65,10 @@ export default class Settings extends EventEmitter { ]); this.getConfigProcess.stdout.on('data', (data: Buffer) => { - const {configs, version} = JSON.parse(data.toString()); - // We can give warnings to versions under 17 now - // See https://github.com/facebook/jest/issues/2343 for moving this into - // the config object - - this.jestVersionMajor = parseInt(version.split('.').shift(), 10); - this.settings = configs; + const settings = JSON.parse(data.toString()); + this.jestVersionMajor = parseInt(settings.version.split('.').shift(), 10); + this.configs = + this.jestVersionMajor >= 21 ? settings.configs : [settings.config]; }); // They could have an older build of Jest which @@ -76,9 +78,9 @@ export default class Settings extends EventEmitter { }); } - getConfig(completed: any) { + getConfig(completed: any, index: number = 0) { this.getConfigs(() => { - this.settings = this.settings[0]; + this.settings = this.configs[index]; completed(); }); } diff --git a/packages/jest-editor-support/src/__tests__/settings.test.js b/packages/jest-editor-support/src/__tests__/settings.test.js index 90ad492eea94..55debc02f7c1 100644 --- a/packages/jest-editor-support/src/__tests__/settings.test.js +++ b/packages/jest-editor-support/src/__tests__/settings.test.js @@ -26,7 +26,7 @@ describe('Settings', () => { expect(settings.settings).toEqual(expect.any(Object)); }); - it('reads and parses the configs', () => { + it('[jest 20] reads and parses the config', () => { const workspace = new ProjectWorkspace( 'root_path', 'path_to_jest', @@ -34,9 +34,9 @@ describe('Settings', () => { 1000, ); const completed = jest.fn(); - const configs = [{cacheDirectory: '/tmp/jest', name: '[md5 hash]'}]; + const config = {cacheDirectory: '/tmp/jest', name: '[md5 hash]'}; const json = { - configs, + config, version: '19.0.0', }; @@ -46,16 +46,16 @@ describe('Settings', () => { const buffer = makeBuffer(JSON.stringify(json)); const settings = new Settings(workspace, {createProcess}); - settings.getConfigs(completed); + settings.getConfig(completed); settings.getConfigProcess.stdout.emit('data', buffer); settings.getConfigProcess.emit('close'); expect(completed).toHaveBeenCalled(); expect(settings.jestVersionMajor).toBe(19); - expect(settings.settings).toEqual(configs); + expect(settings.settings).toEqual(config); }); - it('reads and parses the config', () => { + it('[jest 21] reads and parses the config', () => { const workspace = new ProjectWorkspace( 'root_path', 'path_to_jest', @@ -66,7 +66,7 @@ describe('Settings', () => { const configs = [{cacheDirectory: '/tmp/jest', name: '[md5 hash]'}]; const json = { configs, - version: '19.0.0', + version: '21.0.0', }; const mockProcess: any = new EventEmitter(); @@ -80,10 +80,39 @@ describe('Settings', () => { settings.getConfigProcess.emit('close'); expect(completed).toHaveBeenCalled(); - expect(settings.jestVersionMajor).toBe(19); + expect(settings.jestVersionMajor).toBe(21); expect(settings.settings).toEqual(configs[0]); }); + it('[jest 21] reads and parses the configs', () => { + const workspace = new ProjectWorkspace( + 'root_path', + 'path_to_jest', + 'test', + 1000, + ); + const completed = jest.fn(); + const configs = [{cacheDirectory: '/tmp/jest', name: '[md5 hash]'}]; + const json = { + configs, + version: '21.0.0', + }; + + const mockProcess: any = new EventEmitter(); + mockProcess.stdout = new EventEmitter(); + const createProcess = () => mockProcess; + const buffer = makeBuffer(JSON.stringify(json)); + const settings = new Settings(workspace, {createProcess}); + + settings.getConfigs(completed); + settings.getConfigProcess.stdout.emit('data', buffer); + settings.getConfigProcess.emit('close'); + + expect(completed).toHaveBeenCalled(); + expect(settings.jestVersionMajor).toBe(21); + expect(settings.configs).toEqual(configs); + }); + it('calls callback even if no data is sent', () => { const workspace = new ProjectWorkspace( 'root_path', From b430e51a34b389c0f43dfdcf05fab5220a7b5677 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Sun, 1 Oct 2017 16:01:16 -0400 Subject: [PATCH 14/16] Call chalk.inverse for trailing spaces in jest-matcher-utils (#4578) --- .../__tests__/__snapshots__/matchers.test.js.snap | 11 +++++++++++ packages/expect/src/__tests__/matchers.test.js | 1 + packages/expect/src/to_throw_matchers.js | 3 +-- packages/jest-matcher-utils/src/index.js | 12 ++++-------- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap index 1c7e541cdad8..c98f2be7cb3a 100644 --- a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap @@ -229,6 +229,17 @@ Received: \\"abc\\"" `; +exports[`.toBe() fails for: "with +trailing space" and "without trailing space" 1`] = ` +"expect(received).toBe(expected) + +Expected value to be (using ===): + \\"without trailing space\\" +Received: + \\"with +trailing space\\"" +`; + exports[`.toBe() fails for: [] and [] 1`] = ` "expect(received).toBe(expected) diff --git a/packages/expect/src/__tests__/matchers.test.js b/packages/expect/src/__tests__/matchers.test.js index e19cd982b900..4fbb763be944 100644 --- a/packages/expect/src/__tests__/matchers.test.js +++ b/packages/expect/src/__tests__/matchers.test.js @@ -120,6 +120,7 @@ describe('.toBe()', () => { [{a: 1}, {a: 1}], [{a: 1}, {a: 5}], ['abc', 'cde'], + ['with \ntrailing space', 'without trailing space'], [[], []], [null, undefined], ].forEach(([a, b]) => { diff --git a/packages/expect/src/to_throw_matchers.js b/packages/expect/src/to_throw_matchers.js index 60231cf7bcf0..3a448df231d6 100644 --- a/packages/expect/src/to_throw_matchers.js +++ b/packages/expect/src/to_throw_matchers.js @@ -13,7 +13,6 @@ import getType from 'jest-get-type'; import {escapeStrForRegex} from 'jest-regex-util'; import {formatStackTrace, separateMessageFromStack} from 'jest-message-util'; import { - RECEIVED_BG, RECEIVED_COLOR, highlightTrailingWhitespace, matcherHint, @@ -173,7 +172,7 @@ const printActualErrorMessage = error => { `Instead, it threw:\n` + RECEIVED_COLOR( ' ' + - highlightTrailingWhitespace(message, RECEIVED_BG) + + highlightTrailingWhitespace(message) + formatStackTrace( stack, { diff --git a/packages/jest-matcher-utils/src/index.js b/packages/jest-matcher-utils/src/index.js index c0f99dc331aa..89f2442880e5 100644 --- a/packages/jest-matcher-utils/src/index.js +++ b/packages/jest-matcher-utils/src/index.js @@ -27,9 +27,7 @@ const PLUGINS = [ ]; export const EXPECTED_COLOR = chalk.green; -export const EXPECTED_BG = chalk.bgGreen; export const RECEIVED_COLOR = chalk.red; -export const RECEIVED_BG = chalk.bgRed; const NUMBERS = [ 'zero', @@ -76,15 +74,13 @@ export const stringify = (object: any, maxDepth?: number = 10): string => { : result; }; -export const highlightTrailingWhitespace = ( - text: string, - bgColor: Function, -): string => text.replace(/\s+$/gm, bgColor('$&')); +export const highlightTrailingWhitespace = (text: string): string => + text.replace(/\s+$/gm, chalk.inverse('$&')); export const printReceived = (object: any) => - highlightTrailingWhitespace(RECEIVED_COLOR(stringify(object)), RECEIVED_BG); + RECEIVED_COLOR(highlightTrailingWhitespace(stringify(object))); export const printExpected = (value: any) => - highlightTrailingWhitespace(EXPECTED_COLOR(stringify(value)), EXPECTED_BG); + EXPECTED_COLOR(highlightTrailingWhitespace(stringify(value))); export const printWithType = ( name: string, From d355b3b51e06d4e39aae3a5091ce354b0ff5c261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Mon, 2 Oct 2017 10:03:05 +0200 Subject: [PATCH 15/16] Support rootDir tag in testEnvironment (#4579) --- .../jest-config/src/__tests__/normalize.test.js | 15 +++++++++++++++ packages/jest-config/src/utils.js | 2 +- yarn.lock | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/jest-config/src/__tests__/normalize.test.js b/packages/jest-config/src/__tests__/normalize.test.js index 2b4b2782beb6..c654d4b03f87 100644 --- a/packages/jest-config/src/__tests__/normalize.test.js +++ b/packages/jest-config/src/__tests__/normalize.test.js @@ -583,6 +583,9 @@ describe('testEnvironment', () => { if (name === 'jest-environment-jsdom') { return 'node_modules/jest-environment-jsdom'; } + if (name.startsWith('/root')) { + return name; + } return findNodeModule(name); }); }); @@ -612,6 +615,18 @@ describe('testEnvironment', () => { ), ).toThrowErrorMatchingSnapshot(); }); + + it('works with rootDir', () => { + const {options} = normalize( + { + rootDir: '/root', + testEnvironment: '/testEnvironment.js', + }, + {}, + ); + + expect(options.testEnvironment).toEqual('/root/testEnvironment.js'); + }); }); describe('babel-jest', () => { diff --git a/packages/jest-config/src/utils.js b/packages/jest-config/src/utils.js index 5afccaa0b8ff..b9fde85b7407 100644 --- a/packages/jest-config/src/utils.js +++ b/packages/jest-config/src/utils.js @@ -100,7 +100,7 @@ export const _replaceRootDirTags = (rootDir: string, config: any) => { * 1. looks for relative to Jest. */ export const getTestEnvironment = (config: Object) => { - const env = config.testEnvironment; + const env = _replaceRootDirInPath(config.rootDir, config.testEnvironment); let module = Resolver.findNodeModule(`jest-environment-${env}`, { basedir: config.rootDir, }); diff --git a/yarn.lock b/yarn.lock index 42f4d5265093..d1eade80989c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5837,7 +5837,7 @@ typedarray@^0.0.6, typedarray@~0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -typescript@^2.5.3: +typescript@^2.2.2, typescript@^2.5.3: version "2.5.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.3.tgz#df3dcdc38f3beb800d4bc322646b04a3f6ca7f0d" From 7aa3017f97a4dfa76f566f9bbeded5d03fdd35e7 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Mon, 2 Oct 2017 15:03:08 -0400 Subject: [PATCH 16/16] Increase coverage in jest-diff (#4583) * Increase coverage in jest-diff * Have fun with toJSON --- .../__tests__/__snapshots__/diff.test.js.snap | 15 ++++- packages/jest-diff/src/__tests__/diff.test.js | 67 ++++++++++++------- 2 files changed, 55 insertions(+), 27 deletions(-) diff --git a/packages/jest-diff/src/__tests__/__snapshots__/diff.test.js.snap b/packages/jest-diff/src/__tests__/__snapshots__/diff.test.js.snap index d26f62585094..e6dc7169be86 100644 --- a/packages/jest-diff/src/__tests__/__snapshots__/diff.test.js.snap +++ b/packages/jest-diff/src/__tests__/__snapshots__/diff.test.js.snap @@ -169,7 +169,18 @@ exports[`context number of lines: null (5 default) 1`] = ` }" `; -exports[`falls back to not call toJSON if objects look identical 1`] = ` +exports[`falls back to not call toJSON if it throws and then objects have differences 1`] = ` +"- Expected ++ Received + + Object { +- \\"line\\": 1, ++ \\"line\\": 2, + \\"toJSON\\": [Function toJSON], + }" +`; + +exports[`falls back to not call toJSON if serialization has no differences but then objects have differences 1`] = ` "Compared values serialize to the same structure. Printing internal object structure without calling \`toJSON\` instead. @@ -197,5 +208,3 @@ exports[`highlight only the last in odd length of leading spaces (expanded) 1`] + }, {}); " `; - -exports[`prints a fallback message if two objects truly look identical 1`] = `"Compared values have no visual difference."`; diff --git a/packages/jest-diff/src/__tests__/diff.test.js b/packages/jest-diff/src/__tests__/diff.test.js index 32753279a37b..5733e3e17a8f 100644 --- a/packages/jest-diff/src/__tests__/diff.test.js +++ b/packages/jest-diff/src/__tests__/diff.test.js @@ -10,6 +10,8 @@ const stripAnsi = require('strip-ansi'); const diff = require('../'); +const NO_DIFF_MESSAGE = 'Compared values have no visual difference.'; + const stripped = (a, b, options) => stripAnsi(diff(a, b, options)); const unexpanded = {expand: false}; @@ -17,10 +19,6 @@ const expanded = {expand: true}; const elementSymbol = Symbol.for('react.element'); -const toJSON = function toJSON() { - return 'apple'; -}; - describe('different types', () => { [ [1, 'a', 'number', 'string'], @@ -58,10 +56,13 @@ describe('no visual difference', () => { ].forEach(values => { test(`'${JSON.stringify(values[0])}' and '${JSON.stringify( values[1], - )}'`, () => { - expect(stripped(values[0], values[1])).toBe( - 'Compared values have no visual difference.', - ); + )}' (unexpanded)`, () => { + expect(stripped(values[0], values[1], unexpanded)).toBe(NO_DIFF_MESSAGE); + }); + test(`'${JSON.stringify(values[0])}' and '${JSON.stringify( + values[1], + )}' (expanded)`, () => { + expect(stripped(values[0], values[1], expanded)).toBe(NO_DIFF_MESSAGE); }); }); @@ -69,41 +70,59 @@ describe('no visual difference', () => { const arg1 = new Map([[1, 'foo'], [2, 'bar']]); const arg2 = new Map([[2, 'bar'], [1, 'foo']]); - expect(stripped(arg1, arg2)).toBe( - 'Compared values have no visual difference.', - ); + expect(stripped(arg1, arg2)).toBe(NO_DIFF_MESSAGE); }); test('Set value order should be irrelevant', () => { const arg1 = new Set([1, 2]); const arg2 = new Set([2, 1]); - expect(stripped(arg1, arg2)).toBe( - 'Compared values have no visual difference.', - ); + expect(stripped(arg1, arg2)).toBe(NO_DIFF_MESSAGE); }); }); test('oneline strings', () => { // oneline strings don't produce a diff currently. expect(diff('ab', 'aa')).toBe(null); - expect(diff('a', 'a')).toMatch(/no visual difference/); expect(diff('123456789', '234567890')).toBe(null); // if either string is oneline expect(diff('oneline', 'multi\nline')).toBe(null); expect(diff('multi\nline', 'oneline')).toBe(null); }); -test('falls back to not call toJSON if objects look identical', () => { - const a = {line: 1, toJSON}; - const b = {line: 2, toJSON}; - expect(diff(a, b)).toMatchSnapshot(); -}); +describe('falls back to not call toJSON', () => { + describe('if serialization has no differences', () => { + const toJSON = function toJSON() { + return 'it’s all the same to me'; + }; -test('prints a fallback message if two objects truly look identical', () => { - const a = {line: 2, toJSON}; - const b = {line: 2, toJSON}; - expect(diff(a, b)).toMatchSnapshot(); + test('but then objects have differences', () => { + const a = {line: 1, toJSON}; + const b = {line: 2, toJSON}; + expect(diff(a, b)).toMatchSnapshot(); + }); + test('and then objects have no differences', () => { + const a = {line: 2, toJSON}; + const b = {line: 2, toJSON}; + expect(stripped(a, b)).toBe(NO_DIFF_MESSAGE); + }); + }); + describe('if it throws', () => { + const toJSON = function toJSON() { + throw new Error('catch me if you can'); + }; + + test('and then objects have differences', () => { + const a = {line: 1, toJSON}; + const b = {line: 2, toJSON}; + expect(diff(a, b)).toMatchSnapshot(); + }); + test('and then objects have no differences', () => { + const a = {line: 2, toJSON}; + const b = {line: 2, toJSON}; + expect(stripped(a, b)).toBe(NO_DIFF_MESSAGE); + }); + }); }); // Some of the following assertions seem complex, but compare to alternatives: