diff --git a/CHANGELOG.md b/CHANGELOG.md index edb3ce4a1468..5d24d296c2ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,50 @@ ## master +## jest 19.0.0 + +* Breaking Change: Added a version for snapshots. +* Breaking Change: Removed the `mocksPattern` configuration option, it never worked correctly. +* Breaking Change: Renamed `testPathDirs` to `roots` to avoid confusion when configuring Jest. +* Breaking Change: Updated printing of React elements to cause fewer changes when props change. +* Breaking Change: Updated snapshot format to properly escape data. +* Fixed --color to be recognized correctly again. +* Fixed `babel-plugin-jest-hoist` to work properly with type annotations in tests. +* Fixed behavior for console.log calls and fixed a memory leak (#2539). +* Fixed cache directory path for Jest to avoid ENAMETOOLONG errors. +* Fixed change events to be emitted in jest-haste-map's watch mode. This fixes issues with Jest's new watch mode and react-native-packager. +* Fixed cli arguments to be used when loading the config from file, they were previously ignored. +* Fixed Jest to load json files that include a BOM. +* Fixed Jest to throw errors instead of ignoring invalid cli options. +* Fixed mocking behavior for virtual modules. +* Fixed mocking behavior with transitive dependencies. +* Fixed support for asymmetric matchers in `toMatchObject`. +* Fixed test interruption and `--bail` behavior. +* Fixed watch mode to clean up worker processes when a test run gets interrupted. +* Fixed whitespace to be highlighted in snapshots and assertion errors. +* Improved `babel-jest` plugin: babel is loaded lazily, istanbul comments are only added when coverage is used. +* Improved error for invalid transform config. +* Improved moduleNameMapper to not overwrite mocks when many patterns map to the same file. +* Improved printing of skipped tests in verbose mode. +* Improved resolution code in jest-resolve. +* Improved to only show patch marks in assertion errors when the comparison results in large objects. +* New `--collectCoverageFrom` cli argument. +* New `--coverageDirectory` cli argument. +* New `expect.addSnapshotSerializer` to add custom snapshot serializers for tests. +* New `jest.spyOn`. +* New `testMatch` configuration option that accepts glob patterns. +* New eslint-plugin-jest with no-disabled-tests, no-focuses-tests and no-identical-title rules and default configuration and globals. +* New expect.stringContaining asymmetric matcher. +* New feature to make manual mocks with nested folders work. For example `__mocks__/react-native/Library/Text.js` will now work as expected. +* New jest-phabricator package to integrate Jest code coverage in phabriactor. +* New jest-validate package to improve configuration errors, help with suggestions of correct configuration and to be adopted in other libraries. +* New pretty-printing for asymmetric matchers. +* New RSS feed for Jest's blog. +* New typeahead to filter cached test names added to watch mode. +* New typeahead to filter file names added to watch mode (#2324). +* New way to provide a reducer to extract haste module ids. +* New website, new documentation, new color scheme and new homepage. +* Rewritten watch mode for instant feedback, better code quality and to build new features on top of it (#2362). + ## jest 18.1.0 * Fixed console.log and fake timer behavior in node 7.3. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9d3521cf4105..961976966ae0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,31 +28,37 @@ The core team will be monitoring for pull requests. When we get one, we'll run s git checkout -b my_branch ``` -2. Run `npm install`. We recommend that you use `npm` version 3 or later. +2. Jest uses [Yarn](https://code.facebook.com/posts/1840075619545360) + for running development scripts. If you haven't already done so, + please [install yarn](https://yarnpkg.com/en/docs/install). + +3. Run `yarn install`. ```sh - npm install + yarn install ``` -3. If you've added code that should be tested, add tests. You +4. If you've added code that should be tested, add tests. You can use watch mode that continuously transforms changed files to make your life easier. ```sh # in the background - npm run watch + yarn run watch ``` -4. If you've changed APIs, update the documentation. -5. Ensure the test suite passes via `npm test`. To run the test suite you +5. If you've changed APIs, update the documentation. + +6. Ensure the test suite passes via `yarn test`. To run the test suite you may need to install Mercurial (`hg`). On macOS, this can be done using [homebrew](http://brew.sh/): `brew install hg`. ```sh brew install hg # maybe - npm test + yarn test ``` -6. If you haven't already, complete the CLA. + +7. If you haven't already, complete the CLA. ### Contributor License Agreement (CLA) @@ -60,7 +66,45 @@ In order to accept your pull request, we need you to submit a CLA. You only need [Complete your CLA here.](https://code.facebook.com/cla) -### Bugs +## How to try a development build of Jest in another project + +To link `jest` on the command line to `jest-cli/bin/jest.js` in a development build: + +```sh +cd /path/to/your/Jest_clone/packages/jest-cli +yarn link +``` + +To build Jest: + +```sh +cd /path/to/your/Jest_clone + +# Do one of the following: + +# Check out a commit from another contributor, and then +yarn run build + +# Or, save your changes to Jest, and then +yarn test # which also builds Jest +``` + +To run tests in another project with the development build of Jest: + +```sh +cd /path/to/another/project +jest [options] # run jest-cli/bin/jest.js in the development build +``` + +* To decide whether to specify any options, see `test` under `scripts` in the `package.json` file of the other project. + +To unlink `jest` on the command line from `jest-cli/bin/jest.js` in a development build: + +```sh +yarn unlink jest-cli +``` + +## Bugs ### Where to Find Known Issues @@ -78,7 +122,7 @@ Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe * Discord - [#jest](https://discordapp.com/channels/102860784329052160/103622435865104384) on [Reactiflux](http://www.reactiflux.com/) -### Code Conventions +## Code Conventions * 2 spaces for indentation (no tabs). * 80 character line length strongly preferred. diff --git a/circle.yml b/circle.yml index 32073051ed13..8c77b52eeb2a 100644 --- a/circle.yml +++ b/circle.yml @@ -16,10 +16,11 @@ test: - cd website && npm run test deployment: - production: - branch: /master/ + website: + branch: master + owner: facebook commands: - # generate docs website + # Deploy Jest website - git config --global user.email "jest-bot@users.noreply.github.com" - git config --global user.name "Website Deployment Script" - echo "machine github.com login jest-bot password $GITHUB_TOKEN" > ~/.netrc diff --git a/docs/CLI.md b/docs/CLI.md index 0b0b34233e1a..0d615c4981dc 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -87,7 +87,7 @@ Show some helpful information, similar to this page. Prints the test results in JSON. This mode will send all other test output and user messages to stderr. -### `--jsonOutputFile=` +### `--outputFile=` Write test results to a file when the `--json` option is also specified. diff --git a/docs/ExpectAPI.md b/docs/ExpectAPI.md index 8afd21ebc281..9d6d75cf5712 100644 --- a/docs/ExpectAPI.md +++ b/docs/ExpectAPI.md @@ -149,21 +149,35 @@ test('randocall calls its callback with a number', () => { ### `expect.arrayContaining(array)` -`expect.arrayContaining(array)` matches any array made up entirely of elements in the provided array. You can use it inside `toEqual` or `toBeCalledWith` instead of a literal value. For example, this code checks that `rollDice` returns only valid numbers: +`expect.arrayContaining(array)` matches a received array which contains all of the elements in the expected array. That is, the expected array is a **subset** of the received array. Therefore, it matches a received array which contains elements that are **not** in the expected array. + +You can use it instead of a literal value: + +* in `toEqual` or `toBeCalledWith` +* to match a property in `objectContaining` or `toMatchObject` ```js -// Rolls n virtual dice -function rollDice(n) { - let answer = []; - for (let i = 0; i < n; i++) { - answer.push(Math.floor(Math.random() * 6 + 1)); - } - return answer; -} +describe('arrayContaining', () => { + const expected = ['Alice', 'Bob']; + it('matches even if received contains additional elements', () => { + expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected)); + }); + it('does not match if received does not contain expected elements', () => { + expect(['Bob', 'Eve']).not.toEqual(expect.arrayContaining(expected)); + }); +}); +``` -test('rollDice only returns valid numbers', () => { - expect(rollDice(100)).toEqual(expect.arrayContaining([1, 2, 3, 4, 5, 6])); -}) +```js +describe('Beware of a misunderstanding! A sequence of dice rolls', () => { + const expected = [1, 2, 3, 4, 5, 6]; + it('matches even with an unexpected number 7', () => { + expect([4, 1, 6, 7, 3, 5, 2, 5, 4, 6]).toEqual(expect.arrayContaining(expected)); + }); + it('does not match without an expected number 2', () => { + expect([4, 1, 6, 7, 3, 5, 7, 5, 4, 6]).not.toEqual(expect.arrayContaining(expected)); + }); +}); ``` ### `expect.assertions(number)` @@ -184,52 +198,11 @@ test('prepareState prepares a valid state', () => { The `expect.assertions(1)` call ensures that the `prepareState` callback actually gets called. -### `expect.stringContaining(string)` - -##### available in Jest **19.0.0+** - -`expect.stringContaining(string)` matches any string that contains the exact provided string. - - -### `expect.stringMatching(regexp)` - -`expect.stringMatching(regexp)` matches any string that matches the provided regexp. You can use it inside `toEqual` or `toBeCalledWith` instead of a literal value. For example, let's say you want to test that `randomCoolNames()` only returns names that are cool: - -```js -function randomCoolName() { - // Generate a last name - let lastName = ( - 'TRFGBNMPLZ'[Math.floor(Math.random() * 10)] + - 'aeiou'[Math.floor(Math.random() * 5)] + - 'mnbvxdstrp'[Math.floor(Math.random() * 10)]); - return 'Kevin ' + lastName; -} - -function randomCoolNames() { - let answer = []; - for (let i = 0; i < 100; i++) { - answer.push(randomCoolName()); - } - return answer; -} - -test('randomCoolNames only returns cool names', () => { - // A reasonable proxy for whether a name is cool or not - let coolRegex = /^Kevin/; - - expect(randomCoolNames()).toEqual( - expect.arrayContaining([ - expect.stringMatching(coolRegex) - ]) - ); -}); -``` - -This example also shows how you can nest multiple asymmetric matchers, with `expect.stringMatching` inside the `expect.arrayContaining`. - ### `expect.objectContaining(object)` -`expect.objectContaining(object)` matches any object that recursively matches the provided keys. This is often handy in conjunction with other asymmetric matchers. +`expect.objectContaining(object)` matches any received object that recursively matches the expected properties. That is, the expected object is a **subset** of the received object. Therefore, it matches a received object which contains properties that are **not** in the expected object. + +Instead of literal property values in the expected object, you can use matchers `expect.anything()` and so on. For example, let's say that we expect an `onPress` function to be called with an `Event` object, and all we need to verify is that the event has `event.x` and `event.y` properties. We can do that with: @@ -244,6 +217,39 @@ test('onPress gets called with the right thing', () => { }) ``` +### `expect.stringContaining(string)` + +##### available in Jest **19.0.0+** + +`expect.stringContaining(string)` matches any received string that contains the exact expected string. + +### `expect.stringMatching(regexp)` + +`expect.stringMatching(regexp)` matches any received string that matches the expected regexp. + +You can use it instead of a literal value: + +* in `toEqual` or `toBeCalledWith` +* to match an element in `arrayContaining` +* to match a property in `objectContaining` or `toMatchObject` + +This example also shows how you can nest multiple asymmetric matchers, with `expect.stringMatching` inside the `expect.arrayContaining`. + +```js +describe('stringMatching in arrayContaining', () => { + const expected = [ + expect.stringMatching(/^Alic/), + expect.stringMatching(/^[BR]ob/), + ]; + it('matches even if received contains additional elements', () => { + expect(['Alicia', 'Roberto', 'Evelina']).toEqual(expect.arrayContaining(expected)); + }); + it('does not match if received does not contain expected elements', () => { + expect(['Roberto', 'Evelina']).not.toEqual(expect.arrayContaining(expected)); + }); +}); +``` + ### `expect.addSnapshotSerializer(serializer)` You can call `expect.addSnapshotSerializer` to add a module that formats application-specific data structures. diff --git a/docs/ManualMocks.md b/docs/ManualMocks.md index 2e80ed885930..1bb2396e659b 100644 --- a/docs/ManualMocks.md +++ b/docs/ManualMocks.md @@ -122,7 +122,7 @@ describe('listFilesInDirectorySync', () => { }); ``` -The example mock shown here uses [`jest.genMockFromModule`](/jest/docs/api.html#jestgenmockfrommodulemodulename) +The example mock shown here uses [`jest.genMockFromModule`](/jest/docs/jest-object.html#jestgenmockfrommodulemodulename) to generate an automatic mock, and overrides its default behavior. This is the recommended approach, but is completely optional. If you do not want to use the automatic mock at all, you can simply export your own functions from the mock diff --git a/docs/TimerMocks.md b/docs/TimerMocks.md index f0b660d1fabf..976b7e439e2d 100644 --- a/docs/TimerMocks.md +++ b/docs/TimerMocks.md @@ -131,6 +131,45 @@ describe('infiniteTimerGame', () => { }); }); ``` + +## Run Timers to Time + +Another possibility is use `jest.runTimersToTime(msToRun)`. When this API is called, all pending "macro-tasks" that have been queued via setTimeout() or setInterval(), and would be executed within msToRun milliseconds, will be executed. Additionally if those macro-tasks schedule new macro-tasks that would be executed within the same time frame, those will be executed until there are no more macro-tasks remaining in the queue that should be run within msToRun milliseconds. + +```javascript +// timerGame.js +'use strict'; + +function timerGame(callback) { + console.log('Ready....go!'); + setTimeout(() => { + console.log('Times up -- stop!'); + callback && callback(); + }, 1000); +} + +module.exports = timerGame; +``` + +```javascript +it('calls the callback after 1 second via runTimersToTime', () => { + const timerGame = require('../timerGame'); + const callback = jest.fn(); + + timerGame(callback); + + // At this point in time, the callback should not have been called yet + expect(callback).not.toBeCalled(); + + // Fast-forward until all timers have been executed + jest.runTimersToTime(1000); + + // Now our callback should have been called! + expect(callback).toBeCalled(); + expect(callback.mock.calls.length).toBe(1); + }); +``` + Lastly, it may occasionally be useful in some tests to be able to clear all of the pending timers. For this, we have `jest.clearAllTimers()`. diff --git a/docs/TutorialAsync.md b/docs/TutorialAsync.md index be68af1a0483..c1d3b3052b78 100644 --- a/docs/TutorialAsync.md +++ b/docs/TutorialAsync.md @@ -113,7 +113,7 @@ if a Promise throws and the error is not handled, the test will fail. `expect.as ```js // Testing for async errors can be done using `catch`. it('tests error with promises', () => { - expect.assertions(1) + expect.assertions(1); // to be sure that `Promise` rejected and `expect` has been called once return user.getUserName(3) .catch(e => expect(e).toEqual({ error: 'User with 3 not found.', @@ -122,7 +122,7 @@ it('tests error with promises', () => { // Or try-catch. it('tests error with async/await', async () => { - expect.assertions(1) + expect.assertions(1); // to be sure that `await` throws error and `expect` has been called once try { await user.getUserName(2); } catch (object) { diff --git a/examples/react-native/__tests__/__snapshots__/Intro-test.js.snap b/examples/react-native/__tests__/__snapshots__/Intro-test.js.snap index 666b1d8ee23c..10ea6c84fa8d 100644 --- a/examples/react-native/__tests__/__snapshots__/Intro-test.js.snap +++ b/examples/react-native/__tests__/__snapshots__/Intro-test.js.snap @@ -1,4 +1,6 @@ -exports[`test renders correctly 1`] = ` +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders correctly 1`] = ` `; -exports[`test renders the ActivityIndicator component 1`] = ` +exports[`renders the ActivityIndicator component 1`] = ` `; -exports[`test renders the Image component 1`] = ` +exports[`renders the Image component 1`] = ` `; -exports[`test renders the ListView component 1`] = ` +exports[`renders the ListView component 1`] = ` `; -exports[`test renders the TextInput component 1`] = ` +exports[`renders the TextInput component 1`] = ` 1482363367.071 seconds have ellapsed since the UNIX epoch. diff --git a/examples/snapshot/__tests__/__snapshots__/Link.react-test.js.snap b/examples/snapshot/__tests__/__snapshots__/Link.react-test.js.snap index 66f19ce6544e..62627f397202 100644 --- a/examples/snapshot/__tests__/__snapshots__/Link.react-test.js.snap +++ b/examples/snapshot/__tests__/__snapshots__/Link.react-test.js.snap @@ -1,4 +1,6 @@ -exports[`test changes the class when hovered 1`] = ` +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`changes the class when hovered 1`] = ` `; -exports[`test changes the class when hovered 2`] = ` +exports[`changes the class when hovered 2`] = ` `; -exports[`test changes the class when hovered 3`] = ` +exports[`changes the class when hovered 3`] = ` `; -exports[`test properly escapes quotes 1`] = ` +exports[`properly escapes quotes 1`] = ` `; -exports[`test renders as an anchor when no page is set 1`] = ` +exports[`renders as an anchor when no page is set 1`] = ` `; -exports[`test renders correctly 1`] = ` +exports[`renders correctly 1`] = ` diff --git a/lerna.json b/lerna.json index e44cca1c8269..2da360530079 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "lerna": "2.0.0-beta.32", + "lerna": "2.0.0-beta.37", "version": "18.1.0", "linkedFiles": { "prefix": "/**\n * @flow\n */\n" diff --git a/package.json b/package.json index 486d65c32ddf..7a7ce4e07950 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,28 @@ { "private": true, "devDependencies": { - "babel-core": "^6.18.2", + "babel-core": "^6.23.1", "babel-eslint": "^7.1.1", "babel-plugin-syntax-trailing-function-commas": "^6.13.0", "babel-plugin-transform-async-to-generator": "^6.16.0", - "babel-plugin-transform-es2015-destructuring": "^6.19.0", - "babel-plugin-transform-es2015-parameters": "^6.18.0", + "babel-plugin-transform-es2015-destructuring": "^6.23.0", + "babel-plugin-transform-es2015-parameters": "^6.23.0", "babel-plugin-transform-flow-strip-types": "^6.18.0", "chalk": "^1.1.3", "codecov": "^1.0.1", "eslint": "^3.11.1", "eslint-plugin-babel": "^4.0.0", - "eslint-plugin-flow-vars": "^0.5.0", "eslint-plugin-flowtype": "^2.28.2", "eslint-plugin-react": "^6.7.1", - "flow-bin": "^0.37.4", + "flow-bin": "^0.39.0", "glob": "^7.1.1", "graceful-fs": "^4.1.11", "istanbul-api": "^1.1.0", "istanbul-lib-coverage": "^1.0.0", "jasmine-reporters": "^2.2.0", - "jsdom": "^9.9.1", + "jsdom": "^9.11.0", "left-pad": "^1.1.1", - "lerna": "2.0.0-beta.32", + "lerna": "2.0.0-beta.37", "micromatch": "^2.3.11", "mkdirp": "^0.5.1", "progress": "^1.1.8", @@ -84,6 +83,8 @@ "\\.snap$", "/packages/.*/build" ], - "testMatch": ["**/*-test.js"] + "testMatch": [ + "**/*-test.js" + ] } } diff --git a/packages/babel-jest/package.json b/packages/babel-jest/package.json index c3eb77b594e1..147d26121695 100644 --- a/packages/babel-jest/package.json +++ b/packages/babel-jest/package.json @@ -9,7 +9,7 @@ "main": "build/index.js", "dependencies": { "babel-core": "^6.0.0", - "babel-plugin-istanbul": "^3.1.2", + "babel-plugin-istanbul": "^4.0.0", "babel-preset-jest": "^18.0.0" } } diff --git a/packages/babel-jest/src/index.js b/packages/babel-jest/src/index.js index 92e458d24e2c..2b58168b68a9 100644 --- a/packages/babel-jest/src/index.js +++ b/packages/babel-jest/src/index.js @@ -13,16 +13,18 @@ import type {Config, Path} from 'types/Config'; import type {TransformOptions} from 'types/Transform'; -const babel = require('babel-core'); const crypto = require('crypto'); const fs = require('fs'); const jestPreset = require('babel-preset-jest'); const path = require('path'); const BABELRC_FILENAME = '.babelrc'; +const THIS_FILE = fs.readFileSync(__filename); const cache = Object.create(null); +let babel; + const getBabelRC = (filename, {useCache}) => { const paths = []; let directory = filename; @@ -47,11 +49,12 @@ const getBabelRC = (filename, {useCache}) => { const createTransformer = (options: any) => { options = Object.assign({}, options, { - auxiliaryCommentBefore: ' istanbul ignore next ', + plugins: (options && options.plugins) || [], presets: ((options && options.presets) || []).concat([jestPreset]), retainLines: true, }); delete options.cacheDirectory; + delete options.filename; return { canInstrument: true, @@ -62,11 +65,16 @@ const createTransformer = (options: any) => { {instrument, watch}: TransformOptions, ): string { return crypto.createHash('md5') + .update(THIS_FILE) + .update('\0', 'utf8') .update(fileData) + .update('\0', 'utf8') .update(configString) + .update('\0', 'utf8') // Don't use the in-memory cache in watch mode because the .babelrc // file may be modified. .update(getBabelRC(filename, {useCache: !watch})) + .update('\0', 'utf8') .update(instrument ? 'instrument' : '') .digest('hex'); }, @@ -76,11 +84,20 @@ const createTransformer = (options: any) => { config: Config, transformOptions: TransformOptions, ): string { - let plugins = options.plugins || []; + if (!babel) { + babel = require('babel-core'); + } + + if (!babel.util.canCompile(filename)) { + return src; + } + + const theseOptions = Object.assign({filename}, options); if (transformOptions && transformOptions.instrument) { + theseOptions.auxiliaryCommentBefore = ' istanbul ignore next '; // Copied from jest-runtime transform.js - plugins = plugins.concat([ + theseOptions.plugins = theseOptions.plugins.concat([ [ require('babel-plugin-istanbul').default, { @@ -92,13 +109,7 @@ const createTransformer = (options: any) => { ]); } - if (babel.util.canCompile(filename)) { - return babel.transform( - src, - Object.assign({}, options, {filename, plugins}), - ).code; - } - return src; + return babel.transform(src, theseOptions).code; }, }; }; diff --git a/packages/jest-cli/src/TestNamePatternPrompt.js b/packages/jest-cli/src/TestNamePatternPrompt.js new file mode 100644 index 000000000000..d53e1ab4a1ea --- /dev/null +++ b/packages/jest-cli/src/TestNamePatternPrompt.js @@ -0,0 +1,149 @@ +/** + * 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 type {Config} from 'types/Config'; + +const ansiEscapes = require('ansi-escapes'); +const chalk = require('chalk'); +const {getTerminalWidth} = require('./lib/terminalUtils'); +const stringLength = require('string-length'); +const Prompt = require('./lib/Prompt'); +const formatTestNameByPattern = require('./lib/formatTestNameByPattern'); + +const pluralizeTest = (total: number) => total === 1 ? 'test' : 'tests'; + +const usage = () => + `\n ${chalk.bold('Pattern Mode Usage')}\n` + + ` ${chalk.dim('\u203A Press')} Esc ${chalk.dim('to exit pattern mode.')}\n` + + ` ${chalk.dim('\u203A Press')} Enter ` + + `${chalk.dim('to apply pattern to all tests.')}\n` + + `\n`; + +const usageRows = usage().split('\n').length; + +module.exports = ( + config: Config, + pipe: stream$Writable | tty$WriteStream, + prompt: Prompt, +) => { + class TestNamePatternPrompt { + // $FlowFixMe + _cachedTestResults; + + constructor() { + (this:any).onChange = this.onChange.bind(this); + } + + run( + onSuccess: Function, + onCancel: Function, + ) { + pipe.write(ansiEscapes.cursorHide); + pipe.write(ansiEscapes.clearScreen); + pipe.write(usage()); + pipe.write(ansiEscapes.cursorShow); + + prompt.enter( + this.onChange, + onSuccess, + onCancel, + ); + } + + onChange( + pattern: string, + ) { + pipe.write(ansiEscapes.eraseLine); + pipe.write(ansiEscapes.cursorLeft); + this.printTypeahead(pattern, 10); + } + + printTypeahead( + pattern: string, + max: number, + ) { + const matchedTests = this.getMatchedTests(pattern); + + const total = matchedTests.length; + const results = matchedTests.slice(0, max); + const inputText = `${chalk.dim(' pattern \u203A')} ${pattern}`; + + pipe.write(ansiEscapes.eraseDown); + pipe.write(inputText); + pipe.write(ansiEscapes.cursorSavePosition); + + if (pattern) { + if (total) { + pipe.write(`\n\n Pattern matches ${total} ${pluralizeTest(total)}`); + } else { + pipe.write(`\n\n Pattern matches no tests`); + } + + pipe.write(' from cached test suites.'); + + const width = getTerminalWidth(); + + results.forEach(name => { + const testName = formatTestNameByPattern(name, pattern, width - 4); + + pipe.write(`\n ${chalk.dim('\u203A')} ${testName}`); + }); + + if (total > max) { + const more = total - max; + pipe.write( + // eslint-disable-next-line max-len + `\n ${chalk.dim(`\u203A and ${more} more ${pluralizeTest(more)}`)}`, + ); + } + } else { + // eslint-disable-next-line max-len + pipe.write(`\n\n ${chalk.italic.yellow('Start typing to filter by a test name regex pattern.')}`); + } + + pipe.write(ansiEscapes.cursorTo(stringLength(inputText), usageRows - 1)); + pipe.write(ansiEscapes.cursorRestorePosition); + } + + getMatchedTests(pattern: string) { + let regex; + + try { + regex = new RegExp(pattern, 'i'); + } catch (e) { + return []; + } + + const matchedTests = []; + + this._cachedTestResults.forEach( + ({testResults}) => + testResults.forEach( + ({title}) => { + if (regex.test(title)) { + matchedTests.push(title); + } + }, + ) + ); + + return matchedTests; + } + + // $FlowFixMe + updateCachedTestResults(testResults) { + this._cachedTestResults = testResults || []; + } + } + + return new TestNamePatternPrompt(); +}; diff --git a/packages/jest-cli/src/TestPathPatternPrompt.js b/packages/jest-cli/src/TestPathPatternPrompt.js new file mode 100644 index 000000000000..59a545054784 --- /dev/null +++ b/packages/jest-cli/src/TestPathPatternPrompt.js @@ -0,0 +1,138 @@ +/** + * 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 type {HasteContext} from 'types/HasteMap'; +import type {Config, Path} from 'types/Config'; + +const ansiEscapes = require('ansi-escapes'); +const chalk = require('chalk'); +const {getTerminalWidth} = require('./lib/terminalUtils'); +const highlight = require('./lib/highlight'); +const stringLength = require('string-length'); +const {trimAndFormatPath} = require('./reporters/utils'); +const SearchSource = require('./SearchSource'); +const Prompt = require('./lib/Prompt'); + +const pluralizeFile = (total: number) => total === 1 ? 'file' : 'files'; + +const usage = () => + `\n ${chalk.bold('Pattern Mode Usage')}\n` + + ` ${chalk.dim('\u203A Press')} Esc ${chalk.dim('to exit pattern mode.')}\n` + + ` ${chalk.dim('\u203A Press')} Enter ` + + `${chalk.dim('to apply pattern to all filenames.')}\n` + + `\n`; + +const usageRows = usage().split('\n').length; + +module.exports = ( + config: Config, + pipe: stream$Writable | tty$WriteStream, + prompt: Prompt, +) => { + class TestPathPatternPrompt { + searchSource: SearchSource; + + constructor() { + (this:any).onChange = this.onChange.bind(this); + } + + run( + onSuccess: Function, + onCancel: Function, + ) { + pipe.write(ansiEscapes.cursorHide); + pipe.write(ansiEscapes.clearScreen); + pipe.write(usage()); + pipe.write(ansiEscapes.cursorShow); + + prompt.enter( + this.onChange, + onSuccess, + onCancel, + ); + } + + onChange( + pattern: string, + ) { + let regex; + + try { + regex = new RegExp(pattern, 'i'); + } catch (e) {} + + const paths = regex ? + this.searchSource.findMatchingTests(pattern).paths : []; + + pipe.write(ansiEscapes.eraseLine); + pipe.write(ansiEscapes.cursorLeft); + this.printTypeahead(pattern, paths, 10); + } + + printTypeahead( + pattern: string, + allResults: Array, + max: number, + ) { + const total = allResults.length; + const results = allResults.slice(0, max); + const inputText = `${chalk.dim(' pattern \u203A')} ${pattern}`; + + pipe.write(ansiEscapes.eraseDown); + pipe.write(inputText); + pipe.write(ansiEscapes.cursorSavePosition); + + if (pattern) { + if (total) { + pipe.write(`\n\n Pattern matches ${total} ${pluralizeFile(total)}.`); + } else { + pipe.write(`\n\n Pattern matches no files.`); + } + + const width = getTerminalWidth(); + const prefix = ` ${chalk.dim('\u203A')} `; + const padding = stringLength(prefix) + 2; + + results + .map(rawPath => { + const filePath = trimAndFormatPath(padding, config, rawPath, width); + return highlight(rawPath, filePath, pattern, config.rootDir); + }) + .forEach( + filePath => pipe.write(`\n ${chalk.dim('\u203A')} ${filePath}`) + ); + + if (total > max) { + const more = total - max; + pipe.write( + // eslint-disable-next-line max-len + `\n ${chalk.dim(`\u203A and ${more} more ${pluralizeFile(more)}`)}`, + ); + } + } else { + // eslint-disable-next-line max-len + pipe.write(`\n\n ${chalk.italic.yellow('Start typing to filter by a filename regex pattern.')}`); + } + + pipe.write(ansiEscapes.cursorTo(stringLength(inputText), usageRows - 1)); + pipe.write(ansiEscapes.cursorRestorePosition); + } + + updateSearchSource( + hasteContext: HasteContext, + ) { + this.searchSource = new SearchSource(hasteContext, config); + } + } + + return new TestPathPatternPrompt(); +}; diff --git a/packages/jest-cli/src/TestRunner.js b/packages/jest-cli/src/TestRunner.js index 88dd1258980b..0bebb1f066c9 100644 --- a/packages/jest-cli/src/TestRunner.js +++ b/packages/jest-cli/src/TestRunner.js @@ -11,7 +11,7 @@ import type { AggregatedResult, - Error as TestError, + SerializableError as TestError, TestResult, } from 'types/TestResult'; import type {Config, Path} from 'types/Config'; diff --git a/packages/jest-cli/src/TestWorker.js b/packages/jest-cli/src/TestWorker.js index adf9290b0a07..8b3599c2cdcc 100644 --- a/packages/jest-cli/src/TestWorker.js +++ b/packages/jest-cli/src/TestWorker.js @@ -10,7 +10,7 @@ 'use strict'; import type {Config, Path} from 'types/Config'; -import type {Error, TestResult} from 'types/TestResult'; +import type {SerializableError, TestResult} from 'types/TestResult'; import type {RawModuleMap} from 'types/HasteMap'; // Make sure uncaught errors are logged before we exit. @@ -31,9 +31,9 @@ type WorkerData = {| rawModuleMap?: RawModuleMap, |}; -type WorkerCallback = (error: ?Error, result?: TestResult) => void; +type WorkerCallback = (error: ?SerializableError, result?: TestResult) => void; -const formatError = error => { +const formatError = (error: string|Error): SerializableError => { if (typeof error === 'string') { const {message, stack} = separateMessageFromStack(error); return { @@ -46,7 +46,7 @@ const formatError = error => { return { message: error.message, stack: error.stack, - type: error.type || 'Error', + type: 'Error', }; }; diff --git a/packages/jest-cli/src/__tests__/__snapshots__/generateEmptyCoverage-test.js.snap b/packages/jest-cli/src/__tests__/__snapshots__/generateEmptyCoverage-test.js.snap index 003338f102f8..60ed733287d1 100644 --- a/packages/jest-cli/src/__tests__/__snapshots__/generateEmptyCoverage-test.js.snap +++ b/packages/jest-cli/src/__tests__/__snapshots__/generateEmptyCoverage-test.js.snap @@ -1,4 +1,6 @@ -exports[`test generates an empty coverage object for a file without running it 1`] = ` +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`generates an empty coverage object for a file without running it 1`] = ` Object { "b": Object { "0": Array [ diff --git a/packages/jest-cli/src/__tests__/__snapshots__/watch-pattern-mode-test.js.snap b/packages/jest-cli/src/__tests__/__snapshots__/watch-filename-pattern-mode-test.js.snap similarity index 93% rename from packages/jest-cli/src/__tests__/__snapshots__/watch-pattern-mode-test.js.snap rename to packages/jest-cli/src/__tests__/__snapshots__/watch-filename-pattern-mode-test.js.snap index e0819b6197eb..21d9a952f76a 100644 --- a/packages/jest-cli/src/__tests__/__snapshots__/watch-pattern-mode-test.js.snap +++ b/packages/jest-cli/src/__tests__/__snapshots__/watch-filename-pattern-mode-test.js.snap @@ -1,3 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + exports[`Watch mode flows Pressing "P" enters pattern mode 1`] = ` " @@ -29,7 +31,7 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 1`] = ` › path/to/file10-test.js › and 1 more file -[MOCK - cursorTo(12, 4)] +[MOCK - cursorTo(12, 5)] [MOCK - cursorRestorePosition]" `; @@ -64,7 +66,7 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 2`] = ` › path/to/file10-test.js › and 1 more file -[MOCK - cursorTo(13, 4)] +[MOCK - cursorTo(13, 5)] [MOCK - cursorRestorePosition]" `; @@ -99,7 +101,7 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 3`] = ` › path/to/file10-test.js › and 1 more file -[MOCK - cursorTo(14, 4)] +[MOCK - cursorTo(14, 5)] [MOCK - cursorRestorePosition]" `; @@ -118,7 +120,7 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 4`] = ` › path/to/file10-test.js › path/to/file11-test.js -[MOCK - cursorTo(15, 4)] +[MOCK - cursorTo(15, 5)] [MOCK - cursorRestorePosition]" `; @@ -133,7 +135,7 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 5`] = ` Pattern matches 1 file. › path/to/file10-test.js -[MOCK - cursorTo(16, 4)] +[MOCK - cursorTo(16, 5)] [MOCK - cursorRestorePosition]" `; @@ -152,7 +154,7 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 6`] = ` › path/to/file10-test.js › path/to/file11-test.js -[MOCK - cursorTo(15, 4)] +[MOCK - cursorTo(15, 5)] [MOCK - cursorRestorePosition]" `; @@ -187,7 +189,7 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 7`] = ` › path/to/file10-test.js › and 1 more file -[MOCK - cursorTo(14, 4)] +[MOCK - cursorTo(14, 5)] [MOCK - cursorRestorePosition]" `; @@ -202,7 +204,7 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 8`] = ` Pattern matches 1 file. › path/to/file3-test.js -[MOCK - cursorTo(15, 4)] +[MOCK - cursorTo(15, 5)] [MOCK - cursorRestorePosition]" `; @@ -237,7 +239,7 @@ exports[`Watch mode flows Results in pattern mode get truncated appropriately 1` › path/to/file10-test.js › and 1 more file -[MOCK - cursorTo(12, 4)] +[MOCK - cursorTo(12, 5)] [MOCK - cursorRestorePosition]" `; @@ -272,7 +274,7 @@ exports[`Watch mode flows Results in pattern mode get truncated appropriately 2` › ...o/file10-test.js › and 1 more file -[MOCK - cursorTo(12, 4)] +[MOCK - cursorTo(12, 5)] [MOCK - cursorRestorePosition]" `; @@ -307,6 +309,6 @@ exports[`Watch mode flows Results in pattern mode get truncated appropriately 3` › ...t.js › and 1 more file -[MOCK - cursorTo(12, 4)] +[MOCK - cursorTo(12, 5)] [MOCK - cursorRestorePosition]" `; diff --git a/packages/jest-cli/src/__tests__/__snapshots__/watch-test-name-pattern-mode-test.js.snap b/packages/jest-cli/src/__tests__/__snapshots__/watch-test-name-pattern-mode-test.js.snap new file mode 100644 index 000000000000..f105f90b65dd --- /dev/null +++ b/packages/jest-cli/src/__tests__/__snapshots__/watch-test-name-pattern-mode-test.js.snap @@ -0,0 +1,241 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Watch mode flows Pressing "T" enters pattern mode 1`] = ` +" + + + pattern › c +[MOCK - cursorSavePosition] + + + Pattern matches 9 tests + from cached test suites. + + › should return the correct index when + + › might get confusing + + › should handle length properties that cannot + + › should recognize various types + + › should recognize null and undefined + + › should not output colors to pipe + + › should convert string to a RegExp + + › should escape and convert string to a RegExp + + › should convert grep string to a RegExp +[MOCK - cursorTo(12, 5)] +[MOCK - cursorRestorePosition]" +`; + +exports[`Watch mode flows Pressing "T" enters pattern mode 2`] = ` +" + + + pattern › co +[MOCK - cursorSavePosition] + + + Pattern matches 8 tests + from cached test suites. + + › should return the correct index when + + › might get confusing + + › should recognize various types + + › should recognize null and undefined + + › should not output colors to pipe + + › should convert string to a RegExp + + › should escape and convert string to a RegExp + + › should convert grep string to a RegExp +[MOCK - cursorTo(13, 5)] +[MOCK - cursorRestorePosition]" +`; + +exports[`Watch mode flows Pressing "T" enters pattern mode 3`] = ` +" + + + pattern › con +[MOCK - cursorSavePosition] + + + Pattern matches 4 tests + from cached test suites. + + › might get confusing + + › should convert string to a RegExp + + › should escape and convert string to a RegExp + + › should convert grep string to a RegExp +[MOCK - cursorTo(14, 5)] +[MOCK - cursorRestorePosition]" +`; + +exports[`Watch mode flows Pressing "T" enters pattern mode 4`] = ` +" + + + pattern › con +[MOCK - cursorSavePosition] + + + Pattern matches no tests + from cached test suites. +[MOCK - cursorTo(15, 5)] +[MOCK - cursorRestorePosition]" +`; + +exports[`Watch mode flows Pressing "T" enters pattern mode 5`] = ` +" + + + pattern › con 1 +[MOCK - cursorSavePosition] + + + Pattern matches no tests + from cached test suites. +[MOCK - cursorTo(16, 5)] +[MOCK - cursorRestorePosition]" +`; + +exports[`Watch mode flows Pressing "T" enters pattern mode 6`] = ` +" + + + pattern › con 12 +[MOCK - cursorSavePosition] + + + Pattern matches no tests + from cached test suites. +[MOCK - cursorTo(17, 5)] +[MOCK - cursorRestorePosition]" +`; + +exports[`Watch mode flows Pressing "T" enters pattern mode 7`] = ` +" + + + pattern › con 1 +[MOCK - cursorSavePosition] + + + Pattern matches no tests + from cached test suites. +[MOCK - cursorTo(16, 5)] +[MOCK - cursorRestorePosition]" +`; + +exports[`Watch mode flows Pressing "T" enters pattern mode 8`] = ` +" + + + pattern › con +[MOCK - cursorSavePosition] + + + Pattern matches no tests + from cached test suites. +[MOCK - cursorTo(15, 5)] +[MOCK - cursorRestorePosition]" +`; + +exports[`Watch mode flows Pressing "T" enters pattern mode 9`] = ` +" + + + pattern › con * +[MOCK - cursorSavePosition] + + + Pattern matches 4 tests + from cached test suites. + + › might get confusing + + › should convert string to a RegExp + + › should escape and convert string to a RegExp + + › should convert grep string to a RegExp +[MOCK - cursorTo(16, 5)] +[MOCK - cursorRestorePosition]" +`; + +exports[`Watch mode flows Results in pattern mode get truncated appropriately 1`] = ` +" + + + pattern › t +[MOCK - cursorSavePosition] + + + Pattern matches 9 tests + from cached test suites. + + › should return the correct index when + + › should allow test siblings to modify + + › might get confusing + + › should handle length properties that cannot + + › should recognize various types + + › should not output colors to pipe + + › should convert string to a RegExp + + › should escape and convert string to a RegExp + + › should convert grep string to a RegExp +[MOCK - cursorTo(12, 5)] +[MOCK - cursorRestorePosition]" +`; + +exports[`Watch mode flows Results in pattern mode get truncated appropriately 2`] = ` +" + + + pattern › t +[MOCK - cursorSavePosition] + + + Pattern matches 9 tests + from cached test suites. + + › should return the corre... + + › should allow test sibli... + + › might get confusing + + › should handle length pr... + + › should recognize variou... + + › should not output color... + + › should convert string t... + + › should escape and conve... + + › should convert grep str... +[MOCK - cursorTo(12, 5)] +[MOCK - cursorRestorePosition]" +`; diff --git a/packages/jest-cli/src/__tests__/__snapshots__/watch-test.js.snap b/packages/jest-cli/src/__tests__/__snapshots__/watch-test.js.snap index df2e6271a6cb..7d7874e50262 100644 --- a/packages/jest-cli/src/__tests__/__snapshots__/watch-test.js.snap +++ b/packages/jest-cli/src/__tests__/__snapshots__/watch-test.js.snap @@ -1,9 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + exports[`Watch mode flows Runs Jest once by default and shows usage 1`] = ` Array [ " Watch Usage › Press o to only run tests related to changed files. › Press p to filter by a filename regex pattern. + › Press t to filter by a test name regex pattern. › Press q to quit watch mode. › Press Enter to trigger a test run. ", diff --git a/packages/jest-cli/src/__tests__/watch-pattern-mode-test.js b/packages/jest-cli/src/__tests__/watch-filename-pattern-mode-test.js similarity index 99% rename from packages/jest-cli/src/__tests__/watch-pattern-mode-test.js rename to packages/jest-cli/src/__tests__/watch-filename-pattern-mode-test.js index 3809d16b69d6..6f9c6c8ac52b 100644 --- a/packages/jest-cli/src/__tests__/watch-pattern-mode-test.js +++ b/packages/jest-cli/src/__tests__/watch-filename-pattern-mode-test.js @@ -123,8 +123,8 @@ describe('Watch mode flows', () => { // Argv is updated with the current pattern expect(argv).toEqual({ - '_': ['p.*3'], onlyChanged: false, + testPathPattern: 'p.*3', watch: true, watchAll: false, }); diff --git a/packages/jest-cli/src/__tests__/watch-test-name-pattern-mode-test.js b/packages/jest-cli/src/__tests__/watch-test-name-pattern-mode-test.js new file mode 100644 index 000000000000..ea4f6efaa2fb --- /dev/null +++ b/packages/jest-cli/src/__tests__/watch-test-name-pattern-mode-test.js @@ -0,0 +1,187 @@ +/** + * 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. + * + * @emails oncall+jsinfra + */ + +'use strict'; + +const chalk = require('chalk'); +const {KEYS} = require('../constants'); + +const runJestMock = jest.fn(); + +let terminalWidth; + +jest.mock('ansi-escapes', () => ({ + clearScreen: '[MOCK - clearScreen]', + cursorDown: (count = 1) => `[MOCK - cursorDown(${count})]`, + cursorHide: '[MOCK - cursorHide]', + cursorRestorePosition: '[MOCK - cursorRestorePosition]', + cursorSavePosition: '[MOCK - cursorSavePosition]', + cursorShow: '[MOCK - cursorShow]', + cursorTo: (x, y) => `[MOCK - cursorTo(${x}, ${y})]`, +})); + +jest.mock('../SearchSource', () => class { + findMatchingTests(pattern) { + return {paths: []}; + } +}); + +jest.doMock('chalk', () => Object.assign( + new chalk.constructor({enabled: false}), + {stripColor: str => str}, +)); + +jest.doMock('../runJest', () => function() { + const args = Array.from(arguments); + runJestMock.apply(null, args); + + // Call the callback + args[args.length - 1]({ + snapshot: {}, + testResults: [ + { + testResults: [{title: 'should return the correct index when'}], + }, + { + testResults: [{title: 'should allow test siblings to modify'}], + }, + { + testResults: [{title: 'might get confusing'}], + }, + { + testResults: [{title: 'should handle length properties that cannot'}], + }, + { + testResults: [{title: 'should recognize various types'}], + }, + { + testResults: [{title: 'should recognize null and undefined'}], + }, + { + testResults: [{title: 'should not output colors to pipe'}], + }, + { + testResults: [{title: 'should convert string to a RegExp'}], + }, + { + testResults: [{title: 'should escape and convert string to a RegExp'}], + }, + { + testResults: [{title: 'should convert grep string to a RegExp'}], + }, + ], + }); + + return Promise.resolve(); +}); + +jest.doMock('../lib/terminalUtils', () => ({ + getTerminalWidth: () => terminalWidth, +})); + +const watch = require('../watch'); + +afterEach(runJestMock.mockReset); + +describe('Watch mode flows', () => { + let pipe; + let hasteMap; + let argv; + let hasteContext; + let config; + let stdin; + + beforeEach(() => { + terminalWidth = 80; + pipe = {write: jest.fn()}; + hasteMap = {on: () => {}}; + argv = {}; + hasteContext = {}; + config = {}; + stdin = new MockStdin(); + }); + + it('Pressing "T" enters pattern mode', () => { + config = {rootDir: ''}; + watch(config, pipe, argv, hasteMap, hasteContext, stdin); + + // Write a enter pattern mode + stdin.emit(KEYS.T); + expect(pipe.write).toBeCalledWith(' pattern › '); + + const assertPattern = hex => { + pipe.write.mockReset(); + stdin.emit(hex); + expect(pipe.write.mock.calls.join('\n')).toMatchSnapshot(); + }; + + const toHex = char => Number(char.charCodeAt(0)).toString(16); + + // Write a pattern + ['c', 'o', 'n', ' ', '1', '2'] + .map(toHex) + .forEach(assertPattern); + + [KEYS.BACKSPACE, KEYS.BACKSPACE] + .forEach(assertPattern); + + ['*'] + .map(toHex) + .forEach(assertPattern); + + // Runs Jest again + runJestMock.mockReset(); + stdin.emit(KEYS.ENTER); + expect(runJestMock).toBeCalled(); + + // Argv is updated with the current pattern + expect(argv).toEqual({ + onlyChanged: false, + testNamePattern: 'con *', + watch: true, + watchAll: false, + }); + }); + + it('Results in pattern mode get truncated appropriately', () => { + config = {rootDir: ''}; + watch(config, pipe, argv, hasteMap, hasteContext, stdin); + + stdin.emit(KEYS.T); + + [50, 30].forEach(width => { + terminalWidth = width; + stdin.emit(KEYS.BACKSPACE); + pipe.write.mockReset(); + stdin.emit(KEYS.T); + expect(pipe.write.mock.calls.join('\n')).toMatchSnapshot(); + }); + }); +}); + +class MockStdin { + constructor() { + this._callbacks = []; + } + + setRawMode() {} + + resume() {} + + setEncoding() {} + + on(evt, callback) { + this._callbacks.push(callback); + } + + emit(key) { + this._callbacks.forEach(cb => cb(key)); + } +} diff --git a/packages/jest-cli/src/__tests__/watch-test.js b/packages/jest-cli/src/__tests__/watch-test.js index 9f308a3fb754..6cb3438d8d74 100644 --- a/packages/jest-cli/src/__tests__/watch-test.js +++ b/packages/jest-cli/src/__tests__/watch-test.js @@ -48,6 +48,40 @@ describe('Watch mode flows', () => { stdin = new MockStdin(); }); + it('Correctly passing test path pattern', () => { + argv.testPathPattern = 'test-*'; + config.testPathPattern = 'test-*'; + + watch(config, pipe, argv, hasteMap, hasteContext, stdin); + + expect(runJestMock).toBeCalledWith( + hasteContext, + config, + argv, + pipe, + new TestWatcher({isWatchMode: true}), + jasmine.any(Function), + jasmine.any(Function), + ); + }); + + it('Correctly passing test name pattern', () => { + argv.testNamePattern = 'test-*'; + config.testNamePattern = 'test-*'; + + watch(config, pipe, argv, hasteMap, hasteContext, stdin); + + expect(runJestMock).toBeCalledWith( + hasteContext, + config, + argv, + pipe, + new TestWatcher({isWatchMode: true}), + jasmine.any(Function), + jasmine.any(Function), + ); + }); + it('Runs Jest once by default and shows usage', () => { watch(config, pipe, argv, hasteMap, hasteContext, stdin); expect(runJestMock).toBeCalledWith( @@ -70,7 +104,6 @@ describe('Watch mode flows', () => { expect(runJestMock).toBeCalled(); expect(argv).toEqual({ - '_': '', onlyChanged: true, watch: true, watchAll: false, @@ -85,7 +118,6 @@ describe('Watch mode flows', () => { expect(runJestMock).toBeCalled(); expect(argv).toEqual({ - '_': '', onlyChanged: false, watch: false, watchAll: true, diff --git a/packages/jest-cli/src/assets/jest_logo.png b/packages/jest-cli/src/assets/jest_logo.png index 5c80cb522bd5..079356bc1611 100644 Binary files a/packages/jest-cli/src/assets/jest_logo.png and b/packages/jest-cli/src/assets/jest_logo.png differ diff --git a/packages/jest-cli/src/cli/args.js b/packages/jest-cli/src/cli/args.js index e2823d267254..d721f7928223 100644 --- a/packages/jest-cli/src/cli/args.js +++ b/packages/jest-cli/src/cli/args.js @@ -96,6 +96,11 @@ const options = { 'reported in the output.', type: 'boolean', }, + coverageDirectory: { + default: undefined, + description: 'The directory where Jest should output its coverage files.', + type: 'string', + }, debug: { description: 'Print debugging info about your jest config.', type: 'boolean', diff --git a/packages/jest-cli/src/constants.js b/packages/jest-cli/src/constants.js index f9b7a1248523..a82a66e73d66 100644 --- a/packages/jest-cli/src/constants.js +++ b/packages/jest-cli/src/constants.js @@ -29,6 +29,7 @@ const KEYS = { P: '70', Q: '71', QUESTION_MARK: '3f', + T: '74', U: '75', }; diff --git a/packages/jest-cli/src/lib/Prompt.js b/packages/jest-cli/src/lib/Prompt.js new file mode 100644 index 000000000000..a0b621e6dc13 --- /dev/null +++ b/packages/jest-cli/src/lib/Prompt.js @@ -0,0 +1,88 @@ +/** + * 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'; + +const {KEYS} = require('../constants'); + +class Prompt { + _entering: boolean; + _value: string; + _onChange: Function; + _onSuccess: Function; + _onCancel: Function; + + constructor() { + (this:any)._onResize = this._onResize.bind(this); + } + + _onResize() { + this._onChange(this._value); + } + + enter( + onChange: Function, + onSuccess: Function, + onCancel: Function, + ) { + this._entering = true; + this._value = ''; + this._onChange = onChange; + this._onSuccess = onSuccess; + this._onCancel = onCancel; + + onChange(this._value); + + process.stdout.on('resize', this._onResize); + } + + put( + key: string, + ) { + switch (key) { + case KEYS.ENTER: + this._entering = false; + this._onSuccess(this._value); + this.abort(); + break; + case KEYS.ESCAPE: + this._entering = false; + this._onCancel(this._value); + this.abort(); + break; + case KEYS.ARROW_DOWN: + case KEYS.ARROW_LEFT: + case KEYS.ARROW_RIGHT: + case KEYS.ARROW_UP: + break; + default: + const char = new Buffer(key, 'hex').toString(); + + this._value = key === KEYS.BACKSPACE + ? this._value.slice(0, -1) + : this._value + char; + + this._onChange(this._value); + break; + } + } + + abort() { + this._entering = false; + this._value = ''; + process.stdout.removeListener('resize', this._onResize); + } + + isEntering() { + return this._entering; + } +} + +module.exports = Prompt; diff --git a/packages/jest-cli/src/lib/__tests__/Prompt-test.js b/packages/jest-cli/src/lib/__tests__/Prompt-test.js new file mode 100644 index 000000000000..47913f85c96f --- /dev/null +++ b/packages/jest-cli/src/lib/__tests__/Prompt-test.js @@ -0,0 +1,71 @@ +/** +* 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. +* +* @emails oncall+jsinfra +*/ +'use strict'; + +const Prompt = require('../Prompt'); +let {KEYS} = require('../../constants'); + +KEYS = Object.assign({}, KEYS, { + E: '65', + S: '73', +}); + +it('calls handler on change value', () => { + const prompt = new Prompt(); + const onChange = jest.fn(); + + prompt.enter(onChange, jest.fn(), jest.fn()); + + expect(onChange).toHaveBeenLastCalledWith(''); + + prompt.put(KEYS.T); + expect(onChange).toHaveBeenLastCalledWith('t'); + + prompt.put(KEYS.E); + expect(onChange).toHaveBeenLastCalledWith('te'); + + prompt.put(KEYS.S); + expect(onChange).toHaveBeenLastCalledWith('tes'); + + prompt.put(KEYS.T); + expect(onChange).toHaveBeenLastCalledWith('test'); + + expect(onChange).toHaveBeenCalledTimes(5); +}); + +it('calls handler on success prompt', () => { + const prompt = new Prompt(); + const onSuccess = jest.fn(); + + prompt.enter(jest.fn(), onSuccess, jest.fn()); + + prompt.put(KEYS.T); + prompt.put(KEYS.E); + prompt.put(KEYS.S); + prompt.put(KEYS.T); + prompt.put(KEYS.ENTER); + + expect(onSuccess).toHaveBeenCalledWith('test'); +}); + +it('calls handler on cancel prompt', () => { + const prompt = new Prompt(); + const onCancel = jest.fn(); + + prompt.enter(jest.fn(), jest.fn(), onCancel); + + prompt.put(KEYS.T); + prompt.put(KEYS.E); + prompt.put(KEYS.S); + prompt.put(KEYS.T); + prompt.put(KEYS.ESCAPE); + + expect(onCancel).toHaveBeenCalled(); +}); diff --git a/packages/jest-cli/src/lib/__tests__/__snapshots__/formatTestNameByPattern-test.js.snap b/packages/jest-cli/src/lib/__tests__/__snapshots__/formatTestNameByPattern-test.js.snap new file mode 100644 index 000000000000..b660c992e17a --- /dev/null +++ b/packages/jest-cli/src/lib/__tests__/__snapshots__/formatTestNameByPattern-test.js.snap @@ -0,0 +1,27 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`for multiline test name returns test name with highlighted pattern and replaced line breaks 1`] = `"should⏎ name the ⏎function you at..."`; + +exports[`for multiline test name returns test name with highlighted pattern and replaced line breaks 2`] = `"should⏎ name the ⏎function you at..."`; + +exports[`for multiline test name returns test name with highlighted pattern and replaced line breaks 3`] = `"should⏎ name the ⏎function you at..."`; + +exports[`for one line test name pattern in the middle test name with cutted tail and cutted highlighted pattern 1`] = `"should nam..."`; + +exports[`for one line test name pattern in the middle test name with cutted tail and highlighted pattern 1`] = `"should name the functi..."`; + +exports[`for one line test name pattern in the middle test name with highlighted cutted 1`] = `"sho..."`; + +exports[`for one line test name pattern in the middle test name with highlighted pattern returns 1`] = `"should name the function you attach"`; + +exports[`for one line test name pattern in the tail returns test name with cutted tail and cutted highlighted pattern 1`] = `"should name the function you a..."`; + +exports[`for one line test name pattern in the tail returns test name with highlighted cutted 1`] = `"sho..."`; + +exports[`for one line test name pattern in the tail returns test name with highlighted pattern 1`] = `"should name the function you attach"`; + +exports[`for one line test name with pattern in the head returns test name with cutted tail and cutted highlighted pattern 1`] = `"shoul..."`; + +exports[`for one line test name with pattern in the head returns test name with cutted tail and highlighted pattern 1`] = `"should name the function yo..."`; + +exports[`for one line test name with pattern in the head returns test name with highlighted pattern 1`] = `"should name the function you attach"`; diff --git a/packages/jest-cli/src/lib/__tests__/__snapshots__/highlight-test.js.snap b/packages/jest-cli/src/lib/__tests__/__snapshots__/highlight-test.js.snap index 5894972ca74b..4ce34c19542e 100644 --- a/packages/jest-cli/src/lib/__tests__/__snapshots__/highlight-test.js.snap +++ b/packages/jest-cli/src/lib/__tests__/__snapshots__/highlight-test.js.snap @@ -1,75 +1,77 @@ -exports[`test dims everything when there is no match 1`] = `"jest-cli/__tests__/watch-test.js"`; +// Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`test dims everything when there is no match 2`] = `"...t-cli/__tests__/watch-test.js"`; +exports[`dims everything when there is no match 1`] = `"jest-cli/__tests__/watch-test.js"`; -exports[`test dims everything when there is no match 3`] = `".../__tests__/watch-test.js"`; +exports[`dims everything when there is no match 2`] = `"...t-cli/__tests__/watch-test.js"`; -exports[`test dims everything when there is no match 4`] = `"...sts__/watch-test.js"`; +exports[`dims everything when there is no match 3`] = `".../__tests__/watch-test.js"`; -exports[`test dims everything when there is no match 5`] = `"...watch-test.js"`; +exports[`dims everything when there is no match 4`] = `"...sts__/watch-test.js"`; -exports[`test dims everything when there is no match 6`] = `"...-test.js"`; +exports[`dims everything when there is no match 5`] = `"...watch-test.js"`; -exports[`test dims everything when there is no match 7`] = `"....js"`; +exports[`dims everything when there is no match 6`] = `"...-test.js"`; -exports[`test dims everything when there is no match 8`] = `"./watch-test.js"`; +exports[`dims everything when there is no match 7`] = `"....js"`; -exports[`test highlight the trimmed part when there is only a rootDir match 1`] = `"jest-cli/__tests__/watch-test.js"`; +exports[`dims everything when there is no match 8`] = `"./watch-test.js"`; -exports[`test highlight the trimmed part when there is only a rootDir match 2`] = `"...t-cli/__tests__/watch-test.js"`; +exports[`highlight the trimmed part when there is only a rootDir match 1`] = `"jest-cli/__tests__/watch-test.js"`; -exports[`test highlight the trimmed part when there is only a rootDir match 3`] = `".../__tests__/watch-test.js"`; +exports[`highlight the trimmed part when there is only a rootDir match 2`] = `"...t-cli/__tests__/watch-test.js"`; -exports[`test highlight the trimmed part when there is only a rootDir match 4`] = `"...sts__/watch-test.js"`; +exports[`highlight the trimmed part when there is only a rootDir match 3`] = `".../__tests__/watch-test.js"`; -exports[`test highlight the trimmed part when there is only a rootDir match 5`] = `"...watch-test.js"`; +exports[`highlight the trimmed part when there is only a rootDir match 4`] = `"...sts__/watch-test.js"`; -exports[`test highlight the trimmed part when there is only a rootDir match 6`] = `"...-test.js"`; +exports[`highlight the trimmed part when there is only a rootDir match 5`] = `"...watch-test.js"`; -exports[`test highlight the trimmed part when there is only a rootDir match 7`] = `"....js"`; +exports[`highlight the trimmed part when there is only a rootDir match 6`] = `"...-test.js"`; -exports[`test highlight the trimmed part when there is only a rootDir match 8`] = `"./watch-test.js"`; +exports[`highlight the trimmed part when there is only a rootDir match 7`] = `"....js"`; -exports[`test highlights everything when there is a full match 1`] = `"jest-cli/__tests__/watch-test.js"`; +exports[`highlight the trimmed part when there is only a rootDir match 8`] = `"./watch-test.js"`; -exports[`test highlights everything when there is a full match 2`] = `"...t-cli/__tests__/watch-test.js"`; +exports[`highlights everything when there is a full match 1`] = `"jest-cli/__tests__/watch-test.js"`; -exports[`test highlights everything when there is a full match 3`] = `".../__tests__/watch-test.js"`; +exports[`highlights everything when there is a full match 2`] = `"...t-cli/__tests__/watch-test.js"`; -exports[`test highlights everything when there is a full match 4`] = `"...sts__/watch-test.js"`; +exports[`highlights everything when there is a full match 3`] = `".../__tests__/watch-test.js"`; -exports[`test highlights everything when there is a full match 5`] = `"...watch-test.js"`; +exports[`highlights everything when there is a full match 4`] = `"...sts__/watch-test.js"`; -exports[`test highlights everything when there is a full match 6`] = `"...-test.js"`; +exports[`highlights everything when there is a full match 5`] = `"...watch-test.js"`; -exports[`test highlights everything when there is a full match 7`] = `"....js"`; +exports[`highlights everything when there is a full match 6`] = `"...-test.js"`; -exports[`test highlights everything when there is a full match 8`] = `"./watch-test.js"`; +exports[`highlights everything when there is a full match 7`] = `"....js"`; -exports[`test highlights part of file name when there is a partially match of the file name 1`] = `"jest-cli/__tests__/watch-test.js"`; +exports[`highlights everything when there is a full match 8`] = `"./watch-test.js"`; -exports[`test highlights part of file name when there is a partially match of the file name 2`] = `"...t-cli/__tests__/watch-test.js"`; +exports[`highlights part of file name when there is a partially match of the file name 1`] = `"jest-cli/__tests__/watch-test.js"`; -exports[`test highlights part of file name when there is a partially match of the file name 3`] = `".../__tests__/watch-test.js"`; +exports[`highlights part of file name when there is a partially match of the file name 2`] = `"...t-cli/__tests__/watch-test.js"`; -exports[`test highlights part of file name when there is a partially match of the file name 4`] = `"...sts__/watch-test.js"`; +exports[`highlights part of file name when there is a partially match of the file name 3`] = `".../__tests__/watch-test.js"`; -exports[`test highlights part of file name when there is a partially match of the file name 5`] = `"...watch-test.js"`; +exports[`highlights part of file name when there is a partially match of the file name 4`] = `"...sts__/watch-test.js"`; -exports[`test highlights part of file name when there is a partially match of the file name 6`] = `"...-test.js"`; +exports[`highlights part of file name when there is a partially match of the file name 5`] = `"...watch-test.js"`; -exports[`test highlights part of file name when there is a partially match of the file name 7`] = `"....js"`; +exports[`highlights part of file name when there is a partially match of the file name 6`] = `"...-test.js"`; -exports[`test highlights part of file name when there is a partially match of the file name 8`] = `"./watch-test.js"`; +exports[`highlights part of file name when there is a partially match of the file name 7`] = `"....js"`; -exports[`test highlights the trimmed part there is a non visible match 1`] = `"...t-cli/__tests__/watch-test.js"`; +exports[`highlights part of file name when there is a partially match of the file name 8`] = `"./watch-test.js"`; -exports[`test highlights the trimmed part there is a non visible match 2`] = `".../__tests__/watch-test.js"`; +exports[`highlights the trimmed part there is a non visible match 1`] = `"...t-cli/__tests__/watch-test.js"`; -exports[`test highlights the trimmed part there is a non visible match 3`] = `"...sts__/watch-test.js"`; +exports[`highlights the trimmed part there is a non visible match 2`] = `".../__tests__/watch-test.js"`; -exports[`test highlights the trimmed part there is a non visible match 4`] = `"...watch-test.js"`; +exports[`highlights the trimmed part there is a non visible match 3`] = `"...sts__/watch-test.js"`; -exports[`test highlights the trimmed part there is a non visible match 5`] = `"...-test.js"`; +exports[`highlights the trimmed part there is a non visible match 4`] = `"...watch-test.js"`; -exports[`test highlights the trimmed part there is a non visible match 6`] = `"....js"`; +exports[`highlights the trimmed part there is a non visible match 5`] = `"...-test.js"`; + +exports[`highlights the trimmed part there is a non visible match 6`] = `"....js"`; diff --git a/packages/jest-cli/src/lib/__tests__/formatTestNameByPattern-test.js b/packages/jest-cli/src/lib/__tests__/formatTestNameByPattern-test.js new file mode 100644 index 000000000000..f15810edbb5f --- /dev/null +++ b/packages/jest-cli/src/lib/__tests__/formatTestNameByPattern-test.js @@ -0,0 +1,85 @@ +/** +* 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. +* +* @emails oncall+jsinfra +*/ + +'use strict'; + +const formatTestNameByPattern = require('../formatTestNameByPattern'); + +describe('for multiline test name returns', () => { + const testNames = [ + 'should\n name the \nfunction you attach', + 'should\r\n name the \r\nfunction you attach', + 'should\r name the \rfunction you attach', + ]; + + it('test name with highlighted pattern and replaced line breaks', () => { + const pattern = 'name'; + + testNames.forEach(testName => { + expect(formatTestNameByPattern(testName, pattern, 36)).toMatchSnapshot(); + }); + }); +}); + +describe('for one line test name', () => { + const testName = 'should name the function you attach'; + + describe('with pattern in the head returns', () => { + const pattern = 'should'; + + it('test name with highlighted pattern', () => { + expect(formatTestNameByPattern(testName, pattern, 35)).toMatchSnapshot(); + }); + + it('test name with cutted tail and highlighted pattern', () => { + expect(formatTestNameByPattern(testName, pattern, 30)).toMatchSnapshot(); + }); + + it('test name with cutted tail and cutted highlighted pattern', () => { + expect(formatTestNameByPattern(testName, pattern, 8)).toMatchSnapshot(); + }); + }); + + describe('pattern in the middle', () => { + const pattern = 'name'; + + it('test name with highlighted pattern returns', () => { + expect(formatTestNameByPattern(testName, pattern, 35)).toMatchSnapshot(); + }); + + it('test name with cutted tail and highlighted pattern', () => { + expect(formatTestNameByPattern(testName, pattern, 25)).toMatchSnapshot(); + }); + + it('test name with cutted tail and cutted highlighted pattern', () => { + expect(formatTestNameByPattern(testName, pattern, 13)).toMatchSnapshot(); + }); + + it('test name with highlighted cutted', () => { + expect(formatTestNameByPattern(testName, pattern, 6)).toMatchSnapshot(); + }); + }); + + describe('pattern in the tail returns', () => { + const pattern = 'attach'; + + it('test name with highlighted pattern', () => { + expect(formatTestNameByPattern(testName, pattern, 35)).toMatchSnapshot(); + }); + + it('test name with cutted tail and cutted highlighted pattern', () => { + expect(formatTestNameByPattern(testName, pattern, 33)).toMatchSnapshot(); + }); + + it('test name with highlighted cutted', () => { + expect(formatTestNameByPattern(testName, pattern, 6)).toMatchSnapshot(); + }); + }); +}); diff --git a/packages/jest-cli/src/lib/__tests__/setWatchMode-test.js b/packages/jest-cli/src/lib/__tests__/setState.js similarity index 70% rename from packages/jest-cli/src/lib/__tests__/setWatchMode-test.js rename to packages/jest-cli/src/lib/__tests__/setState.js index e5ba9e38676e..100f000cb0d5 100644 --- a/packages/jest-cli/src/lib/__tests__/setWatchMode-test.js +++ b/packages/jest-cli/src/lib/__tests__/setState.js @@ -9,42 +9,46 @@ */ 'use strict'; -const setWatchMode = require('../setWatchMode'); +const setState = require('../setState'); -describe('setWatchMode()', () => { +describe('setState()', () => { it('Sets watch and watchAll flags based on the mode', () => { let argv = {}; - setWatchMode(argv, 'watch', {}); + setState(argv, 'watch', {}); expect(argv.watch).toBeTruthy(); expect(argv.watchAll).toBeFalsy(); argv = {}; - setWatchMode(argv, 'watchAll', {}); + setState(argv, 'watchAll', {}); expect(argv.watch).toBeFalsy(); expect(argv.watchAll).toBeTruthy(); }); it('Sets the onlyChanged flag then in watch and with no pattern', () => { let argv = {}; - setWatchMode(argv, 'watch', {}); + setState(argv, 'watch', {}); expect(argv.onlyChanged).toBeTruthy(); argv = {testPathPattern: 'jest-cli'}; - setWatchMode(argv, 'watch', {}); + setState(argv, 'watch', {}); + expect(argv.onlyChanged).toBeFalsy(); + + argv = {testNamePattern: 'name-test'}; + setState(argv, 'watch', {}); expect(argv.onlyChanged).toBeFalsy(); argv = {}; - setWatchMode(argv, 'watchAll', {}); + setState(argv, 'watchAll', {}); expect(argv.onlyChanged).toBeFalsy(); }); it('Sets the noSCM flag when the options specify it', () => { let argv = {}; - setWatchMode(argv, 'watch', {noSCM: true}); + setState(argv, 'watch', {noSCM: true}); expect(argv.noSCM).toBeTruthy(); argv = {}; - setWatchMode(argv, 'watch', {noSCM: false}); + setState(argv, 'watch', {noSCM: false}); expect(argv.noSCM).toBeFalsy(); }); }); diff --git a/packages/jest-cli/src/lib/colorize.js b/packages/jest-cli/src/lib/colorize.js new file mode 100644 index 000000000000..a07393899474 --- /dev/null +++ b/packages/jest-cli/src/lib/colorize.js @@ -0,0 +1,19 @@ +/** + * 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'; + +const chalk = require('chalk'); + +module.exports = (str: string, start: number, end: number) => ( + chalk.dim(str.slice(0, start)) + + chalk.reset(str.slice(start, end)) + + chalk.dim(str.slice(end)) +); diff --git a/packages/jest-cli/src/lib/formatTestNameByPattern.js b/packages/jest-cli/src/lib/formatTestNameByPattern.js new file mode 100644 index 000000000000..ea40f58b32f8 --- /dev/null +++ b/packages/jest-cli/src/lib/formatTestNameByPattern.js @@ -0,0 +1,63 @@ +/** + * 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'; + +const chalk = require('chalk'); +const colorize = require('./colorize'); + +const DOTS = '...'; +const ENTER = '⏎'; + +module.exports = (testName: string, pattern: string, width: number) => { + const inlineTestName = testName.replace(/(\r\n|\n|\r)/gm, ENTER); + + let regexp; + + try { + regexp = new RegExp(pattern, 'i'); + } catch (e) { + return chalk.dim(inlineTestName); + } + + const match = inlineTestName.match(regexp); + + if (!match) { + return chalk.dim(inlineTestName); + } + + // $FlowFixMe + const startPatternIndex = Math.max(match.index, 0); + const endPatternIndex = startPatternIndex + match[0].length; + + if (inlineTestName.length <= width) { + return colorize(inlineTestName, startPatternIndex, endPatternIndex); + } + + const slicedTestName = inlineTestName.slice(0, width - DOTS.length); + + if (startPatternIndex < slicedTestName.length) { + if (endPatternIndex > slicedTestName.length) { + return colorize( + slicedTestName + DOTS, + startPatternIndex, + slicedTestName.length + DOTS.length, + ); + } else { + return colorize( + slicedTestName + DOTS, + Math.min(startPatternIndex, slicedTestName.length), + endPatternIndex, + ); + } + } + + return `${chalk.dim(slicedTestName)}${chalk.reset(DOTS)}`; +}; diff --git a/packages/jest-cli/src/lib/highlight.js b/packages/jest-cli/src/lib/highlight.js index fdb04dab7bbd..37247ce73b39 100644 --- a/packages/jest-cli/src/lib/highlight.js +++ b/packages/jest-cli/src/lib/highlight.js @@ -12,19 +12,11 @@ const chalk = require('chalk'); const path = require('path'); - -const hit = chalk.reset; -const miss = chalk.dim; +const colorize = require('./colorize'); const trim = '...'; const relativePathHead = `.${path.sep}`; -const colorize = (str: string, start: number, end: number) => ( - miss(str.slice(0, start)) + - hit(str.slice(start, end)) + - miss(str.slice(end)) -); - const highlight = ( rawPath: string, filePath: string, @@ -37,7 +29,7 @@ const highlight = ( try { regexp = new RegExp(pattern, 'i'); } catch (e) { - return miss(filePath); + return chalk.dim(filePath); } rawPath = chalk.stripColor(rawPath); @@ -45,7 +37,7 @@ const highlight = ( const match = rawPath.match(regexp); if (!match) { - return miss(filePath); + return chalk.dim(filePath); } let offset; diff --git a/packages/jest-cli/src/lib/setWatchMode.js b/packages/jest-cli/src/lib/setState.js similarity index 55% rename from packages/jest-cli/src/lib/setWatchMode.js rename to packages/jest-cli/src/lib/setState.js index c710ba4e108c..41db1f2767d7 100644 --- a/packages/jest-cli/src/lib/setWatchMode.js +++ b/packages/jest-cli/src/lib/setState.js @@ -11,11 +11,13 @@ const buildTestPathPatternInfo = require('./buildTestPathPatternInfo'); -const setWatchMode = ( +module.exports = ( argv: Object, mode: 'watch' | 'watchAll', - options?: Object, + options?: {}, ) => { + options = options || {}; + if (mode === 'watch') { argv.watch = true; argv.watchAll = false; @@ -24,15 +26,25 @@ const setWatchMode = ( argv.watchAll = true; } - // Reset before setting these to the new values - argv._ = (options && options.pattern) || ''; + if (options.testPathPattern) { + argv.testPathPattern = options.testPathPattern; + } else if (options.testPathPattern === '') { + delete argv.testPathPattern; + delete argv._; + } + + if (options.testNamePattern) { + argv.testNamePattern = options.testNamePattern; + } else if (options.testNamePattern === '') { + delete argv.testNamePattern; + } + argv.onlyChanged = false; - argv.onlyChanged = - buildTestPathPatternInfo(argv).input === '' && !argv.watchAll; + argv.onlyChanged = buildTestPathPatternInfo(argv).input === '' + && !argv.watchAll + && !argv.testNamePattern; - if (options && options.noSCM) { + if (options.noSCM) { argv.noSCM = true; } }; - -module.exports = setWatchMode; diff --git a/packages/jest-cli/src/patternMode.js b/packages/jest-cli/src/patternMode.js deleted file mode 100644 index 1cecce80786c..000000000000 --- a/packages/jest-cli/src/patternMode.js +++ /dev/null @@ -1,88 +0,0 @@ -/** - * 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 type {Config, Path} from 'types/Config'; - -const ansiEscapes = require('ansi-escapes'); -const chalk = require('chalk'); -const {getTerminalWidth} = require('./lib/terminalUtils'); -const highlight = require('./lib/highlight'); -const stringLength = require('string-length'); -const {trimAndFormatPath} = require('./reporters/utils'); - -const pluralizeFile = (total: number) => total === 1 ? 'file' : 'files'; - -const usage = (delimiter: string = '\n') => { - const messages = [ - `\n ${chalk.bold('Pattern Mode Usage')}`, - ` ${chalk.dim('\u203A Press')} ESC ${chalk.dim('to exit pattern mode.')}\n`, - ]; - - return messages.filter(message => !!message).join(delimiter) + '\n'; -}; - -const usageRows = usage().split('\n').length; - -const printTypeahead = ( - config: Config, - pipe: stream$Writable | tty$WriteStream, - pattern: string, - allResults: Array, - max: number = 10 -) => { - const total = allResults.length; - const results = allResults.slice(0, max); - const inputText = `${chalk.dim(' pattern \u203A')} ${pattern}`; - - pipe.write(ansiEscapes.eraseDown); - pipe.write(inputText); - pipe.write(ansiEscapes.cursorSavePosition); - - if (pattern) { - if (total) { - pipe.write(`\n\n Pattern matches ${total} ${pluralizeFile(total)}.`); - } else { - pipe.write(`\n\n Pattern matches no files.`); - } - - const width = getTerminalWidth(); - const prefix = ` ${chalk.dim('\u203A')} `; - const padding = stringLength(prefix) + 2; - - results - .map(rawPath => { - const filePath = trimAndFormatPath(padding, config, rawPath, width); - return highlight(rawPath, filePath, pattern, config.rootDir); - }) - .forEach( - filePath => pipe.write(`\n ${chalk.dim('\u203A')} ${filePath}`) - ); - - if (total > max) { - const more = total - max; - pipe.write( - `\n ${chalk.dim(`\u203A and ${more} more ${pluralizeFile(more)}`)}`, - ); - } - } else { - // eslint-disable-next-line max-len - pipe.write(`\n\n ${chalk.italic.yellow('Start typing to filter by a filename regex pattern.')}`); - } - - pipe.write(ansiEscapes.cursorTo(stringLength(inputText), usageRows - 1)); - pipe.write(ansiEscapes.cursorRestorePosition); -}; - -module.exports = { - printTypeahead, - usage, -}; diff --git a/packages/jest-cli/src/reporters/__tests__/__snapshots__/utils-test.js.snap b/packages/jest-cli/src/reporters/__tests__/__snapshots__/utils-test.js.snap index 23ae4c7b0e6f..a359b563e3ca 100644 --- a/packages/jest-cli/src/reporters/__tests__/__snapshots__/utils-test.js.snap +++ b/packages/jest-cli/src/reporters/__tests__/__snapshots__/utils-test.js.snap @@ -1,3 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + exports[`trimAndFormatPath() does not trim anything 1`] = `"1234567890/1234567890/1234.js"`; exports[`trimAndFormatPath() split at the path.sep index 1`] = `".../1234.js"`; @@ -8,9 +10,9 @@ exports[`trimAndFormatPath() trims dirname 1`] = `"...234567890/123 exports[`trimAndFormatPath() trims dirname and basename 1`] = `"...1234.js"`; -exports[`wrapAnsiString() returns the string unaltered if given a terminal width of zero 1`] = `"This string shouldn\'t cause you any trouble"`; +exports[`wrapAnsiString() returns the string unaltered if given a terminal width of zero 1`] = `"This string shouldn't cause you any trouble"`; -exports[`wrapAnsiString() returns the string unaltered if given a terminal width of zero 2`] = `"This string shouldn\'t cause you any trouble"`; +exports[`wrapAnsiString() returns the string unaltered if given a terminal width of zero 2`] = `"This string shouldn't cause you any trouble"`; exports[`wrapAnsiString() wraps a long string containing ansi chars 1`] = ` "abcde red- diff --git a/packages/jest-cli/src/runJest.js b/packages/jest-cli/src/runJest.js index d2caf8c407a9..691cde595fd1 100644 --- a/packages/jest-cli/src/runJest.js +++ b/packages/jest-cli/src/runJest.js @@ -23,7 +23,7 @@ const chalk = require('chalk'); const {Console, formatTestResults} = require('jest-util'); const getMaxWorkers = require('./lib/getMaxWorkers'); const path = require('path'); -const setWatchMode = require('./lib/setWatchMode'); +const setState = require('./lib/setState'); const getTestSummary = ( argv: Object, @@ -68,7 +68,7 @@ const runJest = ( if (patternInfo.onlyChanged && data.noSCM) { if (config.watch) { // Run all the tests - setWatchMode(argv, 'watchAll', { + setState(argv, 'watchAll', { noSCM: true, }); patternInfo = buildTestPathPatternInfo(argv); diff --git a/packages/jest-cli/src/watch.js b/packages/jest-cli/src/watch.js index 12c29a741a26..776b8f5ad3d6 100644 --- a/packages/jest-cli/src/watch.js +++ b/packages/jest-cli/src/watch.js @@ -17,13 +17,16 @@ const chalk = require('chalk'); const createHasteContext = require('./lib/createHasteContext'); const HasteMap = require('jest-haste-map'); const preRunMessage = require('./preRunMessage'); -const patternMode = require('./patternMode'); const runJest = require('./runJest'); -const setWatchMode = require('./lib/setWatchMode'); -const SearchSource = require('./SearchSource'); +const setState = require('./lib/setState'); const TestWatcher = require('./TestWatcher'); +const Prompt = require('./lib/Prompt'); +const TestPathPatternPrompt = require('./TestPathPatternPrompt'); +const TestNamePatternPrompt = require('./TestNamePatternPrompt'); const {KEYS, CLEAR} = require('./constants'); +const SNAPSHOT_EXTENSION = 'snap'; + const watch = ( config: Config, pipe: stream$Writable | tty$WriteStream, @@ -32,47 +35,54 @@ const watch = ( hasteContext: HasteContext, stdin?: stream$Readable | tty$ReadStream = process.stdin ) => { - setWatchMode(argv, argv.watch ? 'watch' : 'watchAll', { - pattern: argv._, + setState(argv, argv.watch ? 'watch' : 'watchAll', { + testNamePattern: argv.testNamePattern, + testPathPattern: argv.testPathPattern || (Array.isArray(argv._) + ? argv._.join('|') + : ''), }); - let currentPattern = ''; + let hasSnapshotFailure = false; - let isEnteringPattern = false; let isRunning = false; let testWatcher; let displayHelp = true; - let searchSource = new SearchSource(hasteContext, config); - - hasteMap.on('change', ({hasteFS, moduleMap}) => { - hasteContext = createHasteContext(config, {hasteFS, moduleMap}); - currentPattern = ''; - isEnteringPattern = false; - searchSource = new SearchSource(hasteContext, config); - startRun(); + + const prompt = new Prompt(); + + const testPathPatternPrompt = TestPathPatternPrompt( + config, + pipe, + prompt, + ); + + testPathPatternPrompt.updateSearchSource(hasteContext); + + const testNamePatternPrompt = TestNamePatternPrompt( + config, + pipe, + prompt, + ); + + hasteMap.on('change', ({eventsQueue, hasteFS, moduleMap}) => { + const hasOnlySnapshotChanges = eventsQueue.every(({filePath}) => { + return filePath.endsWith(`.${SNAPSHOT_EXTENSION}`); + }); + + if (!hasOnlySnapshotChanges) { + hasteContext = createHasteContext(config, {hasteFS, moduleMap}); + prompt.abort(); + testPathPatternPrompt.updateSearchSource(hasteContext); + startRun(); + } }); process.on('exit', () => { - if (isEnteringPattern) { + if (prompt.isEntering()) { pipe.write(ansiEscapes.cursorDown()); pipe.write(ansiEscapes.eraseDown); } }); - const writeCurrentPattern = () => { - let regex; - - try { - regex = new RegExp(currentPattern, 'i'); - } catch (e) {} - - const paths = regex ? - searchSource.findMatchingTests(currentPattern).paths : []; - - pipe.write(ansiEscapes.eraseLine); - pipe.write(ansiEscapes.cursorLeft); - patternMode.printTypeahead(config, pipe, currentPattern, paths); - }; - const startRun = (overrideConfig: Object = {}) => { if (isRunning) { return null; @@ -85,7 +95,10 @@ const watch = ( return runJest( hasteContext, // $FlowFixMe - Object.freeze(Object.assign({}, config, overrideConfig)), + Object.freeze(Object.assign({ + testNamePattern: argv.testNamePattern, + testPathPattern: argv.testPathPattern, + }, config, overrideConfig)), argv, pipe, testWatcher, @@ -101,6 +114,10 @@ const watch = ( pipe.write(usage(argv, hasSnapshotFailure)); displayHelp = !process.env.JEST_HIDE_USAGE; } + + testNamePatternPrompt.updateCachedTestResults( + results.testResults + ); }, ).then( () => {}, @@ -113,36 +130,9 @@ const watch = ( process.exit(0); return; } - if (isEnteringPattern) { - switch (key) { - case KEYS.ENTER: - isEnteringPattern = false; - setWatchMode(argv, 'watch', { - pattern: [currentPattern], - }); - startRun(); - break; - case KEYS.ESCAPE: - isEnteringPattern = false; - pipe.write(ansiEscapes.cursorHide); - pipe.write(ansiEscapes.clearScreen); - pipe.write(usage(argv, hasSnapshotFailure)); - pipe.write(ansiEscapes.cursorShow); - currentPattern = argv._[0]; - break; - case KEYS.ARROW_DOWN: - case KEYS.ARROW_LEFT: - case KEYS.ARROW_RIGHT: - case KEYS.ARROW_UP: - break; - default: - const char = new Buffer(key, 'hex').toString(); - currentPattern = key === KEYS.BACKSPACE - ? currentPattern.slice(0, -1) - : currentPattern + char; - writeCurrentPattern(); - break; - } + + if (prompt.isEntering()) { + prompt.put(key); return; } @@ -150,7 +140,7 @@ const watch = ( if ( isRunning && testWatcher && - [KEYS.Q, KEYS.ENTER, KEYS.A, KEYS.O, KEYS.P].indexOf(key) !== -1 + [KEYS.Q, KEYS.ENTER, KEYS.A, KEYS.O, KEYS.P, KEYS.T].indexOf(key) !== -1 ) { testWatcher.setState({interrupted: true}); return; @@ -167,21 +157,44 @@ const watch = ( startRun({updateSnapshot: true}); break; case KEYS.A: - setWatchMode(argv, 'watchAll'); + setState(argv, 'watchAll', { + testNamePattern: '', + testPathPattern: '', + }); startRun(); break; case KEYS.O: - setWatchMode(argv, 'watch'); + setState(argv, 'watch', { + testNamePattern: '', + testPathPattern: '', + }); startRun(); break; case KEYS.P: - isEnteringPattern = true; - currentPattern = ''; - pipe.write(ansiEscapes.cursorHide); - pipe.write(ansiEscapes.clearScreen); - pipe.write(patternMode.usage()); - pipe.write(ansiEscapes.cursorShow); - writeCurrentPattern(); + testPathPatternPrompt.run( + testPathPattern => { + setState(argv, 'watch', { + testNamePattern: '', + testPathPattern, + }); + + startRun(); + }, + onCancelPatternPrompt, + ); + break; + case KEYS.T: + testNamePatternPrompt.run( + testNamePattern => { + setState(argv, 'watch', { + testNamePattern, + testPathPattern: '', + }); + + startRun(); + }, + onCancelPatternPrompt, + ); break; case KEYS.QUESTION_MARK: if (process.env.JEST_HIDE_USAGE) { @@ -191,6 +204,13 @@ const watch = ( } }; + const onCancelPatternPrompt = () => { + pipe.write(ansiEscapes.cursorHide); + pipe.write(ansiEscapes.clearScreen); + pipe.write(usage(argv, hasSnapshotFailure)); + pipe.write(ansiEscapes.cursorShow); + }; + if (typeof stdin.setRawMode === 'function') { stdin.setRawMode(true); stdin.resume(); @@ -213,13 +233,14 @@ const usage = ( argv.watch ? chalk.dim(' \u203A Press ') + 'a' + chalk.dim(' to run all tests.') : null, - (argv.watchAll || argv._) && !argv.noSCM + (argv.watchAll || argv.testPathPattern || argv.testNamePattern) && !argv.noSCM ? chalk.dim(' \u203A Press ') + 'o' + chalk.dim(' to only run tests related to changed files.') : null, snapshotFailure ? chalk.dim(' \u203A Press ') + 'u' + chalk.dim(' to update failing snapshots.') : null, chalk.dim(' \u203A Press ') + 'p' + chalk.dim(' to filter by a filename regex pattern.'), + chalk.dim(' \u203A Press ') + 't' + chalk.dim(' to filter by a test name regex pattern.'), chalk.dim(' \u203A Press ') + 'q' + chalk.dim(' to quit watch mode.'), chalk.dim(' \u203A Press ') + 'Enter' + chalk.dim(' to trigger a test run.'), ]; diff --git a/packages/jest-config/package.json b/packages/jest-config/package.json index a3eb50cc3243..41d40ba021ce 100644 --- a/packages/jest-config/package.json +++ b/packages/jest-config/package.json @@ -14,7 +14,7 @@ "jest-jasmine2": "^18.1.0", "jest-regex-util": "^18.1.0", "jest-resolve": "^18.1.0", - "jest-validate": "^18.1.0", + "jest-validate": "^18.2.0", "pretty-format": "^18.1.0" } } diff --git a/packages/jest-config/src/__tests__/__snapshots__/normalize-test.js.snap b/packages/jest-config/src/__tests__/__snapshots__/normalize-test.js.snap index a49a145a3fb1..d737c3056952 100644 --- a/packages/jest-config/src/__tests__/__snapshots__/normalize-test.js.snap +++ b/packages/jest-config/src/__tests__/__snapshots__/normalize-test.js.snap @@ -1,3 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + exports[`Upgrade help logs a warning when \`scriptPreprocessor\` and/or \`preprocessorIgnorePatterns\` are used 1`] = ` "● Deprecation Warning: diff --git a/packages/jest-config/src/__tests__/normalize-test.js b/packages/jest-config/src/__tests__/normalize-test.js index a9037ab2557d..16309a9421b7 100644 --- a/packages/jest-config/src/__tests__/normalize-test.js +++ b/packages/jest-config/src/__tests__/normalize-test.js @@ -219,6 +219,28 @@ describe('transform', () => { }); }); +describe('haste', () => { + let Resolver; + beforeEach(() => { + Resolver = require('jest-resolve'); + Resolver.findNodeModule = jest.fn(name => name); + }); + + it('normalizes the path for hasteImplModulePath', () => { + const config = normalize({ + haste: { + hasteImplModulePath: '/hasteImpl.js', + }, + rootDir: '/root/', + }); + + expect(config.haste).toEqual({ + hasteImplModulePath: '/root/hasteImpl.js', + }); + }); +}); + + describe('setupTestFrameworkScriptFile', () => { let Resolver; beforeEach(() => { @@ -479,7 +501,7 @@ describe('babel-jest', () => { beforeEach(() => { Resolver = require('jest-resolve'); Resolver.findNodeModule = jest.fn( - name => '/node_modules' + path.sep + name, + name => path.sep + 'node_modules' + path.sep + name, ); }); @@ -490,9 +512,9 @@ describe('babel-jest', () => { expect(config.transform[0][0]).toBe(DEFAULT_JS_PATTERN); expect(config.transform[0][1]) - .toEqual('/node_modules' + path.sep + 'babel-jest'); + .toEqual(path.sep + 'node_modules' + path.sep + 'babel-jest'); expect(config.setupFiles) - .toEqual(['/node_modules' + path.sep + 'regenerator-runtime/runtime']); + .toEqual([path.sep + 'node_modules' + path.sep + 'regenerator-runtime/runtime']); }); it('uses babel-jest if babel-jest is explicitly specified in a custom transform config', () => { @@ -507,7 +529,7 @@ describe('babel-jest', () => { expect(config.transform[0][0]).toBe(customJSPattern); expect(config.transform[0][1]).toEqual('/node_modules/babel-jest'); expect(config.setupFiles) - .toEqual(['/node_modules' + path.sep + 'regenerator-runtime/runtime']); + .toEqual([path.sep + 'node_modules' + path.sep + 'regenerator-runtime/runtime']); }); it(`doesn't use babel-jest if its not available`, () => { @@ -534,7 +556,7 @@ describe('babel-jest', () => { }); expect(config.setupFiles) - .toEqual(['/node_modules' + path.sep + 'regenerator-runtime/runtime']); + .toEqual([path.sep + 'node_modules' + path.sep + 'regenerator-runtime/runtime']); }); }); @@ -655,6 +677,14 @@ describe('preset', () => { }); describe('preset without setupFiles', () => { + let Resolver; + beforeEach(() => { + Resolver = require('jest-resolve'); + Resolver.findNodeModule = jest.fn( + name => path.sep + 'node_modules' + path.sep + name, + ); + }); + beforeAll(() => { jest.mock( '/node_modules/react-native/jest-preset.json', diff --git a/packages/jest-config/src/normalize.js b/packages/jest-config/src/normalize.js index 1aa5317610b0..f7ee61e9c13d 100644 --- a/packages/jest-config/src/normalize.js +++ b/packages/jest-config/src/normalize.js @@ -322,6 +322,16 @@ function normalize(config: InitialConfig, argv: Object = {}) { case 'unmockedModulePathPatterns': value = normalizeUnmockedModulePathPatterns(config, key); break; + case 'haste': + value = Object.assign({}, config[key]); + if (value.hasteImplModulePath != null) { + value.hasteImplModulePath = resolve( + config.rootDir, + 'haste.hasteImplModulePath', + value.hasteImplModulePath, + ); + } + break; case 'automock': case 'bail': case 'browser': @@ -332,7 +342,6 @@ function normalize(config: InitialConfig, argv: Object = {}) { case 'coverageReporters': case 'coverageThreshold': case 'globals': - case 'haste': case 'logHeapUsage': case 'logTransformErrors': case 'moduleDirectories': diff --git a/packages/jest-config/src/setFromArgv.js b/packages/jest-config/src/setFromArgv.js index ce29233e35a0..611d280ecf63 100644 --- a/packages/jest-config/src/setFromArgv.js +++ b/packages/jest-config/src/setFromArgv.js @@ -13,6 +13,10 @@ function setFromArgv(config, argv) { config.collectCoverage = true; } + if (argv.coverageDirectory) { + config.coverageDirectory = argv.coverageDirectory; + } + if (argv.verbose) { config.verbose = argv.verbose; } 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 8c8ac3073f03..f236c580d46d 100644 --- a/packages/jest-diff/src/__tests__/__snapshots__/diff-test.js.snap +++ b/packages/jest-diff/src/__tests__/__snapshots__/diff-test.js.snap @@ -1,4 +1,6 @@ -exports[`test collapses big diffs to patch format 1`] = ` +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`collapses big diffs to patch format 1`] = ` "- Expected + Received @@ -15,7 +17,7 @@ exports[`test collapses big diffs to patch format 1`] = `  }" `; -exports[`test falls back to not call toJSON if objects look identical 1`] = ` +exports[`falls back to not call toJSON if objects look identical 1`] = ` "Compared values serialize to the same structure. Printing internal object structure without calling \`toJSON\` instead. @@ -29,4 +31,4 @@ Printing internal object structure without calling \`toJSON\` instead.   }" `; -exports[`test prints a fallback message if two objects truly look identical 1`] = `"Compared values have no visual difference."`; +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/index.js b/packages/jest-diff/src/index.js index e8d93049e71c..8beb375bcc94 100644 --- a/packages/jest-diff/src/index.js +++ b/packages/jest-diff/src/index.js @@ -47,15 +47,37 @@ function diff(a: any, b: any, options: ?DiffOptions): ?string { return NO_DIFF_MESSAGE; } - if (getType(a) !== getType(b)) { + const aType = getType(a); + let expectedType = aType; + let omitDifference = false; + if (aType === 'object' && typeof a.asymmetricMatch === 'function') { + if (a.$$typeof !== Symbol.for('jest.asymmetricMatcher')) { + // Do not know expected type of user-defined asymmetric matcher. + return null; + } + if (typeof a.getExpectedType !== 'function') { + // For example, expect.anything() matches either null or undefined + return null; + } + expectedType = a.getExpectedType(); + // Primitive types boolean and number omit difference below. + // For example, omit difference for expect.stringMatching(regexp) + omitDifference = expectedType === 'string'; + } + + if (expectedType !== getType(b)) { return ( ' Comparing two different types of values.' + - ` Expected ${chalk.green(getType(a))} but ` + + ` Expected ${chalk.green(expectedType)} but ` + `received ${chalk.red(getType(b))}.` ); } - switch (getType(a)) { + if (omitDifference) { + return null; + } + + switch (aType) { case 'string': const multiline = a.match(/[\r\n]/) !== -1 && b.indexOf('\n') !== -1; if (multiline) { diff --git a/packages/jest-editor-support/src/TestReconciler.js b/packages/jest-editor-support/src/TestReconciler.js index ccc8bc2d2984..64a67e99811c 100644 --- a/packages/jest-editor-support/src/TestReconciler.js +++ b/packages/jest-editor-support/src/TestReconciler.js @@ -17,7 +17,7 @@ import type { JestAssertionResults, TestFileAssertionStatus, TestAssertionStatus, - TestReconcilationState, + TestReconciliationState, } from './types'; /** @@ -123,7 +123,7 @@ module.exports = class TestReconciler { return parseInt(restOfTrace.split(':')[1], 10); } - statusToReconcilationState(status: string): TestReconcilationState { + statusToReconcilationState(status: string): TestReconciliationState { switch (status) { case 'passed': return 'KnownSuccess'; @@ -134,7 +134,7 @@ module.exports = class TestReconciler { } } - stateForTestFile(file: string): TestReconcilationState { + stateForTestFile(file: string): TestReconciliationState { const results = this.fileStatuses[file]; if (!results) { return 'Unknown'; diff --git a/packages/jest-editor-support/src/types.js b/packages/jest-editor-support/src/types.js index 5baafd44596b..73a22e497697 100644 --- a/packages/jest-editor-support/src/types.js +++ b/packages/jest-editor-support/src/types.js @@ -29,7 +29,7 @@ export type JestFileResults = { name: string, summary: string, message: string, - status: "failed" | "passed", + status: 'failed' | 'passed', startTime: number, endTime: number, assertionResults: Array, @@ -38,7 +38,7 @@ export type JestFileResults = { export type JestAssertionResults = { name: string, title: string, - status: "failed" | "passed", + status: 'failed' | 'passed', failureMessages: string[], } @@ -57,13 +57,13 @@ export type JestTotalResults = { /** * Did the thing pass, fail or was it not run? */ -export type TestReconcilationState = +export type TestReconciliationState = /** This could be the file has not changed, so the watcher didn't hit it */ - | "Unknown" + | 'Unknown' /** Definitely failed */ - | "KnownFail" + | 'KnownFail' /** Definitely passed */ - | "KnownSuccess" + | 'KnownSuccess' /** * The Jest Extension's version of a status for @@ -73,7 +73,7 @@ export type TestReconcilationState = export type TestFileAssertionStatus = { file: string, message: string, - status: TestReconcilationState, + status: TestReconciliationState, assertions: Array, } @@ -84,7 +84,7 @@ export type TestFileAssertionStatus = { */ export type TestAssertionStatus = { title: string, - status: TestReconcilationState, + status: TestReconciliationState, message: string, shortMessage: ?string, terseMessage: ?string, diff --git a/packages/jest-environment-jsdom/package.json b/packages/jest-environment-jsdom/package.json index c28fb0e91c59..5b8fe3dd3604 100644 --- a/packages/jest-environment-jsdom/package.json +++ b/packages/jest-environment-jsdom/package.json @@ -10,6 +10,6 @@ "dependencies": { "jest-mock": "^18.0.0", "jest-util": "^18.1.0", - "jsdom": "^9.9.1" + "jsdom": "^9.11.0" } } diff --git a/packages/jest-haste-map/package.json b/packages/jest-haste-map/package.json index 0c76840394d3..a31b9cb6182a 100644 --- a/packages/jest-haste-map/package.json +++ b/packages/jest-haste-map/package.json @@ -8,10 +8,10 @@ "license": "BSD-3-Clause", "main": "build/index.js", "dependencies": { - "fb-watchman": "^1.9.0", + "fb-watchman": "^2.0.0", "graceful-fs": "^4.1.6", "micromatch": "^2.3.11", - "sane": "~1.4.1", + "sane": "~1.5.0", "worker-farm": "^1.3.1" } } diff --git a/packages/jest-haste-map/src/__tests__/__snapshots__/index-test.js.snap b/packages/jest-haste-map/src/__tests__/__snapshots__/index-test.js.snap index 12bf932648b1..6bc5d020b4dd 100644 --- a/packages/jest-haste-map/src/__tests__/__snapshots__/index-test.js.snap +++ b/packages/jest-haste-map/src/__tests__/__snapshots__/index-test.js.snap @@ -1,3 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + exports[`HasteMap throws on duplicate module ids if "throwOnModuleCollision" is set to true 1`] = ` [Error: jest-haste-map: @providesModule naming collision: Duplicate module name: Strawberry @@ -8,7 +10,7 @@ This error is caused by a @providesModule declaration with the same name across exports[`HasteMap tries to crawl using node as a fallback 1`] = ` "jest-haste-map: Watchman crawl failed. Retrying once with node crawler. - Usually this happens when watchman isn\'t running. Create an empty \`.watchmanconfig\` file in your project\'s root folder or initialize a git or hg repository in your project. + Usually this happens when watchman isn't running. Create an empty \`.watchmanconfig\` file in your project's root folder or initialize a git or hg repository in your project. Error: watchman error" `; diff --git a/packages/jest-haste-map/src/__tests__/hasteImpl.js b/packages/jest-haste-map/src/__tests__/hasteImpl.js new file mode 100644 index 000000000000..db2a3a8b61f7 --- /dev/null +++ b/packages/jest-haste-map/src/__tests__/hasteImpl.js @@ -0,0 +1,14 @@ +/** + * 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'; + +module.exports = { + getHasteName(path) { + return path.substr(path.lastIndexOf('/') + 1).replace(/\.js$/, ''); + }, +}; diff --git a/packages/jest-haste-map/src/__tests__/worker-test.js b/packages/jest-haste-map/src/__tests__/worker-test.js index 5b73f25e4ec2..aad5a7a117a3 100644 --- a/packages/jest-haste-map/src/__tests__/worker-test.js +++ b/packages/jest-haste-map/src/__tests__/worker-test.js @@ -14,6 +14,7 @@ const skipOnWindows = require('skipOnWindows'); const worker = require('../worker'); const fs = require('graceful-fs'); +const path = require('path'); let createCallback; let mockFs; @@ -101,6 +102,25 @@ describe('worker', () => { }); }); + it('delegates to hasteImplModulePath for getting the id', () => { + const callback = createCallback(); + worker({ + filePath: '/fruits/strawberry.js', + hasteImplModulePath: path.resolve(__dirname, 'hasteImpl.js'), + }, callback); + + // Worker is synchronous. callback must have been called by now + expect(callback).toBeCalled(); + + expect(workerError).toBe(null); + expect(moduleData.id).toBe('strawberry'); + expect(moduleData).toEqual(expect.objectContaining({ + dependencies: expect.any(Array), + id: expect.any(String), + module: expect.any(Array), + })); + }); + it('parses package.json files as haste packages', () => { const callback = createCallback(); diff --git a/packages/jest-haste-map/src/crawlers/watchman.js b/packages/jest-haste-map/src/crawlers/watchman.js index 3d63163b529b..a7293d8b7396 100644 --- a/packages/jest-haste-map/src/crawlers/watchman.js +++ b/packages/jest-haste-map/src/crawlers/watchman.js @@ -111,7 +111,9 @@ module.exports = function watchmanCrawl( if (!fileData.exists) { delete files[name]; } else if (!ignore(name)) { - const mtime = fileData.mtime_ms.toNumber(); + const mtime = typeof fileData.mtime_ms === 'number' + ? fileData.mtime_ms + : fileData.mtime_ms.toNumber(); const isNew = !data.files[name] || data.files[name][H.MTIME] !== mtime; if (isNew) { diff --git a/packages/jest-haste-map/src/index.js b/packages/jest-haste-map/src/index.js index 922a24225d4f..46d9a8492fd3 100644 --- a/packages/jest-haste-map/src/index.js +++ b/packages/jest-haste-map/src/index.js @@ -42,6 +42,7 @@ type Options = { console?: Console, extensions: Array, forceNodeFilesystemAPI?: boolean, + hasteImplModulePath?: string, ignorePattern: RegExp, maxWorkers: number, mocksPattern?: string, @@ -60,6 +61,7 @@ type InternalOptions = { cacheDirectory: string, extensions: Array, forceNodeFilesystemAPI: boolean, + hasteImplModulePath?: string, ignorePattern: RegExp, maxWorkers: number, mocksPattern: ?RegExp, @@ -197,6 +199,7 @@ class HasteMap extends EventEmitter { cacheDirectory: options.cacheDirectory || os.tmpdir(), extensions: options.extensions, forceNodeFilesystemAPI: !!options.forceNodeFilesystemAPI, + hasteImplModulePath: options.hasteImplModulePath, ignorePattern: options.ignorePattern, maxWorkers: options.maxWorkers, mocksPattern: @@ -354,7 +357,10 @@ class HasteMap extends EventEmitter { } } - return this._getWorker(workerOptions)({filePath}).then( + return this._getWorker(workerOptions)({ + filePath, + hasteImplModulePath: this._options.hasteImplModulePath, + }).then( metadata => { // `1` for truthy values instead of `true` to save cache space. fileMetadata[H.VISITED] = 1; diff --git a/packages/jest-haste-map/src/types.js b/packages/jest-haste-map/src/types.js index 946dccf23d16..caede80f18e1 100644 --- a/packages/jest-haste-map/src/types.js +++ b/packages/jest-haste-map/src/types.js @@ -9,20 +9,24 @@ */ 'use strict'; -import type {Error} from 'types/TestResult'; +import type {SerializableError} from 'types/TestResult'; import type {InternalHasteMap, ModuleMetaData} from 'types/HasteMap'; export type IgnoreMatcher = (item: string) => boolean; export type WorkerMessage = { filePath: string, + hasteImplModulePath?: string, }; export type WorkerMetadata = { id: ?string, module: ?ModuleMetaData, dependencies: ?Array, }; -export type WorkerCallback = (error: ?Error, metaData: ?WorkerMetadata) => void; +export type WorkerCallback = ( + error: ?SerializableError, + metaData: ?WorkerMetadata, +) => void; export type CrawlerOptions = {| data: InternalHasteMap, @@ -31,3 +35,7 @@ export type CrawlerOptions = {| ignore: IgnoreMatcher, roots: Array, |}; + +export type HasteImpl = { + getHasteName(filePath: string): (string | void), +} diff --git a/packages/jest-haste-map/src/worker.js b/packages/jest-haste-map/src/worker.js index 7b530eb5eb35..4d8a0897b6bc 100644 --- a/packages/jest-haste-map/src/worker.js +++ b/packages/jest-haste-map/src/worker.js @@ -9,8 +9,9 @@ */ 'use strict'; -import type {Error} from 'types/TestResult'; +import type {SerializableError} from 'types/TestResult'; import type { + HasteImpl, WorkerMessage, WorkerCallback, } from './types'; @@ -25,7 +26,10 @@ const path = require('path'); const JSON_EXTENSION = '.json'; const PACKAGE_JSON = path.sep + 'package' + JSON_EXTENSION; -const formatError = (error: string|Error): Error => { +let hasteImpl: ?HasteImpl = null; +let hasteImplModulePath: ?string = null; + +const formatError = (error: string|Error): SerializableError => { if (typeof error === 'string') { return { message: error, @@ -37,12 +41,27 @@ const formatError = (error: string|Error): Error => { return { message: error.message, stack: error.stack, - type: error.type || 'Error', + type: 'Error', }; }; module.exports = (data: WorkerMessage, callback: WorkerCallback): void => { try { + if ( + data.hasteImplModulePath && + data.hasteImplModulePath !== hasteImplModulePath + ) { + if (hasteImpl) { + throw new Error('jest-haste-map: hasteImplModulePath changed'); + } + hasteImplModulePath = data.hasteImplModulePath; + hasteImpl = ( + // $FlowFixMe: dynamic require + require(hasteImplModulePath) + : HasteImpl + ); + } + const filePath = data.filePath; const content = fs.readFileSync(filePath, 'utf8'); let module; @@ -56,8 +75,12 @@ module.exports = (data: WorkerMessage, callback: WorkerCallback): void => { module = [filePath, H.PACKAGE]; } } else if (!filePath.endsWith(JSON_EXTENSION)) { - const doc = docblock.parse(docblock.extract(content)); - id = doc.providesModule || doc.provides; + if (hasteImpl) { + id = hasteImpl.getHasteName(filePath); + } else { + const doc = docblock.parse(docblock.extract(content)); + id = doc.providesModule || doc.provides; + } dependencies = extractRequires(content); if (id) { module = [filePath, H.MODULE]; diff --git a/packages/jest-jasmine2/src/__tests__/__snapshots__/matchers-test.js.snap b/packages/jest-jasmine2/src/__tests__/__snapshots__/matchers-test.js.snap index a25a76055ae6..bf60bb1a78c6 100644 --- a/packages/jest-jasmine2/src/__tests__/__snapshots__/matchers-test.js.snap +++ b/packages/jest-jasmine2/src/__tests__/__snapshots__/matchers-test.js.snap @@ -1,3 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + exports[`matchers proxies matchers to jest-matchers 1`] = ` "expect(received).toBe(expected) diff --git a/packages/jest-jasmine2/src/index.js b/packages/jest-jasmine2/src/index.js index 9faf715f27a1..00ea67602d61 100644 --- a/packages/jest-jasmine2/src/index.js +++ b/packages/jest-jasmine2/src/index.js @@ -87,7 +87,7 @@ function jasmine2( } if (config.testNamePattern) { - const testNameRegex = new RegExp(config.testNamePattern); + const testNameRegex = new RegExp(config.testNamePattern, 'i'); env.specFilter = spec => testNameRegex.test(spec.getFullName()); } diff --git a/packages/jest-jasmine2/src/jasmine-async.js b/packages/jest-jasmine2/src/jasmine-async.js index 2a4f4ae6aab9..1f8a3e011c31 100644 --- a/packages/jest-jasmine2/src/jasmine-async.js +++ b/packages/jest-jasmine2/src/jasmine-async.js @@ -62,7 +62,7 @@ function promisifyLifeCycleFunction(originalFn: Function, env) { const originalBodyFn = fn; fn = function(done) { const returnValue = originalBodyFn.apply(this, arguments); - if (isPromise(returnValue)) { + if (returnValue && isPromise(returnValue)) { returnValue.then(done, done.fail); } else { done(); diff --git a/packages/jest-jasmine2/src/jest-expect.js b/packages/jest-jasmine2/src/jest-expect.js index a7b81b2de09e..753bf6aa2cd5 100644 --- a/packages/jest-jasmine2/src/jest-expect.js +++ b/packages/jest-jasmine2/src/jest-expect.js @@ -22,8 +22,8 @@ const { type JasmineMatcher = { (): JasmineMatcher, - compare: RawMatcherFn, - negativeCompare: ?RawMatcherFn, + compare: () => RawMatcherFn, + negativeCompare: () => RawMatcherFn, }; type JasmineMatchersObject = {[id: string]: JasmineMatcher}; diff --git a/packages/jest-jasmine2/vendor/jasmine-2.5.2.js b/packages/jest-jasmine2/vendor/jasmine-2.5.2.js index a49dc3cdb43f..3e58860683b2 100644 --- a/packages/jest-jasmine2/vendor/jasmine-2.5.2.js +++ b/packages/jest-jasmine2/vendor/jasmine-2.5.2.js @@ -642,10 +642,8 @@ getJasmineRequireObj().Env = function(j$) { if (suiteFullName !== '') { fullName.unshift(suiteFullName); - // CUSTOM JEST CHANGE: we append "test" at the top level for snapshots. - } else if (!suite.parentSuite) { - fullName.unshift('test'); } + return fullName.join(' '); }; diff --git a/packages/jest-matcher-utils/src/__tests__/__snapshots__/index-test.js.snap b/packages/jest-matcher-utils/src/__tests__/__snapshots__/index-test.js.snap index 03143090225c..b4c01d9071ab 100644 --- a/packages/jest-matcher-utils/src/__tests__/__snapshots__/index-test.js.snap +++ b/packages/jest-matcher-utils/src/__tests__/__snapshots__/index-test.js.snap @@ -1,3 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + exports[`.stringify() reduces maxDepth if stringifying very large objects 1`] = `"{\\"a\\": 1, \\"b\\": [Object]}"`; exports[`.stringify() reduces maxDepth if stringifying very large objects 2`] = `"{\\"a\\": 1, \\"b\\": {\\"0\\": \\"test\\", \\"1\\": \\"test\\", \\"2\\": \\"test\\", \\"3\\": \\"test\\", \\"4\\": \\"test\\", \\"5\\": \\"test\\", \\"6\\": \\"test\\", \\"7\\": \\"test\\", \\"8\\": \\"test\\", \\"9\\": \\"test\\"}}"`; diff --git a/packages/jest-matcher-utils/src/index.js b/packages/jest-matcher-utils/src/index.js index 3a058b2bbd06..3776e84f0719 100644 --- a/packages/jest-matcher-utils/src/index.js +++ b/packages/jest-matcher-utils/src/index.js @@ -76,8 +76,6 @@ const getType = (value: any): ValueType => { return 'map'; } else if (value.constructor === Set) { return 'set'; - } else if (value.toString() === 'ArrayContaining') { - return 'array'; } return 'object'; // $FlowFixMe https://github.com/facebook/flow/issues/1015 diff --git a/packages/jest-matchers/src/__tests__/__snapshots__/extend-test.js.snap b/packages/jest-matchers/src/__tests__/__snapshots__/extend-test.js.snap index db8698dac0e6..4ef46beb012f 100644 --- a/packages/jest-matchers/src/__tests__/__snapshots__/extend-test.js.snap +++ b/packages/jest-matchers/src/__tests__/__snapshots__/extend-test.js.snap @@ -1,3 +1,5 @@ -exports[`test is available globally 1`] = `"expected 15 to be divisible by 2"`; +// Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`test is ok if there is no message specified 1`] = `"No message was specified for this matcher."`; +exports[`is available globally 1`] = `"expected 15 to be divisible by 2"`; + +exports[`is ok if there is no message specified 1`] = `"No message was specified for this matcher."`; diff --git a/packages/jest-matchers/src/__tests__/__snapshots__/matchers-test.js.snap b/packages/jest-matchers/src/__tests__/__snapshots__/matchers-test.js.snap index 3a30cbc72350..862c69a08131 100644 --- a/packages/jest-matchers/src/__tests__/__snapshots__/matchers-test.js.snap +++ b/packages/jest-matchers/src/__tests__/__snapshots__/matchers-test.js.snap @@ -1,3 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + exports[`.toBe() does not crash on circular references 1`] = ` "expect(received).toBe(expected) @@ -1733,6 +1735,24 @@ Expected collection to be an array-like structure. Received: null" `; +exports[`.toEqual() expect("Alice").not.toEqual({"asymmetricMatch": [Function asymmetricMatch]}) 1`] = ` +"expect(received).not.toEqual(expected) + +Expected value to not equal: + {\\"asymmetricMatch\\": [Function asymmetricMatch]} +Received: + \\"Alice\\"" +`; + +exports[`.toEqual() expect("Eve").toEqual({"asymmetricMatch": [Function asymmetricMatch]}) 1`] = ` +"expect(received).toEqual(expected) + +Expected value to equal: + {\\"asymmetricMatch\\": [Function asymmetricMatch]} +Received: + \\"Eve\\"" +`; + exports[`.toEqual() expect("abc").not.toEqual("abc") 1`] = ` "expect(received).not.toEqual(expected) @@ -1766,11 +1786,7 @@ exports[`.toEqual() expect("abd").toEqual(StringContaining "bc") 1`] = ` Expected value to equal: StringContaining \\"bc\\" Received: - \\"abd\\" - -Difference: - - Comparing two different types of values. Expected object but received string." + \\"abd\\"" `; exports[`.toEqual() expect("abd").toEqual(StringMatching /bc/i) 1`] = ` @@ -1779,11 +1795,7 @@ exports[`.toEqual() expect("abd").toEqual(StringMatching /bc/i) 1`] = ` Expected value to equal: StringMatching /bc/i Received: - \\"abd\\" - -Difference: - - Comparing two different types of values. Expected object but received string." + \\"abd\\"" `; exports[`.toEqual() expect("banana").toEqual("apple") 1`] = ` @@ -1932,6 +1944,19 @@ Received: 1" `; +exports[`.toEqual() expect(1).toEqual(ArrayContaining [1, 2]) 1`] = ` +"expect(received).toEqual(expected) + +Expected value to equal: + ArrayContaining [1, 2] +Received: + 1 + +Difference: + + Comparing two different types of values. Expected array but received number." +`; + exports[`.toEqual() expect(Set {1, 2}).not.toEqual(Set {1, 2}) 1`] = ` "expect(received).not.toEqual(expected) @@ -1954,6 +1979,19 @@ Difference: Compared values have no visual difference." `; +exports[`.toEqual() expect(false).toEqual(ObjectContaining {"a": 2}) 1`] = ` +"expect(received).toEqual(expected) + +Expected value to equal: + ObjectContaining {\\"a\\": 2} +Received: + false + +Difference: + + Comparing two different types of values. Expected object but received boolean." +`; + exports[`.toEqual() expect(null).toEqual(undefined) 1`] = ` "expect(received).toEqual(expected) @@ -2004,7 +2042,7 @@ Received: Difference: - Comparing two different types of values. Expected object but received undefined." + Comparing two different types of values. Expected function but received undefined." `; exports[`.toEqual() expect(undefined).toEqual(Anything) 1`] = ` @@ -2013,11 +2051,7 @@ exports[`.toEqual() expect(undefined).toEqual(Anything) 1`] = ` Expected value to equal: Anything Received: - undefined - -Difference: - - Comparing two different types of values. Expected object but received undefined." + undefined" `; exports[`.toHaveLength error cases 1`] = ` diff --git a/packages/jest-matchers/src/__tests__/__snapshots__/spyMatchers-test.js.snap b/packages/jest-matchers/src/__tests__/__snapshots__/spyMatchers-test.js.snap index 22287251765a..6475dd88a6a6 100644 --- a/packages/jest-matchers/src/__tests__/__snapshots__/spyMatchers-test.js.snap +++ b/packages/jest-matchers/src/__tests__/__snapshots__/spyMatchers-test.js.snap @@ -1,4 +1,6 @@ -exports[`test lastCalledWith works only on spies or jest.fn 1`] = ` +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`lastCalledWith works only on spies or jest.fn 1`] = ` "expect(jest.fn())[.not].lastCalledWith() jest.fn() value must be a mock function or spy. @@ -6,7 +8,7 @@ Received: function: [Function fn]" `; -exports[`test lastCalledWith works with jest.fn and arguments that don't match 1`] = ` +exports[`lastCalledWith works with jest.fn and arguments that don't match 1`] = ` "expect(jest.fn()).lastCalledWith(expected) Expected mock function to have been last called with: @@ -15,21 +17,21 @@ But it was last called with: [\\"foo\\", \\"bar1\\"]" `; -exports[`test lastCalledWith works with jest.fn and arguments that match 1`] = ` +exports[`lastCalledWith works with jest.fn and arguments that match 1`] = ` "expect(jest.fn()).not.lastCalledWith(expected) Expected mock function to not have been last called with: [\\"foo\\", \\"bar\\"]" `; -exports[`test lastCalledWith works with jest.fn and many arguments 1`] = ` +exports[`lastCalledWith works with jest.fn and many arguments 1`] = ` "expect(jest.fn()).not.lastCalledWith(expected) Expected mock function to not have been last called with: [\\"foo\\", \\"bar\\"]" `; -exports[`test lastCalledWith works with jest.fn and many arguments that don't match 1`] = ` +exports[`lastCalledWith works with jest.fn and many arguments that don't match 1`] = ` "expect(jest.fn()).lastCalledWith(expected) Expected mock function to have been last called with: @@ -39,7 +41,7 @@ But it was last called with: and two more calls." `; -exports[`test lastCalledWith works with jest.fn when not called 1`] = ` +exports[`lastCalledWith works with jest.fn when not called 1`] = ` "expect(jest.fn()).lastCalledWith(expected) Expected mock function to have been last called with: @@ -47,7 +49,7 @@ Expected mock function to have been last called with: But it was not called." `; -exports[`test toBeCalled works only on spies or jest.fn 1`] = ` +exports[`toBeCalled works only on spies or jest.fn 1`] = ` "expect(jest.fn())[.not].toBeCalled() jest.fn() value must be a mock function or spy. @@ -55,20 +57,20 @@ Received: function: [Function fn]" `; -exports[`test toBeCalled works with jasmine.createSpy 1`] = ` +exports[`toBeCalled works with jasmine.createSpy 1`] = ` "expect(spy).toBeCalled() Expected spy to have been called." `; -exports[`test toBeCalled works with jasmine.createSpy 2`] = ` +exports[`toBeCalled works with jasmine.createSpy 2`] = ` "expect(spy).not.toBeCalled() Expected spy not to be called but it was called with: Array []" `; -exports[`test toBeCalled works with jasmine.createSpy 3`] = ` +exports[`toBeCalled works with jasmine.createSpy 3`] = ` "expect(received)[.not].toBeCalled() Matcher does not accept any arguments. @@ -76,20 +78,20 @@ Got: number: 555" `; -exports[`test toBeCalled works with jest.fn 1`] = ` +exports[`toBeCalled works with jest.fn 1`] = ` "expect(jest.fn()).toBeCalled() Expected mock function to have been called." `; -exports[`test toBeCalled works with jest.fn 2`] = ` +exports[`toBeCalled works with jest.fn 2`] = ` "expect(jest.fn()).not.toBeCalled() Expected mock function not to be called but it was called with: Array []" `; -exports[`test toBeCalled works with jest.fn 3`] = ` +exports[`toBeCalled works with jest.fn 3`] = ` "expect(received)[.not].toBeCalled() Matcher does not accept any arguments. @@ -97,7 +99,7 @@ Got: number: 555" `; -exports[`test toBeCalledWith works only on spies or jest.fn 1`] = ` +exports[`toBeCalledWith works only on spies or jest.fn 1`] = ` "expect(jest.fn())[.not].toBeCalledWith() jest.fn() value must be a mock function or spy. @@ -105,7 +107,7 @@ Received: function: [Function fn]" `; -exports[`test toBeCalledWith works with jest.fn and arguments that don't match 1`] = ` +exports[`toBeCalledWith works with jest.fn and arguments that don't match 1`] = ` "expect(jest.fn()).toBeCalledWith(expected) Expected mock function to have been called with: @@ -114,21 +116,21 @@ But it was called with: [\\"foo\\", \\"bar1\\"]" `; -exports[`test toBeCalledWith works with jest.fn and arguments that match 1`] = ` +exports[`toBeCalledWith works with jest.fn and arguments that match 1`] = ` "expect(jest.fn()).not.toBeCalledWith(expected) Expected mock function not to have been called with: [\\"foo\\", \\"bar\\"]" `; -exports[`test toBeCalledWith works with jest.fn and many arguments 1`] = ` +exports[`toBeCalledWith works with jest.fn and many arguments 1`] = ` "expect(jest.fn()).not.toBeCalledWith(expected) Expected mock function not to have been called with: [\\"foo\\", \\"bar\\"]" `; -exports[`test toBeCalledWith works with jest.fn and many arguments that don't match 1`] = ` +exports[`toBeCalledWith works with jest.fn and many arguments that don't match 1`] = ` "expect(jest.fn()).toBeCalledWith(expected) Expected mock function to have been called with: @@ -137,7 +139,7 @@ But it was called with: [\\"foo\\", \\"bar3\\"], [\\"foo\\", \\"bar2\\"], [\\"foo\\", \\"bar1\\"]" `; -exports[`test toBeCalledWith works with jest.fn when not called 1`] = ` +exports[`toBeCalledWith works with jest.fn when not called 1`] = ` "expect(jest.fn()).toBeCalledWith(expected) Expected mock function to have been called with: @@ -145,7 +147,7 @@ Expected mock function to have been called with: But it was not called." `; -exports[`test toHaveBeenCalled works only on spies or jest.fn 1`] = ` +exports[`toHaveBeenCalled works only on spies or jest.fn 1`] = ` "expect(jest.fn())[.not].toHaveBeenCalled() jest.fn() value must be a mock function or spy. @@ -153,20 +155,20 @@ Received: function: [Function fn]" `; -exports[`test toHaveBeenCalled works with jasmine.createSpy 1`] = ` +exports[`toHaveBeenCalled works with jasmine.createSpy 1`] = ` "expect(spy).toHaveBeenCalled() Expected spy to have been called." `; -exports[`test toHaveBeenCalled works with jasmine.createSpy 2`] = ` +exports[`toHaveBeenCalled works with jasmine.createSpy 2`] = ` "expect(spy).not.toHaveBeenCalled() Expected spy not to be called but it was called with: Array []" `; -exports[`test toHaveBeenCalled works with jasmine.createSpy 3`] = ` +exports[`toHaveBeenCalled works with jasmine.createSpy 3`] = ` "expect(received)[.not].toHaveBeenCalled() Matcher does not accept any arguments. @@ -174,20 +176,20 @@ Got: number: 555" `; -exports[`test toHaveBeenCalled works with jest.fn 1`] = ` +exports[`toHaveBeenCalled works with jest.fn 1`] = ` "expect(jest.fn()).toHaveBeenCalled() Expected mock function to have been called." `; -exports[`test toHaveBeenCalled works with jest.fn 2`] = ` +exports[`toHaveBeenCalled works with jest.fn 2`] = ` "expect(jest.fn()).not.toHaveBeenCalled() Expected mock function not to be called but it was called with: Array []" `; -exports[`test toHaveBeenCalled works with jest.fn 3`] = ` +exports[`toHaveBeenCalled works with jest.fn 3`] = ` "expect(received)[.not].toHaveBeenCalled() Matcher does not accept any arguments. @@ -195,7 +197,81 @@ Got: number: 555" `; -exports[`test toHaveBeenCalledWith works only on spies or jest.fn 1`] = ` +exports[`toHaveBeenCalledTimes accepts only numbers 1`] = ` +"expect(received)[.not].toHaveBeenCalledTimes(expected) + +Expected value must be a number. +Got: + object: {}" +`; + +exports[`toHaveBeenCalledTimes accepts only numbers 2`] = ` +"expect(received)[.not].toHaveBeenCalledTimes(expected) + +Expected value must be a number. +Got: + array: Array []" +`; + +exports[`toHaveBeenCalledTimes accepts only numbers 3`] = ` +"expect(received)[.not].toHaveBeenCalledTimes(expected) + +Expected value must be a number. +Got: + boolean: true" +`; + +exports[`toHaveBeenCalledTimes accepts only numbers 4`] = ` +"expect(received)[.not].toHaveBeenCalledTimes(expected) + +Expected value must be a number. +Got: + string: \\"a\\"" +`; + +exports[`toHaveBeenCalledTimes accepts only numbers 5`] = ` +"expect(received)[.not].toHaveBeenCalledTimes(expected) + +Expected value must be a number. +Got: + map: Map {}" +`; + +exports[`toHaveBeenCalledTimes accepts only numbers 6`] = ` +"expect(received)[.not].toHaveBeenCalledTimes(expected) + +Expected value must be a number. +Got: + function: [Function anonymous]" +`; + +exports[`toHaveBeenCalledTimes fails if function called less than expected times 1`] = ` +"expect(spy).toHaveBeenCalledTimes(2) + +Expected spy to have been called two times, but it was called one time." +`; + +exports[`toHaveBeenCalledTimes fails if function called more than expected times 1`] = ` +"expect(spy).toHaveBeenCalledTimes(2) + +Expected spy to have been called two times, but it was called three times." +`; + +exports[`toHaveBeenCalledTimes passes if function called equal to expected times 1`] = ` +"expect(spy).not.toHaveBeenCalledTimes(2) + +Expected spy not to be called two times, but it was called exactly two times." +`; + +exports[`toHaveBeenCalledTimes verifies that actual is a Spy 1`] = ` +"expect(jest.fn())[.not].toHaveBeenCalledTimes() + +jest.fn() value must be a mock function or spy. +Received: + function: [Function fn]" +`; + +exports[`toHaveBeenCalledWith works only on spies or jest.fn 1`] = ` "expect(jest.fn())[.not].toHaveBeenCalledWith() jest.fn() value must be a mock function or spy. @@ -203,7 +279,7 @@ Received: function: [Function fn]" `; -exports[`test toHaveBeenCalledWith works with jasmine.createSpy and arguments that don't match 1`] = ` +exports[`toHaveBeenCalledWith works with jasmine.createSpy and arguments that don't match 1`] = ` "expect(spy).toHaveBeenCalledWith(expected) Expected spy to have been called with: @@ -212,21 +288,21 @@ But it was called with: [\\"foo\\", \\"bar1\\"]" `; -exports[`test toHaveBeenCalledWith works with jasmine.createSpy and arguments that match 1`] = ` +exports[`toHaveBeenCalledWith works with jasmine.createSpy and arguments that match 1`] = ` "expect(spy).not.toHaveBeenCalledWith(expected) Expected spy not to have been called with: [\\"foo\\", \\"bar\\"]" `; -exports[`test toHaveBeenCalledWith works with jasmine.createSpy and many arguments 1`] = ` +exports[`toHaveBeenCalledWith works with jasmine.createSpy and many arguments 1`] = ` "expect(spy).not.toHaveBeenCalledWith(expected) Expected spy not to have been called with: [\\"foo\\", \\"bar\\"]" `; -exports[`test toHaveBeenCalledWith works with jasmine.createSpy and many arguments that don't match 1`] = ` +exports[`toHaveBeenCalledWith works with jasmine.createSpy and many arguments that don't match 1`] = ` "expect(spy).toHaveBeenCalledWith(expected) Expected spy to have been called with: @@ -235,7 +311,7 @@ But it was called with: [\\"foo\\", \\"bar3\\"], [\\"foo\\", \\"bar2\\"], [\\"foo\\", \\"bar1\\"]" `; -exports[`test toHaveBeenCalledWith works with jasmine.createSpy when not called 1`] = ` +exports[`toHaveBeenCalledWith works with jasmine.createSpy when not called 1`] = ` "expect(spy).toHaveBeenCalledWith(expected) Expected spy to have been called with: @@ -243,7 +319,7 @@ Expected spy to have been called with: But it was not called." `; -exports[`test toHaveBeenCalledWith works with jest.fn and arguments that don't match 1`] = ` +exports[`toHaveBeenCalledWith works with jest.fn and arguments that don't match 1`] = ` "expect(jest.fn()).toHaveBeenCalledWith(expected) Expected mock function to have been called with: @@ -252,21 +328,21 @@ But it was called with: [\\"foo\\", \\"bar1\\"]" `; -exports[`test toHaveBeenCalledWith works with jest.fn and arguments that match 1`] = ` +exports[`toHaveBeenCalledWith works with jest.fn and arguments that match 1`] = ` "expect(jest.fn()).not.toHaveBeenCalledWith(expected) Expected mock function not to have been called with: [\\"foo\\", \\"bar\\"]" `; -exports[`test toHaveBeenCalledWith works with jest.fn and many arguments 1`] = ` +exports[`toHaveBeenCalledWith works with jest.fn and many arguments 1`] = ` "expect(jest.fn()).not.toHaveBeenCalledWith(expected) Expected mock function not to have been called with: [\\"foo\\", \\"bar\\"]" `; -exports[`test toHaveBeenCalledWith works with jest.fn and many arguments that don't match 1`] = ` +exports[`toHaveBeenCalledWith works with jest.fn and many arguments that don't match 1`] = ` "expect(jest.fn()).toHaveBeenCalledWith(expected) Expected mock function to have been called with: @@ -275,7 +351,7 @@ But it was called with: [\\"foo\\", \\"bar3\\"], [\\"foo\\", \\"bar2\\"], [\\"foo\\", \\"bar1\\"]" `; -exports[`test toHaveBeenCalledWith works with jest.fn when not called 1`] = ` +exports[`toHaveBeenCalledWith works with jest.fn when not called 1`] = ` "expect(jest.fn()).toHaveBeenCalledWith(expected) Expected mock function to have been called with: @@ -283,7 +359,7 @@ Expected mock function to have been called with: But it was not called." `; -exports[`test toHaveBeenLastCalledWith works only on spies or jest.fn 1`] = ` +exports[`toHaveBeenLastCalledWith works only on spies or jest.fn 1`] = ` "expect(jest.fn())[.not].toHaveBeenLastCalledWith() jest.fn() value must be a mock function or spy. @@ -291,7 +367,7 @@ Received: function: [Function fn]" `; -exports[`test toHaveBeenLastCalledWith works with jasmine.createSpy and arguments that don't match 1`] = ` +exports[`toHaveBeenLastCalledWith works with jasmine.createSpy and arguments that don't match 1`] = ` "expect(spy).toHaveBeenLastCalledWith(expected) Expected spy to have been last called with: @@ -300,21 +376,21 @@ But it was last called with: [\\"foo\\", \\"bar1\\"]" `; -exports[`test toHaveBeenLastCalledWith works with jasmine.createSpy and arguments that match 1`] = ` +exports[`toHaveBeenLastCalledWith works with jasmine.createSpy and arguments that match 1`] = ` "expect(spy).not.toHaveBeenLastCalledWith(expected) Expected spy to not have been last called with: [\\"foo\\", \\"bar\\"]" `; -exports[`test toHaveBeenLastCalledWith works with jasmine.createSpy and many arguments 1`] = ` +exports[`toHaveBeenLastCalledWith works with jasmine.createSpy and many arguments 1`] = ` "expect(spy).not.toHaveBeenLastCalledWith(expected) Expected spy to not have been last called with: [\\"foo\\", \\"bar\\"]" `; -exports[`test toHaveBeenLastCalledWith works with jasmine.createSpy and many arguments that don't match 1`] = ` +exports[`toHaveBeenLastCalledWith works with jasmine.createSpy and many arguments that don't match 1`] = ` "expect(spy).toHaveBeenLastCalledWith(expected) Expected spy to have been last called with: @@ -324,7 +400,7 @@ But it was last called with: and two more calls." `; -exports[`test toHaveBeenLastCalledWith works with jasmine.createSpy when not called 1`] = ` +exports[`toHaveBeenLastCalledWith works with jasmine.createSpy when not called 1`] = ` "expect(spy).toHaveBeenLastCalledWith(expected) Expected spy to have been last called with: @@ -332,7 +408,7 @@ Expected spy to have been last called with: But it was not called." `; -exports[`test toHaveBeenLastCalledWith works with jest.fn and arguments that don't match 1`] = ` +exports[`toHaveBeenLastCalledWith works with jest.fn and arguments that don't match 1`] = ` "expect(jest.fn()).toHaveBeenLastCalledWith(expected) Expected mock function to have been last called with: @@ -341,21 +417,21 @@ But it was last called with: [\\"foo\\", \\"bar1\\"]" `; -exports[`test toHaveBeenLastCalledWith works with jest.fn and arguments that match 1`] = ` +exports[`toHaveBeenLastCalledWith works with jest.fn and arguments that match 1`] = ` "expect(jest.fn()).not.toHaveBeenLastCalledWith(expected) Expected mock function to not have been last called with: [\\"foo\\", \\"bar\\"]" `; -exports[`test toHaveBeenLastCalledWith works with jest.fn and many arguments 1`] = ` +exports[`toHaveBeenLastCalledWith works with jest.fn and many arguments 1`] = ` "expect(jest.fn()).not.toHaveBeenLastCalledWith(expected) Expected mock function to not have been last called with: [\\"foo\\", \\"bar\\"]" `; -exports[`test toHaveBeenLastCalledWith works with jest.fn and many arguments that don't match 1`] = ` +exports[`toHaveBeenLastCalledWith works with jest.fn and many arguments that don't match 1`] = ` "expect(jest.fn()).toHaveBeenLastCalledWith(expected) Expected mock function to have been last called with: @@ -365,84 +441,10 @@ But it was last called with: and two more calls." `; -exports[`test toHaveBeenLastCalledWith works with jest.fn when not called 1`] = ` +exports[`toHaveBeenLastCalledWith works with jest.fn when not called 1`] = ` "expect(jest.fn()).toHaveBeenLastCalledWith(expected) Expected mock function to have been last called with: [\\"foo\\", \\"bar\\"] But it was not called." `; - -exports[`toHaveBeenCalledTimes accepts only numbers 1`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) - -Expected value must be a number. -Got: - object: {}" -`; - -exports[`toHaveBeenCalledTimes accepts only numbers 2`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) - -Expected value must be a number. -Got: - array: Array []" -`; - -exports[`toHaveBeenCalledTimes accepts only numbers 3`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) - -Expected value must be a number. -Got: - boolean: true" -`; - -exports[`toHaveBeenCalledTimes accepts only numbers 4`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) - -Expected value must be a number. -Got: - string: \\"a\\"" -`; - -exports[`toHaveBeenCalledTimes accepts only numbers 5`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) - -Expected value must be a number. -Got: - map: Map {}" -`; - -exports[`toHaveBeenCalledTimes accepts only numbers 6`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) - -Expected value must be a number. -Got: - function: [Function anonymous]" -`; - -exports[`toHaveBeenCalledTimes fails if function called less than expected times 1`] = ` -"expect(spy).toHaveBeenCalledTimes(2) - -Expected spy to have been called two times, but it was called one time." -`; - -exports[`toHaveBeenCalledTimes fails if function called more than expected times 1`] = ` -"expect(spy).toHaveBeenCalledTimes(2) - -Expected spy to have been called two times, but it was called three times." -`; - -exports[`toHaveBeenCalledTimes passes if function called equal to expected times 1`] = ` -"expect(spy).not.toHaveBeenCalledTimes(2) - -Expected spy not to be called two times, but it was called exactly two times." -`; - -exports[`toHaveBeenCalledTimes verifies that actual is a Spy 1`] = ` -"expect(jest.fn())[.not].toHaveBeenCalledTimes() - -jest.fn() value must be a mock function or spy. -Received: - function: [Function fn]" -`; diff --git a/packages/jest-matchers/src/__tests__/__snapshots__/toThrowMatchers-test.js.snap b/packages/jest-matchers/src/__tests__/__snapshots__/toThrowMatchers-test.js.snap index e92c230d22ee..19a5e3099620 100644 --- a/packages/jest-matchers/src/__tests__/__snapshots__/toThrowMatchers-test.js.snap +++ b/packages/jest-matchers/src/__tests__/__snapshots__/toThrowMatchers-test.js.snap @@ -1,3 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + exports[`.toThrow() error class did not throw at all 1`] = ` "expect(function).toThrow(type) diff --git a/packages/jest-matchers/src/__tests__/matchers-test.js b/packages/jest-matchers/src/__tests__/matchers-test.js index dff4bb348841..9d4d4f613b33 100644 --- a/packages/jest-matchers/src/__tests__/matchers-test.js +++ b/packages/jest-matchers/src/__tests__/matchers-test.js @@ -81,11 +81,16 @@ describe('.toEqual()', () => { [null, undefined], [new Set([1, 2]), new Set([2, 1])], [{a: 1, b: 2}, jestExpect.objectContaining({a: 2})], + [false, jestExpect.objectContaining({a: 2})], [[1, 3], jestExpect.arrayContaining([1, 2])], + [1, jestExpect.arrayContaining([1, 2])], ['abd', jestExpect.stringContaining('bc')], ['abd', jestExpect.stringMatching(/bc/i)], [undefined, jestExpect.anything()], [undefined, jestExpect.any(Function)], + ['Eve', { + asymmetricMatch: who => who === 'Alice' || who === 'Bob', + }], ].forEach(([a, b]) => { test(`expect(${stringify(a)}).toEqual(${stringify(b)})`, () => { expect(() => jestExpect(a).toEqual(b)) @@ -114,6 +119,9 @@ describe('.toEqual()', () => { b: jestExpect.any(Function), c: jestExpect.anything(), }], + ['Alice', { + asymmetricMatch: who => who === 'Alice' || who === 'Bob', + }], ].forEach(([a, b]) => { test(`expect(${stringify(a)}).not.toEqual(${stringify(b)})`, () => { expect(() => jestExpect(a).not.toEqual(b)) diff --git a/packages/jest-matchers/src/asymmetric-matchers.js b/packages/jest-matchers/src/asymmetric-matchers.js index adc15ede860c..4d07b4aad735 100644 --- a/packages/jest-matchers/src/asymmetric-matchers.js +++ b/packages/jest-matchers/src/asymmetric-matchers.js @@ -69,6 +69,30 @@ class Any extends AsymmetricMatcher { return 'Any'; } + getExpectedType() { + if (this.sample == String) { + return 'string'; + } + + if (this.sample == Number) { + return 'number'; + } + + if (this.sample == Function) { + return 'function'; + } + + if (this.sample == Object) { + return 'object'; + } + + if (this.sample == Boolean) { + return 'boolean'; + } + + return fnNameFor(this.sample); + } + toAsymmetricMatcher() { return 'Any<' + fnNameFor(this.sample) + '>'; } @@ -83,6 +107,8 @@ class Anything extends AsymmetricMatcher { return 'Anything'; } + // No getExpectedType method, because it matches either null or undefined. + toAsymmetricMatcher() { return 'Anything'; } @@ -117,6 +143,10 @@ class ArrayContaining extends AsymmetricMatcher { toString() { return 'ArrayContaining'; } + + getExpectedType() { + return 'array'; + } } class ObjectContaining extends AsymmetricMatcher { @@ -150,6 +180,10 @@ class ObjectContaining extends AsymmetricMatcher { toString() { return 'ObjectContaining'; } + + getExpectedType() { + return 'object'; + } } class StringContaining extends AsymmetricMatcher { @@ -170,6 +204,10 @@ class StringContaining extends AsymmetricMatcher { toString() { return 'StringContaining'; } + + getExpectedType() { + return 'string'; + } } class StringMatching extends AsymmetricMatcher { @@ -191,6 +229,10 @@ class StringMatching extends AsymmetricMatcher { toString() { return 'StringMatching'; } + + getExpectedType() { + return 'string'; + } } module.exports = { diff --git a/packages/jest-message-util/src/__tests__/__snapshots__/messages-test.js.snap b/packages/jest-message-util/src/__tests__/__snapshots__/messages-test.js.snap index d3eb584cc026..4b52c8f96e50 100644 --- a/packages/jest-message-util/src/__tests__/__snapshots__/messages-test.js.snap +++ b/packages/jest-message-util/src/__tests__/__snapshots__/messages-test.js.snap @@ -1,4 +1,6 @@ -exports[`test should exclude jasmine from stack trace for windows paths 1`] = ` +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should exclude jasmine from stack trace for windows paths 1`] = ` " ● windows test at stack (..\\\\jest-jasmine2\\\\vendor\\\\jasmine-2.4.1.js:1580:17) diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index 58110d9c0edb..45b8bc5eda7e 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -10,7 +10,7 @@ "dependencies": { "babel-core": "^6.0.0", "babel-jest": "^18.0.0", - "babel-plugin-istanbul": "^3.0.0", + "babel-plugin-istanbul": "^4.0.0", "chalk": "^1.1.3", "graceful-fs": "^4.1.6", "jest-config": "^18.1.0", diff --git a/packages/jest-runtime/src/__tests__/__snapshots__/transform-test.js.snap b/packages/jest-runtime/src/__tests__/__snapshots__/transform-test.js.snap index c96d7f56ed11..650d9c57553b 100644 --- a/packages/jest-runtime/src/__tests__/__snapshots__/transform-test.js.snap +++ b/packages/jest-runtime/src/__tests__/__snapshots__/transform-test.js.snap @@ -1,3 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + exports[`transform transforms a file properly 1`] = ` "({\\"Object.\\":function(module,exports,require,__dirname,__filename,global,jest){/* istanbul ignore next */var cov_25u22311x4 = function () {var path = \\"/fruits/banana.js\\",hash = \\"04636d4ae73b4b3e24bf6fba39e08c946fd0afb5\\",global = new Function('return this')(),gcv = \\"__coverage__\\",coverageData = { path: \\"/fruits/banana.js\\", statementMap: { \\"0\\": { start: { line: 1, column: 0 }, end: { line: 1, column: 26 } } }, fnMap: {}, branchMap: {}, s: { \\"0\\": 0 }, f: {}, b: {}, _coverageSchema: \\"332fd63041d2c1bcb487cc26dd0d5f7d97098a6c\\" },coverage = global[gcv] || (global[gcv] = {});if (coverage[path] && coverage[path].hash === hash) {return coverage[path];}coverageData.hash = hash;return coverage[path] = coverageData;}();++cov_25u22311x4.s[0];module.exports = \\"banana\\"; }});" diff --git a/packages/jest-runtime/src/index.js b/packages/jest-runtime/src/index.js index 0ce8af3d40a0..cf71fed41762 100644 --- a/packages/jest-runtime/src/index.js +++ b/packages/jest-runtime/src/index.js @@ -198,6 +198,7 @@ class Runtime { cacheDirectory: config.cacheDirectory, console: options && options.console, extensions: [SNAPSHOT_EXTENSION].concat(config.moduleFileExtensions), + hasteImplModulePath: config.haste.hasteImplModulePath, ignorePattern, maxWorkers: (options && options.maxWorkers) || 1, mocksPattern: escapePathForRegex(path.sep + '__mocks__' + path.sep), diff --git a/packages/jest-snapshot/src/State.js b/packages/jest-snapshot/src/State.js index 519b6aac63c7..d8d1753cad95 100644 --- a/packages/jest-snapshot/src/State.js +++ b/packages/jest-snapshot/src/State.js @@ -19,6 +19,7 @@ const { keyToTestName, serialize, testNameToKey, + unescape, } = require('./utils'); const fileExists = require('jest-file-exists'); const fs = require('fs'); @@ -45,9 +46,10 @@ class SnapshotState { snapshotPath?: string, expand?: boolean, ) { - this._dirty = false; this._snapshotPath = snapshotPath || getSnapshotPath(testPath); - this._snapshotData = getSnapshotData(this._snapshotPath); + const {data, dirty} = getSnapshotData(this._snapshotPath, update); + this._snapshotData = data; + this._dirty = dirty; this._uncheckedKeys = new Set(Object.keys(this._snapshotData)); this._counters = new Map(); this._index = 0; @@ -158,9 +160,9 @@ class SnapshotState { if (!pass) { this.unmatched++; return { - actual: receivedSerialized, + actual: unescape(receivedSerialized), count, - expected, + expected: unescape(expected), pass: false, }; } else { diff --git a/packages/jest-snapshot/src/__tests__/utils-test.js b/packages/jest-snapshot/src/__tests__/utils-test.js index 1abba6a7d4ef..819b6a2b2e7e 100644 --- a/packages/jest-snapshot/src/__tests__/utils-test.js +++ b/packages/jest-snapshot/src/__tests__/utils-test.js @@ -9,17 +9,30 @@ 'use strict'; const { + getSnapshotData, getSnapshotPath, keyToTestName, saveSnapshotFile, testNameToKey, + SNAPSHOT_GUIDE_LINK, + SNAPSHOT_VERSION, + SNAPSHOT_VERSION_WARNING, } = require('../utils'); const fs = require('fs'); const path = require('path'); const writeFileSync = fs.writeFileSync; -beforeEach(() => fs.writeFileSync = jest.fn()); -afterEach(() => fs.writeFileSync = writeFileSync); +const readFileSync = fs.readFileSync; +beforeEach(() => { + fs.writeFileSync = jest.fn(); + fs.readFileSync = jest.fn(); +}); +afterEach(() => { + fs.writeFileSync = writeFileSync; + fs.readFileSync = readFileSync; +}); + +jest.mock('jest-file-exists', () => () => true); test('keyToTestName()', () => { expect(keyToTestName('abc cde 12')).toBe('abc cde'); @@ -41,7 +54,7 @@ test('getSnapshotPath()', () => { ); }); -test('saveSnapshotFile()', () => { +test('saveSnapshotFile() works with \r\n', () => { const filename = path.join(__dirname, 'remove-newlines.snap'); const data = { myKey: '
\r\n
', @@ -49,7 +62,109 @@ test('saveSnapshotFile()', () => { saveSnapshotFile(data, filename); expect(fs.writeFileSync) - .toBeCalledWith(filename, 'exports[`myKey`] = `
\n
`;\n'); + .toBeCalledWith( + filename, + `// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` + + 'exports[`myKey`] = `
\n
`;\n' + ); +}); + +test('saveSnapshotFile() works with \r', () => { + const filename = path.join(__dirname, 'remove-newlines.snap'); + const data = { + myKey: '
\r
', + }; + + saveSnapshotFile(data, filename); + expect(fs.writeFileSync) + .toBeCalledWith( + filename, + `// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` + + 'exports[`myKey`] = `
\n
`;\n' + ); +}); + +test('getSnapshotData() throws when no snapshot version', () => { + const filename = path.join(__dirname, 'old-snapshot.snap'); + fs.readFileSync = jest.fn(() => 'exports[`myKey`] = `
\n
`;\n'); + const update = false; + + expect(() => getSnapshotData(filename, update)).toThrowError( + `Outdated snapshot: No snapshot header found. ` + + `Jest 19 introduced versioned snapshots to ensure all people on ` + + `a project are using the same version of Jest. ` + + `Please update all snapshots during this upgrade of Jest.\n\n` + + SNAPSHOT_VERSION_WARNING + ); +}); + +test('getSnapshotData() throws for older snapshot version', () => { + const filename = path.join(__dirname, 'old-snapshot.snap'); + fs.readFileSync = jest.fn(() => + `// Jest Snapshot v0.99, ${SNAPSHOT_GUIDE_LINK}\n\n` + + 'exports[`myKey`] = `
\n
`;\n' + ); + const update = false; + + expect(() => getSnapshotData(filename, update)).toThrowError( + `Outdated snapshot: The version of the snapshot file associated ` + + `with this test is outdated. The snapshot file version ensures that ` + + `all people on a project are using the same version of Jest. ` + + `Please update all snapshots during this upgrade of Jest.\n\n` + + `Expected: v${SNAPSHOT_VERSION}\n` + + `Received: v0.99\n\n` + + SNAPSHOT_VERSION_WARNING + ); +}); + +test('getSnapshotData() throws for newer snapshot version', () => { + const filename = path.join(__dirname, 'old-snapshot.snap'); + fs.readFileSync = jest.fn(() => + `// Jest Snapshot v2, ${SNAPSHOT_GUIDE_LINK}\n\n` + + 'exports[`myKey`] = `
\n
`;\n' + ); + const update = false; + + expect(() => getSnapshotData(filename, update)).toThrowError( + `Outdated Jest version: the version of this snapshot file indicates ` + + `that this project is meant to be used with a newer version of Jest. ` + + `The snapshot file version ensures that all people on a project ` + + `are using the same version of Jest. ` + + `Please update your version of Jest and re-run the tests.\n\n` + + `Expected: v${SNAPSHOT_VERSION}\n` + + `Received: v2` + ); +}); + +test('getSnapshotData() does not throw for when updating', () => { + const filename = path.join(__dirname, 'old-snapshot.snap'); + fs.readFileSync = jest.fn(() => + 'exports[`myKey`] = `
\n
`;\n' + ); + const update = true; + + expect(() => getSnapshotData(filename, update)).not.toThrow(); +}); + +test('getSnapshotData() marks invalid snapshot dirty when updating', () => { + const filename = path.join(__dirname, 'old-snapshot.snap'); + fs.readFileSync = jest.fn(() => + 'exports[`myKey`] = `
\n
`;\n' + ); + const update = true; + + expect(getSnapshotData(filename, update)).toMatchObject({dirty: true}); +}); + +test('getSnapshotData() marks valid snapshot not dirty when updating', () => { + const filename = path.join(__dirname, 'old-snapshot.snap'); + fs.readFileSync = jest.fn(() => + `// Jest Snapshot v${SNAPSHOT_VERSION}, ${SNAPSHOT_GUIDE_LINK}\n\n` + + 'exports[`myKey`] = `
\n
`;\n' + ); + const update = true; + + expect(getSnapshotData(filename, update)).toMatchObject({dirty: false}); }); test('escaping', () => { @@ -57,7 +172,11 @@ test('escaping', () => { const data = '"\'\\'; saveSnapshotFile({key: data}, filename); const writtenData = fs.writeFileSync.mock.calls[0][1]; - expect(writtenData).toBe("exports[`key`] = `\"'\\\\`;\n"); + expect(writtenData) + .toBe( + `// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` + + "exports[`key`] = `\"'\\\\`;\n" + ); // eslint-disable-next-line no-unused-vars const exports = {}; diff --git a/packages/jest-snapshot/src/utils.js b/packages/jest-snapshot/src/utils.js index 61c7d5c02b21..0b9b190b32ae 100644 --- a/packages/jest-snapshot/src/utils.js +++ b/packages/jest-snapshot/src/utils.js @@ -21,6 +21,57 @@ const naturalCompare = require('natural-compare'); const getSerializers = require('./plugins').getSerializers; const SNAPSHOT_EXTENSION = 'snap'; +const SNAPSHOT_VERSION = '1'; +const SNAPSHOT_VERSION_REGEXP = /^\/\/ Jest Snapshot v(.+),/; +const SNAPSHOT_GUIDE_LINK = 'https://goo.gl/fbAQLP'; +const SNAPSHOT_VERSION_WARNING = + `Warning: It is advised to revert any local changes to tests or ` + + `other code during this upgrade to ensure that no invalid state ` + + `is stored as a snapshot.`; + +const writeSnapshotVersion = () => + `// Jest Snapshot v${SNAPSHOT_VERSION}, ${SNAPSHOT_GUIDE_LINK}`; + +const validateSnapshotVersion = (snapshotContents: string) => { + const versionTest = SNAPSHOT_VERSION_REGEXP.exec(snapshotContents); + const version = (versionTest && versionTest[1]); + + if (!version) { + return new Error( + `Outdated snapshot: No snapshot header found. ` + + `Jest 19 introduced versioned snapshots to ensure all people on ` + + `a project are using the same version of Jest. ` + + `Please update all snapshots during this upgrade of Jest.\n\n` + + SNAPSHOT_VERSION_WARNING + ); + } + + if (version < SNAPSHOT_VERSION) { + return new Error( + `Outdated snapshot: The version of the snapshot file associated ` + + `with this test is outdated. The snapshot file version ensures that ` + + `all people on a project are using the same version of Jest. ` + + `Please update all snapshots during this upgrade of Jest.\n\n` + + `Expected: v${SNAPSHOT_VERSION}\n` + + `Received: v${version}\n\n` + + SNAPSHOT_VERSION_WARNING + ); + } + + if (version > SNAPSHOT_VERSION) { + return new Error( + `Outdated Jest version: the version of this snapshot file indicates ` + + `that this project is meant to be used with a newer version of Jest. ` + + `The snapshot file version ensures that all people on a project ` + + `are using the same version of Jest. ` + + `Please update your version of Jest and re-run the tests.\n\n` + + `Expected: v${SNAPSHOT_VERSION}\n` + + `Received: v${version}` + ); + } + + return null; +}; const testNameToKey = (testName: string, count: number) => testName + ' ' + count; @@ -38,19 +89,32 @@ const getSnapshotPath = (testPath: Path) => path.join( path.basename(testPath) + '.' + SNAPSHOT_EXTENSION, ); -const getSnapshotData = (snapshotPath: Path) => { +const getSnapshotData = (snapshotPath: Path, update: boolean) => { const data = Object.create(null); + let snapshotContents = ''; + let dirty = false; if (fileExists(snapshotPath)) { try { - delete require.cache[require.resolve(snapshotPath)]; - /* eslint-disable no-useless-call */ - Object.assign(data, require.call(null, snapshotPath)); - /* eslint-enable no-useless-call */ + snapshotContents = fs.readFileSync(snapshotPath, 'utf8'); + // eslint-disable-next-line no-new-func + const populate = new Function('exports', snapshotContents); + populate(data); } catch (e) {} } - return data; + const validationResult = validateSnapshotVersion(snapshotContents); + const isInvalid = snapshotContents && validationResult; + + if (!update && isInvalid) { + throw validationResult; + } + + if (update && isInvalid) { + dirty = true; + } + + return {data, dirty}; }; // Extra line breaks at the beginning and at the end of the snapshot are useful @@ -66,6 +130,9 @@ const serialize = (data: any): string => { })); }; +const unescape = (data: any): string => + data.replace(/\\(")/g, '$1'); // unescape double quotes + const printBacktickString = (str: string) => { return '`' + str.replace(/`|\\|\${/g, '\\$&') + '`'; }; @@ -77,7 +144,7 @@ const ensureDirectoryExists = (filePath: Path) => { }; const normalizeNewlines = - string => string.replace(/\r\n/g, '\n'); + string => string.replace(/\r\n|\r/g, '\n'); const saveSnapshotFile = ( snapshotData: {[key: string]: string}, @@ -86,15 +153,21 @@ const saveSnapshotFile = ( const snapshots = Object.keys(snapshotData).sort(naturalCompare) .map(key => 'exports[' + printBacktickString(key) + '] = ' + - printBacktickString(normalizeNewlines(snapshotData[key])) + ';', + printBacktickString(normalizeNewlines(snapshotData[key])) + ';', ); ensureDirectoryExists(snapshotPath); - fs.writeFileSync(snapshotPath, snapshots.join('\n\n') + '\n'); + fs.writeFileSync( + snapshotPath, + writeSnapshotVersion() + '\n\n' + snapshots.join('\n\n') + '\n' + ); }; module.exports = { SNAPSHOT_EXTENSION, + SNAPSHOT_GUIDE_LINK, + SNAPSHOT_VERSION, + SNAPSHOT_VERSION_WARNING, ensureDirectoryExists, getSnapshotData, getSnapshotPath, @@ -102,4 +175,5 @@ module.exports = { saveSnapshotFile, serialize, testNameToKey, + unescape, }; diff --git a/packages/jest-util/package.json b/packages/jest-util/package.json index abf3b33c56de..0f83ef976e2a 100644 --- a/packages/jest-util/package.json +++ b/packages/jest-util/package.json @@ -12,7 +12,7 @@ "graceful-fs": "^4.1.6", "jest-file-exists": "^17.0.0", "jest-mock": "^18.0.0", - "jest-validate": "^18.1.0", + "jest-validate": "^18.2.0", "jest-message-util": "^18.1.0", "leven": "^2.0.0", "mkdirp": "^0.5.1" diff --git a/packages/jest-util/src/__tests__/__snapshots__/FakeTimers-test.js.snap b/packages/jest-util/src/__tests__/__snapshots__/FakeTimers-test.js.snap index b810300ba283..235d2ba741f5 100644 --- a/packages/jest-util/src/__tests__/__snapshots__/FakeTimers-test.js.snap +++ b/packages/jest-util/src/__tests__/__snapshots__/FakeTimers-test.js.snap @@ -1,3 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + exports[`FakeTimers runAllTimers warns when trying to advance timers while real timers are used 1`] = ` "A function to advance timers was called but the timers API is not mocked with fake timers. Call \`jest.useFakeTimers()\` in this test or enable fake timers globally by setting \`\\"timers\\": \\"fake\\"\` in the configuration file. This warning is likely a result of a default configuration change in Jest 15. diff --git a/packages/jest-util/src/__tests__/__snapshots__/validateCLIOptions-test.js.snap b/packages/jest-util/src/__tests__/__snapshots__/validateCLIOptions-test.js.snap index 7d41cfa2b0e9..fc92881b4bc8 100644 --- a/packages/jest-util/src/__tests__/__snapshots__/validateCLIOptions-test.js.snap +++ b/packages/jest-util/src/__tests__/__snapshots__/validateCLIOptions-test.js.snap @@ -1,4 +1,6 @@ -exports[`test fails for multiple unknown options 1`] = ` +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`fails for multiple unknown options 1`] = ` "● Unrecognized CLI Parameters: Following options were not recognized: @@ -9,7 +11,7 @@ exports[`test fails for multiple unknown options 1`] = ` " `; -exports[`test fails for unknown option 1`] = ` +exports[`fails for unknown option 1`] = ` "● Unrecognized CLI Parameter: Unrecognized option \\"unknown\\". diff --git a/packages/jest-validate/package.json b/packages/jest-validate/package.json index 6886ea6644e4..234cebe2f678 100644 --- a/packages/jest-validate/package.json +++ b/packages/jest-validate/package.json @@ -1,6 +1,6 @@ { "name": "jest-validate", - "version": "18.1.0", + "version": "18.2.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git" diff --git a/packages/jest-validate/src/__tests__/__snapshots__/validate-test.js.snap b/packages/jest-validate/src/__tests__/__snapshots__/validate-test.js.snap index b12efc4e599c..21bc4c709443 100644 --- a/packages/jest-validate/src/__tests__/__snapshots__/validate-test.js.snap +++ b/packages/jest-validate/src/__tests__/__snapshots__/validate-test.js.snap @@ -1,4 +1,6 @@ -exports[`test displays warning for deprecated config options 1`] = ` +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`displays warning for deprecated config options 1`] = ` "● Deprecation Warning: Option scriptPreprocessor was replaced by transform, which support multiple preprocessors. @@ -12,7 +14,7 @@ exports[`test displays warning for deprecated config options 1`] = ` " `; -exports[`test displays warning for unknown config options 1`] = ` +exports[`displays warning for unknown config options 1`] = ` "● Validation Warning: Unknown option \\"unkwon\\" with value {} was found. Did you mean \\"unknown\\"? @@ -20,7 +22,7 @@ exports[`test displays warning for unknown config options 1`] = ` " `; -exports[`test pretty prints valid config for Array 1`] = ` +exports[`pretty prints valid config for Array 1`] = ` "● Validation Error: Option \\"coverageReporters\\" must be of type: @@ -35,7 +37,7 @@ exports[`test pretty prints valid config for Array 1`] = ` " `; -exports[`test pretty prints valid config for Boolean 1`] = ` +exports[`pretty prints valid config for Boolean 1`] = ` "● Validation Error: Option \\"automock\\" must be of type: @@ -50,7 +52,7 @@ exports[`test pretty prints valid config for Boolean 1`] = ` " `; -exports[`test pretty prints valid config for Function 1`] = ` +exports[`pretty prints valid config for Function 1`] = ` "● Validation Error: Option \\"fn\\" must be of type: @@ -65,7 +67,7 @@ exports[`test pretty prints valid config for Function 1`] = ` " `; -exports[`test pretty prints valid config for Object 1`] = ` +exports[`pretty prints valid config for Object 1`] = ` "● Validation Error: Option \\"haste\\" must be of type: @@ -80,7 +82,7 @@ exports[`test pretty prints valid config for Object 1`] = ` " `; -exports[`test pretty prints valid config for String 1`] = ` +exports[`pretty prints valid config for String 1`] = ` "● Validation Error: Option \\"preset\\" must be of type: @@ -95,7 +97,7 @@ exports[`test pretty prints valid config for String 1`] = ` " `; -exports[`test works with custom deprecations 1`] = ` +exports[`works with custom deprecations 1`] = ` "My Custom Deprecation Warning: Option scriptPreprocessor was replaced by transform, which support multiple preprocessors. @@ -110,7 +112,7 @@ exports[`test works with custom deprecations 1`] = ` My custom comment" `; -exports[`test works with custom errors 1`] = ` +exports[`works with custom errors 1`] = ` "My Custom Error: Option \\"test\\" must be of type: @@ -126,7 +128,7 @@ exports[`test works with custom errors 1`] = ` My custom comment" `; -exports[`test works with custom warnings 1`] = ` +exports[`works with custom warnings 1`] = ` "My Custom Warning: Unknown option \\"unknown\\" with value \\"string\\" was found. diff --git a/packages/pretty-format/package.json b/packages/pretty-format/package.json index 91e2d4fb27e6..e924cceb3fcd 100644 --- a/packages/pretty-format/package.json +++ b/packages/pretty-format/package.json @@ -10,6 +10,6 @@ "main": "build/index.js", "author": "James Kyle ", "dependencies": { - "ansi-styles": "^2.2.1" + "ansi-styles": "^3.0.0" } } diff --git a/packages/pretty-format/src/__tests__/__snapshots__/pretty-format-test.js.snap b/packages/pretty-format/src/__tests__/__snapshots__/pretty-format-test.js.snap index 668772c55b33..6a0a533778f5 100644 --- a/packages/pretty-format/src/__tests__/__snapshots__/pretty-format-test.js.snap +++ b/packages/pretty-format/src/__tests__/__snapshots__/pretty-format-test.js.snap @@ -1,3 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + exports[`prettyFormat() ReactTestComponent and ReactElement plugins ReactElement plugin highlights syntax 1`] = ` ", providesModuleNodeModules: Array, |}; diff --git a/types/TestResult.js b/types/TestResult.js index 6d54fdd384fa..d19e3dfa1b84 100644 --- a/types/TestResult.js +++ b/types/TestResult.js @@ -52,10 +52,10 @@ export type CoverageMap = {| fileCoverageFor: (file: string) => FileCoverage, |}; -export type Error = {| +export type SerializableError = {| message: string, stack: ?string, - type?: ?string, + type?: string, |}; export type FailedAssertion = {| @@ -134,7 +134,7 @@ export type TestResult = {| unmatched: number, updated: number, |}, - testExecError?: Error, + testExecError?: SerializableError, testFilePath: string, testResults: Array, |}; diff --git a/website/core/home/GridBlock.js b/website/core/home/GridBlock.js index 7caf977f3ddb..3527d0219d9d 100644 --- a/website/core/home/GridBlock.js +++ b/website/core/home/GridBlock.js @@ -31,7 +31,7 @@ class GridBlock extends React.Component { this.renderBlockImage(block.image); return ( -
+
{topLeftImage}
{this.renderBlockTitle(block.title)} diff --git a/website/core/home/HomeSplash.js b/website/core/home/HomeSplash.js index 740a08f38e4a..d289a9aae621 100644 --- a/website/core/home/HomeSplash.js +++ b/website/core/home/HomeSplash.js @@ -8,9 +8,9 @@ const React = require('React'); const siteConfig = require('../../siteConfig.js'); class HomeSplash extends React.Component { - makePromoElements(promoEl) { + makePromoElements(promoEl, index) { return ( -
+
{promoEl}
); diff --git a/website/core/nav/SideNav.js b/website/core/nav/SideNav.js index e13eef26988e..17586e569536 100644 --- a/website/core/nav/SideNav.js +++ b/website/core/nav/SideNav.js @@ -69,7 +69,7 @@ class SideNav extends React.Component { 'navItemActive': (link.id === this.props.current.id), }); return ( -
  • +
  • diff --git a/website/layout/BlogPageLayout.js b/website/layout/BlogPageLayout.js index 98a5da7cd096..b3b48c286645 100644 --- a/website/layout/BlogPageLayout.js +++ b/website/layout/BlogPageLayout.js @@ -44,6 +44,7 @@ const BlogPageLayout = React.createClass({ post={post} content={post.content} truncate={true} + key={post.path + post.title} /> ); }) diff --git a/website/siteConfig.js b/website/siteConfig.js index 0ac77724da6c..1758d8d8a842 100644 --- a/website/siteConfig.js +++ b/website/siteConfig.js @@ -188,6 +188,36 @@ const users = [ image: '/jest/img/logos/wowair.png', infoLink: 'https://wowair.com/', }, + { + caption: 'Audiense', + image: '/jest/img/logos/audiense.png', + infoLink: 'https://audiense.com/', + }, + { + caption: 'Coursera', + image: '/jest/img/logos/coursera.png', + infoLink: 'https://coursera.org/', + }, + { + caption: 'Algolia', + image: '/jest/img/logos/algolia.svg', + infoLink: 'https://algolia.com', + }, + { + caption: 'Help.com', + image: '/jest/img/logos/Help-Clean.png', + infoLink: 'https://help.com', + }, + { + caption: 'Hudl', + image: '/jest/img/logos/hudl.png', + infoLink: 'https://www.hudl.com/', + }, + { + caption: 'Jane', + image: '/jest/img/logos/jane.svg', + infoLink: 'https://jane.com', + }, ]; const siteConfig = { diff --git a/website/src/jest/css/jest.css b/website/src/jest/css/jest.css index 8ef8438ba7b4..9e6ceba7bc65 100644 --- a/website/src/jest/css/jest.css +++ b/website/src/jest/css/jest.css @@ -1634,7 +1634,7 @@ h6:hover .header-link { } .productShowcaseSection .logos img { max-height: 110px; - max-width: 110px; + width: 110px; padding: 20px; } @media only screen and (min-device-width: 360px) and (max-device-width: 736px) { @@ -1645,7 +1645,7 @@ h6:hover .header-link { } .productShowcaseSection .logos img { max-height: 64px; - max-width: 64px; + width: 64px; padding: 20px; } } @@ -1728,14 +1728,14 @@ div.video iframe { } .showcaseSection .logos img { max-height: 128px; - max-width: 128px; + width: 128px; padding: 20px; } @media only screen and (min-device-width: 360px) and (max-device-width: 736px) { .showcaseSection .logos img { max-height: 64px; - max-width: 64px; + width: 64px; padding: 20px; } } diff --git a/website/src/jest/img/logos/Help-Clean.png b/website/src/jest/img/logos/Help-Clean.png new file mode 100644 index 000000000000..944bff7428d6 Binary files /dev/null and b/website/src/jest/img/logos/Help-Clean.png differ diff --git a/website/src/jest/img/logos/algolia.svg b/website/src/jest/img/logos/algolia.svg new file mode 100644 index 000000000000..44086401c816 --- /dev/null +++ b/website/src/jest/img/logos/algolia.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/website/src/jest/img/logos/audiense.png b/website/src/jest/img/logos/audiense.png new file mode 100644 index 000000000000..234474388e8b Binary files /dev/null and b/website/src/jest/img/logos/audiense.png differ diff --git a/website/src/jest/img/logos/coursera.png b/website/src/jest/img/logos/coursera.png new file mode 100644 index 000000000000..e491da96c8c3 Binary files /dev/null and b/website/src/jest/img/logos/coursera.png differ diff --git a/website/src/jest/img/logos/hudl.png b/website/src/jest/img/logos/hudl.png new file mode 100644 index 000000000000..cb9c236330f1 Binary files /dev/null and b/website/src/jest/img/logos/hudl.png differ diff --git a/website/src/jest/img/logos/instagram.png b/website/src/jest/img/logos/instagram.png old mode 100755 new mode 100644 diff --git a/website/src/jest/img/logos/jane.svg b/website/src/jest/img/logos/jane.svg new file mode 100644 index 000000000000..3da566b84a53 --- /dev/null +++ b/website/src/jest/img/logos/jane.svg @@ -0,0 +1,2 @@ + + diff --git a/website/src/jest/index.js b/website/src/jest/index.js index 1488c5960243..e47b35112491 100644 --- a/website/src/jest/index.js +++ b/website/src/jest/index.js @@ -20,7 +20,7 @@ const index = React.createClass({ const showcase = siteConfig.users.filter(user => { return user.pinned; }).map(user => { - return ; + return ; }); return ( diff --git a/website/src/jest/users.js b/website/src/jest/users.js index 03887a14189e..8b6ffdcb85c8 100644 --- a/website/src/jest/users.js +++ b/website/src/jest/users.js @@ -11,7 +11,7 @@ class UserShowcase extends React.Component { render() { const showcase = siteConfig.users.map(user => { return ( - + ); diff --git a/yarn.lock b/yarn.lock index 4f68020570f7..303583c3a628 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,41 +2,37 @@ # yarn lockfile v1 -abab@^1.0.0: +abab@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d" -acorn-globals@^1.0.4: - version "1.0.9" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-1.0.9.tgz#55bb5e98691507b74579d0513413217c380c54cf" +acorn-globals@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf" dependencies: - acorn "^2.1.0" + acorn "^4.0.4" -acorn-jsx@^3.0.0, acorn-jsx@^3.0.1: +acorn-jsx@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" dependencies: acorn "^3.0.4" -acorn@^2.1.0, acorn@^2.4.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-2.7.0.tgz#ab6e7d9d886aaca8b085bc3312b79a198433f0e7" +acorn@4.0.4, acorn@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" acorn@^3.0.4: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" -acorn@^4.0.1: - version "4.0.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" - ajv-keywords@^1.0.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.0.tgz#c11e6859eafff83e0dafc416929472eca946aa2c" + version "1.5.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" ajv@^4.7.0: - version "4.10.4" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.10.4.tgz#c0974dd00b3464984892d6010aa9c2c945933254" + version "4.11.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.2.tgz#f166c3c11cbc6cb9dcc102a5bcfe5b72c95287e6" dependencies: co "^4.6.0" json-stable-stringify "^1.0.1" @@ -58,16 +54,18 @@ ansi-escapes@^1.1.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" ansi-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.0.0.tgz#c5061b6e0ef8a81775e50f5d66151bf6bf371107" + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" -append-transform@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.3.0.tgz#d6933ce4a85f09445d9ccc4cc119051b7381a813" +append-transform@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" + dependencies: + default-require-extensions "^1.0.0" argparse@^1.0.7: version "1.0.9" @@ -97,10 +95,6 @@ array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" -array-from@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195" - array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -115,6 +109,13 @@ array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" +array.prototype.find@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.0.3.tgz#08c3ec33e32ec4bab362a2958e686ae92f59271d" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.7.0" + arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -158,30 +159,30 @@ aws-sign2@~0.6.0: resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" aws4@^1.2.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.5.0.tgz#0a29ffb79c31c9e712eeb087e8e7a64b4a56d755" + version "1.6.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" -babel-code-frame@^6.16.0, babel-code-frame@^6.20.0: - version "6.20.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.20.0.tgz#b968f839090f9a8bc6d41938fb96cb84f7387b26" +babel-code-frame@^6.16.0, babel-code-frame@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" dependencies: chalk "^1.1.0" esutils "^2.0.2" - js-tokens "^2.0.0" - -babel-core@^6.18.0, babel-core@^6.18.2: - version "6.21.0" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.21.0.tgz#75525480c21c803f826ef3867d22c19f080a3724" - dependencies: - babel-code-frame "^6.20.0" - babel-generator "^6.21.0" - babel-helpers "^6.16.0" - babel-messages "^6.8.0" - babel-register "^6.18.0" - babel-runtime "^6.20.0" - babel-template "^6.16.0" - babel-traverse "^6.21.0" - babel-types "^6.21.0" + js-tokens "^3.0.0" + +babel-core@^6.23.0, babel-core@^6.23.1: + version "6.23.1" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.23.1.tgz#c143cb621bb2f621710c220c5d579d15b8a442df" + dependencies: + babel-code-frame "^6.22.0" + babel-generator "^6.23.0" + babel-helpers "^6.23.0" + babel-messages "^6.23.0" + babel-register "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.23.0" + babel-traverse "^6.23.1" + babel-types "^6.23.0" babylon "^6.11.0" convert-source-map "^1.1.0" debug "^2.1.1" @@ -203,73 +204,74 @@ babel-eslint@^7.1.1: babylon "^6.13.0" lodash.pickby "^4.6.0" -babel-generator@^6.18.0, babel-generator@^6.21.0: - version "6.21.0" - resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.21.0.tgz#605f1269c489a1c75deeca7ea16d43d4656c8494" +babel-generator@^6.18.0, babel-generator@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.23.0.tgz#6b8edab956ef3116f79d8c84c5a3c05f32a74bc5" dependencies: - babel-messages "^6.8.0" - babel-runtime "^6.20.0" - babel-types "^6.21.0" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-types "^6.23.0" detect-indent "^4.0.0" jsesc "^1.3.0" lodash "^4.2.0" source-map "^0.5.0" + trim-right "^1.0.1" -babel-helper-call-delegate@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.18.0.tgz#05b14aafa430884b034097ef29e9f067ea4133bd" +babel-helper-call-delegate@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.22.0.tgz#119921b56120f17e9dae3f74b4f5cc7bcc1b37ef" dependencies: - babel-helper-hoist-variables "^6.18.0" - babel-runtime "^6.0.0" - babel-traverse "^6.18.0" - babel-types "^6.18.0" + babel-helper-hoist-variables "^6.22.0" + babel-runtime "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" -babel-helper-function-name@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.18.0.tgz#68ec71aeba1f3e28b2a6f0730190b754a9bf30e6" +babel-helper-function-name@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.22.0.tgz#51f1bdc4bb89b15f57a9b249f33d742816dcbefc" dependencies: - babel-helper-get-function-arity "^6.18.0" - babel-runtime "^6.0.0" - babel-template "^6.8.0" - babel-traverse "^6.18.0" - babel-types "^6.18.0" + babel-helper-get-function-arity "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" -babel-helper-get-function-arity@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.18.0.tgz#a5b19695fd3f9cdfc328398b47dafcd7094f9f24" +babel-helper-get-function-arity@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.22.0.tgz#0beb464ad69dc7347410ac6ade9f03a50634f5ce" dependencies: - babel-runtime "^6.0.0" - babel-types "^6.18.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" -babel-helper-hoist-variables@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.18.0.tgz#a835b5ab8b46d6de9babefae4d98ea41e866b82a" +babel-helper-hoist-variables@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.22.0.tgz#3eacbf731d80705845dd2e9718f600cfb9b4ba72" dependencies: - babel-runtime "^6.0.0" - babel-types "^6.18.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" -babel-helper-remap-async-to-generator@^6.16.0: - version "6.20.3" - resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.20.3.tgz#9dd3b396f13e35ef63e538098500adc24c63c4e7" +babel-helper-remap-async-to-generator@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.22.0.tgz#2186ae73278ed03b8b15ced089609da981053383" dependencies: - babel-helper-function-name "^6.18.0" - babel-runtime "^6.20.0" - babel-template "^6.16.0" - babel-traverse "^6.20.0" - babel-types "^6.20.0" + babel-helper-function-name "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" -babel-helpers@^6.16.0: - version "6.16.0" - resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.16.0.tgz#1095ec10d99279460553e67eb3eee9973d3867e3" +babel-helpers@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.23.0.tgz#4f8f2e092d0b6a8808a4bde79c27f1e2ecf0d992" dependencies: - babel-runtime "^6.0.0" - babel-template "^6.16.0" + babel-runtime "^6.22.0" + babel-template "^6.23.0" -babel-messages@^6.8.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.8.0.tgz#bf504736ca967e6d65ef0adb5a2a5f947c8e0eb9" +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" dependencies: - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" @@ -280,104 +282,104 @@ babel-plugin-syntax-flow@^6.18.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d" babel-plugin-syntax-trailing-function-commas@^6.13.0: - version "6.20.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.20.0.tgz#442835e19179f45b87e92d477d70b9f1f18b5c4f" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" babel-plugin-transform-async-to-generator@^6.16.0: - version "6.16.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.16.0.tgz#19ec36cb1486b59f9f468adfa42ce13908ca2999" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.22.0.tgz#194b6938ec195ad36efc4c33a971acf00d8cd35e" dependencies: - babel-helper-remap-async-to-generator "^6.16.0" + babel-helper-remap-async-to-generator "^6.22.0" babel-plugin-syntax-async-functions "^6.8.0" - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" -babel-plugin-transform-es2015-destructuring@^6.19.0: - version "6.19.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.19.0.tgz#ff1d911c4b3f4cab621bd66702a869acd1900533" +babel-plugin-transform-es2015-destructuring@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" dependencies: - babel-runtime "^6.9.0" + babel-runtime "^6.22.0" -babel-plugin-transform-es2015-parameters@^6.18.0: - version "6.21.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.21.0.tgz#46a655e6864ef984091448cdf024d87b60b2a7d8" +babel-plugin-transform-es2015-parameters@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.23.0.tgz#3a2aabb70c8af945d5ce386f1a4250625a83ae3b" dependencies: - babel-helper-call-delegate "^6.18.0" - babel-helper-get-function-arity "^6.18.0" - babel-runtime "^6.9.0" - babel-template "^6.16.0" - babel-traverse "^6.21.0" - babel-types "^6.21.0" + babel-helper-call-delegate "^6.22.0" + babel-helper-get-function-arity "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.23.0" + babel-traverse "^6.23.0" + babel-types "^6.23.0" babel-plugin-transform-flow-strip-types@^6.18.0: - version "6.21.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.21.0.tgz#2eea3f8b5bb234339b47283feac155cfb237b948" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf" dependencies: babel-plugin-syntax-flow "^6.18.0" - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" -babel-register@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.18.0.tgz#892e2e03865078dd90ad2c715111ec4449b32a68" +babel-register@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.23.0.tgz#c9aa3d4cca94b51da34826c4a0f9e08145d74ff3" dependencies: - babel-core "^6.18.0" - babel-runtime "^6.11.6" + babel-core "^6.23.0" + babel-runtime "^6.22.0" core-js "^2.4.0" home-or-tmp "^2.0.0" lodash "^4.2.0" mkdirp "^0.5.1" source-map-support "^0.4.2" -babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.20.0, babel-runtime@^6.9.0: - version "6.20.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.20.0.tgz#87300bdcf4cd770f09bf0048c64204e17806d16f" +babel-runtime@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.22.0.tgz#1cf8b4ac67c77a4ddb0db2ae1f74de52ac4ca611" dependencies: core-js "^2.4.0" regenerator-runtime "^0.10.0" -babel-template@^6.16.0, babel-template@^6.8.0: - version "6.16.0" - resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.16.0.tgz#e149dd1a9f03a35f817ddbc4d0481988e7ebc8ca" +babel-template@^6.16.0, babel-template@^6.22.0, babel-template@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.23.0.tgz#04d4f270adbb3aa704a8143ae26faa529238e638" dependencies: - babel-runtime "^6.9.0" - babel-traverse "^6.16.0" - babel-types "^6.16.0" + babel-runtime "^6.22.0" + babel-traverse "^6.23.0" + babel-types "^6.23.0" babylon "^6.11.0" lodash "^4.2.0" -babel-traverse@^6.15.0, babel-traverse@^6.16.0, babel-traverse@^6.18.0, babel-traverse@^6.20.0, babel-traverse@^6.21.0: - version "6.21.0" - resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.21.0.tgz#69c6365804f1a4f69eb1213f85b00a818b8c21ad" +babel-traverse@^6.15.0, babel-traverse@^6.18.0, babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-traverse@^6.23.1: + version "6.23.1" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.23.1.tgz#d3cb59010ecd06a97d81310065f966b699e14f48" dependencies: - babel-code-frame "^6.20.0" - babel-messages "^6.8.0" - babel-runtime "^6.20.0" - babel-types "^6.21.0" - babylon "^6.11.0" + babel-code-frame "^6.22.0" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-types "^6.23.0" + babylon "^6.15.0" debug "^2.2.0" globals "^9.0.0" invariant "^2.2.0" lodash "^4.2.0" -babel-types@^6.15.0, babel-types@^6.16.0, babel-types@^6.18.0, babel-types@^6.20.0, babel-types@^6.21.0: - version "6.21.0" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.21.0.tgz#314b92168891ef6d3806b7f7a917fdf87c11a4b2" +babel-types@^6.15.0, babel-types@^6.18.0, babel-types@^6.22.0, babel-types@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.23.0.tgz#bb17179d7538bad38cd0c9e115d340f77e7e9acf" dependencies: - babel-runtime "^6.20.0" + babel-runtime "^6.22.0" esutils "^2.0.2" lodash "^4.2.0" to-fast-properties "^1.0.1" -babylon@^6.11.0, babylon@^6.13.0: - version "6.14.1" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.14.1.tgz#956275fab72753ad9b3435d7afe58f8bf0a29815" +babylon@^6.11.0, babylon@^6.13.0, babylon@^6.15.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.15.0.tgz#ba65cfa1a80e1759b0e89fb562e27dccae70348e" balanced-match@^0.4.1: version "0.4.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" bcrypt-pbkdf@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.0.tgz#3ca76b85241c7170bf7d9703e7b9aa74630040d4" + version "1.0.1" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" dependencies: tweetnacl "^0.14.3" @@ -466,6 +468,12 @@ cli-cursor@^1.0.1: dependencies: restore-cursor "^1.0.1" +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + dependencies: + restore-cursor "^2.0.0" + cli-width@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" @@ -508,12 +516,9 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -command-join@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/command-join/-/command-join-1.1.1.tgz#09e7609012e1dd8b4f0a14fde41a69eff1d2111f" - dependencies: - array-from "^2.1.1" - repeat-string "^1.5.4" +command-join@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/command-join/-/command-join-2.0.0.tgz#52e8b984f4872d952ff1bdc8b98397d27c7144cf" commander@^2.9.0: version "2.9.0" @@ -566,11 +571,11 @@ cryptiles@2.x.x: dependencies: boom "2.x.x" -cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.1.tgz#c9e37ef2490e64f6d1baa10fda852257082c25d3" +cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b" -"cssstyle@>= 0.2.36 < 0.3.0": +"cssstyle@>= 0.2.37 < 0.3.0": version "0.2.37" resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54" dependencies: @@ -608,6 +613,19 @@ deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" +default-require-extensions@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" + dependencies: + strip-bom "^2.0.0" + +define-properties@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" + dependencies: + foreach "^2.0.5" + object-keys "^1.0.8" + del@^2.0.2: version "2.2.2" resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" @@ -655,6 +673,23 @@ error-ex@^1.2.0: dependencies: is-arrayish "^0.2.1" +es-abstract@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.7.0.tgz#dfade774e01bfcd97f96180298c449c8623fb94c" + dependencies: + es-to-primitive "^1.1.1" + function-bind "^1.1.0" + is-callable "^1.1.3" + is-regex "^1.0.3" + +es-to-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" + dependencies: + is-callable "^1.1.1" + is-date-object "^1.0.1" + is-symbol "^1.0.1" + es5-ext@^0.10.7, es5-ext@^0.10.8, es5-ext@~0.10.11, es5-ext@~0.10.2, es5-ext@~0.10.7: version "0.10.12" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.12.tgz#aa84641d4db76b62abba5e45fd805ecbab140047" @@ -732,29 +767,26 @@ escope@^3.6.0: estraverse "^4.1.1" eslint-plugin-babel@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-4.0.0.tgz#a92114e2c493ac3034b030d7ecf96e174a76ef3f" - -eslint-plugin-flow-vars@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-flow-vars/-/eslint-plugin-flow-vars-0.5.0.tgz#a7fb78fd873c86e0e5839df3b3c90d47bc68c6d2" + version "4.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-4.0.1.tgz#77de74dabd67a6bef3b16bf258f5804e971e7349" eslint-plugin-flowtype@^2.28.2: - version "2.29.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.29.2.tgz#91b4fde0400c4c37ca4440b43bdbc95fc405bea9" + version "2.30.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.30.0.tgz#3054a265f9c8afe3046c3d41b72d32a736f9b4ae" dependencies: lodash "^4.15.0" eslint-plugin-react@^6.7.1: - version "6.8.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.8.0.tgz#741ab5438a094532e5ce1bbb935d6832356f492d" + version "6.9.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.9.0.tgz#54c2e9906b76f9d10142030bdc34e9d6840a0bb2" dependencies: + array.prototype.find "^2.0.1" doctrine "^1.2.2" jsx-ast-utils "^1.3.4" eslint@^3.11.1: - version "3.13.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.13.0.tgz#636925fd163c9babe2e8be7ae43caf518d469577" + version "3.15.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.15.0.tgz#bdcc6a6c5ffe08160e7b93c066695362a91e30f2" dependencies: babel-code-frame "^6.16.0" chalk "^1.1.3" @@ -762,7 +794,7 @@ eslint@^3.11.1: debug "^2.1.1" doctrine "^1.2.2" escope "^3.6.0" - espree "^3.3.1" + espree "^3.4.0" estraverse "^4.2.0" esutils "^2.0.2" file-entry-cache "^2.0.0" @@ -791,17 +823,21 @@ eslint@^3.11.1: text-table "~0.2.0" user-home "^2.0.0" -espree@^3.3.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.3.2.tgz#dbf3fadeb4ecb4d4778303e50103b3d36c88b89c" +espree@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.0.tgz#41656fa5628e042878025ef467e78f125cb86e1d" dependencies: - acorn "^4.0.1" + acorn "4.0.4" acorn-jsx "^3.0.0" -esprima@^2.6.0, esprima@^2.7.1: +esprima@^2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" +esprima@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + esrecurse@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220" @@ -862,6 +898,12 @@ extend@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" +external-editor@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.1.tgz#4c597c6c88fa6410e41dbbaa7b1be2336aa31095" + dependencies: + tmp "^0.0.31" + extglob@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" @@ -877,8 +919,8 @@ fast-levenshtein@~2.0.4: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" fbjs@^0.8.4: - version "0.8.8" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.8.tgz#02f1b6e0ea0d46c24e0b51a2d24df069563a5ad6" + version "0.8.9" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.9.tgz#180247fbd347dcc9004517b904f865400a0c8f14" dependencies: core-js "^1.0.0" isomorphic-fetch "^2.1.1" @@ -895,6 +937,12 @@ figures@^1.3.5: escape-string-regexp "^1.0.5" object-assign "^4.1.0" +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" @@ -939,9 +987,9 @@ flat-cache@^1.2.1: graceful-fs "^4.1.2" write "^0.2.1" -flow-bin@^0.37.4: - version "0.37.4" - resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.37.4.tgz#3d8da2ef746e80e730d166e09040f4198969b76b" +flow-bin@^0.39.0: + version "0.39.0" + resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.39.0.tgz#b1012a14460df1aa79d3a728e10f93c6944226d0" for-in@^0.1.5: version "0.1.6" @@ -953,6 +1001,10 @@ for-own@^0.1.4: dependencies: for-in "^0.1.5" +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -969,6 +1021,10 @@ fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" +function-bind@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" + generate-function@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" @@ -1090,8 +1146,8 @@ home-or-tmp@^2.0.0: os-tmpdir "^1.0.1" hosted-git-info@^2.1.4: - version "2.1.5" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.1.5.tgz#0ba81d90da2e25ab34a332e6ec77936e1598118b" + version "2.2.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.2.0.tgz#7a0d097863d886c0fabbdcd37bf1758d8becf8a5" html-encoding-sniffer@^1.0.1: version "1.0.1" @@ -1107,17 +1163,13 @@ http-signature@~1.1.0: jsprim "^1.2.2" sshpk "^1.7.0" -iconv-lite@0.4.13: +iconv-lite@0.4.13, iconv-lite@~0.4.13: version "0.4.13" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" -iconv-lite@^0.4.13, iconv-lite@~0.4.13: - version "0.4.15" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" - ignore@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.0.tgz#8d88f03c3002a0ac52114db25d2c673b0bf1e435" + version "3.2.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.2.tgz#1c51e1ef53bab6ddc15db4d9ac4ec139eceb3410" imurmurhash@^0.1.4: version "0.1.4" @@ -1158,6 +1210,24 @@ inquirer@^0.12.0: strip-ansi "^3.0.0" through "^2.3.6" +inquirer@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.0.1.tgz#6dfbffaf4d697dd76c8fe349f919de01c28afc4d" + dependencies: + ansi-escapes "^1.1.0" + chalk "^1.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^2.0.1" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rx "^4.1.0" + string-width "^2.0.0" + strip-ansi "^3.0.0" + through "^2.3.6" + interpret@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" @@ -1182,6 +1252,14 @@ is-builtin-module@^1.0.0: dependencies: builtin-modules "^1.0.0" +is-callable@^1.1.1, is-callable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + is-dotfile@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" @@ -1255,16 +1333,24 @@ is-path-inside@^1.0.0: is-posix-bracket@^0.1.0: version "0.1.1" - resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + resolved "http://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" is-primitive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + is-property@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" +is-regex@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.3.tgz#0d55182bddf9f2fde278220aec3a75642c908637" + is-resolvable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" @@ -1275,6 +1361,10 @@ is-stream@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" +is-symbol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" + is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -1309,13 +1399,13 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" istanbul-api@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.1.0.tgz#fb3f62edd5bfc6ae09da09453ded6e10ae7e483b" + version "1.1.1" + resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.1.1.tgz#d36e2f1560d1a43ce304c4ff7338182de61c8f73" dependencies: async "^2.1.4" fileset "^2.0.2" istanbul-lib-coverage "^1.0.0" - istanbul-lib-hook "^1.0.0-alpha.4" + istanbul-lib-hook "^1.0.0" istanbul-lib-instrument "^1.3.0" istanbul-lib-report "^1.0.0-alpha.3" istanbul-lib-source-maps "^1.1.0" @@ -1325,14 +1415,14 @@ istanbul-api@^1.1.0: once "^1.4.0" istanbul-lib-coverage@^1.0.0, istanbul-lib-coverage@^1.0.0-alpha, istanbul-lib-coverage@^1.0.0-alpha.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.0.tgz#c3f9b6d226da12424064cce87fce0fb57fdfa7a2" + version "1.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.1.tgz#f263efb519c051c5f1f3343034fc40e7b43ff212" -istanbul-lib-hook@^1.0.0-alpha.4: - version "1.0.0-alpha.4" - resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.0.0-alpha.4.tgz#8c5bb9f6fbd8526e0ae6cf639af28266906b938f" +istanbul-lib-hook@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.0.0.tgz#fc5367ee27f59268e8f060b0c7aaf051d9c425c5" dependencies: - append-transform "^0.3.0" + append-transform "^0.4.0" istanbul-lib-instrument@^1.3.0: version "1.4.2" @@ -1367,8 +1457,8 @@ istanbul-lib-source-maps@^1.1.0: source-map "^0.5.3" istanbul-reports@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.0.0.tgz#24b4eb2b1d29d50f103b369bd422f6e640aa0777" + version "1.0.1" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.0.1.tgz#9a17176bc4a6cbebdae52b2f15961d52fa623fbc" dependencies: handlebars "^4.0.3" @@ -1385,8 +1475,8 @@ jasmine-reporters@^2.2.0: xmldom "^0.1.22" jasmine@^2.4.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-2.5.2.tgz#6283cef7085c095cc25d651e954df004f7e3e421" + version "2.5.3" + resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-2.5.3.tgz#5441f254e1fc2269deb1dfd93e0e57d565ff4d22" dependencies: exit "^0.1.2" glob "^7.0.6" @@ -1398,45 +1488,44 @@ jodid25519@^1.0.0: dependencies: jsbn "~0.1.0" -js-tokens@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-2.0.0.tgz#79903f5563ee778cc1162e6dcf1a0027c97f9cb5" +js-tokens@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" js-yaml@^3.5.1, js-yaml@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" + version "3.8.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.1.tgz#782ba50200be7b9e5a8537001b7804db3ad02628" dependencies: argparse "^1.0.7" - esprima "^2.6.0" + esprima "^3.1.1" jsbn@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd" -jsdom@^9.9.1: - version "9.9.1" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.9.1.tgz#84f3972ad394ab963233af8725211bce4d01bfd5" +jsdom@^9.11.0: + version "9.11.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.11.0.tgz#a95b0304e521a2ca5a63c6ea47bf7708a7a84591" dependencies: - abab "^1.0.0" - acorn "^2.4.0" - acorn-globals "^1.0.4" + abab "^1.0.3" + acorn "^4.0.4" + acorn-globals "^3.1.0" array-equal "^1.0.0" content-type-parser "^1.0.1" - cssom ">= 0.3.0 < 0.4.0" - cssstyle ">= 0.2.36 < 0.3.0" + cssom ">= 0.3.2 < 0.4.0" + cssstyle ">= 0.2.37 < 0.3.0" escodegen "^1.6.1" html-encoding-sniffer "^1.0.1" - iconv-lite "^0.4.13" nwmatcher ">= 1.3.9 < 2.0.0" parse5 "^1.5.1" - request "^2.55.0" - sax "^1.1.4" - symbol-tree ">= 3.1.0 < 4.0.0" - tough-cookie "^2.3.1" - webidl-conversions "^3.0.1" + request "^2.79.0" + sax "^1.2.1" + symbol-tree "^3.2.1" + tough-cookie "^2.3.2" + webidl-conversions "^4.0.0" whatwg-encoding "^1.0.1" - whatwg-url "^4.1.0" - xml-name-validator ">= 2.0.1 < 3.0.0" + whatwg-url "^4.3.0" + xml-name-validator "^2.0.1" jsesc@^1.3.0: version "1.3.0" @@ -1477,10 +1566,9 @@ jsprim@^1.2.2: verror "1.3.6" jsx-ast-utils@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.3.5.tgz#9ba6297198d9f754594d62e59496ffb923778dd4" + version "1.4.0" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.0.tgz#5afe38868f56bc8cc7aeaef0100ba8c75bd12591" dependencies: - acorn-jsx "^3.0.1" object-assign "^4.1.0" kind-of@^3.0.2: @@ -1497,32 +1585,29 @@ left-pad@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.1.3.tgz#612f61c033f3a9e08e939f1caebeea41b6f3199a" -lerna@2.0.0-beta.32: - version "2.0.0-beta.32" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-2.0.0-beta.32.tgz#67d51b1520ce57a27b8445160726b48f6dba3f1c" +lerna@2.0.0-beta.37: + version "2.0.0-beta.37" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-2.0.0-beta.37.tgz#d8e1d25a75102658b12565e4aa12e0423e969aad" dependencies: async "^1.5.0" chalk "^1.1.1" cmd-shim "^2.0.2" - command-join "^1.1.1" + command-join "^2.0.0" cross-spawn "^4.0.0" glob "^7.0.6" - inquirer "^0.12.0" - lodash.find "^4.3.0" - lodash.unionwith "^4.2.0" + inquirer "^3.0.1" + lodash "^4.17.4" meow "^3.7.0" minimatch "^3.0.0" mkdirp "^0.5.1" normalize-path "^2.0.1" - object-assign "^4.0.1" - object-assign-sorted "^1.0.0" - pad "^1.0.0" + object-assign-sorted "^2.0.1" path-exists "^2.1.0" progress "^1.1.8" read-cmd-shim "^1.0.1" rimraf "^2.4.4" semver "^5.1.0" - signal-exit "^2.1.2" + signal-exit "^3.0.2" sync-exec "^0.6.2" levn@^0.3.0, levn@~0.3.0: @@ -1542,19 +1627,11 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" -lodash.find@^4.3.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1" - lodash.pickby@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" -lodash.unionwith@^4.2.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.unionwith/-/lodash.unionwith-4.6.0.tgz#74d140b5ca8146e6c643c3724f5152538d9ac1f0" - -lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.2.0, lodash@^4.3.0: +lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -1563,10 +1640,10 @@ longest@^1.0.1: resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" loose-envify@^1.0.0, loose-envify@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.0.tgz#6b26248c42f6d4fa4b0d8542f78edfcde35642a8" + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" dependencies: - js-tokens "^2.0.0" + js-tokens "^3.0.0" loud-rejection@^1.0.0: version "1.6.0" @@ -1619,15 +1696,19 @@ micromatch@^2.3.11: parse-glob "^3.0.4" regex-cache "^0.4.2" -mime-db@~1.25.0: - version "1.25.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.25.0.tgz#c18dbd7c73a5dbf6f44a024dc0d165a1e7b1c392" +mime-db@~1.26.0: + version "1.26.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.26.0.tgz#eaffcd0e4fc6935cf8134da246e2e6c35305adff" mime-types@^2.1.12, mime-types@~2.1.7: - version "2.1.13" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.13.tgz#e07aaa9c6c6b9a7ca3012c69003ad25a39e92a88" + version "2.1.14" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.14.tgz#f7ef7d97583fcaf3b7d282b6f8b5679dab1e94ee" dependencies: - mime-db "~1.25.0" + mime-db "~1.26.0" + +mimic-fn@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3: version "3.0.3" @@ -1657,6 +1738,10 @@ mute-stream@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -1693,16 +1778,19 @@ oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -object-assign-sorted@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/object-assign-sorted/-/object-assign-sorted-1.0.0.tgz#e739f698164014ec1f050f38decabad1e9b228bf" +object-assign-sorted@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object-assign-sorted/-/object-assign-sorted-2.0.1.tgz#c9983fa9e3ed5807b49cf1a9943378f245d9395b" dependencies: - object-assign "^4.0.1" sorted-object "^2.0.0" object-assign@^4.0.1, object-assign@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-keys@^1.0.8: + version "1.0.11" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" object.omit@^2.0.0: version "2.0.1" @@ -1721,6 +1809,12 @@ onetime@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" +onetime@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.0.tgz#52aa8110e52fc5126ffc667bd8ec21c2ed209ce6" + dependencies: + mimic-fn "^1.0.0" + optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -1743,14 +1837,10 @@ os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" -os-tmpdir@^1.0.1: +os-tmpdir@^1.0.1, os-tmpdir@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" -pad@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pad/-/pad-1.0.2.tgz#f6e36ff3ceb468e4ae2ed33ad5ecf25ace920960" - parse-glob@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" @@ -1823,8 +1913,8 @@ preserve@^0.2.0: resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" private@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/private/-/private-0.1.6.tgz#55c6a976d0f9bafb9924851350fe47b9b5fbb7c1" + version "0.1.7" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1" process-nextick-args@~1.0.6: version "1.0.7" @@ -1934,7 +2024,7 @@ regenerator-runtime@^0.10.0: regex-cache@^0.4.2: version "0.4.3" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" + resolved "http://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" dependencies: is-equal-shallow "^0.1.3" is-primitive "^2.0.0" @@ -1943,7 +2033,7 @@ repeat-element@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" -repeat-string@^1.5.2, repeat-string@^1.5.4: +repeat-string@^1.5.2: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" @@ -1953,7 +2043,7 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@>=2.42.0, request@^2.55.0: +request@>=2.42.0, request@^2.79.0: version "2.79.0" resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" dependencies: @@ -2000,6 +2090,13 @@ restore-cursor@^1.0.1: exit-hook "^1.0.0" onetime "^1.0.0" +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + right-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" @@ -2024,13 +2121,23 @@ run-async@^0.1.0: dependencies: once "^1.3.0" +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + dependencies: + is-promise "^2.1.0" + rx-lite@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" -sax@^1.1.4: - version "1.2.1" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" +rx@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" + +sax@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828" "semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0: version "5.3.0" @@ -2041,18 +2148,14 @@ setimmediate@^1.0.5: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" shelljs@^0.7.5: - version "0.7.5" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.5.tgz#2eef7a50a21e1ccf37da00df767ec69e30ad0675" + version "0.7.6" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.6.tgz#379cccfb56b91c8601e4793356eb5382924de9ad" dependencies: glob "^7.0.0" interpret "^1.0.0" rechoir "^0.6.2" -signal-exit@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-2.1.2.tgz#375879b1f92ebc3b334480d038dc546a6d558564" - -signal-exit@^3.0.0: +signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -2075,8 +2178,8 @@ sorted-object@^2.0.0: resolved "https://registry.yarnpkg.com/sorted-object/-/sorted-object-2.0.1.tgz#7d631f4bd3a798a24af1dffcfbfe83337a5df5fc" source-map-support@^0.4.2: - version "0.4.8" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.8.tgz#4871918d8a3af07289182e974e32844327b2e98b" + version "0.4.11" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.11.tgz#647f939978b38535909530885303daf23279f322" dependencies: source-map "^0.5.3" @@ -2115,8 +2218,8 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" sshpk@^1.7.0: - version "1.10.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.10.1.tgz#30e1a5d329244974a1af61511339d595af6638b0" + version "1.10.2" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.10.2.tgz#d5a804ce22695515638e798dbe23273de070a5fa" dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" @@ -2183,12 +2286,12 @@ supports-color@^2.0.0: resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" supports-color@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" dependencies: has-flag "^1.0.0" -"symbol-tree@>= 3.1.0 < 4.0.0": +symbol-tree@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.1.tgz#8549dd1d01fa9f893c18cc9ab0b106b4d9b168cb" @@ -2221,11 +2324,17 @@ through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" +tmp@^0.0.31: + version "0.0.31" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" + dependencies: + os-tmpdir "~1.0.1" + to-fast-properties@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" -tough-cookie@^2.3.1, tough-cookie@~2.3.0: +tough-cookie@^2.3.2, tough-cookie@~2.3.0: version "2.3.2" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" dependencies: @@ -2239,6 +2348,10 @@ trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + tryit@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" @@ -2262,8 +2375,8 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" typescript@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.1.4.tgz#b53b69fb841126acb1dd4b397d21daba87572251" + version "2.2.0" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.2.0.tgz#626f2fc70087d2480f21ebb12c1888288c8614e3" ua-parser-js@^0.7.9: version "0.7.12" @@ -2313,10 +2426,14 @@ verror@1.3.6: dependencies: extsprintf "1.0.2" -webidl-conversions@^3.0.0, webidl-conversions@^3.0.1: +webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" +webidl-conversions@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.0.tgz#0a8c727ae4e5649687b7742368dcfbf13ed40118" + whatwg-encoding@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.1.tgz#3c6c451a198ee7aec55b1ec61d0920c67801a5f4" @@ -2324,12 +2441,12 @@ whatwg-encoding@^1.0.1: iconv-lite "0.4.13" whatwg-fetch@>=0.10.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.1.tgz#078b9461bbe91cea73cbce8bb122a05f9e92b772" + version "2.0.2" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.2.tgz#fe294d1d89e36c5be8b3195057f2e4bc74fc980e" -whatwg-url@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.2.0.tgz#abf1a3f5ff4bc2005b3f0c2119382631789d8e44" +whatwg-url@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.3.0.tgz#92aaee21f4f2a642074357d70ef8500a7cbb171a" dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" @@ -2366,7 +2483,7 @@ write@^0.2.1: dependencies: mkdirp "^0.5.1" -"xml-name-validator@>= 2.0.1 < 3.0.0": +xml-name-validator@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"