diff --git a/.circleci/config.yml b/.circleci/config.yml index e3cadb0b6788..f7051b683e40 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -26,7 +26,7 @@ jobs: - restore-cache: *restore-cache - run: yarn --no-progress - save-cache: *save-cache - - run: yarn lint --format junit -o reports/junit/js-lint-results.xml && yarn typecheck && yarn lint-es5-build --format junit -o reports/junit/js-es5-lint-results.xml && yarn lint:md:ci + - run: yarn lint --format junit -o reports/junit/js-lint-results.xml && yarn typecheck && yarn lint-es5-build --format junit -o reports/junit/js-es5-lint-results.xml && yarn lint:md:ci && yarn check-copyright-headers - store_test_results: path: reports/junit diff --git a/.flowconfig b/.flowconfig index 27a7723184e7..e4a6b93f5217 100644 --- a/.flowconfig +++ b/.flowconfig @@ -18,4 +18,4 @@ untyped-import untyped-type-import [version] -^0.80.0 +^0.85.0 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000000..1019f4c475e3 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +fixtures/failing-jsons/ +packages/jest-config/src/__tests__/jest-preset.json +packages/pretty-format/perf/world.geo.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dcbe823063a..b4b9f192770a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Features +- `[jest-validate]` Add support for comments in `package.json` using a `"//"` key [#7295](https://github.com/facebook/jest/pull/7295)) +- `[jest-config]` Add shorthand for watch plugins and runners ([#7213](https://github.com/facebook/jest/pull/7213)) - `[jest-config]` [**BREAKING**] Deprecate `setupTestFrameworkScriptFile` in favor of new `setupFilesAfterEnv` ([#7119](https://github.com/facebook/jest/pull/7119)) - `[jest-jasmine2/jest-circus/jest-cli]` Add test.todo ([#6996](https://github.com/facebook/jest/pull/6996)) - `[pretty-format]` Option to not escape strings in diff messages ([#5661](https://github.com/facebook/jest/pull/5661)) @@ -24,11 +26,18 @@ - `[jest-config]` Accept an array as as well as a string for `testRegex`([#7209]https://github.com/facebook/jest/pull/7209)) - `[babel-preset-jest]` [**BREAKING**] Export a function instead of an object for Babel 7 compatibility ([#7203](https://github.com/facebook/jest/pull/7203)) - `[expect]` Check constructor equality in .toStrictEqual() ([#7005](https://github.com/facebook/jest/pull/7005)) +- `[jest-util]` Add `jest.getTimerCount()` to get the count of scheduled fake timers ([#7285](https://github.com/facebook/jest/pull/7285)) +- `[jest-config]` Add `dependencyExtractor` option to use a custom module to extract dependencies from files ([#7313](https://github.com/facebook/jest/pull/7313), [#7349](https://github.com/facebook/jest/pull/7349), [#7350](https://github.com/facebook/jest/pull/7350)) +- `[jest-haste-map]` [**BREAKING**] Expose relative paths when getting the file iterator ([#7321](https://github.com/facebook/jest/pull/7321)) +- `[jest-haste-map]` Accept a `getCacheKey` method in `hasteImplModulePath` modules to reset the cache when the logic changes ([#7350](https://github.com/facebook/jest/pull/7350)) +- `[jest-config]` Add `haste.computeSha1` option to compute the sha-1 of the files in the haste map ([#7345](https://github.com/facebook/jest/pull/7345)) ### Fixes +- `[expect]` Standardize file naming in `expect` ([#7306](https://github.com/facebook/jest/pull/7306)) +- `[jest-each]` Add empty array validation check ([#7249](https://github.com/facebook/jest/pull/7249)) - `[jest-cli]` Interrupt tests if interactive watch plugin key is pressed ([#7222](https://github.com/facebook/jest/pull/7222)) -- `[jest-cli]` Fix coverage summary reporting ([#7058](https://github.com/facebook/jest/pull/7058)) +- `[jest-cli]` [**BREAKING**] Do not use `text-summary` coverage reporter by default if other reporters are configured ([#7058](https://github.com/facebook/jest/pull/7058)) - `[jest-each]` Add each array validation check ([#7033](https://github.com/facebook/jest/pull/7033)) - `[jest-haste-map]` Do not visit again files with the same sha-1 ([#6990](https://github.com/facebook/jest/pull/6990)) - `[jest-jasmine2]` Fix memory leak in Error objects hold by the framework ([#6965](https://github.com/facebook/jest/pull/6965)) @@ -54,9 +63,22 @@ - `[jest-changed-files]` Return correctly the changed files when using `lastCommit=true` on Mercurial repositories ([#7228](https://github.com/facebook/jest/pull/7228)) - `[babel-jest]` Cache includes babel environment variables ([#7239](https://github.com/facebook/jest/pull/7239)) - `[jest-config]` Use strings instead of `RegExp` instances in normalized configuration ([#7251](https://github.com/facebook/jest/pull/7251)) +- `[jest-circus]` Make sure to display real duration even if time is mocked ([#7264](https://github.com/facebook/jest/pull/7264)) +- `[expect]` Improves the failing message for `toStrictEqual` matcher. ([#7224](https://github.com/facebook/jest/pull/7224)) +- `[jest-mock]` [**BREAKING**] Fix bugs with mock/spy result tracking of recursive functions ([#6381](https://github.com/facebook/jest/pull/6381)) +- `[jest-resolve]` Fix not being able to resolve path to mapped file with custom platform ([#7312](https://github.com/facebook/jest/pull/7312)) +- `[jest-message-util]` Improve parsing of error messages for unusually formatted stack traces ([#7319](https://github.com/facebook/jest/pull/7319)) +- `[jest-runtime]` Ensure error message text is not lost on errors with code frames ([#7319](https://github.com/facebook/jest/pull/7319)) +- `[jest-haste-map]` Fix to resolve path that is start with words same as rootDir ([#7324](https://github.com/facebook/jest/pull/7324)) +- `[expect]` Fix toMatchObject matcher when used with `Object.create(null)` ([#7334](https://github.com/facebook/jest/pull/7334)) +- `[jest-haste-map]` [**BREAKING**] Recover files correctly after haste name collisions are fixed ([#7329](https://github.com/facebook/jest/pull/7329)) +- `[jest-haste-map]` Remove legacy condition for duplicate module detection ([#7333](https://github.com/facebook/jest/pull/7333)) +- `[jest-haste-map]` Fix `require` detection with trailing commas and ignore `import typeof` modules ([#7385](https://github.com/facebook/jest/pull/7385)) ### Chore & Maintenance +- `[jest-watcher]` Standardize filenames ([#7314](https://github.com/facebook/jest/pull/7314)) +- `[jest-circus]` Standardize file naming in `jest-circus` ([#7301](https://github.com/facebook/jest/pull/7301)) - `[docs]` Add synchronous test.each setup ([#7150](https://github.com/facebook/jest/pull/7150)) - `[docs]` Add `this.extend` to the Custom Matchers API reference ([#7130](https://github.com/facebook/jest/pull/7130)) - `[docs]` Fix default value for `coverageReporters` value in configuration docs ([#7126](https://github.com/facebook/jest/pull/7126)) @@ -81,6 +103,15 @@ - `[tests]` Free tests from the dependency on value of FORCE_COLOR ([#6585](https://github.com/facebook/jest/pull/6585/files)) - `[jest-diff]` Standardize filenames ([#7238](https://github.com/facebook/jest/pull/7238)) - `[*]` Add babel plugin to make sure Jest is unaffected by fake Promise implementations ([#7225](https://github.com/facebook/jest/pull/7225)) +- `[jest-haste-map]` Standardize filenames ([#7266](https://github.com/facebook/jest/pull/7266)) +- `[*]` [**BREAKING**] Require Node.js 6+ for all packages ([#7258](https://github.com/facebook/jest/pull/7258)) +- `[docs]` Add correct default value for `testUrl` config option ([#7277](https://github.com/facebook/jest/pull/7277)) +- `[jest-util]` [**BREAKING**] Remove long-deprecated globals for fake timers ([#7285](https://github.com/facebook/jest/pull/7285)) +- `[docs]` Remove duplicate code in `MockFunctions` ([#7297](https://github.com/facebook/jest/pull/7297)) +- `[jest-worker]` Standardize filenames ([#7316](https://github.com/facebook/jest/pull/7316)) +- `[pretty-format]` Standardize filenames ([#7316](https://github.com/facebook/jest/pull/7316)) +- `[*]` Add check for Facebook copyright headers on CI ([#7370](https://github.com/facebook/jest/pull/7370)) +- `[jest-haste-map]` Refactor `dependencyExtractor` and tests ([#7385](https://github.com/facebook/jest/pull/7385)) ### Performance @@ -172,9 +203,6 @@ ### Fixes - `[jest-haste-map]` Optimize watchman crawler by using `glob` on initial query ([#6689](https://github.com/facebook/jest/pull/6689)) - -### Fixes - - `[pretty-format]` Fix formatting of invalid Date objects ([#6635](https://github.com/facebook/jest/pull/6635)) ## 23.4.0 @@ -335,7 +363,7 @@ - `[jest-diff]` Support returning diff from oneline strings ([#6221](https://github.com/facebook/jest/pull/6221)) - `[expect]` Improve return matchers ([#6172](https://github.com/facebook/jest/pull/6172)) - `[jest-cli]` Overhaul watch plugin hooks names ([#6249](https://github.com/facebook/jest/pull/6249)) -- `[jest-mock]` Include tracked call results in serialized mock ([#6244](https://github.com/facebook/jest/pull/6244)) +- `[jest-mock]` [**BREAKING**] Include tracked call results in serialized mock ([#6244](https://github.com/facebook/jest/pull/6244)) ### Fixes diff --git a/README.md b/README.md index 6b4a8478b16b..2101801f4e6a 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ npm version SemVer Blazing Fast + PR's welcome

Backers on Open Collective diff --git a/docs/Configuration.md b/docs/Configuration.md index 55281c790207..8cc5493f7864 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -257,6 +257,16 @@ Jest will fail if: - The `./src/api/very-important-module.js` file has less than 100% coverage. - Every remaining file combined has less than 50% coverage (`global`). +### `dependencyExtractor` [string] + +Default: `undefined` + +This option allows the use of a custom dependency extractor. It must be a node module that exports an object with an `extract` function expecting a string as the first argument for the code to analyze and Jest's dependency extractor as the second argument (in case you only want to extend it). + +The function should return an iterable (`Array`, `Set`, etc.) with the dependencies found in the code. + +That module can also contain a `getCacheKey` function to generate a cache key to determine if the logic has changed and any cached artifacts relying on it should be discarded. + ### `errorOnDeprecated` [boolean] Default: `false` @@ -609,6 +619,8 @@ This option allows you to use a custom runner instead of Jest's default test run - [`jest-runner-tsc`](https://github.com/azz/jest-runner-tsc) - [`jest-runner-prettier`](https://github.com/keplersj/jest-runner-prettier) +_Note: The `runner` property value can omit the `jest-runner-` prefix of the package name._ + To write a test-runner, export a class with which accepts `globalConfig` in the constructor, and has a `runTests` method with the signature: ```ts @@ -905,7 +917,7 @@ An example of such function can be found in our default [jasmine2 test runner pa ### `testURL` [string] -Default: `about:blank` +Default: `http://localhost` This option sets the URL for the jsdom environment. It is reflected in properties such as `location.href`. @@ -962,3 +974,37 @@ Default: `[]` An array of RegExp patterns that are matched against all source file paths before re-running tests in watch mode. If the file path matches any of the patterns, when it is updated, it will not trigger a re-run of tests. These patterns match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/node_modules/"]`. + +### `watchPlugins` [array] + +Default: `[]` + +This option allows you to use a custom watch plugins. Read more about watch plugins [here](watch-plugins). + +Examples of watch plugins include: + +- [`jest-watch-master`](https://github.com/rickhanlonii/jest-watch-master) +- [`jest-watch-select-projects`](https://github.com/rogeliog/jest-watch-select-projects) +- [`jest-watch-suspend`](https://github.com/unional/jest-watch-suspend) +- [`jest-watch-typeahead`](https://github.com/jest-community/jest-watch-typeahead) +- [`jest-watch-yarn-workspaces`](https://github.com/cameronhunter/jest-watch-directories/tree/master/packages/jest-watch-yarn-workspaces) + +_Note: The values in the `watchPlugins` property value can omit the `jest-watch-` prefix of the package name._ + +### `//` [string] + +No default + +This option allow comments in `package.json`. Include the comment text as the value of this key anywhere in `package.json`. + +Example: + +```json +{ + "name": "my-project", + "jest": { + "//": "Comment goes here", + "verbose": true + } +} +``` diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 4f861066cab3..699de85fbc22 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -15,6 +15,8 @@ Or [`npm`](https://www.npmjs.com/): npm install --save-dev jest ``` +Note: Jest documentation uses `yarn` commands, but `npm` will also work. You can compare `yarn` and `npm` commands in the [yarn docs, here](https://yarnpkg.com/en/docs/migrating-from-npm#toc-cli-commands-comparison). + Let's get started by writing a test for a hypothetical function that adds two numbers. First, create a `sum.js` file: ```javascript @@ -44,7 +46,7 @@ Add the following section to your `package.json`: } ``` -Finally, run `yarn test` and Jest will print this message: +Finally, run `yarn test` or `npm run test` and Jest will print this message: ```bash PASS ./sum.test.js @@ -57,7 +59,7 @@ This test used `expect` and `toBe` to test that two values were exactly identica ## Running from command line -You can run Jest directly from the CLI (if it's globally available in your `PATH`, e.g. by `yarn global add jest`) with a variety of useful options. +You can run Jest directly from the CLI (if it's globally available in your `PATH`, e.g. by `yarn global add jest` or `npm install jest --global`) with a variety of useful options. Here's how to run Jest on files matching `my-test`, using `config.json` as a configuration file and display a native OS notification after the run: @@ -79,7 +81,7 @@ jest --init ### Using Babel -To use [Babel](http://babeljs.io/), install the `babel-jest` and `@babel/core` packages: +To use [Babel](http://babeljs.io/), install the `babel-jest` and `@babel/core` packages via `yarn`: ```bash yarn add --dev babel-jest @babel/core diff --git a/docs/JestObjectAPI.md b/docs/JestObjectAPI.md index 0442c12a34e6..7d42bb08235e 100644 --- a/docs/JestObjectAPI.md +++ b/docs/JestObjectAPI.md @@ -5,45 +5,7 @@ title: The Jest Object The `jest` object is automatically in scope within every test file. The methods in the `jest` object help create mocks and let you control Jest's overall behavior. -## Methods - -- [`jest.clearAllTimers()`](#jestclearalltimers) -- [`jest.disableAutomock()`](#jestdisableautomock) -- [`jest.enableAutomock()`](#jestenableautomock) -- [`jest.fn(implementation)`](#jestfnimplementation) -- [`jest.isMockFunction(fn)`](#jestismockfunctionfn) -- [`jest.genMockFromModule(moduleName)`](#jestgenmockfrommodulemodulename) -- [`jest.mock(moduleName, factory, options)`](#jestmockmodulename-factory-options) -- [`jest.unmock(moduleName)`](#jestunmockmodulename) -- [`jest.doMock(moduleName, factory, options)`](#jestdomockmodulename-factory-options) -- [`jest.dontMock(moduleName)`](#jestdontmockmodulename) -- [`jest.clearAllMocks()`](#jestclearallmocks) -- [`jest.resetAllMocks()`](#jestresetallmocks) -- [`jest.restoreAllMocks()`](#jestrestoreallmocks) -- [`jest.resetModules()`](#jestresetmodules) -- [`jest.retryTimes()`](#jestretrytimes) -- [`jest.runAllTicks()`](#jestrunallticks) -- [`jest.runAllTimers()`](#jestrunalltimers) -- [`jest.advanceTimersByTime(msToRun)`](#jestadvancetimersbytimemstorun) -- [`jest.runOnlyPendingTimers()`](#jestrunonlypendingtimers) -- [`jest.requireActual()`](#jestrequireactual) -- [`jest.requireMock()`](#jestrequiremock) -- [`jest.setMock(moduleName, moduleExports)`](#jestsetmockmodulename-moduleexports) -- [`jest.setTimeout(timeout)`](#jestsettimeouttimeout) -- [`jest.useFakeTimers()`](#jestusefaketimers) -- [`jest.useRealTimers()`](#jestuserealtimers) -- [`jest.spyOn(object, methodName)`](#jestspyonobject-methodname) -- [`jest.spyOn(object, methodName, accessType?)`](#jestspyonobject-methodname-accesstype) - ---- - -## Reference - -### `jest.clearAllTimers()` - -Removes any pending timers from the timer system. - -This means, if any timers have been scheduled (but have not yet executed), they will be cleared and will never have the opportunity to execute in the future. +## Mock Modules ### `jest.disableAutomock()` @@ -56,7 +18,9 @@ After this method is called, all `require()`s will return the real versions of e Jest configuration: ```json -"automock": true +{ + "automock": true +} ``` Example: @@ -126,24 +90,6 @@ test('original implementation', () => { _Note: this method was previously called `autoMockOn`. When using `babel-jest`, calls to `enableAutomock` will automatically be hoisted to the top of the code block. Use `autoMockOn` if you want to explicitly avoid this behavior._ -### `jest.fn(implementation)` - -Returns a new, unused [mock function](MockFunctionAPI.md). Optionally takes a mock implementation. - -```js -const mockFn = jest.fn(); -mockFn(); -expect(mockFn).toHaveBeenCalled(); - -// With a mock implementation: -const returnsTrue = jest.fn(() => true); -console.log(returnsTrue()); // true; -``` - -### `jest.isMockFunction(fn)` - -Determines if the given function is a mocked function. - ### `jest.genMockFromModule(moduleName)` Given the name of a module, use the automatic mocking system to generate a mocked version of the module for you. @@ -208,9 +154,9 @@ jest.mock( '../moduleName', () => { /* - * Custom implementation of a module that doesn't exist in JS, - * like a generated module or a native module in react-native. - */ + * Custom implementation of a module that doesn't exist in JS, + * like a generated module or a native module in react-native. + */ }, {virtual: true}, ); @@ -266,21 +212,25 @@ When using `babel-jest`, calls to `unmock` will automatically be hoisted to the Returns the `jest` object for chaining. -### `jest.clearAllMocks()` +### `jest.setMock(moduleName, moduleExports)` -Clears the `mock.calls` and `mock.instances` properties of all mocks. Equivalent to calling [`.mockClear()`](MockFunctionAPI.md#mockfnmockclear) on every mocked function. +Explicitly supplies the mock object that the module system should return for the specified module. + +On occasion there are times where the automatically generated mock the module system would normally provide you isn't adequate enough for your testing needs. Normally under those circumstances you should write a [manual mock](ManualMocks.md) that is more adequate for the module in question. However, on extremely rare occasions, even a manual mock isn't suitable for your purposes and you need to build the mock yourself inside your test. + +In these rare scenarios you can use this API to manually fill the slot in the module system's mock-module registry. Returns the `jest` object for chaining. -### `jest.resetAllMocks()` +_Note It is recommended to use [`jest.mock()`](#jestmockmodulename-factory-options) instead. The `jest.mock` API's second argument is a module factory instead of the expected exported module object._ -Resets the state of all mocks. Equivalent to calling [`.mockReset()`](MockFunctionAPI.md#mockfnmockreset) on every mocked function. +### `jest.requireActual(moduleName)` -Returns the `jest` object for chaining. +Returns the actual module instead of a mock, bypassing all checks on whether the module should receive a mock implementation or not. -### `jest.restoreAllMocks()` +### `jest.requireMock(moduleName)` -Restores all mocks back to their original value. Equivalent to calling [`.mockRestore()`](MockFunctionAPI.md#mockfnmockrestore) on every mocked function. Beware that `jest.restoreAllMocks()` only works when mock was created with `jest.spyOn`; other mocks will require you to manually restore them. +Returns a mock module instead of the actual module, bypassing all checks on whether the module should be required normally or not. ### `jest.resetModules()` @@ -315,100 +265,25 @@ test('works too', () => { Returns the `jest` object for chaining. -### `jest.retryTimes()` +## Mock functions -Runs failed tests n-times until they pass or until the max number of retries is exhausted. This only works with [jest-circus](https://github.com/facebook/jest/tree/master/packages/jest-circus)! +### `jest.fn(implementation)` -Example in a test: +Returns a new, unused [mock function](MockFunctionAPI.md). Optionally takes a mock implementation. ```js -jest.retryTimes(3); -test('will fail', () => { - expect(true).toBe(false); -}); -``` - -Returns the `jest` object for chaining. - -### `jest.runAllTicks()` - -Exhausts the **micro**-task queue (usually interfaced in node via `process.nextTick`). - -When this API is called, all pending micro-tasks that have been queued via `process.nextTick` will be executed. Additionally, if those micro-tasks themselves schedule new micro-tasks, those will be continually exhausted until there are no more micro-tasks remaining in the queue. - -### `jest.runAllTimers()` - -Exhausts the **macro**-task queue (i.e., all tasks queued by `setTimeout()`, `setInterval()`, and `setImmediate()`). - -When this API is called, all pending "macro-tasks" that have been queued via `setTimeout()` or `setInterval()` will be executed. Additionally if those macro-tasks themselves schedule new macro-tasks, those will be continually exhausted until there are no more macro-tasks remaining in the queue. - -This is often useful for synchronously executing setTimeouts during a test in order to synchronously assert about some behavior that would only happen after the `setTimeout()` or `setInterval()` callbacks executed. See the [Timer mocks](TimerMocks.md) doc for more information. - -### `jest.runAllImmediates()` - -Exhausts all tasks queued by `setImmediate()`. - -### `jest.advanceTimersByTime(msToRun)` - -##### renamed in Jest **22.0.0+** - -Also under the alias: `.runTimersToTime()` - -Executes only the macro task queue (i.e. all tasks queued by `setTimeout()` or `setInterval()` and `setImmediate()`). - -When this API is called, all timers are advanced by `msToRun` milliseconds. All pending "macro-tasks" that have been queued via `setTimeout()` or `setInterval()`, and would be executed within this time frame 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. - -### `jest.runOnlyPendingTimers()` - -Executes only the macro-tasks that are currently pending (i.e., only the tasks that have been queued by `setTimeout()` or `setInterval()` up to this point). If any of the currently pending macro-tasks schedule new macro-tasks, those new tasks will not be executed by this call. - -This is useful for scenarios such as one where the module being tested schedules a `setTimeout()` whose callback schedules another `setTimeout()` recursively (meaning the scheduling never stops). In these scenarios, it's useful to be able to run forward in time by a single step at a time. - -### `jest.requireActual(moduleName)` - -Returns the actual module instead of a mock, bypassing all checks on whether the module should receive a mock implementation or not. - -### `jest.requireMock(moduleName)` - -Returns a mock module instead of the actual module, bypassing all checks on whether the module should be required normally or not. - -### `jest.setMock(moduleName, moduleExports)` - -Explicitly supplies the mock object that the module system should return for the specified module. - -On occasion there are times where the automatically generated mock the module system would normally provide you isn't adequate enough for your testing needs. Normally under those circumstances you should write a [manual mock](ManualMocks.md) that is more adequate for the module in question. However, on extremely rare occasions, even a manual mock isn't suitable for your purposes and you need to build the mock yourself inside your test. - -In these rare scenarios you can use this API to manually fill the slot in the module system's mock-module registry. - -Returns the `jest` object for chaining. - -_Note It is recommended to use [`jest.mock()`](#jestmockmodulename-factory-options) instead. The `jest.mock` API's second argument is a module factory instead of the expected exported module object._ - -### `jest.setTimeout(timeout)` - -Set the default timeout interval for tests and before/after hooks in milliseconds. - -_Note: The default timeout interval is 5 seconds if this method is not called._ - -_Note: The method must be called after the test framework is installed in the environment and before the test runs. A good place to do this is in the `setupTestFrameworkScriptFile`._ - -Example: +const mockFn = jest.fn(); +mockFn(); +expect(mockFn).toHaveBeenCalled(); -```js -jest.setTimeout(1000); // 1 second +// With a mock implementation: +const returnsTrue = jest.fn(() => true); +console.log(returnsTrue()); // true; ``` -### `jest.useFakeTimers()` - -Instructs Jest to use fake versions of the standard timer functions (`setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`, `nextTick`, `setImmediate` and `clearImmediate`). - -Returns the `jest` object for chaining. - -### `jest.useRealTimers()` - -Instructs Jest to use the real versions of the standard timer functions. +### `jest.isMockFunction(fn)` -Returns the `jest` object for chaining. +Determines if the given function is a mocked function. ### `jest.spyOn(object, methodName)` @@ -501,3 +376,108 @@ test('plays audio', () => { spy.mockRestore(); }); ``` + +### `jest.clearAllMocks()` + +Clears the `mock.calls` and `mock.instances` properties of all mocks. Equivalent to calling [`.mockClear()`](MockFunctionAPI.md#mockfnmockclear) on every mocked function. + +Returns the `jest` object for chaining. + +### `jest.resetAllMocks()` + +Resets the state of all mocks. Equivalent to calling [`.mockReset()`](MockFunctionAPI.md#mockfnmockreset) on every mocked function. + +Returns the `jest` object for chaining. + +### `jest.restoreAllMocks()` + +Restores all mocks back to their original value. Equivalent to calling [`.mockRestore()`](MockFunctionAPI.md#mockfnmockrestore) on every mocked function. Beware that `jest.restoreAllMocks()` only works when mock was created with `jest.spyOn`; other mocks will require you to manually restore them. + +## Mock timers + +### `jest.useFakeTimers()` + +Instructs Jest to use fake versions of the standard timer functions (`setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`, `nextTick`, `setImmediate` and `clearImmediate`). + +Returns the `jest` object for chaining. + +### `jest.useRealTimers()` + +Instructs Jest to use the real versions of the standard timer functions. + +Returns the `jest` object for chaining. + +### `jest.runAllTicks()` + +Exhausts the **micro**-task queue (usually interfaced in node via `process.nextTick`). + +When this API is called, all pending micro-tasks that have been queued via `process.nextTick` will be executed. Additionally, if those micro-tasks themselves schedule new micro-tasks, those will be continually exhausted until there are no more micro-tasks remaining in the queue. + +### `jest.runAllTimers()` + +Exhausts the **macro**-task queue (i.e., all tasks queued by `setTimeout()`, `setInterval()`, and `setImmediate()`). + +When this API is called, all pending "macro-tasks" that have been queued via `setTimeout()` or `setInterval()` will be executed. Additionally if those macro-tasks themselves schedule new macro-tasks, those will be continually exhausted until there are no more macro-tasks remaining in the queue. + +This is often useful for synchronously executing setTimeouts during a test in order to synchronously assert about some behavior that would only happen after the `setTimeout()` or `setInterval()` callbacks executed. See the [Timer mocks](TimerMocks.md) doc for more information. + +### `jest.runAllImmediates()` + +Exhausts all tasks queued by `setImmediate()`. + +### `jest.advanceTimersByTime(msToRun)` + +##### renamed in Jest **22.0.0+** + +Also under the alias: `.runTimersToTime()` + +Executes only the macro task queue (i.e. all tasks queued by `setTimeout()` or `setInterval()` and `setImmediate()`). + +When this API is called, all timers are advanced by `msToRun` milliseconds. All pending "macro-tasks" that have been queued via `setTimeout()` or `setInterval()`, and would be executed within this time frame 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. + +### `jest.runOnlyPendingTimers()` + +Executes only the macro-tasks that are currently pending (i.e., only the tasks that have been queued by `setTimeout()` or `setInterval()` up to this point). If any of the currently pending macro-tasks schedule new macro-tasks, those new tasks will not be executed by this call. + +This is useful for scenarios such as one where the module being tested schedules a `setTimeout()` whose callback schedules another `setTimeout()` recursively (meaning the scheduling never stops). In these scenarios, it's useful to be able to run forward in time by a single step at a time. + +### `jest.clearAllTimers()` + +Removes any pending timers from the timer system. + +This means, if any timers have been scheduled (but have not yet executed), they will be cleared and will never have the opportunity to execute in the future. + +### `jest.getTimerCount()` + +Returns the number of fake timers still left to run. + +## Misc + +### `jest.setTimeout(timeout)` + +Set the default timeout interval for tests and before/after hooks in milliseconds. + +_Note: The default timeout interval is 5 seconds if this method is not called._ + +_Note: The method must be called after the test framework is installed in the environment and before the test runs. A good place to do this is in the `setupTestFrameworkScriptFile`._ + +Example: + +```js +jest.setTimeout(1000); // 1 second +``` + +### `jest.retryTimes()` + +Runs failed tests n-times until they pass or until the max number of retries is exhausted. This only works with [jest-circus](https://github.com/facebook/jest/tree/master/packages/jest-circus)! + +Example in a test: + +```js +jest.retryTimes(3); +test('will fail', () => { + expect(true).toBe(false); +}); +``` + +Returns the `jest` object for chaining. diff --git a/docs/MockFunctionAPI.md b/docs/MockFunctionAPI.md index dfdfb7672ac8..199d7a797175 100644 --- a/docs/MockFunctionAPI.md +++ b/docs/MockFunctionAPI.md @@ -29,24 +29,30 @@ For example: A mock function `f` that has been called twice, with the arguments ### `mockFn.mock.results` -An array containing the results of all calls that have been made to this mock function. Each entry in this array is an object containing a boolean `isThrow` property, and a `value` property. `isThrow` is true if the call terminated due to a `throw`, or false if the the call returned normally. The `value` property contains the value that was thrown or returned. +An array containing the results of all calls that have been made to this mock function. Each entry in this array is an object containing a `type` property, and a `value` property. `type` will be one of the following: -For example: A mock function `f` that has been called three times, returning `result1`, throwing an error, and then returning `result2`, would have a `mock.results` array that looks like this: +- `'return'` - Indicates that the call completed by returning normally. +- `'throw'` - Indicates that the call completed by throwing a value. +- `'incomplete'` - Indicates that the call has not yet completed. This occurs if you test the result from within the mock function itself, or from within a function that was called by the mock. + +The `value` property contains the value that was thrown or returned. `value` is undefined when `type === 'incomplete'`. + +For example: A mock function `f` that has been called three times, returning `'result1'`, throwing an error, and then returning `'result2'`, would have a `mock.results` array that looks like this: ```js [ { - isThrow: false, + type: 'return', value: 'result1', }, { - isThrow: true, + type: 'throw', value: { /* Error instance */ }, }, { - isThrow: false, + type: 'return', value: 'result2', }, ]; diff --git a/docs/MockFunctions.md b/docs/MockFunctions.md index eb96d94ca990..dfc33a7af6f3 100644 --- a/docs/MockFunctions.md +++ b/docs/MockFunctions.md @@ -160,9 +160,6 @@ Still, there are cases where it's useful to go beyond the ability to specify ret ```javascript const myMockFn = jest.fn(cb => cb(null, true)); -myMockFn((err, val) => console.log(val)); -// > true - myMockFn((err, val) => console.log(val)); // > true ``` diff --git a/docs/MongoDB.md b/docs/MongoDB.md index 6d4fcbf88108..2950c2a9c604 100644 --- a/docs/MongoDB.md +++ b/docs/MongoDB.md @@ -26,7 +26,7 @@ const {MongoMemoryServer} = require('mongodb-memory-server'); const globalConfigPath = path.join(__dirname, 'globalConfig.json'); -const mongoServer = new MongoMemoryServer({ +const mongod = new MongoMemoryServer({ autoStart: false, }); @@ -37,14 +37,14 @@ module.exports = async () => { const mongoConfig = { mongoDBName: 'jest', - mongoUri: await mongoServer.getConnectionString(), + mongoUri: await mongod.getConnectionString(), }; // Write global config to disk because all tests run in different contexts. fs.writeFileSync(globalConfigPath, JSON.stringify(mongoConfig)); // Set reference to mongod in order to close the server during teardown. - global.__MONGOD__ = mongoServer; + global.__MONGOD__ = mongod; }; ``` diff --git a/docs/TutorialAsync.md b/docs/TutorialAsync.md index c59146e77b9a..2bcd2e69bf34 100644 --- a/docs/TutorialAsync.md +++ b/docs/TutorialAsync.md @@ -50,13 +50,12 @@ const users = { export default function request(url) { return new Promise((resolve, reject) => { const userID = parseInt(url.substr('/users/'.length), 10); - process.nextTick( - () => - users[userID] - ? resolve(users[userID]) - : reject({ - error: 'User with ' + userID + ' not found.', - }), + process.nextTick(() => + users[userID] + ? resolve(users[userID]) + : reject({ + error: 'User with ' + userID + ' not found.', + }), ); }); } diff --git a/docs/TutorialReact.md b/docs/TutorialReact.md index f8f8938b0986..9fc126fe545a 100644 --- a/docs/TutorialReact.md +++ b/docs/TutorialReact.md @@ -208,9 +208,11 @@ React 16 triggers these warnings due to how it checks element types, and the moc ### DOM Testing -If you'd like to assert, and manipulate your rendered components you can use [Enzyme](http://airbnb.io/enzyme/) or React's [TestUtils](http://facebook.github.io/react/docs/test-utils.html). We use Enzyme for this example. +If you'd like to assert, and manipulate your rendered components you can use [react-testing-library](https://github.com/kentcdodds/react-testing-library), [Enzyme](http://airbnb.io/enzyme/), or React's [TestUtils](http://facebook.github.io/react/docs/test-utils.html). The following two examples use react-testing-library and Enzyme. -You have to run `yarn add --dev enzyme` to use Enzyme. If you are using a React version below 15.5.0, you will also need to install `react-addons-test-utils`. +#### react-testing-library + +You have to run `yarn add --dev react-testing-library` to use react-testing-library. Let's implement a simple checkbox which swaps between two labels: @@ -248,7 +250,35 @@ export default class CheckboxWithLabel extends React.Component { } ``` -We use Enzyme's [shallow renderer](http://airbnb.io/enzyme/docs/api/shallow.html) in this example. +```javascript +// __tests__/CheckboxWithLabel-test.js +import React from 'react'; +import {render, fireEvent, cleanup} from 'react-testing-library'; +import CheckboxWithLabel from '../CheckboxWithLabel'; + +// automatically unmount and cleanup DOM after the test is finished. +afterEach(cleanup); + +it('CheckboxWithLabel changes the text after click', () => { + const {queryByLabelText, getByLabelText} = render( + , + ); + + expect(queryByLabelText(/off/i)).toBeTruthy(); + + fireEvent.click(getByLabelText(/off/i)); + + expect(queryByLabelText(/on/i)).toBeTruthy(); +}); +``` + +The code for this example is available at [examples/react-testing-library](https://github.com/facebook/jest/tree/master/examples/react-testing-library). + +#### Enzyme + +You have to run `yarn add --dev enzyme` to use Enzyme. If you are using a React version below 15.5.0, you will also need to install `react-addons-test-utils`. + +Let's rewrite the test from above using Enzyme instead of react-testing-library. We use Enzyme's [shallow renderer](http://airbnb.io/enzyme/docs/api/shallow.html) in this example. ```javascript // __tests__/CheckboxWithLabel-test.js diff --git a/e2e/__tests__/__snapshots__/coverage_remapping.test.js.snap b/e2e/__tests__/__snapshots__/coverage_remapping.test.js.snap index 00cebd87ecb8..7285790c0bbf 100644 --- a/e2e/__tests__/__snapshots__/coverage_remapping.test.js.snap +++ b/e2e/__tests__/__snapshots__/coverage_remapping.test.js.snap @@ -26,33 +26,33 @@ Object { "0": Object { "loc": Object { "end": Object { - "column": 9, - "line": 5, + "column": 35, + "line": 4, }, "start": Object { - "column": 8, - "line": 5, + "column": 34, + "line": 4, }, }, "locations": Array [ Object { "end": Object { - "column": 9, - "line": 5, + "column": 35, + "line": 4, }, "start": Object { - "column": 8, - "line": 5, + "column": 34, + "line": 4, }, }, Object { "end": Object { - "column": 9, - "line": 6, + "column": 39, + "line": 4, }, "start": Object { - "column": 8, - "line": 6, + "column": 38, + "line": 4, }, }, ], @@ -61,33 +61,33 @@ Object { "1": Object { "loc": Object { "end": Object { - "column": 37, - "line": 7, + "column": 35, + "line": 5, }, "start": Object { - "column": 36, - "line": 7, + "column": 34, + "line": 5, }, }, "locations": Array [ Object { "end": Object { - "column": 37, - "line": 7, + "column": 35, + "line": 5, }, "start": Object { - "column": 36, - "line": 7, + "column": 34, + "line": 5, }, }, Object { "end": Object { - "column": 41, - "line": 7, + "column": 39, + "line": 5, }, "start": Object { - "column": 40, - "line": 7, + "column": 38, + "line": 5, }, }, ], @@ -96,43 +96,43 @@ Object { "2": Object { "loc": Object { "end": Object { - "column": 33, - "line": 8, + "column": 31, + "line": 6, }, "start": Object { - "column": 29, - "line": 8, + "column": 27, + "line": 6, }, }, "locations": Array [ Object { "end": Object { - "column": 33, - "line": 8, + "column": 31, + "line": 6, }, "start": Object { - "column": 29, - "line": 8, + "column": 27, + "line": 6, }, }, Object { "end": Object { - "column": 41, - "line": 8, + "column": 39, + "line": 6, }, "start": Object { - "column": 37, - "line": 8, + "column": 35, + "line": 6, }, }, Object { "end": Object { - "column": 50, - "line": 8, + "column": 48, + "line": 6, }, "start": Object { - "column": 45, - "line": 8, + "column": 43, + "line": 6, }, }, ], @@ -141,33 +141,33 @@ Object { "3": Object { "loc": Object { "end": Object { - "column": 42, - "line": 9, + "column": 40, + "line": 7, }, "start": Object { - "column": 32, - "line": 9, + "column": 30, + "line": 7, }, }, "locations": Array [ Object { "end": Object { - "column": 42, - "line": 9, + "column": 40, + "line": 7, }, "start": Object { - "column": 32, - "line": 9, + "column": 30, + "line": 7, }, }, Object { "end": Object { - "column": 55, - "line": 9, + "column": 53, + "line": 7, }, "start": Object { - "column": 45, - "line": 9, + "column": 43, + "line": 7, }, }, ], @@ -194,7 +194,7 @@ Object { "loc": Object { "end": Object { "column": 1, - "line": 12, + "line": 10, }, "start": Object { "column": 49, @@ -206,22 +206,22 @@ Object { "1": Object { "decl": Object { "end": Object { - "column": 37, - "line": 9, + "column": 35, + "line": 7, }, "start": Object { - "column": 32, - "line": 9, + "column": 30, + "line": 7, }, }, "loc": Object { "end": Object { - "column": 42, - "line": 9, + "column": 40, + "line": 7, }, "start": Object { - "column": 32, - "line": 9, + "column": 30, + "line": 7, }, }, "name": "(anonymous_1)", @@ -229,22 +229,22 @@ Object { "2": Object { "decl": Object { "end": Object { - "column": 50, - "line": 9, + "column": 48, + "line": 7, }, "start": Object { - "column": 45, - "line": 9, + "column": 43, + "line": 7, }, }, "loc": Object { "end": Object { - "column": 55, - "line": 9, + "column": 53, + "line": 7, }, "start": Object { - "column": 45, - "line": 9, + "column": 43, + "line": 7, }, }, "name": "(anonymous_2)", @@ -264,8 +264,8 @@ Object { "statementMap": Object { "0": Object { "end": Object { - "column": 1, - "line": 12, + "column": 2, + "line": 10, }, "start": Object { "column": 0, @@ -274,72 +274,72 @@ Object { }, "1": Object { "end": Object { - "column": 9, - "line": 6, + "column": 39, + "line": 4, }, "start": Object { - "column": 29, + "column": 27, "line": 4, }, }, "2": Object { "end": Object { - "column": 41, - "line": 7, + "column": 39, + "line": 5, }, "start": Object { - "column": 29, - "line": 7, + "column": 27, + "line": 5, }, }, "3": Object { "end": Object { - "column": 50, - "line": 8, + "column": 48, + "line": 6, }, "start": Object { - "column": 29, - "line": 8, + "column": 27, + "line": 6, }, }, "4": Object { "end": Object { - "column": 55, - "line": 9, + "column": 53, + "line": 7, }, "start": Object { - "column": 25, - "line": 9, + "column": 23, + "line": 7, }, }, "5": Object { "end": Object { - "column": 42, - "line": 9, + "column": 40, + "line": 7, }, "start": Object { - "column": 38, - "line": 9, + "column": 36, + "line": 7, }, }, "6": Object { "end": Object { - "column": 55, - "line": 9, + "column": 53, + "line": 7, }, "start": Object { - "column": 51, - "line": 9, + "column": 49, + "line": 7, }, }, "7": Object { "end": Object { - "column": 17, - "line": 11, + "column": 15, + "line": 9, }, "start": Object { - "column": 4, - "line": 11, + "column": 2, + "line": 9, }, }, }, diff --git a/e2e/__tests__/__snapshots__/expect-async-matcher.test.js.snap b/e2e/__tests__/__snapshots__/expect-async-matcher.test.js.snap index 500651f28854..21773aab847b 100644 --- a/e2e/__tests__/__snapshots__/expect-async-matcher.test.js.snap +++ b/e2e/__tests__/__snapshots__/expect-async-matcher.test.js.snap @@ -9,9 +9,8 @@ exports[`shows the correct errors in stderr when failing tests 1`] = ` ● fail with expected non promise values - Error + Expected value to have length: - Error: Expected value to have length: 2 Received: 1 @@ -20,9 +19,8 @@ exports[`shows the correct errors in stderr when failing tests 1`] = ` ● fail with expected non promise values and not - Error + Expected value to not have length: - Error: Expected value to not have length: 2 Received: 1,2 diff --git a/e2e/__tests__/__snapshots__/failures.test.js.snap b/e2e/__tests__/__snapshots__/failures.test.js.snap index f98d981662eb..8388356e0763 100644 --- a/e2e/__tests__/__snapshots__/failures.test.js.snap +++ b/e2e/__tests__/__snapshots__/failures.test.js.snap @@ -13,9 +13,7 @@ exports[`not throwing Error objects 2`] = ` "FAIL __tests__/throw_string.test.js ● Test suite failed to run - Error - - banana + banana " `; @@ -387,8 +385,8 @@ exports[`works with async failures 1`] = ` > 21 | expect(Promise.reject({foo: 'bar'})).resolves.toEqual({foo: 'bar'})); | ^ 22 | - 23 | test( - 24 | 'timeout', + 23 | test('timeout', done => { + 24 | setTimeout(done, 50); at __tests__/async_failures.test.js:21:3 @@ -398,11 +396,11 @@ exports[`works with async failures 1`] = ` 21 | expect(Promise.reject({foo: 'bar'})).resolves.toEqual({foo: 'bar'})); 22 | - > 23 | test( + > 23 | test('timeout', done => { | ^ - 24 | 'timeout', - 25 | done => { - 26 | setTimeout(done, 50); + 24 | setTimeout(done, 50); + 25 | }, 5); + 26 | at __tests__/async_failures.test.js:23:1 diff --git a/e2e/__tests__/__snapshots__/show_config.test.js.snap b/e2e/__tests__/__snapshots__/show_config.test.js.snap index f3982dde87f3..24f419707d6d 100644 --- a/e2e/__tests__/__snapshots__/show_config.test.js.snap +++ b/e2e/__tests__/__snapshots__/show_config.test.js.snap @@ -13,6 +13,7 @@ exports[`--showConfig outputs config info and exits 1`] = ` \\"/node_modules/\\" ], \\"cwd\\": \\"<>\\", + \\"dependencyExtractor\\": null, \\"detectLeaks\\": false, \\"detectOpenHandles\\": false, \\"errorOnDeprecated\\": false, @@ -20,6 +21,7 @@ exports[`--showConfig outputs config info and exits 1`] = ` \\"forceCoverageMatch\\": [], \\"globals\\": {}, \\"haste\\": { + \\"computeSha1\\": false, \\"providesModuleNodeModules\\": [] }, \\"moduleDirectories\\": [ diff --git a/e2e/__tests__/find_related_files.test.js b/e2e/__tests__/find_related_files.test.js index 2024e1dd97e5..a9ddf9f2c3c7 100644 --- a/e2e/__tests__/find_related_files.test.js +++ b/e2e/__tests__/find_related_files.test.js @@ -41,6 +41,48 @@ describe('--findRelatedTests flag', () => { expect(stderr).toMatch(summaryMsg); }); + test('runs tests related to filename with a custom dependency extractor', () => { + writeFiles(DIR, { + '.watchmanconfig': '', + '__tests__/test.test.js': ` + const dynamicImport = path => Promise.resolve(require(path)); + test('a', () => dynamicImport('../a').then(a => { + expect(a.foo).toBe(5); + })); + `, + 'a.js': 'module.exports = {foo: 5};', + 'dependencyExtractor.js': ` + const DYNAMIC_IMPORT_RE = /(?:^|[^.]\\s*)(\\bdynamicImport\\s*?\\(\\s*?)([\`'"])([^\`'"]+)(\\2\\s*?\\))/g; + module.exports = { + extract(code) { + const dependencies = new Set(); + const addDependency = (match, pre, quot, dep, post) => { + dependencies.add(dep); + return match; + }; + code.replace(DYNAMIC_IMPORT_RE, addDependency); + return dependencies; + }, + }; + `, + 'package.json': JSON.stringify({ + jest: { + dependencyExtractor: '/dependencyExtractor.js', + testEnvironment: 'node', + }, + }), + }); + + const {stdout} = runJest(DIR, ['a.js']); + expect(stdout).toMatch(''); + + const {stderr} = runJest(DIR, ['--findRelatedTests', 'a.js']); + expect(stderr).toMatch('PASS __tests__/test.test.js'); + + const summaryMsg = 'Ran all test suites related to files matching /a.js/i.'; + expect(stderr).toMatch(summaryMsg); + }); + test('generates coverage report for filename', () => { writeFiles(DIR, { '.watchmanconfig': '', diff --git a/e2e/__tests__/override-globals.test.js b/e2e/__tests__/override-globals.test.js index a8ad8ac7c252..489f43e76987 100644 --- a/e2e/__tests__/override-globals.test.js +++ b/e2e/__tests__/override-globals.test.js @@ -15,3 +15,14 @@ test('overriding native promise does not freeze Jest', () => { const run = runJest('override-globals'); expect(run.stderr).toMatch(/PASS __tests__(\/|\\)index.js/); }); + +test('has a duration even if time is faked', () => { + const regex = /works well \((\d+)ms\)/; + const {stderr} = runJest('override-globals', ['--verbose']); + + expect(stderr).toMatch(regex); + + const [, duration] = stderr.match(regex); + + expect(Number(duration)).toBeGreaterThan(0); +}); diff --git a/e2e/coverage-remapping/covered.ts b/e2e/coverage-remapping/covered.ts index df5496159f87..28b8d10b0668 100644 --- a/e2e/coverage-remapping/covered.ts +++ b/e2e/coverage-remapping/covered.ts @@ -1,12 +1,10 @@ // Copyright 2004-present Facebook. All Rights Reserved. export = function difference(a: number, b: number): number { - const branch1: boolean = true - ? 1 - : 0; - const branch2: boolean = true ? 1 : 0; - const branch3: boolean = true || true || false; - const fn: Function = true ? () => null : () => null; + const branch1: boolean = true ? 1 : 0; + const branch2: boolean = true ? 1 : 0; + const branch3: boolean = true || true || false; + const fn: Function = true ? () => null : () => null; - return a - b; -} + return a - b; +}; diff --git a/e2e/custom-reporters/package.json b/e2e/custom-reporters/package.json index 590f11ffe692..c08385980ac4 100644 --- a/e2e/custom-reporters/package.json +++ b/e2e/custom-reporters/package.json @@ -1,11 +1,14 @@ { "jest": { "reporters": [ - ["/reporters/TestReporter.js", { - "hello": "world", - "dmitrii": "abramov", - "christoph": "pojer" - }] + [ + "/reporters/TestReporter.js", + { + "hello": "world", + "dmitrii": "abramov", + "christoph": "pojer" + } + ] ] } } diff --git a/e2e/custom-resolver/resolver.js b/e2e/custom-resolver/resolver.js index 7385bfdd28c4..e9a718b139b5 100644 --- a/e2e/custom-resolver/resolver.js +++ b/e2e/custom-resolver/resolver.js @@ -2,7 +2,7 @@ const { default: defaultResolver, -} = require('jest-resolve/build/default_resolver'); +} = require('jest-resolve/build/defaultResolver'); const exportedModules = new Map([ ['foo', 'foo'], diff --git a/e2e/failures/__tests__/async_failures.test.js b/e2e/failures/__tests__/async_failures.test.js index 9eaafa3a8878..b78eecd6f4bd 100644 --- a/e2e/failures/__tests__/async_failures.test.js +++ b/e2e/failures/__tests__/async_failures.test.js @@ -20,10 +20,6 @@ test('expect reject', () => test('expect resolve', () => expect(Promise.reject({foo: 'bar'})).resolves.toEqual({foo: 'bar'})); -test( - 'timeout', - done => { - setTimeout(done, 50); - }, - 5 -); +test('timeout', done => { + setTimeout(done, 50); +}, 5); diff --git a/e2e/jasmine-async/__tests__/promise_before_all.test.js b/e2e/jasmine-async/__tests__/promise_before_all.test.js index 1907239d42f5..b4f1df734707 100644 --- a/e2e/jasmine-async/__tests__/promise_before_all.test.js +++ b/e2e/jasmine-async/__tests__/promise_before_all.test.js @@ -15,7 +15,8 @@ describe('promise beforeAll', () => { process.nextTick(resolve); }).then(() => { flag = 1; - })); + }) + ); beforeAll(() => new Promise(resolve => setTimeout(resolve, 10)), 500); diff --git a/e2e/jasmine-async/__tests__/promise_before_each.test.js b/e2e/jasmine-async/__tests__/promise_before_each.test.js index 53570b9126f0..dbb7fdbc206a 100644 --- a/e2e/jasmine-async/__tests__/promise_before_each.test.js +++ b/e2e/jasmine-async/__tests__/promise_before_each.test.js @@ -13,7 +13,8 @@ describe('promise beforeEach', () => { process.nextTick(resolve); }).then(() => { this.flag = 1; - })); + }) + ); // passing tests it('runs tests after beforeEach asynchronously completes', () => { diff --git a/e2e/json-reporter/package.json b/e2e/json-reporter/package.json index d59562604cad..781ddcb77c5a 100644 --- a/e2e/json-reporter/package.json +++ b/e2e/json-reporter/package.json @@ -1,6 +1,8 @@ { "jest": { "testEnvironment": "node", - "coverageReporters": ["json"] + "coverageReporters": [ + "json" + ] } } diff --git a/e2e/multi-project-config-root/package.json b/e2e/multi-project-config-root/package.json index 58e129a79e90..897e521d1dc8 100644 --- a/e2e/multi-project-config-root/package.json +++ b/e2e/multi-project-config-root/package.json @@ -1,18 +1,18 @@ { "jest": { "projects": [ - { - "rootDir": "/foo", - "testPathIgnorePatterns": [ - "/__tests__/boggus-foo.test.js" - ] - }, - { - "rootDir": "/bar", - "testPathIgnorePatterns": [ - "/__tests__/boggus-bar.test.js" - ] - } + { + "rootDir": "/foo", + "testPathIgnorePatterns": [ + "/__tests__/boggus-foo.test.js" + ] + }, + { + "rootDir": "/bar", + "testPathIgnorePatterns": [ + "/__tests__/boggus-bar.test.js" + ] + } ] } } diff --git a/e2e/override-globals/package.json b/e2e/override-globals/package.json index 148788b25446..b4a435775e2b 100644 --- a/e2e/override-globals/package.json +++ b/e2e/override-globals/package.json @@ -1,5 +1,8 @@ { "jest": { - "testEnvironment": "node" + "testEnvironment": "node", + "setupFiles": [ + "/setup.js" + ] } } diff --git a/e2e/override-globals/setup.js b/e2e/override-globals/setup.js new file mode 100644 index 000000000000..ade70a19010f --- /dev/null +++ b/e2e/override-globals/setup.js @@ -0,0 +1,9 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +Date.now = () => 0; +process.hrtime = () => [0, 5000]; diff --git a/e2e/resolve-node-module/package.json b/e2e/resolve-node-module/package.json index 0a586922137e..7c329bea6ee6 100644 --- a/e2e/resolve-node-module/package.json +++ b/e2e/resolve-node-module/package.json @@ -1,6 +1,10 @@ { "jest": { - "moduleFileExtensions": ["js", "json", "jsx"], + "moduleFileExtensions": [ + "js", + "json", + "jsx" + ], "testEnvironment": "node" } } diff --git a/e2e/resolve/__tests__/resolve.test.js b/e2e/resolve/__tests__/resolve.test.js index 41a840c2d56d..e8a5571f59ef 100644 --- a/e2e/resolve/__tests__/resolve.test.js +++ b/e2e/resolve/__tests__/resolve.test.js @@ -47,6 +47,11 @@ test('should resolve filename.native.js', () => { expect(platform.extension).toBe('native.js'); }); +test('should resolve filename.native.js with moduleNameMapper', () => { + expect(testRequire('test2mapper')).not.toThrow(); + expect(platform.extension).toBe('native.js'); +}); + test('should resolve filename.js', () => { expect(testRequire('../test3')).not.toThrow(); expect(platform.extension).toBe('js'); diff --git a/e2e/resolve/package.json b/e2e/resolve/package.json index 743e0a83f202..f3feb16b420f 100644 --- a/e2e/resolve/package.json +++ b/e2e/resolve/package.json @@ -2,9 +2,14 @@ "name": "custom-resolve", "jest": { "haste": { - "platforms": ["native"], + "platforms": [ + "native" + ], "defaultPlatform": "android" }, - "testEnvironment": "node" + "testEnvironment": "node", + "moduleNameMapper": { + "test2mapper": "/test2mapper" + } } } diff --git a/e2e/resolve/test1.json b/e2e/resolve/test1.json index 13827d9bbdc0..001968d98e1b 100644 --- a/e2e/resolve/test1.json +++ b/e2e/resolve/test1.json @@ -1,3 +1,3 @@ { - "extension": "json" + "extension": "json" } diff --git a/e2e/resolve/test2.json b/e2e/resolve/test2.json index 13827d9bbdc0..001968d98e1b 100644 --- a/e2e/resolve/test2.json +++ b/e2e/resolve/test2.json @@ -1,3 +1,3 @@ { - "extension": "json" + "extension": "json" } diff --git a/e2e/resolve/test2mapper.js b/e2e/resolve/test2mapper.js new file mode 100644 index 000000000000..a265d4b021c8 --- /dev/null +++ b/e2e/resolve/test2mapper.js @@ -0,0 +1,8 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = {extension: '.js'}; diff --git a/e2e/resolve/test2mapper.native.js b/e2e/resolve/test2mapper.native.js new file mode 100644 index 000000000000..68a290a4b715 --- /dev/null +++ b/e2e/resolve/test2mapper.native.js @@ -0,0 +1,8 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = {extension: 'native.js'}; diff --git a/e2e/resolve/test3.json b/e2e/resolve/test3.json index 13827d9bbdc0..001968d98e1b 100644 --- a/e2e/resolve/test3.json +++ b/e2e/resolve/test3.json @@ -1,3 +1,3 @@ { - "extension": "json" + "extension": "json" } diff --git a/e2e/resolve/test4.json b/e2e/resolve/test4.json index 13827d9bbdc0..001968d98e1b 100644 --- a/e2e/resolve/test4.json +++ b/e2e/resolve/test4.json @@ -1,3 +1,3 @@ { - "extension": "json" + "extension": "json" } diff --git a/e2e/stack-trace-source-maps/package.json b/e2e/stack-trace-source-maps/package.json index cb29f0b9a69a..8941387eb502 100644 --- a/e2e/stack-trace-source-maps/package.json +++ b/e2e/stack-trace-source-maps/package.json @@ -5,7 +5,10 @@ "^.+\\.(ts)$": "/Preprocessor.js" }, "testEnvironment": "node", - "moduleFileExtensions": ["ts", "js"], + "moduleFileExtensions": [ + "ts", + "js" + ], "testRegex": "fails" }, "dependencies": { diff --git a/e2e/transform/ecmascript-modules-support/package.json b/e2e/transform/ecmascript-modules-support/package.json index b3145fb854df..d37c7ca66af6 100644 --- a/e2e/transform/ecmascript-modules-support/package.json +++ b/e2e/transform/ecmascript-modules-support/package.json @@ -1,8 +1,16 @@ { "jest": { "testEnvironment": "node", - "moduleFileExtensions": ["js", "json", "jsx", "node", "mjs"], - "testMatch": [ "**/__tests__/**/*.mjs"], + "moduleFileExtensions": [ + "js", + "json", + "jsx", + "node", + "mjs" + ], + "testMatch": [ + "**/__tests__/**/*.mjs" + ], "transform": { "^.+\\.mjs?$": "../../../packages/babel-jest" } diff --git a/e2e/transform/no-babel-jest/package.json b/e2e/transform/no-babel-jest/package.json index 86982fa93b05..9fb09b127a3b 100644 --- a/e2e/transform/no-babel-jest/package.json +++ b/e2e/transform/no-babel-jest/package.json @@ -4,7 +4,9 @@ "/this-directory-is-covered/Covered.js": true, "/this-directory-is-covered/ExcludedFromCoverage.js": true }, - "coveragePathIgnorePatterns": ["ExcludedFromCoverage"], + "coveragePathIgnorePatterns": [ + "ExcludedFromCoverage" + ], "testEnvironment": "node" } } diff --git a/e2e/typescript-coverage/package.json b/e2e/typescript-coverage/package.json index 4c274920cbe4..9b6cf144b886 100644 --- a/e2e/typescript-coverage/package.json +++ b/e2e/typescript-coverage/package.json @@ -4,7 +4,9 @@ "transform": { "^.+\\.(ts|js)$": "/TypescriptPreprocessor.js" }, - "testMatch": ["**/__tests__/*.+(ts|tsx|js)"], + "testMatch": [ + "**/__tests__/*.+(ts|tsx|js)" + ], "testEnvironment": "node", "moduleFileExtensions": [ "ts", diff --git a/examples/async/__mocks__/request.js b/examples/async/__mocks__/request.js index 3dc3d9451003..51784974d8fa 100644 --- a/examples/async/__mocks__/request.js +++ b/examples/async/__mocks__/request.js @@ -10,13 +10,12 @@ const users = { export default function request(url) { return new Promise((resolve, reject) => { const userID = parseInt(url.substr('/users/'.length), 10); - process.nextTick( - () => - users[userID] - ? resolve(users[userID]) - : reject({ - error: 'User with ' + userID + ' not found.', - }) + process.nextTick(() => + users[userID] + ? resolve(users[userID]) + : reject({ + error: 'User with ' + userID + ' not found.', + }) ); }); } diff --git a/examples/enzyme/__tests__/checkbox_with_label.test.js b/examples/enzyme/__tests__/CheckboxWithLabel-test.js similarity index 100% rename from examples/enzyme/__tests__/checkbox_with_label.test.js rename to examples/enzyme/__tests__/CheckboxWithLabel-test.js diff --git a/examples/react-native/ios/jestrn/Images.xcassets/AppIcon.appiconset/Contents.json b/examples/react-native/ios/jestrn/Images.xcassets/AppIcon.appiconset/Contents.json index d3942e941ae8..48e64ae8a3ef 100644 --- a/examples/react-native/ios/jestrn/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/examples/react-native/ios/jestrn/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,38 +1,38 @@ { - "images" : [ + "images": [ { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" + "idiom": "iphone", + "size": "29x29", + "scale": "2x" }, { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" + "idiom": "iphone", + "size": "29x29", + "scale": "3x" }, { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" + "idiom": "iphone", + "size": "40x40", + "scale": "2x" }, { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" + "idiom": "iphone", + "size": "40x40", + "scale": "3x" }, { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" + "idiom": "iphone", + "size": "60x60", + "scale": "2x" }, { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" + "idiom": "iphone", + "size": "60x60", + "scale": "3x" } ], - "info" : { - "version" : 1, - "author" : "xcode" + "info": { + "version": 1, + "author": "xcode" } } diff --git a/examples/react-testing-library/CheckboxWithLabel.js b/examples/react-testing-library/CheckboxWithLabel.js new file mode 100644 index 000000000000..ccad8995d978 --- /dev/null +++ b/examples/react-testing-library/CheckboxWithLabel.js @@ -0,0 +1,31 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +import React from 'react'; + +export default class CheckboxWithLabel extends React.Component { + constructor(props) { + super(props); + this.state = {isChecked: false}; + + // bind manually because React class components don't auto-bind + // http://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding + this.onChange = this.onChange.bind(this); + } + + onChange() { + this.setState({isChecked: !this.state.isChecked}); + } + + render() { + return ( + + ); + } +} diff --git a/examples/react-testing-library/__tests__/CheckboxWithLabel-test.js b/examples/react-testing-library/__tests__/CheckboxWithLabel-test.js new file mode 100644 index 000000000000..cd3cf4832b94 --- /dev/null +++ b/examples/react-testing-library/__tests__/CheckboxWithLabel-test.js @@ -0,0 +1,20 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +import React from 'react'; +import {render, fireEvent, cleanup} from 'react-testing-library'; +import CheckboxWithLabel from '../CheckboxWithLabel'; + +// automatically unmount and cleanup DOM after the test is finished. +afterEach(cleanup); + +it('CheckboxWithLabel changes the text after click', () => { + const {queryByLabelText, getByLabelText} = render( + + ); + + expect(queryByLabelText(/off/i)).toBeTruthy(); + + fireEvent.click(getByLabelText(/off/i)); + + expect(queryByLabelText(/on/i)).toBeTruthy(); +}); diff --git a/examples/react-testing-library/babel.config.js b/examples/react-testing-library/babel.config.js new file mode 100644 index 000000000000..f671719a831e --- /dev/null +++ b/examples/react-testing-library/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ['@babel/preset-env', '@babel/preset-react'], +}; diff --git a/examples/react-testing-library/package.json b/examples/react-testing-library/package.json new file mode 100644 index 000000000000..094c9863557b --- /dev/null +++ b/examples/react-testing-library/package.json @@ -0,0 +1,19 @@ +{ + "private": true, + "version": "0.0.0", + "name": "example-react-testing-library", + "dependencies": { + "react": "^16.4.0", + "react-dom": "^16.4.0" + }, + "devDependencies": { + "@babel/preset-env": "*", + "@babel/preset-react": "*", + "babel-jest": "*", + "react-testing-library": "*", + "jest": "*" + }, + "scripts": { + "test": "jest" + } +} diff --git a/examples/react/__tests__/checkbox_with_label.test.js b/examples/react/__tests__/CheckboxWithLabel-test.js similarity index 100% rename from examples/react/__tests__/checkbox_with_label.test.js rename to examples/react/__tests__/CheckboxWithLabel-test.js diff --git a/examples/typescript/__tests__/checkbox_with_label.test.tsx b/examples/typescript/__tests__/CheckboxWithLabel-test.tsx similarity index 100% rename from examples/typescript/__tests__/checkbox_with_label.test.tsx rename to examples/typescript/__tests__/CheckboxWithLabel-test.tsx diff --git a/jest.config.js b/jest.config.js index 06cb6ca2e84c..9a7e9b0808ce 100644 --- a/jest.config.js +++ b/jest.config.js @@ -25,7 +25,7 @@ module.exports = { projects: ['', '/examples/*/'], setupFilesAfterEnv: ['/testSetupFile.js'], snapshotSerializers: [ - '/packages/pretty-format/build/plugins/convert_ansi.js', + '/packages/pretty-format/build/plugins/ConvertAnsi.js', ], testEnvironment: './packages/jest-environment-node', testPathIgnorePatterns: [ @@ -35,11 +35,12 @@ module.exports = { '\\.snap$', '/packages/.*/build', '/packages/.*/build-es5', - '/packages/.*/src/__tests__/expect_util.js', + '/packages/.*/src/__tests__/getPrettyPrint.js', '/packages/jest-cli/src/__tests__/test_root', '/packages/jest-cli/src/__tests__/__fixtures__/', '/packages/jest-cli/src/lib/__tests__/fixtures/', '/packages/jest-haste-map/src/__tests__/haste_impl.js', + '/packages/jest-haste-map/src/__tests__/dependencyExtractor.js', '/packages/jest-resolve-dependencies/src/__tests__/__fixtures__/', '/packages/jest-runtime/src/__tests__/defaultResolver.js', '/packages/jest-runtime/src/__tests__/module_dir/', diff --git a/package.json b/package.json index 3f1798564d10..bdae6d287631 100644 --- a/package.json +++ b/package.json @@ -16,20 +16,21 @@ "codecov": "^3.0.0", "debug": "^4.0.1", "eslint": "^5.6.0", - "eslint-config-prettier": "^3.0.1", + "eslint-config-prettier": "^3.1.0", "eslint-plugin-babel": "^5.1.0", "eslint-plugin-flowtype": "^2.35.0", "eslint-plugin-import": "^2.6.0", - "eslint-plugin-jest": "^21.0.0", + "eslint-plugin-jest": "^22.0.0", "eslint-plugin-jsx-a11y": "^6.0.2", "eslint-plugin-markdown": "^1.0.0-beta.6", - "eslint-plugin-prettier": "^2.3.1", + "eslint-plugin-prettier": "^2.7.0", "eslint-plugin-react": "^7.1.0", "eslint-plugin-relay": "~0.0.19", "execa": "^1.0.0", - "flow-bin": "^0.80.0", + "flow-bin": "^0.85.0", "glob": "^7.1.1", "graceful-fs": "^4.1.11", + "isbinaryfile": "^3.0.3", "istanbul-api": "^2.0.6", "istanbul-lib-coverage": "^2.0.1", "jasmine-reporters": "^2.2.0", @@ -46,7 +47,7 @@ "mkdirp": "^0.5.1", "mocha": "^5.0.1", "mock-fs": "^4.4.1", - "prettier": "^1.13.3", + "prettier": "^1.15.2", "prettylint": "^1.0.0", "progress": "^2.0.0", "promise": "^8.0.2", @@ -71,6 +72,7 @@ "scripts": { "build-clean": "rm -rf ./packages/*/build ./packages/*/build-es5", "build": "node ./scripts/build.js", + "check-copyright-headers": "node ./scripts/checkCopyrightHeaders.js", "clean-all": "rm -rf ./node_modules && rm -rf ./packages/*/node_modules && yarn clean-e2e && yarn build-clean", "clean-e2e": "find ./e2e -not \\( -path ./e2e/presets/js -prune \\) -not \\( -path ./e2e/presets/json -prune \\) -mindepth 2 -type d \\( -name node_modules -prune \\) -exec rm -r '{}' +", "jest": "node ./packages/jest-cli/bin/jest.js", @@ -115,5 +117,8 @@ "type": "opencollective", "url": "https://opencollective.com/jest", "logo": "https://opencollective.com/jest/logo.txt" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/babel-jest/package.json b/packages/babel-jest/package.json index 568f2fecfd0e..6cc0a5134faf 100644 --- a/packages/babel-jest/package.json +++ b/packages/babel-jest/package.json @@ -17,5 +17,8 @@ }, "peerDependencies": { "@babel/core": "^7.0.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/babel-plugin-jest-hoist/package.json b/packages/babel-plugin-jest-hoist/package.json index c7923258b175..22ddd76455b4 100644 --- a/packages/babel-plugin-jest-hoist/package.json +++ b/packages/babel-plugin-jest-hoist/package.json @@ -5,6 +5,9 @@ "type": "git", "url": "https://github.com/facebook/jest.git" }, + "engines": { + "node": ">= 6" + }, "license": "MIT", "main": "build/index.js" } diff --git a/packages/babel-preset-jest/package.json b/packages/babel-preset-jest/package.json index ab7d750a9817..357ddc7029a4 100644 --- a/packages/babel-preset-jest/package.json +++ b/packages/babel-preset-jest/package.json @@ -10,5 +10,8 @@ "dependencies": { "@babel/plugin-syntax-object-rest-spread": "^7.0.0", "babel-plugin-jest-hoist": "^23.2.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/diff-sequences/package.json b/packages/diff-sequences/package.json index d8d6a7afd7d7..1428313d9399 100644 --- a/packages/diff-sequences/package.json +++ b/packages/diff-sequences/package.json @@ -14,5 +14,8 @@ "callback", "diff" ], + "engines": { + "node": ">= 6" + }, "main": "build/index.js" } diff --git a/packages/eslint-config-fb-strict/package.json b/packages/eslint-config-fb-strict/package.json index 8aada90416df..8e9cb2d7e065 100644 --- a/packages/eslint-config-fb-strict/package.json +++ b/packages/eslint-config-fb-strict/package.json @@ -14,9 +14,12 @@ "eslint": "^4.2.0 || ^5.0.0", "eslint-plugin-babel": "^5.0.0", "eslint-plugin-flowtype": "^2.35.0", - "eslint-plugin-jest": "^21.0.0", + "eslint-plugin-jest": "^21.0.0 || ^22.0.0", "eslint-plugin-jsx-a11y": "^6.0.2", "eslint-plugin-react": "^7.1.0", "eslint-plugin-relay": "~0.0.8" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/expect/package.json b/packages/expect/package.json index 8f488346b5b3..313df003904d 100644 --- a/packages/expect/package.json +++ b/packages/expect/package.json @@ -15,5 +15,8 @@ "jest-matcher-utils": "^23.6.0", "jest-message-util": "^23.4.0", "jest-regex-util": "^23.3.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap index 400e8b6e424d..6e9c035453aa 100644 --- a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap @@ -3479,6 +3479,30 @@ Received: \\"bar\\"" `; +exports[`.toStrictEqual() matches the expected snapshot when it fails 1`] = ` +"expect(received).toStrictEqual(expected) + +Difference: + +- Expected ++ Received + + Object { +- \\"test\\": TestClassA { +- \\"a\\": 1, +- \\"b\\": 2, +- }, ++ \\"test\\": 2, + }" +`; + +exports[`.toStrictEqual() matches the expected snapshot when it fails 2`] = ` +"expect(received).not.toStrictEqual(expected) + +Expected: {\\"test\\": {\\"a\\": 1, \\"b\\": 2}} +Received: {\\"test\\": {\\"a\\": 1, \\"b\\": 2}}" +`; + exports[`toMatchObject() {pass: false} expect([0]).toMatchObject([-0]) 1`] = ` "expect(received).toMatchObject(expected) @@ -3664,6 +3688,23 @@ Difference: }" `; +exports[`toMatchObject() {pass: false} expect({"a": "b"}).toMatchObject({"c": "d"}) 1`] = ` +"expect(received).toMatchObject(expected) + +Expected value to match object: + {\\"c\\": \\"d\\"} +Received: + {\\"a\\": \\"b\\"} +Difference: +- Expected ++ Received + + Object { +- \\"c\\": \\"d\\", ++ \\"a\\": \\"b\\", + }" +`; + exports[`toMatchObject() {pass: false} expect({"a": [{"a": "a", "b": "b"}]}).toMatchObject({"a": [{"a": "c"}]}) 1`] = ` "expect(received).toMatchObject(expected) @@ -4002,6 +4043,15 @@ Received: {\\"a\\": \\"b\\", \\"t\\": {\\"x\\": {\\"r\\": \\"r\\"}, \\"z\\": \\"z\\"}}" `; +exports[`toMatchObject() {pass: true} expect({"a": "b"}).toMatchObject({"a": "b"}) 1`] = ` +"expect(received).not.toMatchObject(expected) + +Expected value not to match object: + {\\"a\\": \\"b\\"} +Received: + {\\"a\\": \\"b\\"}" +`; + exports[`toMatchObject() {pass: true} expect({"a": [{"a": "a", "b": "b"}]}).toMatchObject({"a": [{"a": "a"}]}) 1`] = ` "expect(received).not.toMatchObject(expected) diff --git a/packages/expect/src/__tests__/__snapshots__/spy_matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.js.snap similarity index 94% rename from packages/expect/src/__tests__/__snapshots__/spy_matchers.test.js.snap rename to packages/expect/src/__tests__/__snapshots__/spyMatchers.test.js.snap index 95a15e70ccf4..220a4e47e8de 100644 --- a/packages/expect/src/__tests__/__snapshots__/spy_matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.js.snap @@ -149,6 +149,14 @@ Expected mock function \\"named-mock\\" to have last returned: But it was not called" `; +exports[`lastReturnedWith incomplete recursive calls are handled properly 1`] = ` +"expect(jest.fn()).lastReturnedWith(expected) + +Expected mock function to have last returned: + 0 +But the last call has not returned yet" +`; + exports[`lastReturnedWith works only on spies or jest.fn 1`] = ` "expect(jest.fn())[.not].lastReturnedWith() @@ -237,6 +245,15 @@ But the last call returned: \\"foo\\"" `; +exports[`lastReturnedWith works with three calls 1`] = ` +"expect(jest.fn()).not.lastReturnedWith(expected) + +Expected mock function to not have last returned: + \\"foo3\\" +But it last returned exactly: + \\"foo3\\"" +`; + exports[`lastReturnedWith works with undefined 1`] = ` "expect(jest.fn()).not.lastReturnedWith(expected) @@ -406,6 +423,40 @@ Expected mock function \\"named-mock\\" first call to have returned with: But it was not called" `; +exports[`nthReturnedWith incomplete recursive calls are handled properly 1`] = ` +"expect(jest.fn()).nthReturnedWith(expected) + +Expected mock function first call to have returned with: + 6 +But the first call has not returned yet" +`; + +exports[`nthReturnedWith incomplete recursive calls are handled properly 2`] = ` +"expect(jest.fn()).nthReturnedWith(expected) + +Expected mock function second call to have returned with: + 3 +But the second call has not returned yet" +`; + +exports[`nthReturnedWith incomplete recursive calls are handled properly 3`] = ` +"expect(jest.fn()).not.nthReturnedWith(expected) + +Expected mock function third call to not have returned with: + 1 +But the third call returned exactly: + 1" +`; + +exports[`nthReturnedWith incomplete recursive calls are handled properly 4`] = ` +"expect(jest.fn()).not.nthReturnedWith(expected) + +Expected mock function 4th call to not have returned with: + 0 +But the 4th call returned exactly: + 0" +`; + exports[`nthReturnedWith should reject non integer nth value 1`] = `"nth value 0.1 must be a positive integer greater than 0"`; exports[`nthReturnedWith should reject nth value greater than number of calls 1`] = ` @@ -1437,6 +1488,14 @@ Expected mock function \\"named-mock\\" to have last returned: But it was not called" `; +exports[`toHaveLastReturnedWith incomplete recursive calls are handled properly 1`] = ` +"expect(jest.fn()).toHaveLastReturnedWith(expected) + +Expected mock function to have last returned: + 0 +But the last call has not returned yet" +`; + exports[`toHaveLastReturnedWith works only on spies or jest.fn 1`] = ` "expect(jest.fn())[.not].toHaveLastReturnedWith() @@ -1525,6 +1584,15 @@ But the last call returned: \\"foo\\"" `; +exports[`toHaveLastReturnedWith works with three calls 1`] = ` +"expect(jest.fn()).not.toHaveLastReturnedWith(expected) + +Expected mock function to not have last returned: + \\"foo3\\" +But it last returned exactly: + \\"foo3\\"" +`; + exports[`toHaveLastReturnedWith works with undefined 1`] = ` "expect(jest.fn()).not.toHaveLastReturnedWith(expected) @@ -1558,6 +1626,40 @@ Expected mock function \\"named-mock\\" first call to have returned with: But it was not called" `; +exports[`toHaveNthReturnedWith incomplete recursive calls are handled properly 1`] = ` +"expect(jest.fn()).toHaveNthReturnedWith(expected) + +Expected mock function first call to have returned with: + 6 +But the first call has not returned yet" +`; + +exports[`toHaveNthReturnedWith incomplete recursive calls are handled properly 2`] = ` +"expect(jest.fn()).toHaveNthReturnedWith(expected) + +Expected mock function second call to have returned with: + 3 +But the second call has not returned yet" +`; + +exports[`toHaveNthReturnedWith incomplete recursive calls are handled properly 3`] = ` +"expect(jest.fn()).not.toHaveNthReturnedWith(expected) + +Expected mock function third call to not have returned with: + 1 +But the third call returned exactly: + 1" +`; + +exports[`toHaveNthReturnedWith incomplete recursive calls are handled properly 4`] = ` +"expect(jest.fn()).not.toHaveNthReturnedWith(expected) + +Expected mock function 4th call to not have returned with: + 0 +But the 4th call returned exactly: + 0" +`; + exports[`toHaveNthReturnedWith should reject non integer nth value 1`] = `"nth value 0.1 must be a positive integer greater than 0"`; exports[`toHaveNthReturnedWith should reject nth value greater than number of calls 1`] = ` @@ -1735,6 +1837,12 @@ Expected mock function \\"named-mock\\" not to have returned, but it returned: 42" `; +exports[`toHaveReturned incomplete recursive calls are handled properly 1`] = ` +"expect(jest.fn()).toHaveReturned() + +Expected mock function to have returned." +`; + exports[`toHaveReturned passes when at least one call does not throw 1`] = ` "expect(jest.fn()).not.toHaveReturned() @@ -1850,6 +1958,12 @@ exports[`toHaveReturnedTimes includes the custom mock name in the error message Expected mock function \\"named-mock\\" to have returned one time, but it returned two times." `; +exports[`toHaveReturnedTimes incomplete recursive calls are handled properly 1`] = ` +"expect(jest.fn()).not.toHaveReturnedTimes(2) + +Expected mock function not to have returned two times, but it returned exactly two times." +`; + exports[`toHaveReturnedTimes only accepts a number argument 1`] = ` "expect(received)[.not].toHaveReturnedTimes(expected) @@ -1936,6 +2050,14 @@ Expected mock function \\"named-mock\\" to have returned: But it did not return." `; +exports[`toHaveReturnedWith incomplete recursive calls are handled properly 1`] = ` +"expect(jest.fn()).toHaveReturnedWith(expected) + +Expected mock function to have returned: + undefined +But it did not return." +`; + exports[`toHaveReturnedWith works only on spies or jest.fn 1`] = ` "expect(jest.fn())[.not].toHaveReturnedWith() @@ -2093,6 +2215,12 @@ Expected mock function \\"named-mock\\" not to have returned, but it returned: 42" `; +exports[`toReturn incomplete recursive calls are handled properly 1`] = ` +"expect(jest.fn()).toReturn() + +Expected mock function to have returned." +`; + exports[`toReturn passes when at least one call does not throw 1`] = ` "expect(jest.fn()).not.toReturn() @@ -2208,6 +2336,12 @@ exports[`toReturnTimes includes the custom mock name in the error message 1`] = Expected mock function \\"named-mock\\" to have returned one time, but it returned two times." `; +exports[`toReturnTimes incomplete recursive calls are handled properly 1`] = ` +"expect(jest.fn()).not.toReturnTimes(2) + +Expected mock function not to have returned two times, but it returned exactly two times." +`; + exports[`toReturnTimes only accepts a number argument 1`] = ` "expect(received)[.not].toReturnTimes(expected) @@ -2294,6 +2428,14 @@ Expected mock function \\"named-mock\\" to have returned: But it did not return." `; +exports[`toReturnWith incomplete recursive calls are handled properly 1`] = ` +"expect(jest.fn()).toReturnWith(expected) + +Expected mock function to have returned: + undefined +But it did not return." +`; + exports[`toReturnWith works only on spies or jest.fn 1`] = ` "expect(jest.fn())[.not].toReturnWith() diff --git a/packages/expect/src/__tests__/__snapshots__/to_throw_matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/toThrowMatchers.test.js.snap similarity index 100% rename from packages/expect/src/__tests__/__snapshots__/to_throw_matchers.test.js.snap rename to packages/expect/src/__tests__/__snapshots__/toThrowMatchers.test.js.snap diff --git a/packages/expect/src/__tests__/assertion_counts.test.js b/packages/expect/src/__tests__/assertionCounts.test.js similarity index 100% rename from packages/expect/src/__tests__/assertion_counts.test.js rename to packages/expect/src/__tests__/assertionCounts.test.js diff --git a/packages/expect/src/__tests__/asymmetric_matchers.test.js b/packages/expect/src/__tests__/asymmetricMatchers.test.js similarity index 99% rename from packages/expect/src/__tests__/asymmetric_matchers.test.js rename to packages/expect/src/__tests__/asymmetricMatchers.test.js index 9b7a030d4d54..08b0907d09ee 100644 --- a/packages/expect/src/__tests__/asymmetric_matchers.test.js +++ b/packages/expect/src/__tests__/asymmetricMatchers.test.js @@ -20,7 +20,7 @@ const { stringNotContaining, stringMatching, stringNotMatching, -} = require('../asymmetric_matchers'); +} = require('../asymmetricMatchers'); test('Any.asymmetricMatch()', () => { const Thing = function() {}; diff --git a/packages/expect/src/__tests__/extend.test.js b/packages/expect/src/__tests__/extend.test.js index 8fe6b6f3c78e..e2f3ecf0bdd6 100644 --- a/packages/expect/src/__tests__/extend.test.js +++ b/packages/expect/src/__tests__/extend.test.js @@ -8,7 +8,7 @@ const matcherUtils = require('jest-matcher-utils'); const {iterableEquality, subsetEquality} = require('../utils'); -const {equals} = require('../jasmine_utils'); +const {equals} = require('../jasmineUtils'); const jestExpect = require('../'); jestExpect.extend({ diff --git a/packages/expect/src/__tests__/fake_chalk.test.js b/packages/expect/src/__tests__/fakeChalk.test.js similarity index 88% rename from packages/expect/src/__tests__/fake_chalk.test.js rename to packages/expect/src/__tests__/fakeChalk.test.js index c461b75c1b2d..532dae6144c7 100644 --- a/packages/expect/src/__tests__/fake_chalk.test.js +++ b/packages/expect/src/__tests__/fakeChalk.test.js @@ -6,7 +6,7 @@ */ 'use strict'; -const fakeChalk = jest.requireActual('../fake_chalk'); +const fakeChalk = jest.requireActual('../fakeChalk'); describe('Fake Chalk', () => { it('returns input when invoked', () => { diff --git a/packages/expect/src/__tests__/is_error.test.js b/packages/expect/src/__tests__/isError.test.js similarity index 100% rename from packages/expect/src/__tests__/is_error.test.js rename to packages/expect/src/__tests__/isError.test.js diff --git a/packages/expect/src/__tests__/matchers.test.js b/packages/expect/src/__tests__/matchers.test.js index e695eec418ed..83662ae03c41 100644 --- a/packages/expect/src/__tests__/matchers.test.js +++ b/packages/expect/src/__tests__/matchers.test.js @@ -244,6 +244,20 @@ describe('.toStrictEqual()', () => { }).toStrictEqual({test: new TestClassA(1, 2)}); }); + it('matches the expected snapshot when it fails', () => { + expect(() => + jestExpect({ + test: 2, + }).toStrictEqual({test: new TestClassA(1, 2)}), + ).toThrowErrorMatchingSnapshot(); + + expect(() => + jestExpect({ + test: new TestClassA(1, 2), + }).not.toStrictEqual({test: new TestClassA(1, 2)}), + ).toThrowErrorMatchingSnapshot(); + }); + it('does not pass for different types', () => { expect({ test: new TestClassA(1, 2), @@ -1128,6 +1142,7 @@ describe('toMatchObject()', () => { [new Error('foo'), new Error('foo')], [new Error('bar'), {message: 'bar'}], [new Foo(), {a: undefined, b: 'b'}], + [Object.assign(Object.create(null), {a: 'b'}), {a: 'b'}], ].forEach(([n1, n2]) => { it(`{pass: true} expect(${stringify(n1)}).toMatchObject(${stringify( n2, @@ -1164,6 +1179,7 @@ describe('toMatchObject()', () => { [[1, 2, 3], [2, 3, 1]], [[1, 2, 3], [1, 2, 2]], [new Error('foo'), new Error('bar')], + [Object.assign(Object.create(null), {a: 'b'}), {c: 'd'}], ].forEach(([n1, n2]) => { it(`{pass: false} expect(${stringify(n1)}).toMatchObject(${stringify( n2, diff --git a/packages/expect/src/__tests__/spy_matchers.test.js b/packages/expect/src/__tests__/spyMatchers.test.js similarity index 84% rename from packages/expect/src/__tests__/spy_matchers.test.js rename to packages/expect/src/__tests__/spyMatchers.test.js index 3ae90bfe7b11..d90a959cafd0 100644 --- a/packages/expect/src/__tests__/spy_matchers.test.js +++ b/packages/expect/src/__tests__/spyMatchers.test.js @@ -491,6 +491,25 @@ const jestExpect = require('../'); jestExpect(fn).not[returned](), ).toThrowErrorMatchingSnapshot(); }); + + test(`incomplete recursive calls are handled properly`, () => { + // sums up all integers from 0 -> value, using recursion + const fn = jest.fn(value => { + if (value === 0) { + // Before returning from the base case of recursion, none of the + // calls have returned yet. + jestExpect(fn).not[returned](); + expect(() => + jestExpect(fn)[returned](), + ).toThrowErrorMatchingSnapshot(); + return 0; + } else { + return value + fn(value - 1); + } + }); + + fn(3); + }); }); }); @@ -641,6 +660,29 @@ const jestExpect = require('../'); jestExpect(fn)[returnedTimes](1), ).toThrowErrorMatchingSnapshot(); }); + + test(`incomplete recursive calls are handled properly`, () => { + // sums up all integers from 0 -> value, using recursion + const fn = jest.fn(value => { + if (value === 0) { + return 0; + } else { + const recursiveResult = fn(value - 1); + + if (value === 2) { + // Only 2 of the recursive calls have returned at this point + jestExpect(fn)[returnedTimes](2); + expect(() => + jestExpect(fn).not[returnedTimes](2), + ).toThrowErrorMatchingSnapshot(); + } + + return value + recursiveResult; + } + }); + + fn(3); + }); }); }); @@ -849,10 +891,32 @@ const jestExpect = require('../'); jestExpect(fn)[returnedWith]('bar'); }).toThrowErrorMatchingSnapshot(); }); + + test(`incomplete recursive calls are handled properly`, () => { + // sums up all integers from 0 -> value, using recursion + const fn = jest.fn(value => { + if (value === 0) { + // Before returning from the base case of recursion, none of the + // calls have returned yet. + // This test ensures that the incomplete calls are not incorrectly + // interpretted as have returned undefined + jestExpect(fn).not[returnedWith](undefined); + expect(() => + jestExpect(fn)[returnedWith](undefined), + ).toThrowErrorMatchingSnapshot(); + + return 0; + } else { + return value + fn(value - 1); + } + }); + + fn(3); + }); } - const nthCalled = ['toHaveNthReturnedWith', 'nthReturnedWith']; - if (nthCalled.indexOf(returnedWith) >= 0) { + const nthReturnedWith = ['toHaveNthReturnedWith', 'nthReturnedWith']; + if (nthReturnedWith.indexOf(returnedWith) >= 0) { test(`works with three calls`, () => { const fn = jest.fn(); fn.mockReturnValueOnce('foo1'); @@ -923,6 +987,80 @@ const jestExpect = require('../'); jestExpect(fn)[returnedWith](0.1, 'foo'); }).toThrowErrorMatchingSnapshot(); }); + + test(`incomplete recursive calls are handled properly`, () => { + // sums up all integers from 0 -> value, using recursion + const fn = jest.fn(value => { + if (value === 0) { + return 0; + } else { + const recursiveResult = fn(value - 1); + + if (value === 2) { + // Only 2 of the recursive calls have returned at this point + jestExpect(fn).not[returnedWith](1, 6); + jestExpect(fn).not[returnedWith](2, 3); + jestExpect(fn)[returnedWith](3, 1); + jestExpect(fn)[returnedWith](4, 0); + + expect(() => + jestExpect(fn)[returnedWith](1, 6), + ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn)[returnedWith](2, 3), + ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn).not[returnedWith](3, 1), + ).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn).not[returnedWith](4, 0), + ).toThrowErrorMatchingSnapshot(); + } + + return value + recursiveResult; + } + }); + + fn(3); + }); + } + + const lastReturnedWith = ['toHaveLastReturnedWith', 'lastReturnedWith']; + if (lastReturnedWith.indexOf(returnedWith) >= 0) { + test(`works with three calls`, () => { + const fn = jest.fn(); + fn.mockReturnValueOnce('foo1'); + fn.mockReturnValueOnce('foo2'); + fn.mockReturnValueOnce('foo3'); + fn(); + fn(); + fn(); + + jestExpect(fn)[returnedWith]('foo3'); + + expect(() => { + jestExpect(fn).not[returnedWith]('foo3'); + }).toThrowErrorMatchingSnapshot(); + }); + + test(`incomplete recursive calls are handled properly`, () => { + // sums up all integers from 0 -> value, using recursion + const fn = jest.fn(value => { + if (value === 0) { + // Before returning from the base case of recursion, none of the + // calls have returned yet. + jestExpect(fn).not[returnedWith](0); + expect(() => + jestExpect(fn)[returnedWith](0), + ).toThrowErrorMatchingSnapshot(); + return 0; + } else { + return value + fn(value - 1); + } + }); + + fn(3); + }); } test(`includes the custom mock name in the error message`, () => { diff --git a/packages/expect/src/__tests__/symbol_in_objects.test.js b/packages/expect/src/__tests__/symbolInObjects.test.js similarity index 100% rename from packages/expect/src/__tests__/symbol_in_objects.test.js rename to packages/expect/src/__tests__/symbolInObjects.test.js diff --git a/packages/expect/src/__tests__/to_throw_matchers.test.js b/packages/expect/src/__tests__/toThrowMatchers.test.js similarity index 100% rename from packages/expect/src/__tests__/to_throw_matchers.test.js rename to packages/expect/src/__tests__/toThrowMatchers.test.js diff --git a/packages/expect/src/asymmetric_matchers.js b/packages/expect/src/asymmetricMatchers.js similarity index 98% rename from packages/expect/src/asymmetric_matchers.js rename to packages/expect/src/asymmetricMatchers.js index b182fb1f1c44..01cc89af8550 100644 --- a/packages/expect/src/asymmetric_matchers.js +++ b/packages/expect/src/asymmetricMatchers.js @@ -7,13 +7,7 @@ * @flow */ -import { - equals, - fnNameFor, - hasProperty, - isA, - isUndefined, -} from './jasmine_utils'; +import {equals, fnNameFor, hasProperty, isA, isUndefined} from './jasmineUtils'; import {emptyObject} from './utils'; diff --git a/packages/expect/src/extract_expected_assertions_errors.js b/packages/expect/src/extractExpectedAssertionsErrors.js similarity index 97% rename from packages/expect/src/extract_expected_assertions_errors.js rename to packages/expect/src/extractExpectedAssertionsErrors.js index af7d243142ae..29f766e6d7f9 100644 --- a/packages/expect/src/extract_expected_assertions_errors.js +++ b/packages/expect/src/extractExpectedAssertionsErrors.js @@ -14,7 +14,7 @@ import { pluralize, } from 'jest-matcher-utils'; -import {getState, setState} from './jest_matchers_object'; +import {getState, setState} from './jestMatchersObject'; const resetAssertionsLocalState = () => { setState({ diff --git a/packages/expect/src/fake_chalk.js b/packages/expect/src/fakeChalk.js similarity index 100% rename from packages/expect/src/fake_chalk.js rename to packages/expect/src/fakeChalk.js diff --git a/packages/expect/src/index.js b/packages/expect/src/index.js index 573d1bf9b42c..4c0aef0d3e92 100644 --- a/packages/expect/src/index.js +++ b/packages/expect/src/index.js @@ -23,11 +23,11 @@ import type { import * as matcherUtils from 'jest-matcher-utils'; import {iterableEquality, subsetEquality} from './utils'; import matchers from './matchers'; -import spyMatchers from './spy_matchers'; +import spyMatchers from './spyMatchers'; import toThrowMatchers, { createMatcher as createThrowMatcher, -} from './to_throw_matchers'; -import {equals} from './jasmine_utils'; +} from './toThrowMatchers'; +import {equals} from './jasmineUtils'; import { any, anything, @@ -39,15 +39,15 @@ import { stringNotContaining, stringMatching, stringNotMatching, -} from './asymmetric_matchers'; +} from './asymmetricMatchers'; import { INTERNAL_MATCHER_FLAG, getState, setState, getMatchers, setMatchers, -} from './jest_matchers_object'; -import extractExpectedAssertionsErrors from './extract_expected_assertions_errors'; +} from './jestMatchersObject'; +import extractExpectedAssertionsErrors from './extractExpectedAssertionsErrors'; class JestAssertionError extends Error { matcherResult: any; diff --git a/packages/expect/src/jasmine_utils.js b/packages/expect/src/jasmineUtils.js similarity index 97% rename from packages/expect/src/jasmine_utils.js rename to packages/expect/src/jasmineUtils.js index 144374f0cdff..a1b88aac8f5a 100644 --- a/packages/expect/src/jasmine_utils.js +++ b/packages/expect/src/jasmineUtils.js @@ -39,7 +39,7 @@ export function equals( } function isAsymmetric(obj) { - return obj && isA('Function', obj.asymmetricMatch); + return !!obj && isA('Function', obj.asymmetricMatch); } function asymmetricMatch(a, b) { @@ -51,6 +51,7 @@ function asymmetricMatch(a, b) { } if (asymmetricA) { + // $FlowFixMe – Flow sees `a` as a number return a.asymmetricMatch(b); } @@ -96,6 +97,7 @@ function eq(a, b, aStack, bStack, customTesters, hasKey): boolean { case '[object String]': // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // equivalent to `new String("5")`. + // $FlowFixMe – Flow sees `a` as a number return a == String(b); case '[object Number]': // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for @@ -113,6 +115,7 @@ function eq(a, b, aStack, bStack, customTesters, hasKey): boolean { a.source == b.source && a.global == b.global && a.multiline == b.multiline && + // $FlowFixMe – Flow sees `a` as a number a.ignoreCase == b.ignoreCase ); } diff --git a/packages/expect/src/jest_matchers_object.js b/packages/expect/src/jestMatchersObject.js similarity index 97% rename from packages/expect/src/jest_matchers_object.js rename to packages/expect/src/jestMatchersObject.js index 253681620fce..b3b7d8f87d7d 100644 --- a/packages/expect/src/jest_matchers_object.js +++ b/packages/expect/src/jestMatchersObject.js @@ -7,7 +7,7 @@ * @flow */ -import {AsymmetricMatcher} from './asymmetric_matchers'; +import {AsymmetricMatcher} from './asymmetricMatchers'; import type { Expect, MatchersObject, diff --git a/packages/expect/src/matchers.js b/packages/expect/src/matchers.js index 215114de52d5..13e516e48267 100644 --- a/packages/expect/src/matchers.js +++ b/packages/expect/src/matchers.js @@ -32,7 +32,7 @@ import { typeEquality, isOneline, } from './utils'; -import {equals} from './jasmine_utils'; +import {equals} from './jasmineUtils'; type ContainIterable = | Array @@ -511,10 +511,10 @@ const matchers: MatchersObject = { ` ${printReceived(result.value)}` + (diffString ? `\n\nDifference:\n\n${diffString}` : '') : traversedPath - ? `Received:\n ${RECEIVED_COLOR( - 'object', - )}.${traversedPath}: ${printReceived(lastTraversedObject)}` - : '') + ? `Received:\n ${RECEIVED_COLOR( + 'object', + )}.${traversedPath}: ${printReceived(lastTraversedObject)}` + : '') ); }; if (pass === undefined) { @@ -628,27 +628,20 @@ const matchers: MatchersObject = { true, ); + const hint = matcherHint('.toStrictEqual', undefined, undefined, { + isNot: this.isNot, + }); const message = pass ? () => - matcherHint('.not.toStrictEqual') + + hint + '\n\n' + - `Expected value to not equal:\n` + - ` ${printExpected(expected)}\n` + - `Received:\n` + - ` ${printReceived(received)}` + `Expected: ${printExpected(expected)}\n` + + `Received: ${printReceived(received)}` : () => { const diffString = diff(expected, received, { expand: this.expand, }); - return ( - matcherHint('.toStrictEqual') + - '\n\n' + - `Expected value to equal:\n` + - ` ${printExpected(expected)}\n` + - `Received:\n` + - ` ${printReceived(received)}` + - (diffString ? `\n\nDifference:\n\n${diffString}` : '') - ); + return hint + (diffString ? `\n\nDifference:\n\n${diffString}` : ''); }; // Passing the the actual and expected objects so that a custom reporter diff --git a/packages/expect/src/spy_matchers.js b/packages/expect/src/spyMatchers.js similarity index 92% rename from packages/expect/src/spy_matchers.js rename to packages/expect/src/spyMatchers.js index 2a176fb9e1f1..39c5abb36dea 100644 --- a/packages/expect/src/spy_matchers.js +++ b/packages/expect/src/spyMatchers.js @@ -23,7 +23,7 @@ import { printWithType, RECEIVED_COLOR, } from 'jest-matcher-utils'; -import {equals} from './jasmine_utils'; +import {equals} from './jasmineUtils'; import {iterableEquality, partition, isOneline} from './utils'; import diff from 'jest-diff'; @@ -69,10 +69,9 @@ const createToReturnMatcher = matcherName => (received, expected) => { ? 'mock function' : `mock function "${receivedName}"`; - // List of return values that correspond only to calls that did not throw - // an error + // List of return values that correspond only to calls that returned const returnValues = received.mock.results - .filter(result => !result.isThrow) + .filter(result => result.type === 'return') .map(result => result.value); const count = returnValues.length; @@ -140,9 +139,10 @@ const createToReturnTimesMatcher = (matcherName: string) => ( ? 'mock function' : `mock function "${receivedName}"`; - // List of return results that correspond only to calls that did not throw - // an error - const returnResults = received.mock.results.filter(result => !result.isThrow); + // List of return results that correspond only to calls that returned + const returnResults = received.mock.results.filter( + result => result.type === 'return', + ); const count = returnResults.length; const pass = count === expected; @@ -214,10 +214,9 @@ const createToReturnWithMatcher = matcherName => ( ? 'mock function' : `mock function "${receivedName}"`; - // List of return values that correspond only to calls that did not throw - // an error + // List of return values that correspond only to calls that returned const returnValues = received.mock.results - .filter(result => !result.isThrow) + .filter(result => result.type === 'return') .map(result => result.value); const [match] = partition(returnValues, value => @@ -295,7 +294,7 @@ const createLastReturnedMatcher = matcherName => ( const lastResult = results[results.length - 1]; const pass = !!lastResult && - !lastResult.isThrow && + lastResult.type === 'return' && equals(lastResult.value, expected, [iterableEquality]); const message = pass @@ -313,11 +312,13 @@ const createLastReturnedMatcher = matcherName => ( ` ${printExpected(expected)}\n` + (!lastResult ? `But it was ${RECEIVED_COLOR('not called')}` - : lastResult.isThrow - ? `But the last call ${RECEIVED_COLOR('threw an error')}` - : `But the last call returned:\n ${printReceived( - lastResult.value, - )}`); + : lastResult.type === 'incomplete' + ? `But the last call ${RECEIVED_COLOR('has not returned yet')}` + : lastResult.type === 'throw' + ? `But the last call ${RECEIVED_COLOR('threw an error')}` + : `But the last call returned:\n ${printReceived( + lastResult.value, + )}`); return {message, pass}; }; @@ -400,7 +401,7 @@ const createNthReturnedWithMatcher = (matcherName: string) => ( const nthResult = results[nth - 1]; const pass = !!nthResult && - !nthResult.isThrow && + nthResult.type === 'return' && equals(nthResult.value, expected, [iterableEquality]); const nthString = nthToString(nth); const message = pass @@ -419,12 +420,16 @@ const createNthReturnedWithMatcher = (matcherName: string) => ( (results.length === 0 ? `But it was ${RECEIVED_COLOR('not called')}` : nth > results.length - ? `But it was only called ${printReceived(results.length)} times` - : nthResult.isThrow - ? `But the ${nthString} call ${RECEIVED_COLOR('threw an error')}` - : `But the ${nthString} call returned with:\n ${printReceived( - nthResult.value, - )}`); + ? `But it was only called ${printReceived(results.length)} times` + : nthResult.type === 'incomplete' + ? `But the ${nthString} call ${RECEIVED_COLOR( + 'has not returned yet', + )}` + : nthResult.type === 'throw' + ? `But the ${nthString} call ${RECEIVED_COLOR('threw an error')}` + : `But the ${nthString} call returned with:\n ${printReceived( + nthResult.value, + )}`); return {message, pass}; }; diff --git a/packages/expect/src/to_throw_matchers.js b/packages/expect/src/toThrowMatchers.js similarity index 99% rename from packages/expect/src/to_throw_matchers.js rename to packages/expect/src/toThrowMatchers.js index b5e953a10792..48f69b46d630 100644 --- a/packages/expect/src/to_throw_matchers.js +++ b/packages/expect/src/toThrowMatchers.js @@ -19,7 +19,7 @@ import { printExpected, printWithType, } from 'jest-matcher-utils'; -import {equals} from './jasmine_utils'; +import {equals} from './jasmineUtils'; import {isError} from './utils'; export const createMatcher = (matcherName: string, fromPromise?: boolean) => ( diff --git a/packages/expect/src/utils.js b/packages/expect/src/utils.js index 9a7a0220fac5..24197cc2c50f 100644 --- a/packages/expect/src/utils.js +++ b/packages/expect/src/utils.js @@ -12,7 +12,7 @@ import { isA, isImmutableUnorderedKeyed, isImmutableUnorderedSet, -} from './jasmine_utils'; +} from './jasmineUtils'; type GetPath = { hasEndProp?: boolean, @@ -21,9 +21,17 @@ type GetPath = { value?: any, }; -export const hasOwnProperty = (object: Object, value: string) => - Object.prototype.hasOwnProperty.call(object, value) || - Object.prototype.hasOwnProperty.call(object.constructor.prototype, value); +export const hasOwnProperty = (object: Object, value: string) => { + // Account for objects created using unconventional means such as + // `Object.create(null)`, in which case the `object.constructor` is undefined + // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Custom_and_Null_objects + const objectConstructor = object.constructor || Object; + + return ( + Object.prototype.hasOwnProperty.call(object, value) || + Object.prototype.hasOwnProperty.call(objectConstructor.prototype, value) + ); +}; export const getPath = ( object: Object, diff --git a/packages/jest-changed-files/package.json b/packages/jest-changed-files/package.json index cabb73c57560..b94dae7dcfa4 100644 --- a/packages/jest-changed-files/package.json +++ b/packages/jest-changed-files/package.json @@ -10,5 +10,8 @@ "dependencies": { "execa": "^1.0.0", "throat": "^4.0.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-circus/package.json b/packages/jest-circus/package.json index 07cbc7e8a23e..daa5853f288c 100644 --- a/packages/jest-circus/package.json +++ b/packages/jest-circus/package.json @@ -25,5 +25,8 @@ "devDependencies": { "execa": "^1.0.0", "jest-runtime": "^23.6.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-circus/runner.js b/packages/jest-circus/runner.js index 214956a9f42a..8ed8163ee414 100644 --- a/packages/jest-circus/runner.js +++ b/packages/jest-circus/runner.js @@ -9,5 +9,5 @@ */ // Allow people to use `jest-circus/runner` as a runner. -const runner = require('./build/legacy_code_todo_rewrite/jest_adapter'); +const runner = require('./build/legacy-code-todo-rewrite/jestAdapter'); module.exports = runner; diff --git a/packages/jest-circus/src/__mocks__/test_event_handler.js b/packages/jest-circus/src/__mocks__/testEventHandler.js similarity index 100% rename from packages/jest-circus/src/__mocks__/test_event_handler.js rename to packages/jest-circus/src/__mocks__/testEventHandler.js diff --git a/packages/jest-circus/src/__mocks__/test_utils.js b/packages/jest-circus/src/__mocks__/testUtils.js similarity index 97% rename from packages/jest-circus/src/__mocks__/test_utils.js rename to packages/jest-circus/src/__mocks__/testUtils.js index 06da8d615517..79b3fdd47dc2 100644 --- a/packages/jest-circus/src/__mocks__/test_utils.js +++ b/packages/jest-circus/src/__mocks__/testUtils.js @@ -20,7 +20,7 @@ import {skipSuiteOnWindows} from '../../../../scripts/ConditionalTest'; const CIRCUS_PATH = require.resolve('../../build/index'); const CIRCUS_RUN_PATH = require.resolve('../../build/run'); const CIRCUS_STATE_PATH = require.resolve('../../build/state'); -const TEST_EVENT_HANDLER_PATH = require.resolve('./test_event_handler'); +const TEST_EVENT_HANDLER_PATH = require.resolve('./testEventHandler'); const BABEL_REGISTER_PATH = require.resolve('@babel/register'); skipSuiteOnWindows(); diff --git a/packages/jest-circus/src/__tests__/__snapshots__/after_all.test.js.snap b/packages/jest-circus/src/__tests__/__snapshots__/afterAll.test.js.snap similarity index 100% rename from packages/jest-circus/src/__tests__/__snapshots__/after_all.test.js.snap rename to packages/jest-circus/src/__tests__/__snapshots__/afterAll.test.js.snap diff --git a/packages/jest-circus/src/__tests__/__snapshots__/base_test.test.js.snap b/packages/jest-circus/src/__tests__/__snapshots__/baseTest.test.js.snap similarity index 100% rename from packages/jest-circus/src/__tests__/__snapshots__/base_test.test.js.snap rename to packages/jest-circus/src/__tests__/__snapshots__/baseTest.test.js.snap diff --git a/packages/jest-circus/src/__tests__/after_all.test.js b/packages/jest-circus/src/__tests__/afterAll.test.js similarity index 97% rename from packages/jest-circus/src/__tests__/after_all.test.js rename to packages/jest-circus/src/__tests__/afterAll.test.js index 22e424d96f20..7cccc1f66d49 100644 --- a/packages/jest-circus/src/__tests__/after_all.test.js +++ b/packages/jest-circus/src/__tests__/afterAll.test.js @@ -10,7 +10,7 @@ 'use strict'; -import {runTest} from '../__mocks__/test_utils'; +import {runTest} from '../__mocks__/testUtils'; test('tests are not marked done until their parent afterAll runs', () => { const {stdout} = runTest(` diff --git a/packages/jest-circus/src/__tests__/base_test.test.js b/packages/jest-circus/src/__tests__/baseTest.test.js similarity index 94% rename from packages/jest-circus/src/__tests__/base_test.test.js rename to packages/jest-circus/src/__tests__/baseTest.test.js index ec83663ba8db..1cc1c2abab94 100644 --- a/packages/jest-circus/src/__tests__/base_test.test.js +++ b/packages/jest-circus/src/__tests__/baseTest.test.js @@ -10,7 +10,7 @@ 'use strict'; -import {runTest} from '../__mocks__/test_utils'; +import {runTest} from '../__mocks__/testUtils'; test('simple test', () => { const {stdout} = runTest(` diff --git a/packages/jest-circus/src/__tests__/circus_it_test_error.test.js b/packages/jest-circus/src/__tests__/circusItTestError.test.js similarity index 100% rename from packages/jest-circus/src/__tests__/circus_it_test_error.test.js rename to packages/jest-circus/src/__tests__/circusItTestError.test.js diff --git a/packages/jest-circus/src/__tests__/circus_todo_test_error.test.js b/packages/jest-circus/src/__tests__/circusItTodoTestError.test.js similarity index 100% rename from packages/jest-circus/src/__tests__/circus_todo_test_error.test.js rename to packages/jest-circus/src/__tests__/circusItTodoTestError.test.js diff --git a/packages/jest-circus/src/__tests__/hooks.test.js b/packages/jest-circus/src/__tests__/hooks.test.js index 2486cc3d6e62..6d9b58bce341 100644 --- a/packages/jest-circus/src/__tests__/hooks.test.js +++ b/packages/jest-circus/src/__tests__/hooks.test.js @@ -10,7 +10,7 @@ 'use strict'; -import {runTest} from '../__mocks__/test_utils'; +import {runTest} from '../__mocks__/testUtils'; test('beforeEach is executed before each test in current/child describe blocks', () => { const {stdout} = runTest(` diff --git a/packages/jest-circus/src/__tests__/hooks_error.test.js b/packages/jest-circus/src/__tests__/hooksError.test.js similarity index 100% rename from packages/jest-circus/src/__tests__/hooks_error.test.js rename to packages/jest-circus/src/__tests__/hooksError.test.js diff --git a/packages/jest-circus/src/event_handler.js b/packages/jest-circus/src/eventHandler.js similarity index 97% rename from packages/jest-circus/src/event_handler.js rename to packages/jest-circus/src/eventHandler.js index fb5af141b802..893e61f61410 100644 --- a/packages/jest-circus/src/event_handler.js +++ b/packages/jest-circus/src/eventHandler.js @@ -20,12 +20,12 @@ import { import { injectGlobalErrorHandlers, restoreGlobalErrorHandlers, -} from './error_handlers'; +} from './globalErrorHandlers'; // To pass this value from Runtime object to state we need to use global[sym] const TEST_TIMEOUT_SYMBOL = Symbol.for('TEST_TIMEOUT_SYMBOL'); -const handler: EventHandler = (event, state): void => { +const eventHandler: EventHandler = (event, state): void => { switch (event.name) { case 'include_test_location_in_result': { state.includeTestLocationInResult = true; @@ -183,4 +183,4 @@ const handler: EventHandler = (event, state): void => { } }; -export default handler; +export default eventHandler; diff --git a/packages/jest-circus/src/format_node_assert_errors.js b/packages/jest-circus/src/formatNodeAssertErrors.js similarity index 97% rename from packages/jest-circus/src/format_node_assert_errors.js rename to packages/jest-circus/src/formatNodeAssertErrors.js index 9151fa9fe3fa..d6080ce0b168 100644 --- a/packages/jest-circus/src/format_node_assert_errors.js +++ b/packages/jest-circus/src/formatNodeAssertErrors.js @@ -43,7 +43,7 @@ const humanReadableOperators = { strictEqual: 'to strictly be equal', }; -export default (event: Event, state: State) => { +const formatNodeAssertErrors = (event: Event, state: State) => { switch (event.name) { case 'test_done': { event.test.errors = event.test.errors.map(errors => { @@ -167,3 +167,5 @@ function assertionErrorMessage(error: AssertionError, options: DiffOptions) { trimmedStack ); } + +export default formatNodeAssertErrors; diff --git a/packages/jest-circus/src/error_handlers.js b/packages/jest-circus/src/globalErrorHandlers.js similarity index 100% rename from packages/jest-circus/src/error_handlers.js rename to packages/jest-circus/src/globalErrorHandlers.js diff --git a/packages/jest-circus/src/legacy_code_todo_rewrite/jest_adapter.js b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.js similarity index 95% rename from packages/jest-circus/src/legacy_code_todo_rewrite/jest_adapter.js rename to packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.js index b3a590f037fc..ebb588670c0b 100644 --- a/packages/jest-circus/src/legacy_code_todo_rewrite/jest_adapter.js +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.js @@ -12,7 +12,7 @@ import type {GlobalConfig, ProjectConfig} from 'types/Config'; import type {TestResult} from 'types/TestResult'; import type Runtime from 'jest-runtime'; -const FRAMEWORK_INITIALIZER = require.resolve('./jest_adapter_init'); +const FRAMEWORK_INITIALIZER = require.resolve('./jestAdapterInit'); import path from 'path'; const jestAdapter = async ( @@ -28,7 +28,7 @@ const jestAdapter = async ( } = runtime.requireInternalModule(FRAMEWORK_INITIALIZER); runtime - .requireInternalModule(path.resolve(__dirname, './jest_expect.js')) + .requireInternalModule(path.resolve(__dirname, './jestExpect.js')) .default({ expand: globalConfig.expand, }); diff --git a/packages/jest-circus/src/legacy_code_todo_rewrite/jest_adapter_init.js b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.js similarity index 100% rename from packages/jest-circus/src/legacy_code_todo_rewrite/jest_adapter_init.js rename to packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.js diff --git a/packages/jest-circus/src/legacy_code_todo_rewrite/jest_expect.js b/packages/jest-circus/src/legacy-code-todo-rewrite/jestExpect.js similarity index 100% rename from packages/jest-circus/src/legacy_code_todo_rewrite/jest_expect.js rename to packages/jest-circus/src/legacy-code-todo-rewrite/jestExpect.js diff --git a/packages/jest-circus/src/state.js b/packages/jest-circus/src/state.js index 3187775bcc2f..36d4fa69cf04 100644 --- a/packages/jest-circus/src/state.js +++ b/packages/jest-circus/src/state.js @@ -10,8 +10,8 @@ import type {Event, State, EventHandler} from 'types/Circus'; import {makeDescribe} from './utils'; -import eventHandler from './event_handler'; -import formatNodeAssertErrors from './format_node_assert_errors'; +import eventHandler from './eventHandler'; +import formatNodeAssertErrors from './formatNodeAssertErrors'; const eventHandlers: Array = [ eventHandler, diff --git a/packages/jest-circus/src/utils.js b/packages/jest-circus/src/utils.js index 96da54744ecf..328566bbcfb5 100644 --- a/packages/jest-circus/src/utils.js +++ b/packages/jest-circus/src/utils.js @@ -240,7 +240,7 @@ export const callAsyncCircusFn = ( export const getTestDuration = (test: TestEntry): ?number => { const {startedAt} = test; - return startedAt ? Date.now() - startedAt : null; + return typeof startedAt === 'number' ? Date.now() - startedAt : null; }; export const makeRunResult = ( diff --git a/packages/jest-cli/src/__tests__/test_sequencer.test.js b/packages/jest-cli/src/__tests__/test_sequencer.test.js index ae3b3a6f10fb..47cd3131e984 100644 --- a/packages/jest-cli/src/__tests__/test_sequencer.test.js +++ b/packages/jest-cli/src/__tests__/test_sequencer.test.js @@ -203,16 +203,15 @@ test('writes the cache based on the results', () => { }); test('works with multiple contexts', () => { - fs.readFileSync = jest.fn( - cacheName => - cacheName.startsWith(path.sep + 'cache' + path.sep) - ? JSON.stringify({ - '/test-a.js': [SUCCESS, 5], - '/test-b.js': [FAIL, 1], - }) - : JSON.stringify({ - '/test-c.js': [FAIL], - }), + fs.readFileSync = jest.fn(cacheName => + cacheName.startsWith(path.sep + 'cache' + path.sep) + ? JSON.stringify({ + '/test-a.js': [SUCCESS, 5], + '/test-b.js': [FAIL, 1], + }) + : JSON.stringify({ + '/test-c.js': [FAIL], + }), ); const testPaths = [ diff --git a/packages/jest-cli/src/lib/__tests__/__snapshots__/init.test.js.snap b/packages/jest-cli/src/lib/__tests__/__snapshots__/init.test.js.snap index 8f49360f6ebc..d0b63fa1c18a 100644 --- a/packages/jest-cli/src/lib/__tests__/__snapshots__/init.test.js.snap +++ b/packages/jest-cli/src/lib/__tests__/__snapshots__/init.test.js.snap @@ -63,6 +63,9 @@ module.exports = { // An object that configures minimum threshold enforcement for coverage results // coverageThreshold: null, + // A path to a custom dependency extractor + // dependencyExtractor: null, + // Make calling deprecated APIs throw helpful error messages // errorOnDeprecated: false, diff --git a/packages/jest-cli/src/lib/__tests__/init.test.js b/packages/jest-cli/src/lib/__tests__/init.test.js index a38c59492e18..81981ce6e0c2 100644 --- a/packages/jest-cli/src/lib/__tests__/init.test.js +++ b/packages/jest-cli/src/lib/__tests__/init.test.js @@ -31,7 +31,9 @@ describe('init', () => { beforeEach(() => { // $FlowFixMe mock console.log to reduce noise from the tests console.log = jest.fn(); + // $FlowFixMe mock fs.writeFileSync = jest.fn(); + // $FlowFixMe mock path.sep = '/'; }); @@ -39,7 +41,9 @@ describe('init', () => { jest.clearAllMocks(); // $FlowFixMe console.log = consoleLog; + // $FlowFixMe mock fs.writeFileSync = writeFileSync; + // $FlowFixMe mock path.sep = sep; }); diff --git a/packages/jest-cli/src/reporters/verbose_reporter.js b/packages/jest-cli/src/reporters/verbose_reporter.js index 9883c1844049..ff82c3f573ea 100644 --- a/packages/jest-cli/src/reporters/verbose_reporter.js +++ b/packages/jest-cli/src/reporters/verbose_reporter.js @@ -28,7 +28,9 @@ export default class VerboseReporter extends DefaultReporter { this._globalConfig = globalConfig; } - static filterTestResults(testResults: Array) { + static filterTestResults( + testResults: Array, + ): Array { return testResults.filter(({status}) => status !== 'pending'); } diff --git a/packages/jest-config/package.json b/packages/jest-config/package.json index cf2083a7daee..b00d0b7c52c7 100644 --- a/packages/jest-config/package.json +++ b/packages/jest-config/package.json @@ -22,5 +22,8 @@ "jest-validate": "^23.6.0", "micromatch": "^2.3.11", "pretty-format": "^23.6.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-config/src/Defaults.js b/packages/jest-config/src/Defaults.js index b6aafa6e13ad..85b29b28f577 100644 --- a/packages/jest-config/src/Defaults.js +++ b/packages/jest-config/src/Defaults.js @@ -30,6 +30,7 @@ export default ({ coverageReporters: ['json', 'text', 'lcov', 'clover'], coverageThreshold: null, cwd: process.cwd(), + dependencyExtractor: null, detectLeaks: false, detectOpenHandles: false, errorOnDeprecated: false, @@ -40,6 +41,7 @@ export default ({ globalTeardown: null, globals: {}, haste: { + computeSha1: false, providesModuleNodeModules: [], }, moduleDirectories: ['node_modules'], diff --git a/packages/jest-config/src/Descriptions.js b/packages/jest-config/src/Descriptions.js index 48c3b2d321ad..6ff881d1501d 100644 --- a/packages/jest-config/src/Descriptions.js +++ b/packages/jest-config/src/Descriptions.js @@ -26,6 +26,7 @@ export default ({ 'A list of reporter names that Jest uses when writing coverage reports', coverageThreshold: 'An object that configures minimum threshold enforcement for coverage results', + dependencyExtractor: 'A path to a custom dependency extractor', errorOnDeprecated: 'Make calling deprecated APIs throw helpful error messages', forceCoverageMatch: diff --git a/packages/jest-config/src/ValidConfig.js b/packages/jest-config/src/ValidConfig.js index cb732dc76555..c3da446f08a5 100644 --- a/packages/jest-config/src/ValidConfig.js +++ b/packages/jest-config/src/ValidConfig.js @@ -40,6 +40,7 @@ export default ({ statements: 100, }, }, + dependencyExtractor: '/dependencyExtractor.js', displayName: 'project-name', errorOnDeprecated: false, expand: false, @@ -50,6 +51,7 @@ export default ({ globalTeardown: 'teardown.js', globals: {__DEV__: true}, haste: { + computeSha1: true, defaultPlatform: 'ios', hasteImplModulePath: '/haste_impl.js', platforms: ['ios', 'android'], 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 c8edc2f5911b..ea5a367f3d53 100644 --- a/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap +++ b/packages/jest-config/src/__tests__/__snapshots__/normalize.test.js.snap @@ -47,6 +47,16 @@ exports[`rootDir throws if the options is missing a rootDir property 1`] = ` " `; +exports[`runner throw error when a runner is not found 1`] = ` +"Validation Error: + + Jest Runner missing-runner cannot be found. Make sure the runner configuration option points to an existing node module. + + Configuration Documentation: + https://jestjs.io/docs/configuration.html +" +`; + exports[`setupTestFrameworkScriptFile logs a deprecation warning when \`setupTestFrameworkScriptFile\` is used 1`] = ` " Deprecation Warning: @@ -93,3 +103,13 @@ exports[`testMatch throws if testRegex and testMatch are both specified 1`] = ` exports[`testPathPattern ignores invalid regular expressions and logs a warning 1`] = `" Invalid testPattern a( supplied. Running all tests instead."`; exports[`testPathPattern --testPathPattern ignores invalid regular expressions and logs a warning 1`] = `" Invalid testPattern a( supplied. Running all tests instead."`; + +exports[`watchPlugins throw error when a watch plugin is not found 1`] = ` +"Validation Error: + + Watch plugin missing-plugin cannot be found. Make sure the watchPlugins configuration option points to an existing node module. + + Configuration Documentation: + https://jestjs.io/docs/configuration.html +" +`; diff --git a/packages/jest-config/src/__tests__/normalize.test.js b/packages/jest-config/src/__tests__/normalize.test.js index 48c2fc306b01..28c2aea79422 100644 --- a/packages/jest-config/src/__tests__/normalize.test.js +++ b/packages/jest-config/src/__tests__/normalize.test.js @@ -334,9 +334,8 @@ describe('setupFilesAfterEnv', () => { let Resolver; beforeEach(() => { Resolver = require('jest-resolve'); - Resolver.findNodeModule = jest.fn( - name => - name.startsWith('/') ? name : '/root/path/foo' + path.sep + name, + Resolver.findNodeModule = jest.fn(name => + name.startsWith('/') ? name : '/root/path/foo' + path.sep + name, ); }); @@ -385,9 +384,8 @@ describe('setupTestFrameworkScriptFile', () => { console.warn = jest.fn(); consoleWarn = console.warn; Resolver = require('jest-resolve'); - Resolver.findNodeModule = jest.fn( - name => - name.startsWith('/') ? name : '/root/path/foo' + path.sep + name, + Resolver.findNodeModule = jest.fn(name => + name.startsWith('/') ? name : '/root/path/foo' + path.sep + name, ); }); @@ -671,11 +669,8 @@ describe('testEnvironment', () => { beforeEach(() => { Resolver = require('jest-resolve'); Resolver.findNodeModule = jest.fn(name => { - if (name === 'jsdom') { - return 'node_modules/jsdom'; - } - if (name === 'jest-environment-jsdom') { - return 'node_modules/jest-environment-jsdom'; + if (['jsdom', 'jest-environment-jsdom'].includes(name)) { + return `node_modules/${name}`; } if (name.startsWith('/root')) { return name; @@ -727,11 +722,10 @@ describe('babel-jest', () => { let Resolver; beforeEach(() => { Resolver = require('jest-resolve'); - Resolver.findNodeModule = jest.fn( - name => - name.indexOf('babel-jest') === -1 - ? path.sep + 'node_modules' + path.sep + name - : name, + Resolver.findNodeModule = jest.fn(name => + name.indexOf('babel-jest') === -1 + ? path.sep + 'node_modules' + path.sep + name + : name, ); }); @@ -1205,15 +1199,79 @@ describe('preset without setupFiles', () => { }); }); +describe('runner', () => { + let Resolver; + beforeEach(() => { + Resolver = require('jest-resolve'); + Resolver.findNodeModule = jest.fn(name => { + if (['eslint', 'jest-runner-eslint', 'my-runner-foo'].includes(name)) { + return `node_modules/${name}`; + } + if (name.startsWith('/root')) { + return name; + } + return findNodeModule(name); + }); + }); + + it('defaults to `jest-runner`', () => { + const {options} = normalize({rootDir: '/root'}, {}); + + expect(options.runner).toBe('jest-runner'); + }); + + it('resolves to runners that do not have the prefix', () => { + const {options} = normalize( + { + rootDir: '/root/', + runner: 'my-runner-foo', + }, + {}, + ); + + expect(options.runner).toBe('node_modules/my-runner-foo'); + }); + + it('resolves to runners and prefers jest-runner-`name`', () => { + const {options} = normalize( + { + rootDir: '/root/', + runner: 'eslint', + }, + {}, + ); + + expect(options.runner).toBe('node_modules/jest-runner-eslint'); + }); + + it('throw error when a runner is not found', () => { + expect(() => + normalize( + { + rootDir: '/root/', + runner: 'missing-runner', + }, + {}, + ), + ).toThrowErrorMatchingSnapshot(); + }); +}); + describe('watchPlugins', () => { let Resolver; beforeEach(() => { Resolver = require('jest-resolve'); Resolver.findNodeModule = jest.fn(name => { - if (name.startsWith(path.sep)) { + if ( + ['typeahead', 'jest-watch-typeahead', 'my-watch-plugin'].includes(name) + ) { + return `node_modules/${name}`; + } + + if (name.startsWith('/root')) { return name; } - return path.sep + 'node_modules' + path.sep + name; + return findNodeModule(name); }); }); @@ -1223,20 +1281,60 @@ describe('watchPlugins', () => { expect(options.watchPlugins).toEqual(undefined); }); - it('normalizes watchPlugins', () => { + it('resolves to watch plugins and prefers jest-watch-`name`', () => { + const {options} = normalize( + { + rootDir: '/root/', + watchPlugins: ['typeahead'], + }, + {}, + ); + + expect(options.watchPlugins).toEqual([ + {config: {}, path: 'node_modules/jest-watch-typeahead'}, + ]); + }); + + it('resolves watch plugins that do not have the prefix', () => { const {options} = normalize( { rootDir: '/root/', - watchPlugins: ['my-watch-plugin', '/path/to/plugin'], + watchPlugins: ['my-watch-plugin'], }, {}, ); expect(options.watchPlugins).toEqual([ - {config: {}, path: '/node_modules/my-watch-plugin'}, + {config: {}, path: 'node_modules/my-watch-plugin'}, + ]); + }); + + it('normalizes multiple watchPlugins', () => { + const {options} = normalize( + { + rootDir: '/root/', + watchPlugins: ['jest-watch-typeahead', '/path/to/plugin'], + }, + {}, + ); + + expect(options.watchPlugins).toEqual([ + {config: {}, path: 'node_modules/jest-watch-typeahead'}, {config: {}, path: '/root/path/to/plugin'}, ]); }); + + it('throw error when a watch plugin is not found', () => { + expect(() => + normalize( + { + rootDir: '/root/', + watchPlugins: ['missing-plugin'], + }, + {}, + ), + ).toThrowErrorMatchingSnapshot(); + }); }); describe('testPathPattern', () => { diff --git a/packages/jest-config/src/index.js b/packages/jest-config/src/index.js index ec6a856c75f0..b1832b94af6a 100644 --- a/packages/jest-config/src/index.js +++ b/packages/jest-config/src/index.js @@ -160,6 +160,7 @@ const groupOptions = ( clearMocks: options.clearMocks, coveragePathIgnorePatterns: options.coveragePathIgnorePatterns, cwd: options.cwd, + dependencyExtractor: options.dependencyExtractor, detectLeaks: options.detectLeaks, detectOpenHandles: options.detectOpenHandles, displayName: options.displayName, diff --git a/packages/jest-config/src/normalize.js b/packages/jest-config/src/normalize.js index 018f223d87f9..b29e3e690091 100644 --- a/packages/jest-config/src/normalize.js +++ b/packages/jest-config/src/normalize.js @@ -8,7 +8,13 @@ */ import type {Argv} from 'types/Argv'; -import type {InitialOptions, ReporterConfig} from 'types/Config'; +import type { + InitialOptions, + DefaultOptions, + ReporterConfig, + GlobalConfig, + ProjectConfig, +} from 'types/Config'; import crypto from 'crypto'; import glob from 'glob'; @@ -28,6 +34,8 @@ import { _replaceRootDirTags, escapeGlobCharacters, getTestEnvironment, + getRunner, + getWatchPlugin, resolve, } from './utils'; import {DEFAULT_JS_PATTERN, DEFAULT_REPORTER_LABEL} from './constants'; @@ -394,7 +402,10 @@ export default function normalize(options: InitialOptions, argv: Argv) { } if (options.testEnvironment) { - options.testEnvironment = getTestEnvironment(options); + options.testEnvironment = getTestEnvironment({ + rootDir: options.rootDir, + testEnvironment: options.testEnvironment, + }); } if (!options.roots && options.testPathDirs) { @@ -414,9 +425,9 @@ export default function normalize(options: InitialOptions, argv: Argv) { } const babelJest = setupBabelJest(options); - const newOptions = Object.assign({}, DEFAULT_CONFIG); - // Cast back to exact type - options = (options: InitialOptions); + const newOptions: $Shape< + DefaultOptions & ProjectConfig & GlobalConfig, + > = (Object.assign({}, DEFAULT_CONFIG): any); if (options.resolver) { newOptions.resolver = resolve(null, { @@ -426,7 +437,7 @@ export default function normalize(options: InitialOptions, argv: Argv) { }); } - Object.keys(options).reduce((newOptions, key) => { + Object.keys(options).reduce((newOptions, key: $Keys) => { // The resolver has been resolved separately; skip it if (key === 'resolver') { return newOptions; @@ -472,10 +483,10 @@ export default function normalize(options: InitialOptions, argv: Argv) { replaceRootDirInPath(options.rootDir, options[key]), ); break; + case 'dependencyExtractor': case 'globalSetup': case 'globalTeardown': case 'moduleLoader': - case 'runner': case 'snapshotResolver': case 'testResultsProcessor': case 'testRunner': @@ -488,6 +499,14 @@ export default function normalize(options: InitialOptions, argv: Argv) { rootDir: options.rootDir, }); break; + case 'runner': + value = + options[key] && + getRunner(newOptions.resolver, { + filePath: options[key], + rootDir: options.rootDir, + }); + break; case 'prettierPath': // We only want this to throw if "prettierPath" is explicitly passed // from config or CLI, and the requested path isn't found. Otherwise we @@ -546,11 +565,10 @@ export default function normalize(options: InitialOptions, argv: Argv) { break; case 'projects': value = (options[key] || []) - .map( - project => - typeof project === 'string' - ? _replaceRootDirTags(options.rootDir, project) - : project, + .map(project => + typeof project === 'string' + ? _replaceRootDirTags(options.rootDir, project) + : project, ) .reduce((projects, project) => { // Project can be specified as globs. If a glob matches any files, @@ -658,18 +676,16 @@ export default function normalize(options: InitialOptions, argv: Argv) { if (typeof watchPlugin === 'string') { return { config: {}, - path: resolve(newOptions.resolver, { + path: getWatchPlugin(newOptions.resolver, { filePath: watchPlugin, - key, rootDir: options.rootDir, }), }; } else { return { config: watchPlugin[1] || {}, - path: resolve(newOptions.resolver, { + path: getWatchPlugin(newOptions.resolver, { filePath: watchPlugin[0], - key, rootDir: options.rootDir, }), }; @@ -677,6 +693,7 @@ export default function normalize(options: InitialOptions, argv: Argv) { }); break; } + // $FlowFixMe - automock is missing in GlobalConfig, so what newOptions[key] = value; return newOptions; }, newOptions); @@ -709,8 +726,8 @@ export default function normalize(options: InitialOptions, argv: Argv) { argv.ci && !argv.updateSnapshot ? 'none' : argv.updateSnapshot - ? 'all' - : 'new'; + ? 'all' + : 'new'; newOptions.maxWorkers = getMaxWorkers(argv); diff --git a/packages/jest-config/src/utils.js b/packages/jest-config/src/utils.js index fb4aae572ae1..95f3b0a976ab 100644 --- a/packages/jest-config/src/utils.js +++ b/packages/jest-config/src/utils.js @@ -102,45 +102,119 @@ export const _replaceRootDirTags = (rootDir: string, config: any) => { return config; }; -/** - * Finds the test environment to use: - * - * 1. looks for jest-environment- relative to project. - * 1. looks for jest-environment- relative to Jest. - * 1. looks for relative to project. - * 1. looks for relative to Jest. - */ -export const getTestEnvironment = (config: Object) => { - const env = replaceRootDirInPath(config.rootDir, config.testEnvironment); - let module = Resolver.findNodeModule(`jest-environment-${env}`, { - basedir: config.rootDir, +export const resolveWithPrefix = ( + resolver: ?string, + { + filePath, + humanOptionName, + optionName, + prefix, + rootDir, + }: { + filePath: string, + humanOptionName: string, + optionName: string, + prefix: string, + rootDir: string, + }, +) => { + const fileName = replaceRootDirInPath(rootDir, filePath); + let module = Resolver.findNodeModule(`${prefix}${fileName}`, { + basedir: rootDir, + resolver, }); if (module) { return module; } try { - return require.resolve(`jest-environment-${env}`); + return require.resolve(`${prefix}${fileName}`); } catch (e) {} - module = Resolver.findNodeModule(env, {basedir: config.rootDir}); + module = Resolver.findNodeModule(fileName, { + basedir: rootDir, + resolver, + }); if (module) { return module; } try { - return require.resolve(env); + return require.resolve(fileName); } catch (e) {} throw createValidationError( - ` Test environment ${chalk.bold( - env, + ` ${humanOptionName} ${chalk.bold( + fileName, )} cannot be found. Make sure the ${chalk.bold( - 'testEnvironment', + optionName, )} configuration option points to an existing node module.`, ); }; +/** + * Finds the test environment to use: + * + * 1. looks for jest-environment- relative to project. + * 1. looks for jest-environment- relative to Jest. + * 1. looks for relative to project. + * 1. looks for relative to Jest. + */ +export const getTestEnvironment = ({ + rootDir, + testEnvironment: filePath, +}: { + rootDir: string, + testEnvironment: string, +}) => + resolveWithPrefix(null, { + filePath, + humanOptionName: 'Test environment', + optionName: 'testEnvironment', + prefix: 'jest-environment-', + rootDir, + }); + +/** + * Finds the watch plugins to use: + * + * 1. looks for jest-watch- relative to project. + * 1. looks for jest-watch- relative to Jest. + * 1. looks for relative to project. + * 1. looks for relative to Jest. + */ +export const getWatchPlugin = ( + resolver: ?string, + {filePath, rootDir}: {filePath: string, rootDir: string}, +) => + resolveWithPrefix(resolver, { + filePath, + humanOptionName: 'Watch plugin', + optionName: 'watchPlugins', + prefix: 'jest-watch-', + rootDir, + }); + +/** + * Finds the runner to use: + * + * 1. looks for jest-runner- relative to project. + * 1. looks for jest-runner- relative to Jest. + * 1. looks for relative to project. + * 1. looks for relative to Jest. + */ +export const getRunner = ( + resolver: ?string, + {filePath, rootDir}: {filePath: string, rootDir: string}, +) => + resolveWithPrefix(resolver, { + filePath, + humanOptionName: 'Jest Runner', + optionName: 'runner', + prefix: 'jest-runner-', + rootDir, + }); + export const isJSONString = (text: ?string) => text && typeof text === 'string' && diff --git a/packages/jest-diff/package.json b/packages/jest-diff/package.json index 4410a02757a9..f8d179035aad 100644 --- a/packages/jest-diff/package.json +++ b/packages/jest-diff/package.json @@ -12,5 +12,8 @@ "diff": "^3.2.0", "jest-get-type": "^22.1.0", "pretty-format": "^23.6.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-docblock/package.json b/packages/jest-docblock/package.json index f93615b234db..fe5b997592e5 100644 --- a/packages/jest-docblock/package.json +++ b/packages/jest-docblock/package.json @@ -9,5 +9,8 @@ "main": "build/index.js", "dependencies": { "detect-newline": "^2.1.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-each/package.json b/packages/jest-each/package.json index b985159c7057..342bc636b433 100644 --- a/packages/jest-each/package.json +++ b/packages/jest-each/package.json @@ -19,5 +19,8 @@ "chalk": "^2.0.1", "jest-util": "^23.4.0", "pretty-format": "^23.6.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-each/src/__tests__/__snapshots__/array.test.js.snap b/packages/jest-each/src/__tests__/__snapshots__/array.test.js.snap index 75cc81d64769..d779a5e5df50 100644 --- a/packages/jest-each/src/__tests__/__snapshots__/array.test.js.snap +++ b/packages/jest-each/src/__tests__/__snapshots__/array.test.js.snap @@ -1,56 +1,96 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`jest-each .describe throws an error when called with an empty array 1`] = ` +"Error: \`.each\` called with an empty Array of table data. +" +`; + exports[`jest-each .describe throws an error when not called with an array 1`] = ` -"\`.each\` must be called with an Array or Tagged Template String. +"\`.each\` must be called with an Array or Tagged Template Literal. Instead was called with: undefined " `; +exports[`jest-each .describe.only throws an error when called with an empty array 1`] = ` +"Error: \`.each\` called with an empty Array of table data. +" +`; + exports[`jest-each .describe.only throws an error when not called with an array 1`] = ` -"\`.each\` must be called with an Array or Tagged Template String. +"\`.each\` must be called with an Array or Tagged Template Literal. Instead was called with: undefined " `; +exports[`jest-each .fdescribe throws an error when called with an empty array 1`] = ` +"Error: \`.each\` called with an empty Array of table data. +" +`; + exports[`jest-each .fdescribe throws an error when not called with an array 1`] = ` -"\`.each\` must be called with an Array or Tagged Template String. +"\`.each\` must be called with an Array or Tagged Template Literal. Instead was called with: undefined " `; +exports[`jest-each .fit throws an error when called with an empty array 1`] = ` +"Error: \`.each\` called with an empty Array of table data. +" +`; + exports[`jest-each .fit throws an error when not called with an array 1`] = ` -"\`.each\` must be called with an Array or Tagged Template String. +"\`.each\` must be called with an Array or Tagged Template Literal. Instead was called with: undefined " `; +exports[`jest-each .it throws an error when called with an empty array 1`] = ` +"Error: \`.each\` called with an empty Array of table data. +" +`; + exports[`jest-each .it throws an error when not called with an array 1`] = ` -"\`.each\` must be called with an Array or Tagged Template String. +"\`.each\` must be called with an Array or Tagged Template Literal. Instead was called with: undefined " `; +exports[`jest-each .it.only throws an error when called with an empty array 1`] = ` +"Error: \`.each\` called with an empty Array of table data. +" +`; + exports[`jest-each .it.only throws an error when not called with an array 1`] = ` -"\`.each\` must be called with an Array or Tagged Template String. +"\`.each\` must be called with an Array or Tagged Template Literal. Instead was called with: undefined " `; +exports[`jest-each .test throws an error when called with an empty array 1`] = ` +"Error: \`.each\` called with an empty Array of table data. +" +`; + exports[`jest-each .test throws an error when not called with an array 1`] = ` -"\`.each\` must be called with an Array or Tagged Template String. +"\`.each\` must be called with an Array or Tagged Template Literal. Instead was called with: undefined " `; +exports[`jest-each .test.only throws an error when called with an empty array 1`] = ` +"Error: \`.each\` called with an empty Array of table data. +" +`; + exports[`jest-each .test.only throws an error when not called with an array 1`] = ` -"\`.each\` must be called with an Array or Tagged Template String. +"\`.each\` must be called with an Array or Tagged Template Literal. Instead was called with: undefined " diff --git a/packages/jest-each/src/__tests__/__snapshots__/template.test.js.snap b/packages/jest-each/src/__tests__/__snapshots__/template.test.js.snap index f99bb2662c27..f5097aa6218d 100644 --- a/packages/jest-each/src/__tests__/__snapshots__/template.test.js.snap +++ b/packages/jest-each/src/__tests__/__snapshots__/template.test.js.snap @@ -1,5 +1,10 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`jest-each .describe throws an error when called with an empty string 1`] = ` +"Error: \`.each\` called with an empty Tagged Template Literal of table data. +" +`; + exports[`jest-each .describe throws error when there are fewer arguments than headings over multiple rows 1`] = ` "Not enough arguments supplied for given headings: a | b | expected @@ -29,6 +34,16 @@ Received: Missing 2 arguments" `; +exports[`jest-each .describe throws error when there are no arguments for given headings 1`] = ` +"Error: \`.each\` called with a Tagged Template Literal with no data, remember to interpolate with \${expression} syntax. +" +`; + +exports[`jest-each .describe.only throws an error when called with an empty string 1`] = ` +"Error: \`.each\` called with an empty Tagged Template Literal of table data. +" +`; + exports[`jest-each .describe.only throws error when there are fewer arguments than headings over multiple rows 1`] = ` "Not enough arguments supplied for given headings: a | b | expected @@ -58,6 +73,16 @@ Received: Missing 2 arguments" `; +exports[`jest-each .describe.only throws error when there are no arguments for given headings 1`] = ` +"Error: \`.each\` called with a Tagged Template Literal with no data, remember to interpolate with \${expression} syntax. +" +`; + +exports[`jest-each .fdescribe throws an error when called with an empty string 1`] = ` +"Error: \`.each\` called with an empty Tagged Template Literal of table data. +" +`; + exports[`jest-each .fdescribe throws error when there are fewer arguments than headings over multiple rows 1`] = ` "Not enough arguments supplied for given headings: a | b | expected @@ -87,6 +112,16 @@ Received: Missing 2 arguments" `; +exports[`jest-each .fdescribe throws error when there are no arguments for given headings 1`] = ` +"Error: \`.each\` called with a Tagged Template Literal with no data, remember to interpolate with \${expression} syntax. +" +`; + +exports[`jest-each .fit throws an error when called with an empty string 1`] = ` +"Error: \`.each\` called with an empty Tagged Template Literal of table data. +" +`; + exports[`jest-each .fit throws error when there are fewer arguments than headings over multiple rows 1`] = ` "Not enough arguments supplied for given headings: a | b | expected @@ -116,6 +151,16 @@ Received: Missing 2 arguments" `; +exports[`jest-each .fit throws error when there are no arguments for given headings 1`] = ` +"Error: \`.each\` called with a Tagged Template Literal with no data, remember to interpolate with \${expression} syntax. +" +`; + +exports[`jest-each .it throws an error when called with an empty string 1`] = ` +"Error: \`.each\` called with an empty Tagged Template Literal of table data. +" +`; + exports[`jest-each .it throws error when there are fewer arguments than headings over multiple rows 1`] = ` "Not enough arguments supplied for given headings: a | b | expected @@ -145,6 +190,16 @@ Received: Missing 2 arguments" `; +exports[`jest-each .it throws error when there are no arguments for given headings 1`] = ` +"Error: \`.each\` called with a Tagged Template Literal with no data, remember to interpolate with \${expression} syntax. +" +`; + +exports[`jest-each .it.only throws an error when called with an empty string 1`] = ` +"Error: \`.each\` called with an empty Tagged Template Literal of table data. +" +`; + exports[`jest-each .it.only throws error when there are fewer arguments than headings over multiple rows 1`] = ` "Not enough arguments supplied for given headings: a | b | expected @@ -174,6 +229,16 @@ Received: Missing 2 arguments" `; +exports[`jest-each .it.only throws error when there are no arguments for given headings 1`] = ` +"Error: \`.each\` called with a Tagged Template Literal with no data, remember to interpolate with \${expression} syntax. +" +`; + +exports[`jest-each .test throws an error when called with an empty string 1`] = ` +"Error: \`.each\` called with an empty Tagged Template Literal of table data. +" +`; + exports[`jest-each .test throws error when there are fewer arguments than headings over multiple rows 1`] = ` "Not enough arguments supplied for given headings: a | b | expected @@ -203,6 +268,16 @@ Received: Missing 2 arguments" `; +exports[`jest-each .test throws error when there are no arguments for given headings 1`] = ` +"Error: \`.each\` called with a Tagged Template Literal with no data, remember to interpolate with \${expression} syntax. +" +`; + +exports[`jest-each .test.only throws an error when called with an empty string 1`] = ` +"Error: \`.each\` called with an empty Tagged Template Literal of table data. +" +`; + exports[`jest-each .test.only throws error when there are fewer arguments than headings over multiple rows 1`] = ` "Not enough arguments supplied for given headings: a | b | expected @@ -231,3 +306,8 @@ Received: Missing 2 arguments" `; + +exports[`jest-each .test.only throws error when there are no arguments for given headings 1`] = ` +"Error: \`.each\` called with a Tagged Template Literal with no data, remember to interpolate with \${expression} syntax. +" +`; diff --git a/packages/jest-each/src/__tests__/array.test.js b/packages/jest-each/src/__tests__/array.test.js index 594b25ef0161..95268afa597d 100644 --- a/packages/jest-each/src/__tests__/array.test.js +++ b/packages/jest-each/src/__tests__/array.test.js @@ -60,6 +60,19 @@ describe('jest-each', () => { ).toThrowErrorMatchingSnapshot(); }); + test('throws an error when called with an empty array', () => { + const globalTestMocks = getGlobalTestMocks(); + const eachObject = each.withGlobal(globalTestMocks)([]); + const testFunction = get(eachObject, keyPath); + + testFunction('expected string', noop); + const globalMock = get(globalTestMocks, keyPath); + + expect(() => + globalMock.mock.calls[0][1](), + ).toThrowErrorMatchingSnapshot(); + }); + test('calls global with given title', () => { const globalTestMocks = getGlobalTestMocks(); const eachObject = each.withGlobal(globalTestMocks)([[]]); diff --git a/packages/jest-each/src/__tests__/template.test.js b/packages/jest-each/src/__tests__/template.test.js index 30f368ef54ce..ed5f9664d2eb 100644 --- a/packages/jest-each/src/__tests__/template.test.js +++ b/packages/jest-each/src/__tests__/template.test.js @@ -46,6 +46,23 @@ describe('jest-each', () => { ['describe', 'only'], ].forEach(keyPath => { describe(`.${keyPath.join('.')}`, () => { + test('throws error when there are no arguments for given headings', () => { + const globalTestMocks = getGlobalTestMocks(); + const eachObject = each.withGlobal(globalTestMocks)` + a | b | expected + `; + const testFunction = get(eachObject, keyPath); + const testCallBack = jest.fn(); + testFunction('this will blow up :(', testCallBack); + + const globalMock = get(globalTestMocks, keyPath); + + expect(() => + globalMock.mock.calls[0][1](), + ).toThrowErrorMatchingSnapshot(); + expect(testCallBack).not.toHaveBeenCalled(); + }); + test('throws error when there are fewer arguments than headings when given one row', () => { const globalTestMocks = getGlobalTestMocks(); const eachObject = each.withGlobal(globalTestMocks)` @@ -83,6 +100,21 @@ describe('jest-each', () => { expect(testCallBack).not.toHaveBeenCalled(); }); + test('throws an error when called with an empty string', () => { + const globalTestMocks = getGlobalTestMocks(); + const eachObject = each.withGlobal(globalTestMocks)` `; + const testFunction = get(eachObject, keyPath); + const testCallBack = jest.fn(); + testFunction('this will blow up :(', testCallBack); + + const globalMock = get(globalTestMocks, keyPath); + + expect(() => + globalMock.mock.calls[0][1](), + ).toThrowErrorMatchingSnapshot(); + expect(testCallBack).not.toHaveBeenCalled(); + }); + test('calls global with given title', () => { const globalTestMocks = getGlobalTestMocks(); const eachObject = each.withGlobal(globalTestMocks)` diff --git a/packages/jest-each/src/bind.js b/packages/jest-each/src/bind.js index 9d78fc25dc81..d2a7d1ebd550 100644 --- a/packages/jest-each/src/bind.js +++ b/packages/jest-each/src/bind.js @@ -31,7 +31,7 @@ export default (cb: Function, supportsDone: boolean = true) => (...args: any) => if (!Array.isArray(tableArg)) { const error = new ErrorWithStack( - '`.each` must be called with an Array or Tagged Template String.\n\n' + + '`.each` must be called with an Array or Tagged Template Literal.\n\n' + `Instead was called with: ${pretty(tableArg, { maxDepth: 1, min: true, @@ -42,6 +42,36 @@ export default (cb: Function, supportsDone: boolean = true) => (...args: any) => throw error; }); } + + if (isTaggedTemplateLiteral(tableArg)) { + if (isEmptyString(tableArg[0])) { + const error = new ErrorWithStack( + 'Error: `.each` called with an empty Tagged Template Literal of table data.\n', + eachBind, + ); + return cb(title, () => { + throw error; + }); + } + + const error = new ErrorWithStack( + 'Error: `.each` called with a Tagged Template Literal with no data, remember to interpolate with ${expression} syntax.\n', + eachBind, + ); + return cb(title, () => { + throw error; + }); + } + + if (isEmptyTable(tableArg)) { + const error = new ErrorWithStack( + 'Error: `.each` called with an empty Array of table data.\n', + eachBind, + ); + return cb(title, () => { + throw error; + }); + } const table: Table = tableArg.every(Array.isArray) ? tableArg : tableArg.map(entry => [entry]); @@ -91,6 +121,10 @@ export default (cb: Function, supportsDone: boolean = true) => (...args: any) => ); }; +const isTaggedTemplateLiteral = array => array.raw !== undefined; +const isEmptyTable = table => table.length === 0; +const isEmptyString = str => typeof str === 'string' && str.trim() === ''; + const getPrettyIndexes = placeholders => placeholders.reduce( (indexes, placeholder, index) => diff --git a/packages/jest-environment-jsdom/package.json b/packages/jest-environment-jsdom/package.json index f985f45ab81f..7d0b1df0de4d 100644 --- a/packages/jest-environment-jsdom/package.json +++ b/packages/jest-environment-jsdom/package.json @@ -11,5 +11,8 @@ "jest-mock": "^23.2.0", "jest-util": "^23.4.0", "jsdom": "^11.5.1" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-environment-jsdom/src/__mocks__/index.js b/packages/jest-environment-jsdom/src/__mocks__/index.js index 68f8411ac835..8ae042ba08ed 100644 --- a/packages/jest-environment-jsdom/src/__mocks__/index.js +++ b/packages/jest-environment-jsdom/src/__mocks__/index.js @@ -14,7 +14,6 @@ JSDOMEnvironment.mockImplementation(function(config) { this.global = { JSON, console: {}, - mockClearTimers: jest.fn(), }; const globalValues = Object.assign({}, config.globals); diff --git a/packages/jest-environment-node/package.json b/packages/jest-environment-node/package.json index 956538995feb..c56b96ff082f 100644 --- a/packages/jest-environment-node/package.json +++ b/packages/jest-environment-node/package.json @@ -10,5 +10,8 @@ "dependencies": { "jest-mock": "^23.2.0", "jest-util": "^23.4.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-get-type/package.json b/packages/jest-get-type/package.json index 1ee397bab10d..31bc2a8e0593 100644 --- a/packages/jest-get-type/package.json +++ b/packages/jest-get-type/package.json @@ -6,6 +6,9 @@ "type": "git", "url": "https://github.com/facebook/jest.git" }, + "engines": { + "node": ">= 6" + }, "license": "MIT", "main": "build/index.js" } diff --git a/packages/jest-haste-map/package.json b/packages/jest-haste-map/package.json index b0e5089cf015..0d00cc5b2fb1 100644 --- a/packages/jest-haste-map/package.json +++ b/packages/jest-haste-map/package.json @@ -11,10 +11,12 @@ "fb-watchman": "^2.0.0", "graceful-fs": "^4.1.11", "invariant": "^2.2.4", - "jest-docblock": "^23.2.0", "jest-serializer": "^23.0.1", "jest-worker": "^23.2.0", "micromatch": "^2.3.11", "sane": "^3.0.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-haste-map/src/haste_fs.js b/packages/jest-haste-map/src/HasteFS.js similarity index 87% rename from packages/jest-haste-map/src/haste_fs.js rename to packages/jest-haste-map/src/HasteFS.js index 69df848a5975..03b60a1e946e 100644 --- a/packages/jest-haste-map/src/haste_fs.js +++ b/packages/jest-haste-map/src/HasteFS.js @@ -43,10 +43,14 @@ export default class HasteFS { } getAllFiles(): Array { - return Array.from(this.getFileIterator()); + return Array.from(this.getAbsoluteFileIterator()); } - *getFileIterator(): Iterator { + getFileIterator(): Iterator { + return this._files.keys(); + } + + *getAbsoluteFileIterator(): Iterator { for (const file of this._files.keys()) { yield fastPath.resolve(this._rootDir, file); } @@ -57,7 +61,7 @@ export default class HasteFS { pattern = new RegExp(pattern); } const files = []; - for (const file of this.getFileIterator()) { + for (const file of this.getAbsoluteFileIterator()) { if (pattern.test(file)) { files.push(file); } @@ -67,7 +71,7 @@ export default class HasteFS { matchFilesWithGlob(globs: Array, root: ?Path): Set { const files = new Set(); - for (const file of this.getFileIterator()) { + for (const file of this.getAbsoluteFileIterator()) { const filePath = root ? fastPath.relative(root, file) : file; if (micromatch([filePath], globs).length) { files.add(file); diff --git a/packages/jest-haste-map/src/module_map.js b/packages/jest-haste-map/src/ModuleMap.js similarity index 90% rename from packages/jest-haste-map/src/module_map.js rename to packages/jest-haste-map/src/ModuleMap.js index a305d04c720a..1f5351613ed4 100644 --- a/packages/jest-haste-map/src/module_map.js +++ b/packages/jest-haste-map/src/ModuleMap.js @@ -21,7 +21,8 @@ import type { import * as fastPath from './lib/fast_path'; import H from './constants'; -const EMPTY_MAP = {}; +const EMPTY_OBJ = {}; +const EMPTY_MAP = new Map(); export opaque type SerializableModuleMap = { // There is no easier way to extract the type of the entries of a Map @@ -117,14 +118,14 @@ export default class ModuleMap { platform: ?string, supportsNativePlatform: boolean, ): ?ModuleMetaData { - const map = this._raw.map.get(name) || EMPTY_MAP; + const map = this._raw.map.get(name) || EMPTY_OBJ; const dupMap = this._raw.duplicates.get(name) || EMPTY_MAP; if (platform != null) { this._assertNoDuplicates( name, platform, supportsNativePlatform, - dupMap[platform], + dupMap.get(platform), ); if (map[platform] != null) { return map[platform]; @@ -135,7 +136,7 @@ export default class ModuleMap { name, H.NATIVE_PLATFORM, supportsNativePlatform, - dupMap[H.NATIVE_PLATFORM], + dupMap.get(H.NATIVE_PLATFORM), ); if (map[H.NATIVE_PLATFORM]) { return map[H.NATIVE_PLATFORM]; @@ -145,7 +146,7 @@ export default class ModuleMap { name, H.GENERIC_PLATFORM, supportsNativePlatform, - dupMap[H.GENERIC_PLATFORM], + dupMap.get(H.GENERIC_PLATFORM), ); if (map[H.GENERIC_PLATFORM]) { return map[H.GENERIC_PLATFORM]; @@ -163,17 +164,19 @@ export default class ModuleMap { return; } // Force flow refinement - const previousSet: DuplicatesSet = relativePathSet; - const set = Object.keys(previousSet).reduce((set, relativePath) => { + const previousSet = relativePathSet; + const duplicates = new Map(); + + for (const [relativePath, type] of previousSet) { const duplicatePath = fastPath.resolve(this._raw.rootDir, relativePath); - set[duplicatePath] = previousSet[relativePath]; - return set; - }, Object.create(null)); + duplicates.set(duplicatePath, type); + } + throw new DuplicateHasteCandidatesError( name, platform, supportsNativePlatform, - set, + duplicates, ); } @@ -206,12 +209,12 @@ class DuplicateHasteCandidatesError extends Error { `files, or packages, that provide a module for ` + `that particular name and platform. ${platformMessage} You must ` + `delete or blacklist files until there remains only one of these:\n\n` + - Object.keys(duplicatesSet) + Array.from(duplicatesSet) + .map( + ([dupFilePath, dupFileType]) => + ` * \`${dupFilePath}\` (${getTypeMessage(dupFileType)})\n`, + ) .sort() - .map(dupFilePath => { - const typeMessage = getTypeMessage(duplicatesSet[dupFilePath]); - return ` * \`${dupFilePath}\` (${typeMessage})\n`; - }) .join(''), ); this.hasteName = name; diff --git a/packages/jest-haste-map/src/__tests__/dependencyExtractor.js b/packages/jest-haste-map/src/__tests__/dependencyExtractor.js new file mode 100644 index 000000000000..a1d8e5bbc6db --- /dev/null +++ b/packages/jest-haste-map/src/__tests__/dependencyExtractor.js @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +const blockCommentRe = /\/\*[^]*?\*\//g; +const lineCommentRe = /\/\/.*/g; +const LOAD_MODULE_RE = /(?:^|[^.]\s*)(\bloadModule\s*?\(\s*?)([`'"])([^`'"]+)(\2\s*?\))/g; + +export function extract(code, defaultDependencyExtractor) { + const dependencies = defaultDependencyExtractor(code); + + const addDependency = (match, pre, quot, dep, post) => { + dependencies.add(dep); + return match; + }; + + code + .replace(blockCommentRe, '') + .replace(lineCommentRe, '') + .replace(LOAD_MODULE_RE, addDependency); + + return dependencies; +} + +let cacheKey; + +export function getCacheKey() { + return cacheKey; +} + +export function setCacheKey(key) { + cacheKey = key; +} diff --git a/packages/jest-haste-map/src/__tests__/get_mock_name.test.js b/packages/jest-haste-map/src/__tests__/get_mock_name.test.js index 4009cbab2e2f..939a2364c6bc 100644 --- a/packages/jest-haste-map/src/__tests__/get_mock_name.test.js +++ b/packages/jest-haste-map/src/__tests__/get_mock_name.test.js @@ -8,7 +8,7 @@ 'use strict'; import path from 'path'; -import getMockName from '../get_mock_name'; +import getMockName from '../getMockName'; describe('getMockName', () => { it('extracts mock name from file path', () => { diff --git a/packages/jest-haste-map/src/__tests__/haste_impl.js b/packages/jest-haste-map/src/__tests__/haste_impl.js index 368879354ac2..ce58fef46fee 100644 --- a/packages/jest-haste-map/src/__tests__/haste_impl.js +++ b/packages/jest-haste-map/src/__tests__/haste_impl.js @@ -7,8 +7,13 @@ 'use strict'; const path = require('path'); +let cacheKey; module.exports = { + getCacheKey() { + return cacheKey; + }, + getHasteName(filename) { if ( filename.includes('__mocks__') || @@ -23,4 +28,8 @@ module.exports = { .substr(filename.lastIndexOf(path.sep) + 1) .replace(/(\.(android|ios|native))?\.js$/, ''); }, + + setCacheKey(key) { + cacheKey = key; + }, }; diff --git a/packages/jest-haste-map/src/__tests__/index.test.js b/packages/jest-haste-map/src/__tests__/index.test.js index bb14132a63fb..79383443fff5 100644 --- a/packages/jest-haste-map/src/__tests__/index.test.js +++ b/packages/jest-haste-map/src/__tests__/index.test.js @@ -75,7 +75,7 @@ jest.mock('sane', () => ({ WatchmanWatcher: mockWatcherConstructor, })); -jest.mock('../lib/watchman_watcher.js', () => mockWatcherConstructor); +jest.mock('../lib/WatchmanWatcher.js', () => mockWatcherConstructor); let mockChangedFiles; let mockFs; @@ -108,18 +108,25 @@ const object = data => Object.assign(Object.create(null), data); const createMap = obj => new Map(Object.keys(obj).map(key => [key, obj[key]])); // Jest toEqual does not match Map instances from different contexts -const normalizePersisted = hasteMap => ({ - clocks: normalizeMap(hasteMap.clocks), - duplicates: normalizeMap(hasteMap.duplicates), - files: normalizeMap(hasteMap.files), - map: normalizeMap(hasteMap.map), - mocks: normalizeMap(hasteMap.mocks), -}); -const normalizeMap = map => { - if (Object.prototype.toString.call(map) !== '[object Map]') { - throw new TypeError('expected map instance'); +// This normalizes them for the uses cases in this test +const useBuitinsInContext = value => { + const stringTag = Object.prototype.toString.call(value); + switch (stringTag) { + case '[object Map]': + return new Map( + Array.from(value).map(([k, v]) => [ + useBuitinsInContext(k), + useBuitinsInContext(v), + ]), + ); + case '[object Object]': + return Object.keys(value).reduce((obj, key) => { + obj[key] = useBuitinsInContext(value[key]); + return obj; + }, {}); + default: + return value; } - return new Map(map); }; let consoleWarn; @@ -225,6 +232,31 @@ describe('HasteMap', () => { expect(hasteMap1.getCacheFilePath()).not.toBe(hasteMap2.getCacheFilePath()); }); + it('creates different cache file paths for different dependency extractor cache keys', () => { + jest.resetModuleRegistry(); + const HasteMap = require('../'); + const dependencyExtractor = require('./dependencyExtractor'); + const config = Object.assign({}, defaultConfig, { + dependencyExtractor: require.resolve('./dependencyExtractor'), + }); + dependencyExtractor.setCacheKey('foo'); + const hasteMap1 = new HasteMap(config); + dependencyExtractor.setCacheKey('bar'); + const hasteMap2 = new HasteMap(config); + expect(hasteMap1.getCacheFilePath()).not.toBe(hasteMap2.getCacheFilePath()); + }); + + it('creates different cache file paths for different hasteImplModulePath cache keys', () => { + jest.resetModuleRegistry(); + const HasteMap = require('../'); + const hasteImpl = require('./haste_impl'); + hasteImpl.setCacheKey('foo'); + const hasteMap1 = new HasteMap(defaultConfig); + hasteImpl.setCacheKey('bar'); + const hasteMap2 = new HasteMap(defaultConfig); + expect(hasteMap1.getCacheFilePath()).not.toBe(hasteMap2.getCacheFilePath()); + }); + it('creates different cache file paths for different projects', () => { jest.resetModuleRegistry(); const HasteMap = require('../'); @@ -359,7 +391,7 @@ describe('HasteMap', () => { // The cache file must exactly mirror the data structure returned from a // build - expect(normalizePersisted(hasteMap.read())).toEqual(data); + expect(useBuitinsInContext(hasteMap.read())).toEqual(data); }); }); @@ -433,7 +465,7 @@ describe('HasteMap', () => { }), ); - expect(normalizePersisted(hasteMap.read())).toEqual(data); + expect(useBuitinsInContext(hasteMap.read())).toEqual(data); }); }); }); @@ -523,6 +555,18 @@ describe('HasteMap', () => { }); }); + it('warns on duplicate module ids only once', async () => { + mockFs['/project/fruits/other/Strawberry.js'] = ` + const Banana = require("Banana"); + `; + + await new HasteMap(defaultConfig).build(); + expect(console.warn).toHaveBeenCalledTimes(1); + + await new HasteMap(defaultConfig).build(); + expect(console.warn).toHaveBeenCalledTimes(1); + }); + it('throws on duplicate module ids if "throwOnModuleCollision" is set to true', () => { // Raspberry thinks it is a Strawberry mockFs['/project/fruits/another/Strawberry.js'] = ` @@ -615,9 +659,9 @@ describe('HasteMap', () => { } else { expect(fs.readFileSync).toBeCalledWith(cacheFilePath, 'utf8'); } - expect(normalizeMap(data.clocks)).toEqual(mockClocks); - expect(normalizeMap(data.files)).toEqual(initialData.files); - expect(normalizeMap(data.map)).toEqual(initialData.map); + expect(useBuitinsInContext(data.clocks)).toEqual(mockClocks); + expect(useBuitinsInContext(data.files)).toEqual(initialData.files); + expect(useBuitinsInContext(data.map)).toEqual(initialData.map); }); })); @@ -655,15 +699,15 @@ describe('HasteMap', () => { 'utf8', ); - expect(normalizeMap(data.clocks)).toEqual(mockClocks); + expect(useBuitinsInContext(data.clocks)).toEqual(mockClocks); const files = new Map(initialData.files); files.set('fruits/Banana.js', ['Banana', 32, 1, ['Kiwi'], null]); - expect(normalizeMap(data.files)).toEqual(files); + expect(useBuitinsInContext(data.files)).toEqual(files); const map = new Map(initialData.map); - expect(normalizeMap(data.map)).toEqual(map); + expect(useBuitinsInContext(data.map)).toEqual(map); }); })); @@ -690,11 +734,11 @@ describe('HasteMap', () => { .then(({__hasteMapForTest: data}) => { const files = new Map(initialData.files); files.delete('fruits/Banana.js'); - expect(normalizeMap(data.files)).toEqual(files); + expect(useBuitinsInContext(data.files)).toEqual(files); const map = new Map(initialData.map); map.delete('Banana'); - expect(normalizeMap(data.map)).toEqual(map); + expect(useBuitinsInContext(data.map)).toEqual(map); }); })); @@ -781,11 +825,14 @@ describe('HasteMap', () => { const {__hasteMapForTest: data} = await new HasteMap( defaultConfig, ).build(); - expect(normalizeMap(data.duplicates)).toEqual( + expect(useBuitinsInContext(data.duplicates)).toEqual( createMap({ - Strawberry: { - g: {'fruits/Strawberry.js': 0, 'fruits/another/Strawberry.js': 0}, - }, + Strawberry: createMap({ + g: createMap({ + 'fruits/Strawberry.js': H.MODULE, + 'fruits/another/Strawberry.js': H.MODULE, + }), + }), }), ); expect(data.map.get('Strawberry')).toEqual({}); @@ -804,12 +851,56 @@ describe('HasteMap', () => { const {__hasteMapForTest: data} = await new HasteMap( defaultConfig, ).build(); - expect(normalizeMap(data.duplicates)).toEqual(new Map()); + expect(useBuitinsInContext(data.duplicates)).toEqual(new Map()); expect(data.map.get('Strawberry')).toEqual({ - g: ['fruits/Strawberry.js', 0], + g: ['fruits/Strawberry.js', H.MODULE], }); // Make sure the other files are not affected. - expect(data.map.get('Banana')).toEqual({g: ['fruits/Banana.js', 0]}); + expect(data.map.get('Banana')).toEqual({ + g: ['fruits/Banana.js', H.MODULE], + }); + }); + + it('recovers with the correct type when a duplicate file is deleted', async () => { + mockFs['/project/fruits/strawberryPackage/package.json'] = ` + {"name": "Strawberry"} + `; + + const {__hasteMapForTest: data} = await new HasteMap( + defaultConfig, + ).build(); + + expect(useBuitinsInContext(data.duplicates)).toEqual( + createMap({ + Strawberry: createMap({ + g: createMap({ + 'fruits/Strawberry.js': H.MODULE, + 'fruits/another/Strawberry.js': H.MODULE, + 'fruits/strawberryPackage/package.json': H.PACKAGE, + }), + }), + }), + ); + + delete mockFs['/project/fruits/another/Strawberry.js']; + delete mockFs['/project/fruits/strawberryPackage/package.json']; + + mockChangedFiles = object({ + '/project/fruits/another/Strawberry.js': null, + '/project/fruits/strawberryPackage/package.json': null, + }); + mockClocks = createMap({ + fruits: 'c:fake-clock:4', + }); + + const {__hasteMapForTest: correctData} = await new HasteMap( + defaultConfig, + ).build(); + + expect(useBuitinsInContext(correctData.duplicates)).toEqual(new Map()); + expect(correctData.map.get('Strawberry')).toEqual({ + g: ['fruits/Strawberry.js', H.MODULE], + }); }); it('recovers when a duplicate module is renamed', async () => { @@ -827,15 +918,17 @@ describe('HasteMap', () => { const {__hasteMapForTest: data} = await new HasteMap( defaultConfig, ).build(); - expect(normalizeMap(data.duplicates)).toEqual(new Map()); + expect(useBuitinsInContext(data.duplicates)).toEqual(new Map()); expect(data.map.get('Strawberry')).toEqual({ - g: ['fruits/Strawberry.js', 0], + g: ['fruits/Strawberry.js', H.MODULE], }); expect(data.map.get('Pineapple')).toEqual({ - g: ['fruits/another/Pineapple.js', 0], + g: ['fruits/another/Pineapple.js', H.MODULE], }); // Make sure the other files are not affected. - expect(data.map.get('Banana')).toEqual({g: ['fruits/Banana.js', 0]}); + expect(data.map.get('Banana')).toEqual({ + g: ['fruits/Banana.js', H.MODULE], + }); }); }); @@ -885,8 +978,11 @@ describe('HasteMap', () => { it('distributes work across workers', () => { const jestWorker = require('jest-worker'); + const path = require('path'); + const dependencyExtractor = path.join(__dirname, 'dependencyExtractor.js'); return new HasteMap( Object.assign({}, defaultConfig, { + dependencyExtractor, hasteImplModulePath: undefined, maxWorkers: 4, }), @@ -902,6 +998,7 @@ describe('HasteMap', () => { { computeDependencies: true, computeSha1: false, + dependencyExtractor, filePath: '/project/fruits/Banana.js', hasteImplModulePath: undefined, rootDir: '/project', @@ -911,6 +1008,7 @@ describe('HasteMap', () => { { computeDependencies: true, computeSha1: false, + dependencyExtractor, filePath: '/project/fruits/Pear.js', hasteImplModulePath: undefined, rootDir: '/project', @@ -920,6 +1018,7 @@ describe('HasteMap', () => { { computeDependencies: true, computeSha1: false, + dependencyExtractor, filePath: '/project/fruits/Strawberry.js', hasteImplModulePath: undefined, rootDir: '/project', @@ -929,6 +1028,7 @@ describe('HasteMap', () => { { computeDependencies: true, computeSha1: false, + dependencyExtractor, filePath: '/project/fruits/__mocks__/Pear.js', hasteImplModulePath: undefined, rootDir: '/project', @@ -938,6 +1038,7 @@ describe('HasteMap', () => { { computeDependencies: true, computeSha1: false, + dependencyExtractor, filePath: '/project/vegetables/Melon.js', hasteImplModulePath: undefined, rootDir: '/project', @@ -1230,15 +1331,17 @@ describe('HasteMap', () => { } catch (error) { const { DuplicateHasteCandidatesError, - } = require('../module_map').default; + } = require('../ModuleMap').default; expect(error).toBeInstanceOf(DuplicateHasteCandidatesError); expect(error.hasteName).toBe('Pear'); expect(error.platform).toBe('g'); expect(error.supportsNativePlatform).toBe(false); - expect(error.duplicatesSet).toEqual({ - '/project/fruits/Pear.js': 0, - '/project/fruits/another/Pear.js': 0, - }); + expect(error.duplicatesSet).toEqual( + createMap({ + '/project/fruits/Pear.js': H.MODULE, + '/project/fruits/another/Pear.js': H.MODULE, + }), + ); expect(error.message).toMatchSnapshot(); } } diff --git a/packages/jest-haste-map/src/__tests__/worker.test.js b/packages/jest-haste-map/src/__tests__/worker.test.js index 426502dd3d92..3c044a3668c3 100644 --- a/packages/jest-haste-map/src/__tests__/worker.test.js +++ b/packages/jest-haste-map/src/__tests__/worker.test.js @@ -32,6 +32,7 @@ describe('worker', () => { '/project/fruits/Pear.js': ` const Banana = require("Banana"); const Strawberry = require('Strawberry'); + const Lime = loadModule('Lime'); `, '/project/fruits/Strawberry.js': ` // Strawberry! @@ -95,6 +96,19 @@ describe('worker', () => { }); }); + it('accepts a custom dependency extractor', async () => { + expect( + await worker({ + computeDependencies: true, + dependencyExtractor: path.join(__dirname, 'dependencyExtractor.js'), + filePath: '/project/fruits/Pear.js', + rootDir, + }), + ).toEqual({ + dependencies: ['Banana', 'Strawberry', 'Lime'], + }); + }); + it('delegates to hasteImplModulePath for getting the id', async () => { expect( await worker({ @@ -179,7 +193,7 @@ describe('worker', () => { filePath: '/project/fruits/Pear.js', rootDir, }), - ).toEqual({sha1: '0cb0930919e068f146da84d9a0ad0182e4bdb673'}); + ).toEqual({sha1: 'c7a7a68a1c8aaf452669dd2ca52ac4a434d25552'}); await expect( getSha1({computeSha1: true, filePath: '/i/dont/exist.js', rootDir}), diff --git a/packages/jest-haste-map/src/crawlers/__tests__/watchman.test.js b/packages/jest-haste-map/src/crawlers/__tests__/watchman.test.js index 7f80e7dadf7e..b67b849fe840 100644 --- a/packages/jest-haste-map/src/crawlers/__tests__/watchman.test.js +++ b/packages/jest-haste-map/src/crawlers/__tests__/watchman.test.js @@ -11,7 +11,7 @@ const path = require('path'); jest.mock('fb-watchman', () => { - const normalizePathSep = require('../../lib/normalize_path_sep').default; + const normalizePathSep = require('../../lib/normalizePathSep').default; const Client = jest.fn(); Client.prototype.command = jest.fn((args, callback) => setImmediate(() => { diff --git a/packages/jest-haste-map/src/crawlers/watchman.js b/packages/jest-haste-map/src/crawlers/watchman.js index d8fb38c5cf2f..2b8b903899dd 100644 --- a/packages/jest-haste-map/src/crawlers/watchman.js +++ b/packages/jest-haste-map/src/crawlers/watchman.js @@ -11,7 +11,7 @@ import type {InternalHasteMap} from 'types/HasteMap'; import type {CrawlerOptions} from '../types'; import * as fastPath from '../lib/fast_path'; -import normalizePathSep from '../lib/normalize_path_sep'; +import normalizePathSep from '../lib/normalizePathSep'; import path from 'path'; import watchman from 'fb-watchman'; import H from '../constants'; @@ -44,10 +44,8 @@ module.exports = async function watchmanCrawl( const cmd = (...args) => new Promise((resolve, reject) => - client.command( - args, - (error, result) => - error ? reject(WatchmanError(error)) : resolve(result), + client.command(args, (error, result) => + error ? reject(WatchmanError(error)) : resolve(result), ), ); diff --git a/packages/jest-haste-map/src/get_mock_name.js b/packages/jest-haste-map/src/getMockName.js similarity index 100% rename from packages/jest-haste-map/src/get_mock_name.js rename to packages/jest-haste-map/src/getMockName.js diff --git a/packages/jest-haste-map/src/index.js b/packages/jest-haste-map/src/index.js index 12b56a3c4505..50fe4eee43ee 100644 --- a/packages/jest-haste-map/src/index.js +++ b/packages/jest-haste-map/src/index.js @@ -13,22 +13,22 @@ import {getSha1, worker} from './worker'; import crypto from 'crypto'; import EventEmitter from 'events'; import fs from 'fs'; -import getMockName from './get_mock_name'; -import getPlatformExtension from './lib/get_platform_extension'; +import getMockName from './getMockName'; +import getPlatformExtension from './lib/getPlatformExtension'; import H from './constants'; -import HasteFS from './haste_fs'; -import HasteModuleMap from './module_map'; +import HasteFS from './HasteFS'; +import HasteModuleMap from './ModuleMap'; import invariant from 'invariant'; // eslint-disable-next-line import/default import nodeCrawl from './crawlers/node'; -import normalizePathSep from './lib/normalize_path_sep'; +import normalizePathSep from './lib/normalizePathSep'; import os from 'os'; import path from 'path'; import sane from 'sane'; import serializer from 'jest-serializer'; // eslint-disable-next-line import/default import watchmanCrawl from './crawlers/watchman'; -import WatchmanWatcher from './lib/watchman_watcher'; +import WatchmanWatcher from './lib/WatchmanWatcher'; import * as fastPath from './lib/fast_path'; import Worker from 'jest-worker'; @@ -43,7 +43,7 @@ import type { HasteRegExp, MockData, } from 'types/HasteMap'; -import type {SerializableModuleMap as HasteSerializableModuleMap} from './module_map'; +import type {SerializableModuleMap as HasteSerializableModuleMap} from './ModuleMap'; type HType = typeof H; @@ -52,6 +52,7 @@ type Options = { computeDependencies?: boolean, computeSha1?: boolean, console?: Console, + dependencyExtractor?: string, extensions: Array, forceNodeFilesystemAPI?: boolean, hasteImplModulePath?: string, @@ -75,6 +76,7 @@ type InternalOptions = { cacheDirectory: string, computeDependencies: boolean, computeSha1: boolean, + dependencyExtractor?: string, extensions: Array, forceNodeFilesystemAPI: boolean, hasteImplModulePath?: string, @@ -233,6 +235,7 @@ class HasteMap extends EventEmitter { ? true : options.computeDependencies, computeSha1: options.computeSha1 || false, + dependencyExtractor: options.dependencyExtractor, extensions: options.extensions, forceNodeFilesystemAPI: !!options.forceNodeFilesystemAPI, hasteImplModulePath: options.hasteImplModulePath, @@ -258,10 +261,30 @@ class HasteMap extends EventEmitter { 'deprecated. Provide a RegExp instead. See https://github.com/facebook/jest/pull/4063.', ); } + const rootDirHash = crypto .createHash('md5') .update(options.rootDir) .digest('hex'); + let hasteImplHash = ''; + let dependencyExtractorHash = ''; + + if (options.hasteImplModulePath) { + // $FlowFixMe: dynamic require + const hasteImpl = require(options.hasteImplModulePath); + if (hasteImpl.getCacheKey) { + hasteImplHash = String(hasteImpl.getCacheKey()); + } + } + + if (options.dependencyExtractor) { + // $FlowFixMe: dynamic require + const dependencyExtractor = require(options.dependencyExtractor); + if (dependencyExtractor.getCacheKey) { + dependencyExtractorHash = String(dependencyExtractor.getCacheKey()); + } + } + this._cachePath = HasteMap.getCacheFilePath( this._options.cacheDirectory, `haste-map-${this._options.name}-${rootDirHash}`, @@ -275,6 +298,8 @@ class HasteMap extends EventEmitter { this._options.computeSha1.toString(), options.mocksPattern || '', (options.ignorePattern || '').toString(), + hasteImplHash, + dependencyExtractorHash, ); this._whitelist = getWhiteList(options.providesModuleNodeModules); this._buildPromise = null; @@ -372,10 +397,9 @@ class HasteMap extends EventEmitter { cachedFiles.push({moduleName, path: relativeFilePath}); } return this._crawl(cachedHasteMap).then(hasteMap => { - const deprecatedFiles = cachedFiles.filter(file => { - const fileData = hasteMap.files.get(file.path); - return fileData == null || file.moduleName !== fileData[H.ID]; - }); + const deprecatedFiles = cachedFiles.filter( + file => !hasteMap.files.has(file.path), + ); return {deprecatedFiles, hasteMap}; }); }); @@ -427,20 +451,24 @@ class HasteMap extends EventEmitter { } let dupsByPlatform = hasteMap.duplicates.get(id); if (dupsByPlatform == null) { - dupsByPlatform = Object.create(null); + dupsByPlatform = new Map(); hasteMap.duplicates.set(id, dupsByPlatform); } - const dups = (dupsByPlatform[platform] = Object.create(null)); - dups[module[H.PATH]] = module[H.TYPE]; - dups[existingModule[H.PATH]] = existingModule[H.TYPE]; + + const dups = new Map([ + [module[H.PATH], module[H.TYPE]], + [existingModule[H.PATH], existingModule[H.TYPE]], + ]); + dupsByPlatform.set(platform, dups); + return; } const dupsByPlatform = hasteMap.duplicates.get(id); if (dupsByPlatform != null) { - const dups = dupsByPlatform[platform]; + const dups = dupsByPlatform.get(platform); if (dups != null) { - dups[module[H.PATH]] = module[H.TYPE]; + dups.set(module[H.PATH], module[H.TYPE]); } return; } @@ -504,6 +532,7 @@ class HasteMap extends EventEmitter { .getSha1({ computeDependencies: this._options.computeDependencies, computeSha1, + dependencyExtractor: this._options.dependencyExtractor, filePath, hasteImplModulePath: this._options.hasteImplModulePath, rootDir, @@ -567,6 +596,7 @@ class HasteMap extends EventEmitter { .worker({ computeDependencies: this._options.computeDependencies, computeSha1, + dependencyExtractor: this._options.dependencyExtractor, filePath, hasteImplModulePath: this._options.hasteImplModulePath, rootDir, @@ -725,8 +755,8 @@ class HasteMap extends EventEmitter { canUseWatchman && this._options.useWatchman ? WatchmanWatcher : os.platform() === 'darwin' - ? sane.FSEventsWatcher - : sane.NodeWatcher; + ? sane.FSEventsWatcher + : sane.NodeWatcher; const extensions = this._options.extensions; const ignorePattern = this._options.ignorePattern; const rootDir = this._options.rootDir; @@ -923,31 +953,37 @@ class HasteMap extends EventEmitter { const platform = getPlatformExtension(relativeFilePath, this._options.platforms) || H.GENERIC_PLATFORM; - let dups = dupsByPlatform[platform]; + let dups = dupsByPlatform.get(platform); if (dups == null) { return; } - dupsByPlatform = copy(dupsByPlatform); + dupsByPlatform = copyMap(dupsByPlatform); hasteMap.duplicates.set(moduleName, dupsByPlatform); - dups = copy(dups); - dupsByPlatform[platform] = dups; - const dedupType = dups[relativeFilePath]; - delete dups[relativeFilePath]; - const filePaths = Object.keys(dups); - if (filePaths.length > 1) { + dups = copyMap(dups); + dupsByPlatform.set(platform, dups); + dups.delete(relativeFilePath); + + if (dups.size !== 1) { + return; + } + + const uniqueModule = dups.entries().next().value; + + if (!uniqueModule) { return; } let dedupMap = hasteMap.map.get(moduleName); + if (dedupMap == null) { dedupMap = Object.create(null); hasteMap.map.set(moduleName, dedupMap); } - dedupMap[platform] = [filePaths[0], dedupType]; - delete dupsByPlatform[platform]; - if (Object.keys(dupsByPlatform).length === 0) { + dedupMap[platform] = uniqueModule; + dupsByPlatform.delete(platform); + if (dupsByPlatform.size === 0) { hasteMap.duplicates.delete(moduleName); } } @@ -1021,6 +1057,10 @@ class HasteMap extends EventEmitter { const copy = object => Object.assign(Object.create(null), object); +function copyMap(input: Map): Map { + return new Map(input); +} + HasteMap.H = H; HasteMap.ModuleMap = HasteModuleMap; diff --git a/packages/jest-haste-map/src/lib/watchman_watcher.js b/packages/jest-haste-map/src/lib/WatchmanWatcher.js similarity index 100% rename from packages/jest-haste-map/src/lib/watchman_watcher.js rename to packages/jest-haste-map/src/lib/WatchmanWatcher.js diff --git a/packages/jest-haste-map/src/lib/__tests__/dependencyExtractor.test.js b/packages/jest-haste-map/src/lib/__tests__/dependencyExtractor.test.js new file mode 100644 index 000000000000..7d10e43b2ea7 --- /dev/null +++ b/packages/jest-haste-map/src/lib/__tests__/dependencyExtractor.test.js @@ -0,0 +1,267 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import {extract} from '../dependencyExtractor'; +import isRegExpSupported from '../isRegExpSupported'; + +const COMMENT_NO_NEG_LB = isRegExpSupported('(? { + it('should not extract dependencies inside comments', () => { + const code = ` + // import a from 'ignore-line-comment'; + // require('ignore-line-comment'); + /* + * import a from 'ignore-block-comment'; + * require('ignore-block-comment'); + */ + `; + expect(extract(code)).toEqual(new Set()); + }); + + it('should not extract dependencies inside comments (windows line endings)', () => { + const code = [ + '// const module1 = require("module1");', + '/**', + ' * const module2 = require("module2");', + ' */', + ].join('\r\n'); + + expect(extract(code)).toEqual(new Set([])); + }); + + it('should not extract dependencies inside comments (unicode line endings)', () => { + const code = [ + '// const module1 = require("module1");\u2028', + '// const module1 = require("module2");\u2029', + '/*\u2028', + 'const module2 = require("module3");\u2029', + ' */', + ].join(''); + + expect(extract(code)).toEqual(new Set([])); + }); + + it('should extract dependencies from `import` statements', () => { + const code = ` + // Good + import * as depNS from 'dep1'; + import { + a as aliased_a, + b, + } from 'dep2'; + import depDefault from 'dep3'; + import * as depNS, { + a as aliased_a, + b, + }, depDefault from 'dep4'; + + // Bad + ${COMMENT_NO_NEG_LB} foo . import ('inv1'); + ${COMMENT_NO_NEG_LB} foo . export ('inv2'); + `; + expect(extract(code)).toEqual(new Set(['dep1', 'dep2', 'dep3', 'dep4'])); + }); + + it('should not extract dependencies from `import type/typeof` statements', () => { + const code = ` + // Bad + import typeof {foo} from 'inv1'; + import type {foo} from 'inv2'; + `; + expect(extract(code)).toEqual(new Set([])); + }); + + it('should extract dependencies from `export` statements', () => { + const code = ` + // Good + export * as depNS from 'dep1'; + export { + a as aliased_a, + b, + } from 'dep2'; + export depDefault from 'dep3'; + export * as depNS, { + a as aliased_a, + b, + }, depDefault from 'dep4'; + + // Bad + ${COMMENT_NO_NEG_LB} foo . export ('inv1'); + ${COMMENT_NO_NEG_LB} foo . export ('inv2'); + `; + expect(extract(code)).toEqual(new Set(['dep1', 'dep2', 'dep3', 'dep4'])); + }); + + it('should extract dependencies from `export-from` statements', () => { + const code = ` + // Good + export * as depNS from 'dep1'; + export { + a as aliased_a, + b, + } from 'dep2'; + export depDefault from 'dep3'; + export * as depNS, { + a as aliased_a, + b, + }, depDefault from 'dep4'; + + // Bad + ${COMMENT_NO_NEG_LB} foo . export ('inv1'); + ${COMMENT_NO_NEG_LB} foo . export ('inv2'); + `; + expect(extract(code)).toEqual(new Set(['dep1', 'dep2', 'dep3', 'dep4'])); + }); + + it('should not extract dependencies from `export type/typeof` statements', () => { + const code = ` + // Bad + export typeof {foo} from 'inv1'; + export type {foo} from 'inv2'; + `; + expect(extract(code)).toEqual(new Set([])); + }); + + it('should extract dependencies from dynamic `import` calls', () => { + const code = ` + // Good + import('dep1').then(); + const dep2 = await import( + "dep2", + ); + if (await import(\`dep3\`)) {} + + // Bad + ${COMMENT_NO_NEG_LB} await foo . import('inv1') + await ximport('inv2'); + importx('inv3'); + import('inv4', 'inv5'); + `; + expect(extract(code)).toEqual(new Set(['dep1', 'dep2', 'dep3'])); + }); + + it('should extract dependencies from `require` calls', () => { + const code = ` + // Good + require('dep1'); + const dep2 = require( + "dep2", + ); + if (require(\`dep3\`).cond) {} + + // Bad + ${COMMENT_NO_NEG_LB} foo . require('inv1') + xrequire('inv2'); + requirex('inv3'); + require('inv4', 'inv5'); + `; + expect(extract(code)).toEqual(new Set(['dep1', 'dep2', 'dep3'])); + }); + + it('should extract dependencies from `require.requireActual` calls', () => { + const code = ` + // Good + require.requireActual('dep1'); + const dep2 = require.requireActual( + "dep2", + ); + if (require.requireActual(\`dep3\`).cond) {} + require + .requireActual('dep4'); + + // Bad + ${COMMENT_NO_NEG_LB} foo . require.requireActual('inv1') + xrequire.requireActual('inv2'); + require.requireActualx('inv3'); + require.requireActual('inv4', 'inv5'); + `; + expect(extract(code)).toEqual(new Set(['dep1', 'dep2', 'dep3', 'dep4'])); + }); + + it('should extract dependencies from `require.requireMock` calls', () => { + const code = ` + // Good + require.requireMock('dep1'); + const dep2 = require.requireMock( + "dep2", + ); + if (require.requireMock(\`dep3\`).cond) {} + require + .requireMock('dep4'); + + // Bad + ${COMMENT_NO_NEG_LB} foo . require.requireMock('inv1') + xrequire.requireMock('inv2'); + require.requireMockx('inv3'); + require.requireMock('inv4', 'inv5'); + `; + expect(extract(code)).toEqual(new Set(['dep1', 'dep2', 'dep3', 'dep4'])); + }); + + it('should extract dependencies from `jest.requireActual` calls', () => { + const code = ` + // Good + jest.requireActual('dep1'); + const dep2 = jest.requireActual( + "dep2", + ); + if (jest.requireActual(\`dep3\`).cond) {} + require + .requireActual('dep4'); + + // Bad + ${COMMENT_NO_NEG_LB} foo . jest.requireActual('inv1') + xjest.requireActual('inv2'); + jest.requireActualx('inv3'); + jest.requireActual('inv4', 'inv5'); + `; + expect(extract(code)).toEqual(new Set(['dep1', 'dep2', 'dep3', 'dep4'])); + }); + + it('should extract dependencies from `jest.requireMock` calls', () => { + const code = ` + // Good + jest.requireMock('dep1'); + const dep2 = jest.requireMock( + "dep2", + ); + if (jest.requireMock(\`dep3\`).cond) {} + require + .requireMock('dep4'); + + // Bad + ${COMMENT_NO_NEG_LB} foo . jest.requireMock('inv1') + xjest.requireMock('inv2'); + jest.requireMockx('inv3'); + jest.requireMock('inv4', 'inv5'); + `; + expect(extract(code)).toEqual(new Set(['dep1', 'dep2', 'dep3', 'dep4'])); + }); + + it('should extract dependencies from `jest.genMockFromModule` calls', () => { + const code = ` + // Good + jest.genMockFromModule('dep1'); + const dep2 = jest.genMockFromModule( + "dep2", + ); + if (jest.genMockFromModule(\`dep3\`).cond) {} + require + .requireMock('dep4'); + + // Bad + ${COMMENT_NO_NEG_LB} foo . jest.genMockFromModule('inv1') + xjest.genMockFromModule('inv2'); + jest.genMockFromModulex('inv3'); + jest.genMockFromModule('inv4', 'inv5'); + `; + expect(extract(code)).toEqual(new Set(['dep1', 'dep2', 'dep3', 'dep4'])); + }); +}); diff --git a/packages/jest-haste-map/src/lib/__tests__/extract_requires.test.js b/packages/jest-haste-map/src/lib/__tests__/extract_requires.test.js deleted file mode 100644 index b612edcf8cdf..000000000000 --- a/packages/jest-haste-map/src/lib/__tests__/extract_requires.test.js +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ -'use strict'; - -import extractRequires from '../extract_requires'; - -it('extracts both requires and imports from code', () => { - const code = ` - import module1 from 'module1'; - const module2 = require('module2'); - import('module3').then(module3 => {})'; - `; - - expect(extractRequires(code)).toEqual(['module1', 'module2', 'module3']); -}); - -it('extracts requires in order', () => { - const code = ` - const module1 = require('module1'); - const module2 = require('module2'); - const module3 = require('module3'); - `; - - expect(extractRequires(code)).toEqual(['module1', 'module2', 'module3']); -}); - -it('strips out comments from code', () => { - const code = `// comment const module2 = require('module2');`; - - expect(extractRequires(code)).toEqual([]); -}); - -it('ignores requires in comments', () => { - const code = [ - '// const module1 = require("module1");', - '/**', - ' * const module2 = require("module2");', - ' */', - ].join('\n'); - - expect(extractRequires(code)).toEqual([]); -}); - -it('ignores requires in comments with Windows line endings', () => { - const code = [ - '// const module1 = require("module1");', - '/**', - ' * const module2 = require("module2");', - ' */', - ].join('\r\n'); - - expect(extractRequires(code)).toEqual([]); -}); - -it('ignores requires in comments with unicode line endings', () => { - const code = [ - '// const module1 = require("module1");\u2028', - '// const module1 = require("module2");\u2029', - '/*\u2028', - 'const module2 = require("module3");\u2029', - ' */', - ].join(''); - - expect(extractRequires(code)).toEqual([]); -}); - -it('does not contain duplicates', () => { - const code = ` - const module1 = require('module1'); - const module1Dup = require('module1'); - `; - - expect(extractRequires(code)).toEqual(['module1']); -}); - -it('ignores type imports', () => { - const code = [ - "import type foo from 'bar';", - 'import type {', - ' bar,', - ' baz,', - "} from 'wham'", - ].join('\r\n'); - - expect(extractRequires(code)).toEqual([]); -}); - -it('ignores type exports', () => { - const code = [ - 'export type Foo = number;', - 'export default {}', - "export * from 'module1'", - ].join('\r\n'); - - expect(extractRequires(code)).toEqual(['module1']); -}); - -it('understands require.requireActual', () => { - const code = `require.requireActual('pizza');`; - expect(extractRequires(code)).toEqual(['pizza']); -}); - -it('understands jest.requireActual', () => { - const code = `jest.requireActual('whiskey');`; - expect(extractRequires(code)).toEqual(['whiskey']); -}); - -it('understands require.requireMock', () => { - const code = `require.requireMock('cheeseburger');`; - expect(extractRequires(code)).toEqual(['cheeseburger']); -}); - -it('understands jest.requireMock', () => { - const code = `jest.requireMock('scotch');`; - expect(extractRequires(code)).toEqual(['scotch']); -}); diff --git a/packages/jest-haste-map/src/lib/__tests__/fast_path.test.js b/packages/jest-haste-map/src/lib/__tests__/fast_path.test.js index d6872fec82de..5eaae66edb0d 100644 --- a/packages/jest-haste-map/src/lib/__tests__/fast_path.test.js +++ b/packages/jest-haste-map/src/lib/__tests__/fast_path.test.js @@ -26,6 +26,13 @@ describe('fastPath.relative', () => { const relativeFilename = path.join('..', 'baz', 'foobar'); expect(relative(root, filename)).toBe(relativeFilename); }); + + it('should get relative paths outside the root when start with same word', () => { + const root = path.join(__dirname, 'foo', 'bar'); + const filename = path.join(__dirname, 'foo', 'barbaz', 'foobar'); + const relativeFilename = path.join('..', 'barbaz', 'foobar'); + expect(relative(root, filename)).toBe(relativeFilename); + }); }); describe('fastPath.resolve', () => { diff --git a/packages/jest-haste-map/src/lib/__tests__/get_platform_extension.test.js b/packages/jest-haste-map/src/lib/__tests__/getPlatformExtension.test.js similarity index 92% rename from packages/jest-haste-map/src/lib/__tests__/get_platform_extension.test.js rename to packages/jest-haste-map/src/lib/__tests__/getPlatformExtension.test.js index df2c4fdc2db2..3c5da8702c24 100644 --- a/packages/jest-haste-map/src/lib/__tests__/get_platform_extension.test.js +++ b/packages/jest-haste-map/src/lib/__tests__/getPlatformExtension.test.js @@ -9,7 +9,7 @@ 'use strict'; -import getPlatformExtension from '../get_platform_extension'; +import getPlatformExtension from '../getPlatformExtension'; describe('getPlatformExtension', () => { it('should get platform ext', () => { diff --git a/packages/jest-haste-map/src/lib/__tests__/isRegExpSupported.test.js b/packages/jest-haste-map/src/lib/__tests__/isRegExpSupported.test.js new file mode 100644 index 000000000000..5408244073c9 --- /dev/null +++ b/packages/jest-haste-map/src/lib/__tests__/isRegExpSupported.test.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import isRegExpSupported from '../isRegExpSupported'; + +describe('isRegExpSupported', () => { + it('should return true when passing valid regular expression', () => { + expect(isRegExpSupported('(?:foo|bar)')).toBe(true); + }); + + it('should return false when passing an invalid regular expression', () => { + expect(isRegExpSupported('(?_foo|bar)')).toBe(false); + }); +}); diff --git a/packages/jest-haste-map/src/lib/__tests__/normalize_path_sep.test.js b/packages/jest-haste-map/src/lib/__tests__/normalizePathSep.test.js similarity index 82% rename from packages/jest-haste-map/src/lib/__tests__/normalize_path_sep.test.js rename to packages/jest-haste-map/src/lib/__tests__/normalizePathSep.test.js index c6ed861b54a7..ab466fe6a62a 100644 --- a/packages/jest-haste-map/src/lib/__tests__/normalize_path_sep.test.js +++ b/packages/jest-haste-map/src/lib/__tests__/normalizePathSep.test.js @@ -13,14 +13,14 @@ describe('normalizePathSep', () => { it('does nothing on posix', () => { jest.resetModules(); jest.mock('path', () => jest.requireActual('path').posix); - const normalizePathSep = require('../normalize_path_sep').default; + const normalizePathSep = require('../normalizePathSep').default; expect(normalizePathSep('foo/bar/baz.js')).toEqual('foo/bar/baz.js'); }); it('replace slashes on windows', () => { jest.resetModules(); jest.mock('path', () => jest.requireActual('path').win32); - const normalizePathSep = require('../normalize_path_sep').default; + const normalizePathSep = require('../normalizePathSep').default; expect(normalizePathSep('foo/bar/baz.js')).toEqual('foo\\bar\\baz.js'); }); }); diff --git a/packages/jest-haste-map/src/lib/dependencyExtractor.js b/packages/jest-haste-map/src/lib/dependencyExtractor.js new file mode 100644 index 000000000000..038e6084c2ce --- /dev/null +++ b/packages/jest-haste-map/src/lib/dependencyExtractor.js @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import isRegExpSupported from './isRegExpSupported'; + +// Negative look behind is only supported in Node 9+ +const NOT_A_DOT = isRegExpSupported('(? `([\`'"])([^'"\`]*?)(?:\\${pos})`; +const WORD_SEPARATOR = '\\b'; +const LEFT_PARENTHESIS = '\\('; +const RIGHT_PARENTHESIS = '\\)'; +const WHITESPACE = '\\s*'; +const OPTIONAL_COMMA = '(:?,\\s*)?'; + +function createRegExp(parts, flags) { + return new RegExp(parts.join(''), flags); +} + +function alternatives(...parts) { + return `(?:${parts.join('|')})`; +} + +function functionCallStart(...names) { + return [ + NOT_A_DOT, + WORD_SEPARATOR, + alternatives(...names), + WHITESPACE, + LEFT_PARENTHESIS, + WHITESPACE, + ]; +} + +const BLOCK_COMMENT_RE = /\/\*[^]*?\*\//g; +const LINE_COMMENT_RE = /\/\/.*/g; + +const REQUIRE_OR_DYNAMIC_IMPORT_RE = createRegExp( + [ + ...functionCallStart('require', 'import'), + CAPTURE_STRING_LITERAL(1), + WHITESPACE, + OPTIONAL_COMMA, + RIGHT_PARENTHESIS, + ], + 'g', +); + +const IMPORT_OR_EXPORT_RE = createRegExp( + [ + '\\b(?:import|export)\\s+(?!type(?:of)?\\s+)[^\'"]+\\s+from\\s+', + CAPTURE_STRING_LITERAL(1), + ], + 'g', +); + +const JEST_EXTENSIONS_RE = createRegExp( + [ + ...functionCallStart( + 'require\\s*\\.\\s*(?:requireActual|requireMock)', + 'jest\\s*\\.\\s*(?:requireActual|requireMock|genMockFromModule)', + ), + CAPTURE_STRING_LITERAL(1), + WHITESPACE, + OPTIONAL_COMMA, + RIGHT_PARENTHESIS, + ], + 'g', +); + +export function extract(code: string): Set { + const dependencies = new Set(); + + const addDependency = (match: string, q: string, dep: string) => { + dependencies.add(dep); + return match; + }; + + code + .replace(BLOCK_COMMENT_RE, '') + .replace(LINE_COMMENT_RE, '') + .replace(IMPORT_OR_EXPORT_RE, addDependency) + .replace(REQUIRE_OR_DYNAMIC_IMPORT_RE, addDependency) + .replace(JEST_EXTENSIONS_RE, addDependency); + + return dependencies; +} diff --git a/packages/jest-haste-map/src/lib/extract_requires.js b/packages/jest-haste-map/src/lib/extract_requires.js deleted file mode 100644 index 7457f44b6236..000000000000 --- a/packages/jest-haste-map/src/lib/extract_requires.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -const blockCommentRe = /\/\*[^]*?\*\//g; -const lineCommentRe = /\/\/.*/g; - -const replacePatterns = { - DYNAMIC_IMPORT_RE: /(?:^|[^.]\s*)(\bimport\s*?\(\s*?)([`'"])([^`'"]+)(\2\s*?\))/g, - EXPORT_RE: /(\bexport\s+(?!type )(?:[^'"]+\s+from\s+)??)(['"])([^'"]+)(\2)/g, - IMPORT_RE: /(\bimport\s+(?!type )(?:[^'"]+\s+from\s+)??)(['"])([^'"]+)(\2)/g, - REQUIRE_EXTENSIONS_PATTERN: /(?:^|[^.]\s*)(\b(?:require\s*?\.\s*?(?:requireActual|requireMock)|jest\s*?\.\s*?(?:requireActual|requireMock|genMockFromModule))\s*?\(\s*?)([`'"])([^`'"]+)(\2\s*?\))/g, - REQUIRE_RE: /(?:^|[^.]\s*)(\brequire\s*?\(\s*?)([`'"])([^`'"]+)(\2\s*?\))/g, -}; - -export default function extractRequires(code: string): Array { - const dependencies = new Set(); - const addDependency = (match, pre, quot, dep, post) => { - dependencies.add(dep); - return match; - }; - - code - .replace(blockCommentRe, '') - .replace(lineCommentRe, '') - .replace(replacePatterns.EXPORT_RE, addDependency) - .replace(replacePatterns.IMPORT_RE, addDependency) - .replace(replacePatterns.REQUIRE_EXTENSIONS_PATTERN, addDependency) - .replace(replacePatterns.REQUIRE_RE, addDependency) - .replace(replacePatterns.DYNAMIC_IMPORT_RE, addDependency); - - return Array.from(dependencies); -} diff --git a/packages/jest-haste-map/src/lib/fast_path.js b/packages/jest-haste-map/src/lib/fast_path.js index f161ae48bf9e..1f05b4ffb0cc 100644 --- a/packages/jest-haste-map/src/lib/fast_path.js +++ b/packages/jest-haste-map/src/lib/fast_path.js @@ -11,7 +11,7 @@ import path from 'path'; // rootDir and filename must be absolute paths (resolved) export function relative(rootDir: string, filename: string): string { - return filename.indexOf(rootDir) === 0 + return filename.indexOf(rootDir + path.sep) === 0 ? filename.substr(rootDir.length + 1) : path.relative(rootDir, filename); } diff --git a/packages/jest-haste-map/src/lib/get_platform_extension.js b/packages/jest-haste-map/src/lib/getPlatformExtension.js similarity index 100% rename from packages/jest-haste-map/src/lib/get_platform_extension.js rename to packages/jest-haste-map/src/lib/getPlatformExtension.js diff --git a/packages/jest-haste-map/src/lib/isRegExpSupported.js b/packages/jest-haste-map/src/lib/isRegExpSupported.js new file mode 100644 index 000000000000..d20cb1a6e21e --- /dev/null +++ b/packages/jest-haste-map/src/lib/isRegExpSupported.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +export default function isRegExpSupported(value: string): boolean { + try { + // eslint-disable-next-line no-new + new RegExp(value); + return true; + } catch (e) { + return false; + } +} diff --git a/packages/jest-haste-map/src/lib/normalize_path_sep.js b/packages/jest-haste-map/src/lib/normalizePathSep.js similarity index 100% rename from packages/jest-haste-map/src/lib/normalize_path_sep.js rename to packages/jest-haste-map/src/lib/normalizePathSep.js diff --git a/packages/jest-haste-map/src/types.js b/packages/jest-haste-map/src/types.js index caba081b9a9a..8ee12a279fb5 100644 --- a/packages/jest-haste-map/src/types.js +++ b/packages/jest-haste-map/src/types.js @@ -15,6 +15,7 @@ export type Mapper = (item: string) => ?Array; export type WorkerMessage = { computeDependencies: boolean, computeSha1: boolean, + dependencyExtractor?: string, rootDir: string, filePath: string, hasteImplModulePath?: string, diff --git a/packages/jest-haste-map/src/worker.js b/packages/jest-haste-map/src/worker.js index 53bc98be296d..51388240e377 100644 --- a/packages/jest-haste-map/src/worker.js +++ b/packages/jest-haste-map/src/worker.js @@ -14,7 +14,7 @@ import path from 'path'; import fs from 'graceful-fs'; import blacklist from './blacklist'; import H from './constants'; -import extractRequires from './lib/extract_requires'; +import * as dependencyExtractor from './lib/dependencyExtractor'; const PACKAGE_JSON = path.sep + 'package.json'; @@ -77,7 +77,16 @@ export async function worker(data: WorkerMessage): Promise { } if (computeDependencies) { - dependencies = extractRequires(getContent()); + const content = getContent(); + dependencies = Array.from( + data.dependencyExtractor + ? // $FlowFixMe + require(data.dependencyExtractor).extract( + content, + dependencyExtractor.extract, + ) + : dependencyExtractor.extract(content), + ); } if (id) { diff --git a/packages/jest-jasmine2/package.json b/packages/jest-jasmine2/package.json index 2d995dfd068c..b22a9a9dec34 100644 --- a/packages/jest-jasmine2/package.json +++ b/packages/jest-jasmine2/package.json @@ -23,5 +23,8 @@ }, "devDependencies": { "jest-runtime": "^23.6.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-jasmine2/src/expectation_failed.js b/packages/jest-jasmine2/src/ExpectationFailed.js similarity index 100% rename from packages/jest-jasmine2/src/expectation_failed.js rename to packages/jest-jasmine2/src/ExpectationFailed.js diff --git a/packages/jest-jasmine2/src/p_cancelable.js b/packages/jest-jasmine2/src/PCancelable.js similarity index 100% rename from packages/jest-jasmine2/src/p_cancelable.js rename to packages/jest-jasmine2/src/PCancelable.js diff --git a/packages/jest-jasmine2/src/__tests__/__snapshots__/expectation_result_factory.test.js.snap b/packages/jest-jasmine2/src/__tests__/__snapshots__/expectationResultFactory.test.js.snap similarity index 100% rename from packages/jest-jasmine2/src/__tests__/__snapshots__/expectation_result_factory.test.js.snap rename to packages/jest-jasmine2/src/__tests__/__snapshots__/expectationResultFactory.test.js.snap diff --git a/packages/jest-jasmine2/src/__tests__/expectation_result_factory.test.js b/packages/jest-jasmine2/src/__tests__/expectationResultFactory.test.js similarity index 96% rename from packages/jest-jasmine2/src/__tests__/expectation_result_factory.test.js rename to packages/jest-jasmine2/src/__tests__/expectationResultFactory.test.js index d036f36c0f06..06bb8c151935 100644 --- a/packages/jest-jasmine2/src/__tests__/expectation_result_factory.test.js +++ b/packages/jest-jasmine2/src/__tests__/expectationResultFactory.test.js @@ -8,7 +8,7 @@ 'use strict'; -import expectationResultFactory from '../expectation_result_factory'; +import expectationResultFactory from '../expectationResultFactory'; describe('expectationResultFactory', () => { it('returns the result if passed.', () => { diff --git a/packages/jest-jasmine2/src/__tests__/hooks_error.test.js b/packages/jest-jasmine2/src/__tests__/hooksError.test.js similarity index 100% rename from packages/jest-jasmine2/src/__tests__/hooks_error.test.js rename to packages/jest-jasmine2/src/__tests__/hooksError.test.js diff --git a/packages/jest-jasmine2/src/__tests__/it_test_error.test.js b/packages/jest-jasmine2/src/__tests__/itTestError.test.js similarity index 100% rename from packages/jest-jasmine2/src/__tests__/it_test_error.test.js rename to packages/jest-jasmine2/src/__tests__/itTestError.test.js diff --git a/packages/jest-jasmine2/src/__tests__/it_to_test_alias.test.js b/packages/jest-jasmine2/src/__tests__/itToTestAlias.test.js similarity index 100% rename from packages/jest-jasmine2/src/__tests__/it_to_test_alias.test.js rename to packages/jest-jasmine2/src/__tests__/itToTestAlias.test.js diff --git a/packages/jest-jasmine2/src/__tests__/p_timeout.test.js b/packages/jest-jasmine2/src/__tests__/pTimeout.test.js similarity index 97% rename from packages/jest-jasmine2/src/__tests__/p_timeout.test.js rename to packages/jest-jasmine2/src/__tests__/pTimeout.test.js index d0bccd1d237a..4daa94b78865 100644 --- a/packages/jest-jasmine2/src/__tests__/p_timeout.test.js +++ b/packages/jest-jasmine2/src/__tests__/pTimeout.test.js @@ -10,7 +10,7 @@ jest.useFakeTimers(); -import pTimeout from '../p_timeout'; +import pTimeout from '../pTimeout'; describe('pTimeout', () => { it('calls `clearTimeout` and resolves when `promise` resolves.', async () => { diff --git a/packages/jest-jasmine2/src/__tests__/queue_runner.test.js b/packages/jest-jasmine2/src/__tests__/queueRunner.test.js similarity index 98% rename from packages/jest-jasmine2/src/__tests__/queue_runner.test.js rename to packages/jest-jasmine2/src/__tests__/queueRunner.test.js index 516604a0156a..80c2b15ab202 100644 --- a/packages/jest-jasmine2/src/__tests__/queue_runner.test.js +++ b/packages/jest-jasmine2/src/__tests__/queueRunner.test.js @@ -8,7 +8,7 @@ 'use strict'; -import queueRunner from '../queue_runner'; +import queueRunner from '../queueRunner'; describe('queueRunner', () => { it('runs every function in the queue.', async () => { diff --git a/packages/jest-jasmine2/src/__tests__/todo_error.test.js b/packages/jest-jasmine2/src/__tests__/todoError.test.js similarity index 100% rename from packages/jest-jasmine2/src/__tests__/todo_error.test.js rename to packages/jest-jasmine2/src/__tests__/todoError.test.js diff --git a/packages/jest-jasmine2/src/assert_support.js b/packages/jest-jasmine2/src/assertionErrorMessage.js similarity index 100% rename from packages/jest-jasmine2/src/assert_support.js rename to packages/jest-jasmine2/src/assertionErrorMessage.js diff --git a/packages/jest-jasmine2/src/error_on_private.js b/packages/jest-jasmine2/src/errorOnPrivate.js similarity index 100% rename from packages/jest-jasmine2/src/error_on_private.js rename to packages/jest-jasmine2/src/errorOnPrivate.js diff --git a/packages/jest-jasmine2/src/expectation_result_factory.js b/packages/jest-jasmine2/src/expectationResultFactory.js similarity index 100% rename from packages/jest-jasmine2/src/expectation_result_factory.js rename to packages/jest-jasmine2/src/expectationResultFactory.js diff --git a/packages/jest-jasmine2/src/index.js b/packages/jest-jasmine2/src/index.js index b6fef61428b1..4ec456f130a5 100644 --- a/packages/jest-jasmine2/src/index.js +++ b/packages/jest-jasmine2/src/index.js @@ -16,12 +16,12 @@ import type Runtime from 'jest-runtime'; import path from 'path'; import installEach from './each'; -import {installErrorOnPrivate} from './error_on_private'; +import {installErrorOnPrivate} from './errorOnPrivate'; import {getCallsite} from 'jest-util'; import JasmineReporter from './reporter'; -import {install as jasmineAsyncInstall} from './jasmine_async'; +import jasmineAsyncInstall from './jasmineAsyncInstall'; -const JASMINE = require.resolve('./jasmine/jasmine_light.js'); +const JASMINE = require.resolve('./jasmine/jasmineLight.js'); async function jasmine2( globalConfig: GlobalConfig, @@ -117,7 +117,7 @@ async function jasmine2( env.addReporter(reporter); runtime - .requireInternalModule(path.resolve(__dirname, './jest_expect.js')) + .requireInternalModule(path.resolve(__dirname, './jestExpect.js')) .default({ expand: globalConfig.expand, }); diff --git a/packages/jest-jasmine2/src/is_error.js b/packages/jest-jasmine2/src/isError.js similarity index 100% rename from packages/jest-jasmine2/src/is_error.js rename to packages/jest-jasmine2/src/isError.js diff --git a/packages/jest-jasmine2/src/jasmine/call_tracker.js b/packages/jest-jasmine2/src/jasmine/CallTracker.js similarity index 100% rename from packages/jest-jasmine2/src/jasmine/call_tracker.js rename to packages/jest-jasmine2/src/jasmine/CallTracker.js diff --git a/packages/jest-jasmine2/src/jasmine/Env.js b/packages/jest-jasmine2/src/jasmine/Env.js index 21d27d753c23..c6556fec8a61 100644 --- a/packages/jest-jasmine2/src/jasmine/Env.js +++ b/packages/jest-jasmine2/src/jasmine/Env.js @@ -31,10 +31,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* eslint-disable sort-keys */ import {AssertionError} from 'assert'; -import queueRunner from '../queue_runner'; -import treeProcessor from '../tree_processor'; -import checkIsError from '../is_error'; -import assertionErrorMessage from '../assert_support'; +import queueRunner from '../queueRunner'; +import treeProcessor from '../treeProcessor'; +import isError from '../isError'; +import assertionErrorMessage from '../assertionErrorMessage'; import {ErrorWithStack} from 'jest-util'; export default function(j$) { @@ -573,16 +573,16 @@ export default function(j$) { }; this.fail = function(error) { - let isError; + let checkIsError; let message; if (error instanceof AssertionError) { - isError = false; + checkIsError = false; message = assertionErrorMessage(error, {expand: j$.Spec.expand}); } else { - const check = checkIsError(error); + const check = isError(error); - isError = check.isError; + checkIsError = check.isError; message = check.message; } @@ -592,7 +592,7 @@ export default function(j$) { expected: '', actual: '', message, - error: isError ? error : new Error(message), + error: checkIsError ? error : new Error(message), }); }; } diff --git a/packages/jest-jasmine2/src/jasmine/js_api_reporter.js b/packages/jest-jasmine2/src/jasmine/JsApiReporter.js similarity index 100% rename from packages/jest-jasmine2/src/jasmine/js_api_reporter.js rename to packages/jest-jasmine2/src/jasmine/JsApiReporter.js diff --git a/packages/jest-jasmine2/src/jasmine/report_dispatcher.js b/packages/jest-jasmine2/src/jasmine/ReportDispatcher.js similarity index 100% rename from packages/jest-jasmine2/src/jasmine/report_dispatcher.js rename to packages/jest-jasmine2/src/jasmine/ReportDispatcher.js diff --git a/packages/jest-jasmine2/src/jasmine/Spec.js b/packages/jest-jasmine2/src/jasmine/Spec.js index c7ab00dfe70e..9f3bc12945ce 100644 --- a/packages/jest-jasmine2/src/jasmine/Spec.js +++ b/packages/jest-jasmine2/src/jasmine/Spec.js @@ -33,11 +33,11 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import {AssertionError} from 'assert'; -import ExpectationFailed from '../expectation_failed'; +import ExpectationFailed from '../ExpectationFailed'; -import expectationResultFactory from '../expectation_result_factory'; +import expectationResultFactory from '../expectationResultFactory'; -import assertionErrorMessage from '../assert_support'; +import assertionErrorMessage from '../assertionErrorMessage'; export default function Spec(attrs: Object) { this.resultCallback = attrs.resultCallback || function() {}; diff --git a/packages/jest-jasmine2/src/jasmine/spy_strategy.js b/packages/jest-jasmine2/src/jasmine/SpyStrategy.js similarity index 100% rename from packages/jest-jasmine2/src/jasmine/spy_strategy.js rename to packages/jest-jasmine2/src/jasmine/SpyStrategy.js diff --git a/packages/jest-jasmine2/src/jasmine/Suite.js b/packages/jest-jasmine2/src/jasmine/Suite.js index 307840c2dec6..90bd2a7cf21c 100644 --- a/packages/jest-jasmine2/src/jasmine/Suite.js +++ b/packages/jest-jasmine2/src/jasmine/Suite.js @@ -32,8 +32,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* eslint-disable sort-keys */ import {convertDescriptorToString} from 'jest-util'; -import ExpectationFailed from '../expectation_failed'; -import expectationResultFactory from '../expectation_result_factory'; +import ExpectationFailed from '../ExpectationFailed'; +import expectationResultFactory from '../expectationResultFactory'; export default function Suite(attrs: Object) { this.id = attrs.id; diff --git a/packages/jest-jasmine2/src/jasmine/create_spy.js b/packages/jest-jasmine2/src/jasmine/createSpy.js similarity index 96% rename from packages/jest-jasmine2/src/jasmine/create_spy.js rename to packages/jest-jasmine2/src/jasmine/createSpy.js index 7cd6325c3ead..ba05e617f86e 100644 --- a/packages/jest-jasmine2/src/jasmine/create_spy.js +++ b/packages/jest-jasmine2/src/jasmine/createSpy.js @@ -30,9 +30,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* eslint-disable sort-keys */ -import CallTracker from './call_tracker'; +import CallTracker from './CallTracker'; -import SpyStrategy from './spy_strategy'; +import SpyStrategy from './SpyStrategy'; function createSpy(name, originalFn) { const spyStrategy = new SpyStrategy({ diff --git a/packages/jest-jasmine2/src/jasmine/jasmine_light.js b/packages/jest-jasmine2/src/jasmine/jasmineLight.js similarity index 95% rename from packages/jest-jasmine2/src/jasmine/jasmine_light.js rename to packages/jest-jasmine2/src/jasmine/jasmineLight.js index f79918edcdeb..0d1b748bb3a3 100644 --- a/packages/jest-jasmine2/src/jasmine/jasmine_light.js +++ b/packages/jest-jasmine2/src/jasmine/jasmineLight.js @@ -33,12 +33,12 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import type {Jasmine} from 'types/Jasmine'; -import createSpy from './create_spy'; +import createSpy from './createSpy'; import Env from './Env'; -import JsApiReporter from './js_api_reporter'; -import ReportDispatcher from './report_dispatcher'; +import JsApiReporter from './JsApiReporter'; +import ReportDispatcher from './ReportDispatcher'; import Spec from './Spec'; -import SpyRegistry from './spy_registry'; +import SpyRegistry from './spyRegistry'; import Suite from './Suite'; import Timer from './Timer'; diff --git a/packages/jest-jasmine2/src/jasmine/spy_registry.js b/packages/jest-jasmine2/src/jasmine/spyRegistry.js similarity index 97% rename from packages/jest-jasmine2/src/jasmine/spy_registry.js rename to packages/jest-jasmine2/src/jasmine/spyRegistry.js index 4a619eaba36f..b5775a24f1db 100644 --- a/packages/jest-jasmine2/src/jasmine/spy_registry.js +++ b/packages/jest-jasmine2/src/jasmine/spyRegistry.js @@ -30,10 +30,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* @flow */ -import CallTracker from './call_tracker'; +import CallTracker from './CallTracker'; -import createSpy from './create_spy'; -import SpyStrategy from './spy_strategy'; +import createSpy from './createSpy'; +import SpyStrategy from './SpyStrategy'; const formatErrorMsg = (domain: string, usage?: string) => { const usageDefinition = usage ? '\nUsage: ' + usage : ''; diff --git a/packages/jest-jasmine2/src/jasmine_async.js b/packages/jest-jasmine2/src/jasmineAsyncInstall.js similarity index 93% rename from packages/jest-jasmine2/src/jasmine_async.js rename to packages/jest-jasmine2/src/jasmineAsyncInstall.js index a45e9ac21c15..90f5556d77ca 100644 --- a/packages/jest-jasmine2/src/jasmine_async.js +++ b/packages/jest-jasmine2/src/jasmineAsyncInstall.js @@ -16,7 +16,7 @@ import type {Global} from 'types/Global'; import isGeneratorFn from 'is-generator-fn'; import co from 'co'; -import checkIsError from './is_error'; +import isError from './isError'; function isPromise(obj) { return obj && typeof obj.then === 'function'; @@ -51,12 +51,12 @@ function promisifyLifeCycleFunction(originalFn, env) { if (isPromise(returnValue)) { returnValue.then(done.bind(null, null), error => { - const {isError, message} = checkIsError(error); + const {isError: checkIsError, message} = isError(error); if (message) { extraError.message = message; } - done.fail(isError ? error : extraError); + done.fail(checkIsError ? error : extraError); }); } else { done(); @@ -97,7 +97,7 @@ function promisifyIt(originalFn, env, jasmine) { if (isPromise(returnValue)) { returnValue.then(done.bind(null, null), error => { - const {isError, message} = checkIsError(error); + const {isError: checkIsError, message} = isError(error); if (message) { extraError.message = message; @@ -107,7 +107,7 @@ function promisifyIt(originalFn, env, jasmine) { env.pending(message); done(); } else { - done.fail(isError ? error : extraError); + done.fail(checkIsError ? error : extraError); } }); } else if (returnValue === undefined) { @@ -148,7 +148,7 @@ function makeConcurrent(originalFn: Function, env) { }; } -export function install(global: Global) { +export default function jasmineAsyncInstall(global: Global) { const jasmine = global.jasmine; const env = jasmine.getEnv(); diff --git a/packages/jest-jasmine2/src/jest_expect.js b/packages/jest-jasmine2/src/jestExpect.js similarity index 100% rename from packages/jest-jasmine2/src/jest_expect.js rename to packages/jest-jasmine2/src/jestExpect.js diff --git a/packages/jest-jasmine2/src/p_timeout.js b/packages/jest-jasmine2/src/pTimeout.js similarity index 100% rename from packages/jest-jasmine2/src/p_timeout.js rename to packages/jest-jasmine2/src/pTimeout.js diff --git a/packages/jest-jasmine2/src/queue_runner.js b/packages/jest-jasmine2/src/queueRunner.js similarity index 96% rename from packages/jest-jasmine2/src/queue_runner.js rename to packages/jest-jasmine2/src/queueRunner.js index 280a1ff73edd..de1aa47a7c95 100644 --- a/packages/jest-jasmine2/src/queue_runner.js +++ b/packages/jest-jasmine2/src/queueRunner.js @@ -7,8 +7,8 @@ * @flow */ -import PCancelable from './p_cancelable'; -import pTimeout from './p_timeout'; +import PCancelable from './PCancelable'; +import pTimeout from './pTimeout'; type Options = { clearTimeout: (timeoutID: number) => void, diff --git a/packages/jest-jasmine2/src/tree_processor.js b/packages/jest-jasmine2/src/treeProcessor.js similarity index 100% rename from packages/jest-jasmine2/src/tree_processor.js rename to packages/jest-jasmine2/src/treeProcessor.js diff --git a/packages/jest-leak-detector/package.json b/packages/jest-leak-detector/package.json index d3c68e6ea4c5..d4d03fa78829 100644 --- a/packages/jest-leak-detector/package.json +++ b/packages/jest-leak-detector/package.json @@ -12,5 +12,8 @@ }, "devDependencies": { "weak": "^1.0.1" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-matcher-utils/package.json b/packages/jest-matcher-utils/package.json index ffd725d22d1c..baf7017cbd44 100644 --- a/packages/jest-matcher-utils/package.json +++ b/packages/jest-matcher-utils/package.json @@ -6,6 +6,9 @@ "type": "git", "url": "https://github.com/facebook/jest.git" }, + "engines": { + "node": ">= 6" + }, "license": "MIT", "main": "build/index.js", "dependencies": { diff --git a/packages/jest-message-util/package.json b/packages/jest-message-util/package.json index d8e34d972d0f..9812ad2ed322 100644 --- a/packages/jest-message-util/package.json +++ b/packages/jest-message-util/package.json @@ -5,6 +5,9 @@ "type": "git", "url": "https://github.com/facebook/jest.git" }, + "engines": { + "node": ">= 6" + }, "license": "MIT", "main": "build/index.js", "dependencies": { 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 a39921bd1894..4cf7e417890e 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 @@ -22,6 +22,22 @@ exports[`formatStackTrace should strip node internals 1`] = ` " `; +exports[`retains message in babel code frame error 1`] = ` +" Babel test + + + packages/react/src/forwardRef.js: Unexpected token, expected , (20:10) + + 18 | false, + 19 | 'forwardRef requires a render function but received a \`memo\` ' + > 20 | 'component. Instead of forwardRef(memo(...)), use ' + + | ^ + 21 | 'memo(forwardRef(...)).', + 22 | ); + 23 | } else if (typeof render !== 'function') { +" +`; + exports[`should exclude jasmine from stack trace for Unix paths. 1`] = ` " Unix test diff --git a/packages/jest-message-util/src/__tests__/messages.test.js b/packages/jest-message-util/src/__tests__/messages.test.js index 6f8c9964e0c6..0a2078ca299c 100644 --- a/packages/jest-message-util/src/__tests__/messages.test.js +++ b/packages/jest-message-util/src/__tests__/messages.test.js @@ -58,6 +58,19 @@ const vendorStack = at Object.asyncFn (__tests__/vendor/sulu/node_modules/sulu-content-bundle/best_component.js:1:5) `; +const babelStack = + ' ' + + ` + packages/react/src/forwardRef.js: Unexpected token, expected , (20:10) + \u001b[0m \u001b[90m 18 | \u001b[39m \u001b[36mfalse\u001b[39m\u001b[33m,\u001b[39m + \u001b[90m 19 | \u001b[39m \u001b[32m'forwardRef requires a render function but received a \`memo\` '\u001b[39m + \u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 20 | \u001b[39m \u001b[32m'component. Instead of forwardRef(memo(...)), use '\u001b[39m \u001b[33m+\u001b[39m + \u001b[90m | \u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m + \u001b[90m 21 | \u001b[39m \u001b[32m'memo(forwardRef(...)).'\u001b[39m\u001b[33m,\u001b[39m + \u001b[90m 22 | \u001b[39m )\u001b[33m;\u001b[39m + \u001b[90m 23 | \u001b[39m } \u001b[36melse\u001b[39m \u001b[36mif\u001b[39m (\u001b[36mtypeof\u001b[39m render \u001b[33m!==\u001b[39m \u001b[32m'function'\u001b[39m) {\u001b[0m +`; + it('should exclude jasmine from stack trace for Unix paths.', () => { const messages = formatResultsErrors( [ @@ -134,3 +147,23 @@ it('should not exclude vendor from stack trace', () => { expect(messages).toMatchSnapshot(); }); + +it('retains message in babel code frame error', () => { + const messages = formatResultsErrors( + [ + { + ancestorTitles: [], + failureMessages: [babelStack], + title: 'Babel test', + }, + ], + { + rootDir: '', + }, + { + noStackTrace: false, + }, + ); + + expect(messages).toMatchSnapshot(); +}); diff --git a/packages/jest-message-util/src/index.js b/packages/jest-message-util/src/index.js index 1f8f4d5f62ba..c09af5c3f84f 100644 --- a/packages/jest-message-util/src/index.js +++ b/packages/jest-message-util/src/index.js @@ -62,7 +62,6 @@ const TITLE_BULLET = chalk.bold('\u25cf '); const STACK_TRACE_COLOR = chalk.dim; const STACK_PATH_REGEXP = /\s*at.*\(?(\:\d*\:\d*|native)\)?/; const EXEC_ERROR_MESSAGE = 'Test suite failed to run'; -const ERROR_TEXT = 'Error: '; const NOT_EMPTY_LINE_REGEXP = /^(?!$)/gm; const indentAllLines = (lines: string, indent: string) => @@ -335,13 +334,18 @@ export const separateMessageFromStack = (content: string) => { return {message: '', stack: ''}; } - const messageMatch = content.match(/(^(.|\n)*?(?=\n\s*at\s.*\:\d*\:\d*))/); - let message = messageMatch ? messageMatch[0] : 'Error'; - const stack = messageMatch ? content.slice(message.length) : content; - // If the error is a plain error instead of a SyntaxError or TypeError - // we remove it from the message because it is generally not useful. - if (message.startsWith(ERROR_TEXT)) { - message = message.substr(ERROR_TEXT.length); + // All lines up to what looks like a stack -- or if nothing looks like a stack + // (maybe it's a code frame instead), just the first non-empty line. + // If the error is a plain "Error:" instead of a SyntaxError or TypeError we + // remove the prefix from the message because it is generally not useful. + const messageMatch = content.match( + /^(?:Error: )?([\s\S]*?(?=\n\s*at\s.*\:\d*\:\d*)|\s*.*)([\s\S]*)$/, + ); + if (!messageMatch) { + // For flow + throw new Error('If you hit this error, the regex above is buggy.'); } + const message = messageMatch[1]; + const stack = messageMatch[2]; return {message, stack}; }; diff --git a/packages/jest-mock/package.json b/packages/jest-mock/package.json index e6826a9e65ea..7d7f2958a197 100644 --- a/packages/jest-mock/package.json +++ b/packages/jest-mock/package.json @@ -5,6 +5,9 @@ "type": "git", "url": "https://github.com/facebook/jest.git" }, + "engines": { + "node": ">= 6" + }, "license": "MIT", "main": "build/index.js", "browser": "build-es5/index.js" diff --git a/packages/jest-mock/src/__tests__/jest_mock.test.js b/packages/jest-mock/src/__tests__/jest_mock.test.js index 5b440f0728d5..1e678adb12d9 100644 --- a/packages/jest-mock/src/__tests__/jest_mock.test.js +++ b/packages/jest-mock/src/__tests__/jest_mock.test.js @@ -579,11 +579,11 @@ describe('moduleMocker', () => { expect(fn.mock.results).toEqual([ { - isThrow: false, + type: 'return', value: 2, }, { - isThrow: false, + type: 'return', value: 4, }, ]); @@ -598,11 +598,11 @@ describe('moduleMocker', () => { expect(fn.mock.results).toEqual([ { - isThrow: false, + type: 'return', value: 'MOCKED!', }, { - isThrow: false, + type: 'return', value: 4, }, ]); @@ -618,11 +618,11 @@ describe('moduleMocker', () => { expect(fn.mock.results).toEqual([ { - isThrow: false, + type: 'return', value: 2, }, { - isThrow: false, + type: 'return', value: 4, }, ]); @@ -662,15 +662,15 @@ describe('moduleMocker', () => { // Results are tracked expect(fn.mock.results).toEqual([ { - isThrow: false, + type: 'return', value: 8, }, { - isThrow: true, + type: 'throw', value: error, }, { - isThrow: false, + type: 'return', value: 18, }, ]); @@ -693,12 +693,130 @@ describe('moduleMocker', () => { // Results are tracked expect(fn.mock.results).toEqual([ { - isThrow: true, + type: 'throw', value: undefined, }, ]); }); + it('results of recursive calls are tracked properly', () => { + // sums up all integers from 0 -> value, using recursion + const fn = moduleMocker.fn(value => { + if (value === 0) { + return 0; + } else { + return value + fn(value - 1); + } + }); + + fn(4); + + // All call args tracked + expect(fn.mock.calls).toEqual([[4], [3], [2], [1], [0]]); + // Results are tracked + // (in correct order of calls, rather than order of returns) + expect(fn.mock.results).toEqual([ + { + type: 'return', + value: 10, + }, + { + type: 'return', + value: 6, + }, + { + type: 'return', + value: 3, + }, + { + type: 'return', + value: 1, + }, + { + type: 'return', + value: 0, + }, + ]); + }); + + it('test results of recursive calls from within the recursive call', () => { + // sums up all integers from 0 -> value, using recursion + const fn = moduleMocker.fn(value => { + if (value === 0) { + return 0; + } else { + const recursiveResult = fn(value - 1); + + if (value === 3) { + // All recursive calls have been made at this point. + expect(fn.mock.calls).toEqual([[4], [3], [2], [1], [0]]); + // But only the last 3 calls have returned at this point. + expect(fn.mock.results).toEqual([ + { + type: 'incomplete', + value: undefined, + }, + { + type: 'incomplete', + value: undefined, + }, + { + type: 'return', + value: 3, + }, + { + type: 'return', + value: 1, + }, + { + type: 'return', + value: 0, + }, + ]); + } + + return value + recursiveResult; + } + }); + + fn(4); + }); + + it('call mockClear inside recursive mock', () => { + // sums up all integers from 0 -> value, using recursion + const fn = moduleMocker.fn(value => { + if (value === 3) { + fn.mockClear(); + } + + if (value === 0) { + return 0; + } else { + return value + fn(value - 1); + } + }); + + fn(3); + + // All call args (after the call that cleared the mock) are tracked + expect(fn.mock.calls).toEqual([[2], [1], [0]]); + // Results (after the call that cleared the mock) are tracked + expect(fn.mock.results).toEqual([ + { + type: 'return', + value: 3, + }, + { + type: 'return', + value: 1, + }, + { + type: 'return', + value: 0, + }, + ]); + }); + describe('invocationCallOrder', () => { it('tracks invocationCallOrder made by mocks', () => { const fn1 = moduleMocker.fn(); diff --git a/packages/jest-mock/src/index.js b/packages/jest-mock/src/index.js index 4bde745a3c25..b3c901ecf64e 100644 --- a/packages/jest-mock/src/index.js +++ b/packages/jest-mock/src/index.js @@ -21,17 +21,27 @@ export type MockFunctionMetadata = { length?: number, }; +/** + * Possible types of a MockFunctionResult. + * 'return': The call completed by returning normally. + * 'throw': The call completed by throwing a value. + * 'incomplete': The call has not completed yet. This is possible if you read + * the mock function result from within the mock function itself + * (or a function called by the mock function). + */ +type MockFunctionResultType = 'return' | 'throw' | 'incomplete'; + /** * Represents the result of a single call to a mock function. */ type MockFunctionResult = { /** - * True if the function threw. - * False if the function returned. + * Indicates how the call completed. */ - isThrow: boolean, + type: MockFunctionResultType, /** * The value that was either thrown or returned by the function. + * Undefined when type === 'incomplete'. */ value: any, }; @@ -379,6 +389,15 @@ class ModuleMockerClass { const mockConfig = mocker._ensureMockConfig(f); mockState.instances.push(this); mockState.calls.push(Array.prototype.slice.call(arguments)); + // Create and record an "incomplete" mock result immediately upon + // calling rather than waiting for the mock to return. This avoids + // issues caused by recursion where results can be recorded in the + // wrong order. + const mockResult = { + type: 'incomplete', + value: undefined, + }; + mockState.results.push(mockResult); mockState.invocationCallOrder.push(mocker._invocationCallCounter++); // Will be set to the return value of the mock if an error is not thrown @@ -457,11 +476,12 @@ class ModuleMockerClass { callDidThrowError = true; throw error; } finally { - // Record the result of the function - mockState.results.push({ - isThrow: callDidThrowError, - value: callDidThrowError ? thrownError : finalReturnValue, - }); + // Record the result of the function. + // NOTE: Intentionally NOT pushing/indexing into the array of mock + // results here to avoid corrupting results data if mockClear() + // is called during the execution of the mock. + mockResult.type = callDidThrowError ? 'throw' : 'return'; + mockResult.value = callDidThrowError ? thrownError : finalReturnValue; } return finalReturnValue; diff --git a/packages/jest-phabricator/package.json b/packages/jest-phabricator/package.json index 7706682a88e2..913598cd7a36 100644 --- a/packages/jest-phabricator/package.json +++ b/packages/jest-phabricator/package.json @@ -5,6 +5,9 @@ "type": "git", "url": "https://github.com/facebook/jest.git" }, + "engines": { + "node": ">= 6" + }, "license": "MIT", "main": "build/index.js" } diff --git a/packages/jest-regex-util/package.json b/packages/jest-regex-util/package.json index bd2a9ad7d06f..ab665d8b6805 100644 --- a/packages/jest-regex-util/package.json +++ b/packages/jest-regex-util/package.json @@ -5,6 +5,9 @@ "type": "git", "url": "https://github.com/facebook/jest.git" }, + "engines": { + "node": ">= 6" + }, "license": "MIT", "main": "build/index.js" } diff --git a/packages/jest-repl/package.json b/packages/jest-repl/package.json index 4f23fc46bfe6..95286104461f 100644 --- a/packages/jest-repl/package.json +++ b/packages/jest-repl/package.json @@ -16,5 +16,8 @@ }, "bin": { "jest-repl": "./bin/jest-repl.js" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-resolve-dependencies/package.json b/packages/jest-resolve-dependencies/package.json index f7783fd0a313..20ed227945ae 100644 --- a/packages/jest-resolve-dependencies/package.json +++ b/packages/jest-resolve-dependencies/package.json @@ -10,5 +10,8 @@ "dependencies": { "jest-regex-util": "^23.3.0", "jest-snapshot": "^23.6.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-resolve-dependencies/src/index.js b/packages/jest-resolve-dependencies/src/index.js index b5e5f5a4efbc..64e3474037df 100644 --- a/packages/jest-resolve-dependencies/src/index.js +++ b/packages/jest-resolve-dependencies/src/index.js @@ -97,7 +97,7 @@ class DependencyResolver { } } const modules = []; - for (const file of this._hasteFS.getFileIterator()) { + for (const file of this._hasteFS.getAbsoluteFileIterator()) { modules.push({ dependencies: this.resolve(file, options), file, diff --git a/packages/jest-resolve/package.json b/packages/jest-resolve/package.json index 27690d89d9f7..4b380f43aa79 100644 --- a/packages/jest-resolve/package.json +++ b/packages/jest-resolve/package.json @@ -14,5 +14,8 @@ }, "devDependencies": { "jest-haste-map": "^23.6.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-resolve/src/__tests__/is_builtin_module.test.js b/packages/jest-resolve/src/__tests__/isBuiltinModule.test.js similarity index 92% rename from packages/jest-resolve/src/__tests__/is_builtin_module.test.js rename to packages/jest-resolve/src/__tests__/isBuiltinModule.test.js index 7be6a38dcae6..a18f8b4a0b0d 100644 --- a/packages/jest-resolve/src/__tests__/is_builtin_module.test.js +++ b/packages/jest-resolve/src/__tests__/isBuiltinModule.test.js @@ -1,7 +1,7 @@ // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. // @flow -import isBuiltinModule from '../is_builtin_module'; +import isBuiltinModule from '../isBuiltinModule'; describe('isBuiltinModule', () => { it('should return true for the `path` module', () => { diff --git a/packages/jest-resolve/src/__tests__/resolve.test.js b/packages/jest-resolve/src/__tests__/resolve.test.js index 7d17eebac935..8d3b91d9bfb9 100644 --- a/packages/jest-resolve/src/__tests__/resolve.test.js +++ b/packages/jest-resolve/src/__tests__/resolve.test.js @@ -15,7 +15,7 @@ const path = require('path'); const ModuleMap = require('jest-haste-map').ModuleMap; const Resolver = require('../'); const userResolver = require('../__mocks__/userResolver'); -const nodeModulesPaths = require('../node_modules_paths').default; +const nodeModulesPaths = require('../nodeModulesPaths').default; beforeEach(() => { userResolver.mockClear(); @@ -161,6 +161,7 @@ describe('getMockModule', () => { const moduleMap = ModuleMap.create('/'); const resolver = new Resolver(moduleMap, { + extensions: ['.js'], moduleNameMapper: [ { moduleName: '$1', diff --git a/packages/jest-resolve/src/default_resolver.js b/packages/jest-resolve/src/defaultResolver.js similarity index 97% rename from packages/jest-resolve/src/default_resolver.js rename to packages/jest-resolve/src/defaultResolver.js index ae85c4e888d8..02f311c22ece 100644 --- a/packages/jest-resolve/src/default_resolver.js +++ b/packages/jest-resolve/src/defaultResolver.js @@ -13,8 +13,8 @@ import type {ErrorWithCode} from 'types/Errors'; import browserResolve from 'browser-resolve'; import fs from 'fs'; import path from 'path'; -import isBuiltinModule from './is_builtin_module'; -import nodeModulesPaths from './node_modules_paths'; +import isBuiltinModule from './isBuiltinModule'; +import nodeModulesPaths from './nodeModulesPaths'; type ResolverOptions = {| basedir: Path, diff --git a/packages/jest-resolve/src/index.js b/packages/jest-resolve/src/index.js index 4d3c042d7117..55562ac8b7f7 100644 --- a/packages/jest-resolve/src/index.js +++ b/packages/jest-resolve/src/index.js @@ -14,9 +14,9 @@ import type {ErrorWithCode} from 'types/Errors'; import fs from 'fs'; import path from 'path'; -import nodeModulesPaths from './node_modules_paths'; -import isBuiltinModule from './is_builtin_module'; -import defaultResolver from './default_resolver.js'; +import nodeModulesPaths from './nodeModulesPaths'; +import isBuiltinModule from './isBuiltinModule'; +import defaultResolver from './defaultResolver'; import chalk from 'chalk'; type ResolverConfig = {| @@ -328,8 +328,8 @@ class Resolver { return virtualMocks[virtualMockPath] ? virtualMockPath : moduleName - ? this.resolveModule(from, moduleName) - : from; + ? this.resolveModule(from, moduleName) + : from; } _isModuleResolved(from: Path, moduleName: string): boolean { @@ -341,10 +341,23 @@ class Resolver { _resolveStubModuleName(from: Path, moduleName: string): ?Path { const dirname = path.dirname(from); const paths = this._options.modulePaths; - const extensions = this._options.extensions; + const extensions = this._options.extensions.slice(); const moduleDirectory = this._options.moduleDirectories; const moduleNameMapper = this._options.moduleNameMapper; const resolver = this._options.resolver; + const defaultPlatform = this._options.defaultPlatform; + + if (this._supportsNativePlatform) { + extensions.unshift( + ...this._options.extensions.map(ext => '.' + NATIVE_PLATFORM + ext), + ); + } + + if (defaultPlatform) { + extensions.unshift( + ...this._options.extensions.map(ext => '.' + defaultPlatform + ext), + ); + } if (moduleNameMapper) { for (const {moduleName: mappedModuleName, regex} of moduleNameMapper) { diff --git a/packages/jest-resolve/src/is_builtin_module.js b/packages/jest-resolve/src/isBuiltinModule.js similarity index 100% rename from packages/jest-resolve/src/is_builtin_module.js rename to packages/jest-resolve/src/isBuiltinModule.js diff --git a/packages/jest-resolve/src/node_modules_paths.js b/packages/jest-resolve/src/nodeModulesPaths.js similarity index 88% rename from packages/jest-resolve/src/node_modules_paths.js rename to packages/jest-resolve/src/nodeModulesPaths.js index 84cb68a2c3fb..32d447aa90d0 100644 --- a/packages/jest-resolve/src/node_modules_paths.js +++ b/packages/jest-resolve/src/nodeModulesPaths.js @@ -59,13 +59,12 @@ export default function nodeModulesPaths( .reduce( (dirs, aPath) => dirs.concat( - modules.map( - moduleDir => - path.isAbsolute(moduleDir) - ? aPath === basedirAbs - ? moduleDir - : '' - : path.join(prefix, aPath, moduleDir), + modules.map(moduleDir => + path.isAbsolute(moduleDir) + ? aPath === basedirAbs + ? moduleDir + : '' + : path.join(prefix, aPath, moduleDir), ), ), [], diff --git a/packages/jest-runner/package.json b/packages/jest-runner/package.json index ae9d25724778..e3cfd5d9609e 100644 --- a/packages/jest-runner/package.json +++ b/packages/jest-runner/package.json @@ -21,5 +21,8 @@ "jest-worker": "^23.2.0", "source-map-support": "^0.5.6", "throat": "^4.0.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-runner/src/run_test.js b/packages/jest-runner/src/run_test.js index bba71b2c2dfe..8908a54b58ec 100644 --- a/packages/jest-runner/src/run_test.js +++ b/packages/jest-runner/src/run_test.js @@ -58,6 +58,7 @@ async function runTestInternal( if (customEnvironment) { testEnvironment = getTestEnvironment( Object.assign({}, config, { + // $FlowFixMe testEnvironment: customEnvironment, }), ); @@ -70,9 +71,8 @@ async function runTestInternal( : /* $FlowFixMe */ require(config.testRunner)): TestFramework); /* $FlowFixMe */ - const Runtime = (require(config.moduleLoader || 'jest-runtime'): Class< - RuntimeClass, - >); + const Runtime = (require(config.moduleLoader || + 'jest-runtime'): Class); let runtime = undefined; diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index 47c2e817bf1c..d9b7061017da 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -37,5 +37,8 @@ }, "bin": { "jest-runtime": "./bin/jest-runtime.js" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-runtime/src/__tests__/defaultResolver.js b/packages/jest-runtime/src/__tests__/defaultResolver.js index 089aa58e6055..adb6f54aa902 100644 --- a/packages/jest-runtime/src/__tests__/defaultResolver.js +++ b/packages/jest-runtime/src/__tests__/defaultResolver.js @@ -6,5 +6,5 @@ * of patent rights can be found in the PATENTS file in the same directory. * */ -import resolver from 'jest-resolve/build/default_resolver.js'; +import resolver from 'jest-resolve/build/defaultResolver.js'; module.exports = resolver; diff --git a/packages/jest-runtime/src/index.js b/packages/jest-runtime/src/index.js index 8adede5ec442..f91e481a2925 100644 --- a/packages/jest-runtime/src/index.js +++ b/packages/jest-runtime/src/index.js @@ -236,7 +236,9 @@ class Runtime { return new HasteMap({ cacheDirectory: config.cacheDirectory, + computeSha1: config.haste.computeSha1, console: options && options.console, + dependencyExtractor: config.dependencyExtractor, extensions: [Snapshot.EXTENSION].concat(config.moduleFileExtensions), hasteImplModulePath: config.haste.hasteImplModulePath, ignorePattern, @@ -445,20 +447,22 @@ class Runtime { this._mockRegistry = Object.create(null); this._moduleRegistry = Object.create(null); - if (this._environment && this._environment.global) { - const envGlobal = this._environment.global; - Object.keys(envGlobal).forEach(key => { - const globalMock = envGlobal[key]; - if ( - (typeof globalMock === 'object' && globalMock !== null) || - typeof globalMock === 'function' - ) { - globalMock._isMockFunction === true && globalMock.mockClear(); - } - }); + if (this._environment) { + if (this._environment.global) { + const envGlobal = this._environment.global; + Object.keys(envGlobal).forEach(key => { + const globalMock = envGlobal[key]; + if ( + (typeof globalMock === 'object' && globalMock !== null) || + typeof globalMock === 'function' + ) { + globalMock._isMockFunction === true && globalMock.mockClear(); + } + }); + } - if (envGlobal.mockClearTimers) { - envGlobal.mockClearTimers(); + if (this._environment.fakeTimers) { + this._environment.fakeTimers.clearAllTimers(); } } } @@ -932,6 +936,7 @@ class Runtime { fn, genMockFromModule: (moduleName: string) => this._generateMock(from, moduleName), + getTimerCount: () => this._environment.fakeTimers.getTimerCount(), isMockFunction: this._moduleMocker.isMockFunction, mock, requireActual: localRequire.requireActual, diff --git a/packages/jest-runtime/src/script_transformer.js b/packages/jest-runtime/src/script_transformer.js index d45ed07c0230..439972da93b0 100644 --- a/packages/jest-runtime/src/script_transformer.js +++ b/packages/jest-runtime/src/script_transformer.js @@ -337,7 +337,7 @@ export default class ScriptTransformer { }; } catch (e) { if (e.codeFrame) { - e.stack = e.codeFrame; + e.stack = e.message + '\n' + e.codeFrame; } if ( diff --git a/packages/jest-serializer/package.json b/packages/jest-serializer/package.json index 4b99781fe5f6..0bbb47d5cc72 100644 --- a/packages/jest-serializer/package.json +++ b/packages/jest-serializer/package.json @@ -5,6 +5,9 @@ "type": "git", "url": "https://github.com/facebook/jest.git" }, + "engines": { + "node": ">= 6" + }, "license": "MIT", "main": "build/index.js" } diff --git a/packages/jest-snapshot/package.json b/packages/jest-snapshot/package.json index 8f8d1a96069b..0be2703adf33 100644 --- a/packages/jest-snapshot/package.json +++ b/packages/jest-snapshot/package.json @@ -21,5 +21,8 @@ }, "devDependencies": { "prettier": "^1.13.4" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-snapshot/src/__tests__/__snapshots__/mock_serializer.test.js.snap b/packages/jest-snapshot/src/__tests__/__snapshots__/mock_serializer.test.js.snap index 95477e6fff5c..f1fa6c9bd478 100644 --- a/packages/jest-snapshot/src/__tests__/__snapshots__/mock_serializer.test.js.snap +++ b/packages/jest-snapshot/src/__tests__/__snapshots__/mock_serializer.test.js.snap @@ -24,7 +24,7 @@ Object { ], "results": Array [ Object { - "isThrow": false, + "type": "return", "value": undefined, }, ], @@ -43,7 +43,7 @@ exports[`mock with 1 calls in React element 1`] = ` ], "results": Array [ Object { - "isThrow": false, + "type": "return", "value": undefined, }, ], @@ -67,11 +67,11 @@ exports[`mock with 2 calls 1`] = ` ], "results": Array [ Object { - "isThrow": false, + "type": "return", "value": undefined, }, Object { - "isThrow": false, + "type": "return", "value": undefined, }, ], @@ -90,11 +90,11 @@ exports[`mock with 2 calls, 1 return, 1 throw 1`] = ` ], "results": Array [ Object { - "isThrow": false, + "type": "return", "value": 4, }, Object { - "isThrow": true, + "type": "throw", "value": [Error: Error Message!], }, ], diff --git a/packages/jest-snapshot/src/__tests__/inline_snapshots.test.js b/packages/jest-snapshot/src/__tests__/inline_snapshots.test.js index 1a1d411bbe3c..486e6e332082 100644 --- a/packages/jest-snapshot/src/__tests__/inline_snapshots.test.js +++ b/packages/jest-snapshot/src/__tests__/inline_snapshots.test.js @@ -23,27 +23,37 @@ const existsSync = fs.existsSync; const statSync = fs.statSync; const readdirSync = fs.readdirSync; beforeEach(() => { + // $FlowFixMe mock fs.writeFileSync = jest.fn(); + // $FlowFixMe mock fs.readFileSync = jest.fn(); + // $FlowFixMe mock fs.existsSync = jest.fn(() => true); // $FlowFixMe mock fs.statSync = jest.fn(filePath => ({ isDirectory: () => !filePath.endsWith('.js'), })); + // $FlowFixMe mock fs.readdirSync = jest.fn(() => []); prettier.resolveConfig.sync.mockReset(); }); afterEach(() => { + // $FlowFixMe mock fs.writeFileSync = writeFileSync; + // $FlowFixMe mock fs.readFileSync = readFileSync; + // $FlowFixMe mock fs.existsSync = existsSync; + // $FlowFixMe mock fs.statSync = statSync; + // $FlowFixMe mock fs.readdirSync = readdirSync; }); test('saveInlineSnapshots() replaces empty function call with a template literal', () => { const filename = path.join(__dirname, 'my.test.js'); + // $FlowFixMe mock fs.readFileSync = (jest.fn( () => `expect(1).toMatchInlineSnapshot();\n`, ): any); @@ -69,6 +79,7 @@ test.each([['babylon'], ['flow'], ['typescript']])( 'saveInlineSnapshots() replaces existing template literal - %s parser', parser => { const filename = path.join(__dirname, 'my.test.js'); + // $FlowFixMe mock fs.readFileSync = (jest.fn( () => 'expect(1).toMatchInlineSnapshot(`2`);\n', ): any); @@ -97,6 +108,7 @@ test.each([['babylon'], ['flow'], ['typescript']])( test('saveInlineSnapshots() replaces existing template literal with property matchers', () => { const filename = path.join(__dirname, 'my.test.js'); + // $FlowFixMe mock fs.readFileSync = (jest.fn( () => 'expect(1).toMatchInlineSnapshot({}, `2`);\n', ): any); @@ -120,6 +132,7 @@ test('saveInlineSnapshots() replaces existing template literal with property mat test('saveInlineSnapshots() throws if frame does not match', () => { const filename = path.join(__dirname, 'my.test.js'); + // $FlowFixMe mock fs.readFileSync = (jest.fn( () => 'expect(1).toMatchInlineSnapshot();\n', ): any); @@ -141,6 +154,7 @@ test('saveInlineSnapshots() throws if frame does not match', () => { test('saveInlineSnapshots() throws if multiple calls to to the same location', () => { const filename = path.join(__dirname, 'my.test.js'); + // $FlowFixMe mock fs.readFileSync = (jest.fn( () => 'expect(1).toMatchInlineSnapshot();\n', ): any); @@ -160,6 +174,7 @@ test('saveInlineSnapshots() throws if multiple calls to to the same location', ( test('saveInlineSnapshots() uses escaped backticks', () => { const filename = path.join(__dirname, 'my.test.js'); + // $FlowFixMe mock fs.readFileSync = (jest.fn( () => 'expect("`").toMatchInlineSnapshot();\n', ): any); @@ -175,6 +190,7 @@ test('saveInlineSnapshots() uses escaped backticks', () => { test('saveInlineSnapshots() works with non-literals in expect call', () => { const filename = path.join(__dirname, 'my.test.js'); + // $FlowFixMe mock fs.readFileSync = (jest.fn( () => `expect({a: 'a'}).toMatchInlineSnapshot();\n`, ): any); diff --git a/packages/jest-snapshot/src/__tests__/mock_serializer.test.js b/packages/jest-snapshot/src/__tests__/mock_serializer.test.js index 986cff352f01..a2ff554e6548 100644 --- a/packages/jest-snapshot/src/__tests__/mock_serializer.test.js +++ b/packages/jest-snapshot/src/__tests__/mock_serializer.test.js @@ -101,7 +101,7 @@ test('indent option', () => { '],', '"results": Array [', 'Object {', - '"isThrow": false,', + '"type": "return",', '"value": Object {', '"key": "value",', '},', @@ -116,7 +116,7 @@ test('min option', () => { const fn = jest.fn(val => val); fn({key: 'value'}); const expected = - '[MockFunction] {"calls": [[{"key": "value"}]], "results": [{"isThrow": false, "value": {"key": "value"}}]}'; + '[MockFunction] {"calls": [[{"key": "value"}]], "results": [{"type": "return", "value": {"key": "value"}}]}'; expect(prettyFormat(fn, {min: true, plugins: [plugin]})).toBe(expected); }); @@ -150,7 +150,7 @@ test('maxDepth option', () => { ' ],', ' "results": Array [', // ++depth === 2 ' Object {', // ++depth === 3 - ' "isThrow": false,', + ' "type": "return",', ' "value": undefined,', ' },', ' ],', diff --git a/packages/jest-util/package.json b/packages/jest-util/package.json index c629f0caadbb..c2b0962f73a1 100644 --- a/packages/jest-util/package.json +++ b/packages/jest-util/package.json @@ -19,5 +19,8 @@ }, "devDependencies": { "jest-mock": "^23.2.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-util/src/buffered_console.js b/packages/jest-util/src/BufferedConsole.js similarity index 98% rename from packages/jest-util/src/buffered_console.js rename to packages/jest-util/src/BufferedConsole.js index 434d8d194927..7b3b2a4a7129 100644 --- a/packages/jest-util/src/buffered_console.js +++ b/packages/jest-util/src/BufferedConsole.js @@ -21,7 +21,7 @@ import assert from 'assert'; import {Console} from 'console'; import {format} from 'util'; import chalk from 'chalk'; -import getCallsite from './get_callsite'; +import getCallsite from './getCallsite'; export default class BufferedConsole extends Console { _buffer: ConsoleBuffer; diff --git a/packages/jest-util/src/Console.js b/packages/jest-util/src/CustomConsole.js similarity index 98% rename from packages/jest-util/src/Console.js rename to packages/jest-util/src/CustomConsole.js index 064d9842fc2c..92335c788e67 100644 --- a/packages/jest-util/src/Console.js +++ b/packages/jest-util/src/CustomConsole.js @@ -14,7 +14,7 @@ import assert from 'assert'; import {format} from 'util'; import {Console} from 'console'; import chalk from 'chalk'; -import clearLine from './clear_line'; +import clearLine from './clearLine'; type Formatter = (type: LogType, message: LogMessage) => string; diff --git a/packages/jest-util/src/error_with_stack.js b/packages/jest-util/src/ErrorWithStack.js similarity index 100% rename from packages/jest-util/src/error_with_stack.js rename to packages/jest-util/src/ErrorWithStack.js diff --git a/packages/jest-util/src/fake_timers.js b/packages/jest-util/src/FakeTimers.js similarity index 95% rename from packages/jest-util/src/fake_timers.js rename to packages/jest-util/src/FakeTimers.js index a31cb67b2c72..953820c9995e 100644 --- a/packages/jest-util/src/fake_timers.js +++ b/packages/jest-util/src/FakeTimers.js @@ -12,7 +12,7 @@ import type {Global} from 'types/Global'; import type {ModuleMocker} from 'types/Mock'; import {formatStackTrace} from 'jest-message-util'; -import setGlobal from './set_global'; +import setGlobal from './setGlobal'; /** * We don't know the type of arguments for a callback ahead of time which is why @@ -114,16 +114,6 @@ export default class FakeTimers { this.reset(); this._createMocks(); - - // These globally-accessible function are now deprecated! - // They will go away very soon, so do not use them! - // Instead, use the versions available on the `jest` object - global.mockRunTicksRepeatedly = this.runAllTicks.bind(this); - global.mockRunTimersOnce = this.runOnlyPendingTimers.bind(this); - global.mockAdvanceTimersByTime = this.advanceTimersByTime.bind(this); - global.mockRunTimersRepeatedly = this.runAllTimers.bind(this); - global.mockClearTimers = this.clearAllTimers.bind(this); - global.mockGetTimersCount = () => Object.keys(this._timers).length; } clearAllTimers() { @@ -352,6 +342,12 @@ export default class FakeTimers { global.process.nextTick = this._fakeTimerAPIs.nextTick; } + getTimerCount() { + this._checkFakeTimers(); + + return Object.keys(this._timers).length; + } + _checkFakeTimers() { if (this._global.setTimeout !== this._fakeTimerAPIs.setTimeout) { this._global.console.warn( diff --git a/packages/jest-util/src/null_console.js b/packages/jest-util/src/NullConsole.js similarity index 77% rename from packages/jest-util/src/null_console.js rename to packages/jest-util/src/NullConsole.js index d89d6f70f685..bfc753f926cf 100644 --- a/packages/jest-util/src/null_console.js +++ b/packages/jest-util/src/NullConsole.js @@ -7,9 +7,9 @@ * @flow */ -import Console from './Console'; +import CustomConsole from './CustomConsole'; -export default class NullConsole extends Console { +export default class NullConsole extends CustomConsole { assert() {} debug() {} dir() {} diff --git a/packages/jest-util/src/__tests__/__snapshots__/fake_timers.test.js.snap b/packages/jest-util/src/__tests__/__snapshots__/fakeTimers.test.js.snap similarity index 100% rename from packages/jest-util/src/__tests__/__snapshots__/fake_timers.test.js.snap rename to packages/jest-util/src/__tests__/__snapshots__/fakeTimers.test.js.snap diff --git a/packages/jest-util/src/__tests__/buffered_console.test.js b/packages/jest-util/src/__tests__/bufferedConsole.test.js similarity index 98% rename from packages/jest-util/src/__tests__/buffered_console.test.js rename to packages/jest-util/src/__tests__/bufferedConsole.test.js index ede81e84c810..368c03e0b89e 100644 --- a/packages/jest-util/src/__tests__/buffered_console.test.js +++ b/packages/jest-util/src/__tests__/bufferedConsole.test.js @@ -8,7 +8,7 @@ */ import chalk from 'chalk'; -import BufferedConsole from '../buffered_console'; +import BufferedConsole from '../BufferedConsole'; describe('CustomConsole', () => { let _console; diff --git a/packages/jest-util/src/__tests__/console.test.js b/packages/jest-util/src/__tests__/console.test.js index 4502f8ac32f6..375160a4698b 100644 --- a/packages/jest-util/src/__tests__/console.test.js +++ b/packages/jest-util/src/__tests__/console.test.js @@ -8,7 +8,7 @@ */ import chalk from 'chalk'; -import CustomConsole from '../Console'; +import CustomConsole from '../CustomConsole'; describe('CustomConsole', () => { let _console; diff --git a/packages/jest-util/src/__tests__/create_process_object.test.js b/packages/jest-util/src/__tests__/createProcessObject.test.js similarity index 98% rename from packages/jest-util/src/__tests__/create_process_object.test.js rename to packages/jest-util/src/__tests__/createProcessObject.test.js index dafb1ef4578e..55f582c4b200 100644 --- a/packages/jest-util/src/__tests__/create_process_object.test.js +++ b/packages/jest-util/src/__tests__/createProcessObject.test.js @@ -6,7 +6,7 @@ */ import EventEmitter from 'events'; -import createProcessObject from '../create_process_object'; +import createProcessObject from '../createProcessObject'; it('creates a process object that looks like the original one', () => { const fakeProcess = createProcessObject(); diff --git a/packages/jest-util/src/__tests__/deep_cyclic_copy.test.js b/packages/jest-util/src/__tests__/deepCyclicCopy.test.js similarity index 99% rename from packages/jest-util/src/__tests__/deep_cyclic_copy.test.js rename to packages/jest-util/src/__tests__/deepCyclicCopy.test.js index 3f26bff2ebad..556c516a1743 100644 --- a/packages/jest-util/src/__tests__/deep_cyclic_copy.test.js +++ b/packages/jest-util/src/__tests__/deepCyclicCopy.test.js @@ -6,7 +6,7 @@ * */ -import deepCyclicCopy from '../deep_cyclic_copy'; +import deepCyclicCopy from '../deepCyclicCopy'; it('returns the same value for primitive or function values', () => { const fn = () => {}; diff --git a/packages/jest-util/src/__tests__/error_with_stack.test.js b/packages/jest-util/src/__tests__/errorWithStack.test.js similarity index 93% rename from packages/jest-util/src/__tests__/error_with_stack.test.js rename to packages/jest-util/src/__tests__/errorWithStack.test.js index 98c35c57c6b9..6de67fe8489f 100644 --- a/packages/jest-util/src/__tests__/error_with_stack.test.js +++ b/packages/jest-util/src/__tests__/errorWithStack.test.js @@ -7,7 +7,7 @@ * @flow */ -import ErrorWithStack from '../error_with_stack'; +import ErrorWithStack from '../ErrorWithStack'; describe('ErrorWithStack', () => { const message = '💩 something went wrong'; diff --git a/packages/jest-util/src/__tests__/fake_timers.test.js b/packages/jest-util/src/__tests__/fakeTimers.test.js similarity index 97% rename from packages/jest-util/src/__tests__/fake_timers.test.js rename to packages/jest-util/src/__tests__/fakeTimers.test.js index a9d8434532bf..8a05fde1dee0 100644 --- a/packages/jest-util/src/__tests__/fake_timers.test.js +++ b/packages/jest-util/src/__tests__/fakeTimers.test.js @@ -14,7 +14,7 @@ describe('FakeTimers', () => { let FakeTimers, moduleMocker, timerConfig; beforeEach(() => { - FakeTimers = require('../fake_timers').default; + FakeTimers = require('../FakeTimers').default; const mock = require('jest-mock'); const global = vm.runInNewContext('this'); moduleMocker = new mock.ModuleMocker(global); @@ -943,4 +943,26 @@ describe('FakeTimers', () => { expect(global.clearImmediate).not.toBe(nativeClearImmediate); }); }); + + describe('getTimerCount', () => { + it('returns the correct count', () => { + const timers = new FakeTimers({global, moduleMocker, timerConfig}); + + timers.useFakeTimers(); + + global.setTimeout(() => {}, 0); + global.setTimeout(() => {}, 0); + global.setTimeout(() => {}, 10); + + expect(timers.getTimerCount()).toEqual(3); + + timers.advanceTimersByTime(5); + + expect(timers.getTimerCount()).toEqual(1); + + timers.advanceTimersByTime(5); + + expect(timers.getTimerCount()).toEqual(0); + }); + }); }); diff --git a/packages/jest-util/src/__tests__/format_test_results.test.js b/packages/jest-util/src/__tests__/formatTestResults.test.js similarity index 93% rename from packages/jest-util/src/__tests__/format_test_results.test.js rename to packages/jest-util/src/__tests__/formatTestResults.test.js index f2187c9fbb8c..6d03ccee06fe 100644 --- a/packages/jest-util/src/__tests__/format_test_results.test.js +++ b/packages/jest-util/src/__tests__/formatTestResults.test.js @@ -8,7 +8,7 @@ 'use strict'; -import formatTestResults from '../format_test_results'; +import formatTestResults from '../formatTestResults'; describe('formatTestResults', () => { const assertion = { diff --git a/packages/jest-util/src/__tests__/get_callsite.test.js b/packages/jest-util/src/__tests__/getCallsite.test.js similarity index 97% rename from packages/jest-util/src/__tests__/get_callsite.test.js rename to packages/jest-util/src/__tests__/getCallsite.test.js index 98598f049f87..f615ddcd083b 100644 --- a/packages/jest-util/src/__tests__/get_callsite.test.js +++ b/packages/jest-util/src/__tests__/getCallsite.test.js @@ -2,7 +2,7 @@ import fs from 'fs'; import SourceMap from 'source-map'; -import getCallsite from '../get_callsite'; +import getCallsite from '../getCallsite'; // Node 10.5.x compatibility jest.mock('fs', () => diff --git a/packages/jest-util/src/__tests__/get_failed_snapshot.test.js b/packages/jest-util/src/__tests__/getFailedSnapshot.test.js similarity index 95% rename from packages/jest-util/src/__tests__/get_failed_snapshot.test.js rename to packages/jest-util/src/__tests__/getFailedSnapshot.test.js index ca9992978bb3..e2d26e30a304 100644 --- a/packages/jest-util/src/__tests__/get_failed_snapshot.test.js +++ b/packages/jest-util/src/__tests__/getFailedSnapshot.test.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import getFailedSnapshotTests from '../get_failed_snapshot_tests'; +import getFailedSnapshotTests from '../getFailedSnapshotTests'; test('return a list of path', () => { const targetFilename = 'somewhere.js'; diff --git a/packages/jest-util/src/__tests__/install_common_globals.test.js b/packages/jest-util/src/__tests__/installCommonGlobals.test.js similarity index 93% rename from packages/jest-util/src/__tests__/install_common_globals.test.js rename to packages/jest-util/src/__tests__/installCommonGlobals.test.js index b606ee81cc85..0aa0d42e454b 100644 --- a/packages/jest-util/src/__tests__/install_common_globals.test.js +++ b/packages/jest-util/src/__tests__/installCommonGlobals.test.js @@ -18,7 +18,7 @@ beforeEach(() => { fake = jest.fn(); global.DTRACE_NET_SERVER_CONNECTION = fake; - installCommonGlobals = require('../install_common_globals').default; + installCommonGlobals = require('../installCommonGlobals').default; }); it('returns the passed object', () => { diff --git a/packages/jest-util/src/__tests__/is_interactive.test.js b/packages/jest-util/src/__tests__/isInteractive.test.js similarity index 84% rename from packages/jest-util/src/__tests__/is_interactive.test.js rename to packages/jest-util/src/__tests__/isInteractive.test.js index d4b7efae05b2..5df194f2411a 100644 --- a/packages/jest-util/src/__tests__/is_interactive.test.js +++ b/packages/jest-util/src/__tests__/isInteractive.test.js @@ -19,7 +19,7 @@ it('Returns true when running on interactive environment', () => { process.stdout.isTTY = true; process.env.TERM = 'xterm-256color'; - const isInteractive = require('../is_interative').default; + const isInteractive = require('../isInteractive').default; expect(isInteractive).toBe(true); }); @@ -31,7 +31,7 @@ it('Returns false when running on a non-interactive environment', () => { jest.doMock('is-ci', () => true); process.stdout.isTTY = false; process.env.TERM = 'xterm-256color'; - isInteractive = require('../is_interative').default; + isInteractive = require('../isInteractive').default; expect(isInteractive).toBe(expectedResult); // Test with is-ci being false and isTTY false @@ -39,7 +39,7 @@ it('Returns false when running on a non-interactive environment', () => { jest.doMock('is-ci', () => false); process.stdout.isTTY = false; process.env.TERM = 'xterm-256color'; - isInteractive = require('../is_interative').default; + isInteractive = require('../isInteractive').default; expect(isInteractive).toBe(expectedResult); // Test with is-ci being true and isTTY true @@ -47,7 +47,7 @@ it('Returns false when running on a non-interactive environment', () => { jest.doMock('is-ci', () => true); process.stdout.isTTY = true; process.env.TERM = 'xterm-256color'; - isInteractive = require('../is_interative').default; + isInteractive = require('../isInteractive').default; expect(isInteractive).toBe(expectedResult); // Test with dumb terminal @@ -55,6 +55,6 @@ it('Returns false when running on a non-interactive environment', () => { jest.doMock('is-ci', () => false); process.stdout.isTTY = false; process.env.TERM = 'dumb'; - isInteractive = require('../is_interative').default; + isInteractive = require('../isInteractive').default; expect(isInteractive).toBe(expectedResult); }); diff --git a/packages/jest-util/src/clear_line.js b/packages/jest-util/src/clearLine.js similarity index 100% rename from packages/jest-util/src/clear_line.js rename to packages/jest-util/src/clearLine.js diff --git a/packages/jest-util/src/convert_descriptor_to_string.js b/packages/jest-util/src/convertDescriptorToString.js similarity index 100% rename from packages/jest-util/src/convert_descriptor_to_string.js rename to packages/jest-util/src/convertDescriptorToString.js diff --git a/packages/jest-util/src/create_process_object.js b/packages/jest-util/src/createProcessObject.js similarity index 98% rename from packages/jest-util/src/create_process_object.js rename to packages/jest-util/src/createProcessObject.js index 426b0dfcfd61..02619dcf4439 100644 --- a/packages/jest-util/src/create_process_object.js +++ b/packages/jest-util/src/createProcessObject.js @@ -7,7 +7,7 @@ * @flow */ -import deepCyclicCopy from './deep_cyclic_copy'; +import deepCyclicCopy from './deepCyclicCopy'; const BLACKLIST = new Set(['env', 'mainModule', '_events']); diff --git a/packages/jest-util/src/deep_cyclic_copy.js b/packages/jest-util/src/deepCyclicCopy.js similarity index 100% rename from packages/jest-util/src/deep_cyclic_copy.js rename to packages/jest-util/src/deepCyclicCopy.js diff --git a/packages/jest-util/src/format_test_results.js b/packages/jest-util/src/formatTestResults.js similarity index 100% rename from packages/jest-util/src/format_test_results.js rename to packages/jest-util/src/formatTestResults.js diff --git a/packages/jest-util/src/get_callsite.js b/packages/jest-util/src/getCallsite.js similarity index 100% rename from packages/jest-util/src/get_callsite.js rename to packages/jest-util/src/getCallsite.js diff --git a/packages/jest-util/src/get_console_output.js b/packages/jest-util/src/getConsoleOutput.js similarity index 100% rename from packages/jest-util/src/get_console_output.js rename to packages/jest-util/src/getConsoleOutput.js diff --git a/packages/jest-util/src/get_failed_snapshot_tests.js b/packages/jest-util/src/getFailedSnapshotTests.js similarity index 100% rename from packages/jest-util/src/get_failed_snapshot_tests.js rename to packages/jest-util/src/getFailedSnapshotTests.js diff --git a/packages/jest-util/src/index.js b/packages/jest-util/src/index.js index 0d206057f5a5..5cd88557a8ff 100644 --- a/packages/jest-util/src/index.js +++ b/packages/jest-util/src/index.js @@ -9,21 +9,21 @@ import mkdirp from 'mkdirp'; -import BufferedConsole from './buffered_console'; -import clearLine from './clear_line'; -import Console from './Console'; -import ErrorWithStack from './error_with_stack'; -import FakeTimers from './fake_timers'; -import formatTestResults from './format_test_results'; -import getFailedSnapshotTests from './get_failed_snapshot_tests'; -import getConsoleOutput from './get_console_output'; -import installCommonGlobals from './install_common_globals'; -import NullConsole from './null_console'; -import isInteractive from './is_interative'; -import getCallsite from './get_callsite'; -import setGlobal from './set_global'; -import deepCyclicCopy from './deep_cyclic_copy'; -import convertDescriptorToString from './convert_descriptor_to_string'; +import BufferedConsole from './BufferedConsole'; +import clearLine from './clearLine'; +import CustomConsole from './CustomConsole'; +import ErrorWithStack from './ErrorWithStack'; +import FakeTimers from './FakeTimers'; +import formatTestResults from './formatTestResults'; +import getFailedSnapshotTests from './getFailedSnapshotTests'; +import getConsoleOutput from './getConsoleOutput'; +import installCommonGlobals from './installCommonGlobals'; +import NullConsole from './NullConsole'; +import isInteractive from './isInteractive'; +import getCallsite from './getCallsite'; +import setGlobal from './setGlobal'; +import deepCyclicCopy from './deepCyclicCopy'; +import convertDescriptorToString from './convertDescriptorToString'; const createDirectory = (path: string) => { try { @@ -37,7 +37,7 @@ const createDirectory = (path: string) => { module.exports = { BufferedConsole, - Console, + Console: CustomConsole, ErrorWithStack, FakeTimers, NullConsole, diff --git a/packages/jest-util/src/install_common_globals.js b/packages/jest-util/src/installCommonGlobals.js similarity index 72% rename from packages/jest-util/src/install_common_globals.js rename to packages/jest-util/src/installCommonGlobals.js index 5eada9d75747..571972449af7 100644 --- a/packages/jest-util/src/install_common_globals.js +++ b/packages/jest-util/src/installCommonGlobals.js @@ -10,16 +10,20 @@ import type {ConfigGlobals} from 'types/Config'; import type {Global} from 'types/Global'; -import createProcessObject from './create_process_object'; -import deepCyclicCopy from './deep_cyclic_copy'; +import createProcessObject from './createProcessObject'; +import deepCyclicCopy from './deepCyclicCopy'; const DTRACE = Object.keys(global).filter(key => key.startsWith('DTRACE')); export default function(globalObject: Global, globals: ConfigGlobals) { globalObject.process = createProcessObject(); - // Keep a reference to "Promise", since "jasmine_light.js" needs it. - globalObject[globalObject.Symbol.for('jest-native-promise')] = Promise; + const symbol = globalObject.Symbol; + // Keep a reference to some globals that Jest needs + globalObject[symbol.for('jest-native-promise')] = Promise; + globalObject[symbol.for('jest-native-now')] = globalObject.Date.now.bind( + globalObject.Date, + ); // Forward some APIs. DTRACE.forEach(dtrace => { diff --git a/packages/jest-util/src/is_interative.js b/packages/jest-util/src/isInteractive.js similarity index 100% rename from packages/jest-util/src/is_interative.js rename to packages/jest-util/src/isInteractive.js diff --git a/packages/jest-util/src/set_global.js b/packages/jest-util/src/setGlobal.js similarity index 100% rename from packages/jest-util/src/set_global.js rename to packages/jest-util/src/setGlobal.js diff --git a/packages/jest-validate/package.json b/packages/jest-validate/package.json index e9a5683cebec..33a9d90b4bb4 100644 --- a/packages/jest-validate/package.json +++ b/packages/jest-validate/package.json @@ -12,5 +12,8 @@ "jest-get-type": "^22.1.0", "leven": "^2.1.0", "pretty-format": "^23.6.0" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-validate/src/__tests__/__snapshots__/validate_cli_options.test.js.snap b/packages/jest-validate/src/__tests__/__snapshots__/validateCLIOptions.test.js.snap similarity index 100% rename from packages/jest-validate/src/__tests__/__snapshots__/validate_cli_options.test.js.snap rename to packages/jest-validate/src/__tests__/__snapshots__/validateCLIOptions.test.js.snap diff --git a/packages/jest-validate/src/__tests__/fixtures/jest_config.js b/packages/jest-validate/src/__tests__/fixtures/jestConfig.js similarity index 100% rename from packages/jest-validate/src/__tests__/fixtures/jest_config.js rename to packages/jest-validate/src/__tests__/fixtures/jestConfig.js diff --git a/packages/jest-validate/src/__tests__/validate.test.js b/packages/jest-validate/src/__tests__/validate.test.js index a03895a813c6..9f93b9a713e2 100644 --- a/packages/jest-validate/src/__tests__/validate.test.js +++ b/packages/jest-validate/src/__tests__/validate.test.js @@ -10,14 +10,14 @@ import validate from '../validate'; import {multipleValidOptions} from '../condition'; -import jestValidateExampleConfig from '../example_config'; -import jestValidateDefaultConfig from '../default_config'; +import jestValidateExampleConfig from '../exampleConfig'; +import jestValidateDefaultConfig from '../defaultConfig'; const { defaultConfig, validConfig, deprecatedConfig, -} = require('./fixtures/jest_config'); +} = require('./fixtures/jestConfig'); test('recursively validates default Jest config', () => { expect( @@ -266,3 +266,23 @@ test('Repeated types within multiple valid examples are coalesced in error repor ), ).toThrowErrorMatchingSnapshot(); }); + +test('Comments in config JSON using "//" key are not warned', () => { + jest.spyOn(console, 'warn').mockImplementation(() => {}); + const config = {'//': 'a comment'}; + + validate(config, { + exampleConfig: validConfig, + }); + expect(console.warn).not.toBeCalled(); + + console.warn.mockReset(); + + validate(config, { + exampleConfig: validConfig, + recursiveBlacklist: [('myCustomKey': "don't validate this")], + }); + expect(console.warn).not.toBeCalled(); + + console.warn.mockRestore(); +}); diff --git a/packages/jest-validate/src/__tests__/validate_cli_options.test.js b/packages/jest-validate/src/__tests__/validateCLIOptions.test.js similarity index 94% rename from packages/jest-validate/src/__tests__/validate_cli_options.test.js rename to packages/jest-validate/src/__tests__/validateCLIOptions.test.js index 44348836fc1e..fb4eba73321d 100644 --- a/packages/jest-validate/src/__tests__/validate_cli_options.test.js +++ b/packages/jest-validate/src/__tests__/validateCLIOptions.test.js @@ -8,7 +8,7 @@ 'use strict'; -import validateCLIOptions from '../validate_cli_options'; +import validateCLIOptions from '../validateCLIOptions'; test('validates yargs special options', () => { const options = ['$0', '_', 'help', 'h']; diff --git a/packages/jest-validate/src/default_config.js b/packages/jest-validate/src/defaultConfig.js similarity index 89% rename from packages/jest-validate/src/default_config.js rename to packages/jest-validate/src/defaultConfig.js index 2d3d097e6c67..cef77d526f4d 100644 --- a/packages/jest-validate/src/default_config.js +++ b/packages/jest-validate/src/defaultConfig.js @@ -23,7 +23,8 @@ export default ({ error: errorMessage, exampleConfig: {}, recursive: true, - recursiveBlacklist: [], + // Allow NPM-sanctioned comments in package.json. Use a "//" key. + recursiveBlacklist: ['//'], title: { deprecation: DEPRECATION, error: ERROR, diff --git a/packages/jest-validate/src/example_config.js b/packages/jest-validate/src/exampleConfig.js similarity index 100% rename from packages/jest-validate/src/example_config.js rename to packages/jest-validate/src/exampleConfig.js diff --git a/packages/jest-validate/src/index.js b/packages/jest-validate/src/index.js index 3a62ea7e5315..75806e93bb2a 100644 --- a/packages/jest-validate/src/index.js +++ b/packages/jest-validate/src/index.js @@ -14,7 +14,7 @@ import { ValidationError, } from './utils'; import validate from './validate'; -import validateCLIOptions from './validate_cli_options'; +import validateCLIOptions from './validateCLIOptions'; import {multipleValidOptions} from './condition'; module.exports = { diff --git a/packages/jest-validate/src/validate.js b/packages/jest-validate/src/validate.js index 96c54cdcf7a4..832cba8814ee 100644 --- a/packages/jest-validate/src/validate.js +++ b/packages/jest-validate/src/validate.js @@ -9,7 +9,7 @@ import type {ValidationOptions} from './types'; -import defaultConfig from './default_config'; +import defaultConfig from './defaultConfig'; let hasDeprecationWarnings = false; @@ -81,10 +81,17 @@ const _validate = ( const validate = (config: Object, options: ValidationOptions) => { hasDeprecationWarnings = false; + // Preserve default blacklist entries even with user-supplied blacklist + const combinedBlacklist: Array = [].concat( + defaultConfig.recursiveBlacklist || [], + options.recursiveBlacklist || [], + ); + const defaultedOptions: ValidationOptions = Object.assign( {}, defaultConfig, options, + {recursiveBlacklist: combinedBlacklist}, {title: Object.assign({}, defaultConfig.title, options.title)}, ); diff --git a/packages/jest-validate/src/validate_cli_options.js b/packages/jest-validate/src/validateCLIOptions.js similarity index 98% rename from packages/jest-validate/src/validate_cli_options.js rename to packages/jest-validate/src/validateCLIOptions.js index 28f3debff0bf..255093959030 100644 --- a/packages/jest-validate/src/validate_cli_options.js +++ b/packages/jest-validate/src/validateCLIOptions.js @@ -12,7 +12,7 @@ import type {Argv} from 'types/Argv'; import chalk from 'chalk'; import {createDidYouMeanMessage, format, ValidationError} from './utils'; import {deprecationWarning} from './deprecated'; -import defaultConfig from './default_config'; +import defaultConfig from './defaultConfig'; const BULLET: string = chalk.bold('\u25cf'); export const DOCUMENTATION_NOTE = ` ${chalk.bold('CLI Options Documentation:')} diff --git a/packages/jest-watcher/package.json b/packages/jest-watcher/package.json index 3a3aa657ae26..a2da8dd11379 100644 --- a/packages/jest-watcher/package.json +++ b/packages/jest-watcher/package.json @@ -15,6 +15,9 @@ "bugs": { "url": "https://github.com/facebook/jest/issues" }, + "engines": { + "node": ">= 6" + }, "homepage": "https://jestjs.io/", "license": "MIT" } diff --git a/packages/jest-watcher/src/base_watch_plugin.js b/packages/jest-watcher/src/BaseWatchPlugin.js similarity index 100% rename from packages/jest-watcher/src/base_watch_plugin.js rename to packages/jest-watcher/src/BaseWatchPlugin.js diff --git a/packages/jest-watcher/src/jest_hooks.js b/packages/jest-watcher/src/JestHooks.js similarity index 100% rename from packages/jest-watcher/src/jest_hooks.js rename to packages/jest-watcher/src/JestHooks.js diff --git a/packages/jest-watcher/src/pattern_prompt.js b/packages/jest-watcher/src/PatternPrompt.js similarity index 100% rename from packages/jest-watcher/src/pattern_prompt.js rename to packages/jest-watcher/src/PatternPrompt.js diff --git a/packages/jest-watcher/src/index.js b/packages/jest-watcher/src/index.js index fbe13be62b2c..ec145efba3bd 100644 --- a/packages/jest-watcher/src/index.js +++ b/packages/jest-watcher/src/index.js @@ -7,9 +7,9 @@ * @flow */ -export {default as BaseWatchPlugin} from './base_watch_plugin'; -export {default as JestHook} from './jest_hooks'; -export {default as PatternPrompt} from './pattern_prompt'; +export {default as BaseWatchPlugin} from './BaseWatchPlugin'; +export {default as JestHook} from './JestHooks'; +export {default as PatternPrompt} from './PatternPrompt'; export * from './constants'; export {default as Prompt} from './lib/Prompt'; -export * from './lib/pattern_mode_helpers'; +export * from './lib/patternModeHelpers'; diff --git a/packages/jest-watcher/src/lib/__tests__/__snapshots__/format_test_name_by_pattern.test.js.snap b/packages/jest-watcher/src/lib/__tests__/__snapshots__/formatTestNameByPattern.test.js.snap similarity index 100% rename from packages/jest-watcher/src/lib/__tests__/__snapshots__/format_test_name_by_pattern.test.js.snap rename to packages/jest-watcher/src/lib/__tests__/__snapshots__/formatTestNameByPattern.test.js.snap diff --git a/packages/jest-watcher/src/lib/__tests__/format_test_name_by_pattern.test.js b/packages/jest-watcher/src/lib/__tests__/formatTestNameByPattern.test.js similarity index 97% rename from packages/jest-watcher/src/lib/__tests__/format_test_name_by_pattern.test.js rename to packages/jest-watcher/src/lib/__tests__/formatTestNameByPattern.test.js index 08b688f77aae..16bea7d02681 100644 --- a/packages/jest-watcher/src/lib/__tests__/format_test_name_by_pattern.test.js +++ b/packages/jest-watcher/src/lib/__tests__/formatTestNameByPattern.test.js @@ -9,7 +9,7 @@ 'use strict'; -import formatTestNameByPattern from '../format_test_name_by_pattern'; +import formatTestNameByPattern from '../formatTestNameByPattern'; describe('for multiline test name returns', () => { const testNames = [ diff --git a/packages/jest-watcher/src/lib/__tests__/scroll_list.test.js b/packages/jest-watcher/src/lib/__tests__/scroll.test.js similarity index 97% rename from packages/jest-watcher/src/lib/__tests__/scroll_list.test.js rename to packages/jest-watcher/src/lib/__tests__/scroll.test.js index c63a357cb3f6..171e5c98b03e 100644 --- a/packages/jest-watcher/src/lib/__tests__/scroll_list.test.js +++ b/packages/jest-watcher/src/lib/__tests__/scroll.test.js @@ -1,6 +1,6 @@ // Copyright (c) 2014-present, Facebook, Inc. All rights reserved. -import scroll from '../scroll_list'; +import scroll from '../scroll'; it('When offset is -1', () => { expect(scroll(25, {max: 10, offset: -1})).toEqual({ diff --git a/packages/jest-watcher/src/lib/format_test_name_by_pattern.js b/packages/jest-watcher/src/lib/formatTestNameByPattern.js similarity index 98% rename from packages/jest-watcher/src/lib/format_test_name_by_pattern.js rename to packages/jest-watcher/src/lib/formatTestNameByPattern.js index ea07bfc726ee..ac4b3b090bec 100644 --- a/packages/jest-watcher/src/lib/format_test_name_by_pattern.js +++ b/packages/jest-watcher/src/lib/formatTestNameByPattern.js @@ -31,7 +31,6 @@ export default (testName: string, pattern: string, width: number) => { return chalk.dim(inlineTestName); } - // $FlowFixMe const startPatternIndex = Math.max(match.index, 0); const endPatternIndex = startPatternIndex + match[0].length; diff --git a/packages/jest-watcher/src/lib/pattern_mode_helpers.js b/packages/jest-watcher/src/lib/patternModeHelpers.js similarity index 100% rename from packages/jest-watcher/src/lib/pattern_mode_helpers.js rename to packages/jest-watcher/src/lib/patternModeHelpers.js diff --git a/packages/jest-watcher/src/lib/scroll_list.js b/packages/jest-watcher/src/lib/scroll.js similarity index 100% rename from packages/jest-watcher/src/lib/scroll_list.js rename to packages/jest-watcher/src/lib/scroll.js diff --git a/packages/jest-worker/README.md b/packages/jest-worker/README.md index 7f4fa10d6212..7913d73d3409 100644 --- a/packages/jest-worker/README.md +++ b/packages/jest-worker/README.md @@ -24,7 +24,7 @@ This example covers the minimal usage: import Worker from 'jest-worker'; async function main() { - const worker = new Worker(require.resolve('./worker')); + const worker = new Worker(require.resolve('./Worker')); const result = await worker.hello('Alice'); // "Hello, Alice" } @@ -114,7 +114,7 @@ This example covers the standard usage: import Worker from 'jest-worker'; async function main() { - const myWorker = new Worker(require.resolve('./worker'), { + const myWorker = new Worker(require.resolve('./Worker'), { exposedMethods: ['foo', 'bar', 'getWorkerId'], numWorkers: 4, }); @@ -155,7 +155,7 @@ This example covers the usage with a `computeWorkerKey` method: import Worker from 'jest-worker'; async function main() { - const myWorker = new Worker(require.resolve('./worker'), { + const myWorker = new Worker(require.resolve('./Worker'), { computeWorkerKey: (method, filename) => filename, }); diff --git a/packages/jest-worker/package.json b/packages/jest-worker/package.json index ee62bac69d07..5506a0d7160e 100644 --- a/packages/jest-worker/package.json +++ b/packages/jest-worker/package.json @@ -9,5 +9,8 @@ "main": "build/index.js", "dependencies": { "merge-stream": "^1.0.1" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/jest-worker/src/worker.js b/packages/jest-worker/src/Worker.js similarity index 100% rename from packages/jest-worker/src/worker.js rename to packages/jest-worker/src/Worker.js diff --git a/packages/jest-worker/src/__tests__/worker.test.js b/packages/jest-worker/src/__tests__/Worker.test.js similarity index 99% rename from packages/jest-worker/src/__tests__/worker.test.js rename to packages/jest-worker/src/__tests__/Worker.test.js index 4fd91d9e3de6..cad8347df5b9 100644 --- a/packages/jest-worker/src/__tests__/worker.test.js +++ b/packages/jest-worker/src/__tests__/Worker.test.js @@ -38,7 +38,7 @@ beforeEach(() => { return forkInterface; }); - Worker = require('../worker').default; + Worker = require('../Worker').default; }); afterEach(() => { diff --git a/packages/jest-worker/src/__tests__/index.test.js b/packages/jest-worker/src/__tests__/index.test.js index 349a91797b5f..ba49151b6ec2 100644 --- a/packages/jest-worker/src/__tests__/index.test.js +++ b/packages/jest-worker/src/__tests__/index.test.js @@ -30,7 +30,7 @@ beforeEach(() => { // The worker mock returns a worker with custom methods, plus it stores them // in a global list, so that they can be accessed later. This list is reset in // every test. - jest.mock('../worker', () => { + jest.mock('../Worker', () => { const fakeClass = jest.fn(() => { const fakeWorker = { getStderr: () => ({once() {}, pipe() {}}), @@ -63,7 +63,7 @@ beforeEach(() => { virtual: true, }); - Worker = require('../worker').default; + Worker = require('../Worker').default; Farm = require('../index').default; }); diff --git a/packages/jest-worker/src/index.js b/packages/jest-worker/src/index.js index 0cb636c2fc6c..be634f2fecbd 100644 --- a/packages/jest-worker/src/index.js +++ b/packages/jest-worker/src/index.js @@ -17,7 +17,7 @@ import type {FarmOptions} from './types'; import type {Readable} from 'stream'; import {CHILD_MESSAGE_CALL, CHILD_MESSAGE_END} from './types'; -import Worker from './worker'; +import Worker from './Worker'; /* istanbul ignore next */ const emptyMethod = () => {}; diff --git a/packages/jest-worker/src/types.js b/packages/jest-worker/src/types.js index a8afc71f9f58..5d7108f9fb30 100644 --- a/packages/jest-worker/src/types.js +++ b/packages/jest-worker/src/types.js @@ -29,7 +29,7 @@ export type PARENT_MESSAGE_ERROR = // Option objects. -import type Worker from './worker'; +import type Worker from './Worker'; export type ForkOptions = { cwd?: string, diff --git a/packages/pretty-format/package.json b/packages/pretty-format/package.json index 242e4ae48e62..916a74227d6e 100644 --- a/packages/pretty-format/package.json +++ b/packages/pretty-format/package.json @@ -19,5 +19,8 @@ "react": "*", "react-dom": "*", "react-test-renderer": "*" + }, + "engines": { + "node": ">= 6" } } diff --git a/packages/pretty-format/perf/test.js b/packages/pretty-format/perf/test.js index 9bc97b44099f..e8ac99df480c 100644 --- a/packages/pretty-format/perf/test.js +++ b/packages/pretty-format/perf/test.js @@ -12,7 +12,7 @@ const React = require('react'); const ReactTestRenderer = require('react-test-renderer'); const leftPad = require('left-pad'); const prettyFormat = require('../build'); -const ReactTestComponent = require('../build/plugins/react_test_component'); +const ReactTestComponent = require('../build/plugins/ReactTestComponent'); const worldGeoJson = require('./world.geo.json'); const NANOSECONDS = 1000000000; diff --git a/packages/pretty-format/src/__tests__/asymmetric_matcher.test.js b/packages/pretty-format/src/__tests__/AsymmetricMatcher.test.js similarity index 100% rename from packages/pretty-format/src/__tests__/asymmetric_matcher.test.js rename to packages/pretty-format/src/__tests__/AsymmetricMatcher.test.js diff --git a/packages/pretty-format/src/__tests__/convert_ansi.test.js b/packages/pretty-format/src/__tests__/ConvertAnsi.test.js similarity index 100% rename from packages/pretty-format/src/__tests__/convert_ansi.test.js rename to packages/pretty-format/src/__tests__/ConvertAnsi.test.js diff --git a/packages/pretty-format/src/__tests__/dom_collection.test.js b/packages/pretty-format/src/__tests__/DOMCollection.test.js similarity index 98% rename from packages/pretty-format/src/__tests__/dom_collection.test.js rename to packages/pretty-format/src/__tests__/DOMCollection.test.js index cf0d498f8b18..204068c2561c 100644 --- a/packages/pretty-format/src/__tests__/dom_collection.test.js +++ b/packages/pretty-format/src/__tests__/DOMCollection.test.js @@ -13,7 +13,7 @@ const prettyFormat = require('../'); const {DOMCollection, DOMElement} = prettyFormat.plugins; -const toPrettyPrintTo = require('./expect_util').getPrettyPrint([ +const toPrettyPrintTo = require('./getPrettyPrint').default([ DOMCollection, DOMElement, ]); diff --git a/packages/pretty-format/src/__tests__/dom_element.test.js b/packages/pretty-format/src/__tests__/DOMElement.test.js similarity index 99% rename from packages/pretty-format/src/__tests__/dom_element.test.js rename to packages/pretty-format/src/__tests__/DOMElement.test.js index 9b06b105766c..e211b33665d9 100644 --- a/packages/pretty-format/src/__tests__/dom_element.test.js +++ b/packages/pretty-format/src/__tests__/DOMElement.test.js @@ -13,7 +13,7 @@ const prettyFormat = require('../'); const {DOMElement} = prettyFormat.plugins; -const toPrettyPrintTo = require('./expect_util').getPrettyPrint([DOMElement]); +const toPrettyPrintTo = require('./getPrettyPrint').default([DOMElement]); const expect: any = global.expect; expect.extend({toPrettyPrintTo}); diff --git a/packages/pretty-format/src/__tests__/immutable.test.js b/packages/pretty-format/src/__tests__/Immutable.test.js similarity index 99% rename from packages/pretty-format/src/__tests__/immutable.test.js rename to packages/pretty-format/src/__tests__/Immutable.test.js index b402605cf85c..4e0e40cf91e7 100644 --- a/packages/pretty-format/src/__tests__/immutable.test.js +++ b/packages/pretty-format/src/__tests__/Immutable.test.js @@ -11,7 +11,7 @@ import React from 'react'; import Immutable from 'immutable'; -import {getPrettyPrint} from './expect_util'; +import getPrettyPrint from './getPrettyPrint'; const {plugins: prettyPlugins} = require('..'); const {Immutable: ImmutablePlugin, ReactElement} = prettyPlugins; diff --git a/packages/pretty-format/src/__tests__/expect_util.js b/packages/pretty-format/src/__tests__/getPrettyPrint.js similarity index 95% rename from packages/pretty-format/src/__tests__/expect_util.js rename to packages/pretty-format/src/__tests__/getPrettyPrint.js index 1fb9a1dc0d8b..5ee5ee04929b 100644 --- a/packages/pretty-format/src/__tests__/expect_util.js +++ b/packages/pretty-format/src/__tests__/getPrettyPrint.js @@ -14,7 +14,7 @@ import type {OptionsReceived, Plugins} from 'types/PrettyFormat'; const diff = require('jest-diff'); const prettyFormat = require('../'); -export const getPrettyPrint = (plugins: Plugins) => +const getPrettyPrint = (plugins: Plugins) => function(received: any, expected: any, options?: OptionsReceived) { const prettyFormatted = prettyFormat( received, @@ -52,3 +52,5 @@ export const getPrettyPrint = (plugins: Plugins) => return {actual: prettyFormatted, message, pass}; }; + +export default getPrettyPrint; diff --git a/packages/pretty-format/src/__tests__/pretty_format.test.js b/packages/pretty-format/src/__tests__/prettyFormat.test.js similarity index 100% rename from packages/pretty-format/src/__tests__/pretty_format.test.js rename to packages/pretty-format/src/__tests__/prettyFormat.test.js diff --git a/packages/pretty-format/src/__tests__/react.test.js b/packages/pretty-format/src/__tests__/react.test.js index 77ed951983e4..ebd2a4fd4cd1 100644 --- a/packages/pretty-format/src/__tests__/react.test.js +++ b/packages/pretty-format/src/__tests__/react.test.js @@ -272,12 +272,7 @@ test('supports a single element with custom React elements with props (using dis test('supports a single element with custom React elements with props (using anonymous function)', () => { assertPrintedJSX( React.createElement('Mouse', { - prop: React.createElement( - () => { - React.createElement('div'); - }, - {foo: 'bar'}, - ), + prop: React.createElement(() => React.createElement('div'), {foo: 'bar'}), }), '\n }\n/>', ); diff --git a/packages/pretty-format/src/index.js b/packages/pretty-format/src/index.js index 3c193f1a17d3..618a6fc62971 100644 --- a/packages/pretty-format/src/index.js +++ b/packages/pretty-format/src/index.js @@ -28,13 +28,13 @@ import { printObjectProperties, } from './collections'; -import AsymmetricMatcher from './plugins/asymmetric_matcher'; -import ConvertAnsi from './plugins/convert_ansi'; -import DOMCollection from './plugins/dom_collection'; -import DOMElement from './plugins/dom_element'; -import Immutable from './plugins/immutable'; -import ReactElement from './plugins/react_element'; -import ReactTestComponent from './plugins/react_test_component'; +import AsymmetricMatcher from './plugins/AsymmetricMatcher'; +import ConvertAnsi from './plugins/ConvertAnsi'; +import DOMCollection from './plugins/DOMCollection'; +import DOMElement from './plugins/DOMElement'; +import Immutable from './plugins/Immutable'; +import ReactElement from './plugins/ReactElement'; +import ReactTestComponent from './plugins/ReactTestComponent'; const toString = Object.prototype.toString; const toISOString = Date.prototype.toISOString; diff --git a/packages/pretty-format/src/plugins/asymmetric_matcher.js b/packages/pretty-format/src/plugins/AsymmetricMatcher.js similarity index 100% rename from packages/pretty-format/src/plugins/asymmetric_matcher.js rename to packages/pretty-format/src/plugins/AsymmetricMatcher.js diff --git a/packages/pretty-format/src/plugins/convert_ansi.js b/packages/pretty-format/src/plugins/ConvertAnsi.js similarity index 100% rename from packages/pretty-format/src/plugins/convert_ansi.js rename to packages/pretty-format/src/plugins/ConvertAnsi.js diff --git a/packages/pretty-format/src/plugins/dom_collection.js b/packages/pretty-format/src/plugins/DOMCollection.js similarity index 100% rename from packages/pretty-format/src/plugins/dom_collection.js rename to packages/pretty-format/src/plugins/DOMCollection.js diff --git a/packages/pretty-format/src/plugins/dom_element.js b/packages/pretty-format/src/plugins/DOMElement.js similarity index 100% rename from packages/pretty-format/src/plugins/dom_element.js rename to packages/pretty-format/src/plugins/DOMElement.js diff --git a/packages/pretty-format/src/plugins/immutable.js b/packages/pretty-format/src/plugins/Immutable.js similarity index 100% rename from packages/pretty-format/src/plugins/immutable.js rename to packages/pretty-format/src/plugins/Immutable.js diff --git a/packages/pretty-format/src/plugins/react_element.js b/packages/pretty-format/src/plugins/ReactElement.js similarity index 100% rename from packages/pretty-format/src/plugins/react_element.js rename to packages/pretty-format/src/plugins/ReactElement.js diff --git a/packages/pretty-format/src/plugins/react_test_component.js b/packages/pretty-format/src/plugins/ReactTestComponent.js similarity index 100% rename from packages/pretty-format/src/plugins/react_test_component.js rename to packages/pretty-format/src/plugins/ReactTestComponent.js diff --git a/packages/pretty-format/src/plugins/lib/escape_html.js b/packages/pretty-format/src/plugins/lib/escapeHTML.js similarity index 100% rename from packages/pretty-format/src/plugins/lib/escape_html.js rename to packages/pretty-format/src/plugins/lib/escapeHTML.js diff --git a/packages/pretty-format/src/plugins/lib/markup.js b/packages/pretty-format/src/plugins/lib/markup.js index eb45835588a0..70ebceebc806 100644 --- a/packages/pretty-format/src/plugins/lib/markup.js +++ b/packages/pretty-format/src/plugins/lib/markup.js @@ -9,7 +9,7 @@ import type {Config, Printer, Refs} from 'types/PrettyFormat'; -import escapeHTML from './escape_html'; +import escapeHTML from './escapeHTML'; // Return empty string if keys is empty. export const printProps = ( diff --git a/scripts/babel-plugin-jest-native-promise.js b/scripts/babel-plugin-jest-native-globals.js similarity index 52% rename from scripts/babel-plugin-jest-native-promise.js rename to scripts/babel-plugin-jest-native-globals.js index b0fa9e57db85..a0e01e20b942 100644 --- a/scripts/babel-plugin-jest-native-promise.js +++ b/scripts/babel-plugin-jest-native-globals.js @@ -14,17 +14,34 @@ module.exports = ({template}) => { const promiseDeclaration = template(` var Promise = global[Symbol.for('jest-native-promise')] || global.Promise; `); + const nowDeclaration = template(` + var jestNow = global[Symbol.for('jest-native-now')] || global.Date.now; + `); return { - name: 'jest-native-promise', + name: 'jest-native-globals', visitor: { ReferencedIdentifier(path, state) { - if (path.node.name === 'Promise' && !state.injectedPromise) { - state.injectedPromise = true; + if (path.node.name === 'Promise' && !state.jestInjectedPromise) { + state.jestInjectedPromise = true; path .findParent(p => p.isProgram()) .unshiftContainer('body', promiseDeclaration()); } + if ( + path.node.name === 'Date' && + path.parent.property && + path.parent.property.name === 'now' + ) { + if (!state.jestInjectedNow) { + state.jestInjectedNow = true; + path + .findParent(p => p.isProgram()) + .unshiftContainer('body', nowDeclaration()); + } + + path.parentPath.replaceWithSourceString('jestNow'); + } }, }, }; diff --git a/scripts/browserBuild.js b/scripts/browserBuild.js index a807294210b8..d7d879820b58 100644 --- a/scripts/browserBuild.js +++ b/scripts/browserBuild.js @@ -48,7 +48,7 @@ function browserBuild(pkgName, entryPath, destination) { { resolveId(id) { return id === 'chalk' - ? path.resolve(__dirname, '../packages/expect/build/fake_chalk.js') + ? path.resolve(__dirname, '../packages/expect/build/fakeChalk.js') : undefined; }, }, diff --git a/scripts/build.js b/scripts/build.js index 23fba8be03c9..fa3e256272a1 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -151,9 +151,9 @@ function buildFile(file, silent) { if (INLINE_REQUIRE_BLACKLIST.test(file)) { // The modules in the blacklist are injected into the user's sandbox - // We need to guard `Promise` there. + // We need to guard some globals there. options.plugins.push( - require.resolve('./babel-plugin-jest-native-promise') + require.resolve('./babel-plugin-jest-native-globals') ); } else { options.plugins = options.plugins.map(plugin => { diff --git a/scripts/checkCopyrightHeaders.js b/scripts/checkCopyrightHeaders.js new file mode 100755 index 000000000000..43ecbc7c7388 --- /dev/null +++ b/scripts/checkCopyrightHeaders.js @@ -0,0 +1,148 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +const fs = require('fs'); +const {execSync} = require('child_process'); +const isbinaryfile = require('isbinaryfile'); + +const getFileContents = path => fs.readFileSync(path, {encoding: 'utf-8'}); +const isDirectory = path => fs.lstatSync(path).isDirectory(); +const createRegExp = pattern => new RegExp(pattern); + +// Important: this patterns must be in sync with internal Facebook tools + +const GENERIC_IGNORED_EXTENSIONS = [ + 'lock', + 'patch', + 'exe', + 'bin', + 'cfg', + 'config', + 'conf', + 'html', + 'md', + 'markdown', + 'opam', + 'osm', + 'descr', + 'rst', + 'json', + 'key', + 'ini', + 'plist', + 'snap', + 'svg', + 'txt', + 'xcodeproj', + 'xcscheme', + 'xml', + 'yaml', + 'yml', + 'textile', + 'tsv', + 'csv', + 'pem', + 'csr', + 'der', + 'crt', + 'cert', + 'cer', + 'p7b', + 'iml', + 'org', + 'podspec', + 'modulemap', + 'pch', + 'lproj', + 'xcworkspace', + 'storyboard', + 'tvml', + 'xib', + 'pbxproj', + 'xcworkspacedata', + 'xccheckout', + 'xcsettings', + 'strings', + 'ipynb', + 'htm', + 'toml', +].map(extension => createRegExp(`\.${extension}$`)); + +const GENERIC_IGNORED_PATTERNS = [ + '(^|/)\\.[^/]+(/|$)', + + 'third[_\\-. ]party/', + '^node[_\\-. ]modules/', + 'gradlew\\.bat$', + 'gradlew$', + 'gradle/wrapper/', + '.idea/', + '__init__\\.py$', + '^Setup.hs$', + '^(readme|README|Readme)\\..*$', + 'Cargo\\.toml$', + '^Cartfile.*$', + '^.*\\.xcodeproj/$', + '^.*\\.xcworkspace/$', + '^.*\\.lproj/$', + '^.*\\.bundle/$', + '^MANIFEST\\.in$', +].map(createRegExp); + +const CUSTOM_IGNORED_PATTERNS = [ + '\\.(example|map)$', + '^examples/.*', + '^flow-typed/.*', + '^packages/expect/src/jasmineUtils\\.js$', + '^packages/jest-config/src/vendor/jsonlint\\.js$', +].map(createRegExp); + +const IGNORED_PATTERNS = [ + ...GENERIC_IGNORED_EXTENSIONS, + ...GENERIC_IGNORED_PATTERNS, + ...CUSTOM_IGNORED_PATTERNS, +]; + +const INCLUDED_PATTERNS = [ + // Any file with an extension + /\.[^/]+$/, +]; + +const COPYRIGHT_HEADER_RE = /(Facebook, Inc(.|,)? and its affiliates)|([0-9]{4}-present(.|,)? Facebook)|([0-9]{4}(.|,)? Facebook)/; + +function needsCopyrightHeader(file) { + const contents = getFileContents(file); + return contents.trim().length > 0 && !COPYRIGHT_HEADER_RE.test(contents); +} + +function check() { + const allFiles = execSync('git ls-files', {encoding: 'utf-8'}) + .trim() + .split('\n'); + + const invalidFiles = allFiles.filter( + file => + INCLUDED_PATTERNS.some(pattern => pattern.test(file)) && + !IGNORED_PATTERNS.some(pattern => pattern.test(file)) && + !isDirectory(file) && + !isbinaryfile.sync(file) && + needsCopyrightHeader(file) + ); + + if (invalidFiles.length > 0) { + console.log(`Facebook copyright header check failed for the following files: + + ${invalidFiles.join('\n ')} + +Please include the header or blacklist the files in \`scripts/checkCopyrightHeaders.js\``); + process.exit(1); + } +} + +check(); diff --git a/types/Config.js b/types/Config.js index c152976c0957..39eac782b764 100644 --- a/types/Config.js +++ b/types/Config.js @@ -11,6 +11,7 @@ export type Path = string; export type Glob = string; export type HasteConfig = {| + computeSha1?: boolean, defaultPlatform?: ?string, hasteImplModulePath?: string, platforms?: Array, @@ -36,6 +37,7 @@ export type DefaultOptions = {| coverageReporters: Array, coverageThreshold: ?{global: {[key: string]: number}}, cwd: Path, + dependencyExtractor: ?string, errorOnDeprecated: boolean, expand: boolean, filter: ?Path, @@ -104,6 +106,7 @@ export type InitialOptions = { coveragePathIgnorePatterns?: Array, coverageReporters?: Array, coverageThreshold?: {global: {[key: string]: number}}, + dependencyExtractor?: string, detectLeaks?: boolean, detectOpenHandles?: boolean, displayName?: string, @@ -249,6 +252,7 @@ export type ProjectConfig = {| clearMocks: boolean, coveragePathIgnorePatterns: Array, cwd: Path, + dependencyExtractor?: string, detectLeaks: boolean, detectOpenHandles: boolean, displayName: ?string, diff --git a/types/Environment.js b/types/Environment.js index ed6ee314c490..07128ef7da27 100644 --- a/types/Environment.js +++ b/types/Environment.js @@ -28,6 +28,7 @@ declare class $JestEnvironment { advanceTimersByTime(msToRun: number): void, runOnlyPendingTimers(): void, runWithRealTimers(callback: any): void, + getTimerCount(): number, useFakeTimers(): void, useRealTimers(): void, }; diff --git a/types/HasteMap.js b/types/HasteMap.js index 01865ad57790..ed9825535b4f 100644 --- a/types/HasteMap.js +++ b/types/HasteMap.js @@ -24,14 +24,8 @@ export type ModuleMapData = Map; export type WatchmanClocks = Map; export type HasteRegExp = RegExp | ((str: string) => boolean); -export type DuplicatesSet = { - [filePath: string]: /* type */ number, - __proto__: null, -}; -export type DuplicatesIndex = Map< - string, - {[platform: string]: DuplicatesSet, __proto__: null}, ->; +export type DuplicatesSet = Map; +export type DuplicatesIndex = Map>; export type InternalHasteMap = {| clocks: WatchmanClocks, diff --git a/types/Jest.js b/types/Jest.js index 88966e299f2c..889c45382a95 100644 --- a/types/Jest.js +++ b/types/Jest.js @@ -39,6 +39,7 @@ export type Jest = {| runOnlyPendingTimers(): void, advanceTimersByTime(msToRun: number): void, runTimersToTime(msToRun: number): void, + getTimerCount(): number, setMock(moduleName: string, moduleExports: any): Jest, setTimeout(timeout: number): Jest, spyOn( diff --git a/types/Matchers.js b/types/Matchers.js index 70be4f1a50ab..79e89d433fb8 100644 --- a/types/Matchers.js +++ b/types/Matchers.js @@ -50,7 +50,7 @@ export type Expect = { assertions(number): void, extend(any): void, extractExpectedAssertionsErrors: () => Array<{ - actual: string, + actual: string | number, error: Error, expected: string, }>, diff --git a/types/TestResult.js b/types/TestResult.js index 7ef862e2308e..2cf15a5dbfa9 100644 --- a/types/TestResult.js +++ b/types/TestResult.js @@ -83,7 +83,13 @@ export type AssertionLocation = {| path: string, |}; -export type Status = 'passed' | 'failed' | 'skipped' | 'pending' | 'todo'; +export type Status = + | 'passed' + | 'failed' + | 'skipped' + | 'pending' + | 'todo' + | 'disabled'; export type Bytes = number; export type Milliseconds = number; diff --git a/website/blog/2018-06-27-supporting-jest-open-source.md b/website/blog/2018-06-27-supporting-jest-open-source.md index b8c541695bd3..efb7b4bb4128 100644 --- a/website/blog/2018-06-27-supporting-jest-open-source.md +++ b/website/blog/2018-06-27-supporting-jest-open-source.md @@ -43,11 +43,11 @@ There are two levels of support for the collective: Backer and Sponsor. ### Backers -Backers of the collective are individuals contributing at least $2/month. We'll include a list of backers on the Jest homepage, README on github/yarn/npm, and Contributors page. +Backers of the collective are individuals contributing at least \$2/month. We'll include a list of backers on the Jest homepage, README on github/yarn/npm, and Contributors page. ### Sponsors -Sponsors of the collective are individuals and organizations contributing at least $100/month. We'll place sponsor logos with a link to their site on the Jest homepage, README on github/yarn/npm, and Contributors page. +Sponsors of the collective are individuals and organizations contributing at least \$100/month. We'll place sponsor logos with a link to their site on the Jest homepage, README on github/yarn/npm, and Contributors page. ## What is the goal of the collective diff --git a/website/pages/en/index.js b/website/pages/en/index.js index fe1f7c3ca8b1..f66efc3201f7 100755 --- a/website/pages/en/index.js +++ b/website/pages/en/index.js @@ -49,25 +49,29 @@ class Contributors extends React.Component {

- {backers.filter(b => b.tier === 'sponsor').map(b => ( - - { - { - } - - ))} + {backers + .filter(b => b.tier === 'sponsor') + .map(b => ( + + { + { + } + + ))}

- {backers.filter(b => b.tier === 'backer').map(b => ( - - { - { - } - - ))} + {backers + .filter(b => b.tier === 'backer') + .map(b => ( + + { + { + } + + ))}
{ /* - * Custom implementation of a module that doesn't exist in JS, - * like a generated module or a native module in react-native. - */ + * Custom implementation of a module that doesn't exist in JS, + * like a generated module or a native module in react-native. + */ }, {virtual: true}, ); diff --git a/website/versioned_docs/version-22.0/TutorialAsync.md b/website/versioned_docs/version-22.0/TutorialAsync.md index 63e56c14cc19..49c559a21b1e 100644 --- a/website/versioned_docs/version-22.0/TutorialAsync.md +++ b/website/versioned_docs/version-22.0/TutorialAsync.md @@ -51,13 +51,12 @@ const users = { export default function request(url) { return new Promise((resolve, reject) => { const userID = parseInt(url.substr('/users/'.length), 10); - process.nextTick( - () => - users[userID] - ? resolve(users[userID]) - : reject({ - error: 'User with ' + userID + ' not found.', - }), + process.nextTick(() => + users[userID] + ? resolve(users[userID]) + : reject({ + error: 'User with ' + userID + ' not found.', + }), ); }); } diff --git a/website/versioned_docs/version-22.1/JestObjectAPI.md b/website/versioned_docs/version-22.1/JestObjectAPI.md index 0bccfffd50f5..1ba24bd06849 100644 --- a/website/versioned_docs/version-22.1/JestObjectAPI.md +++ b/website/versioned_docs/version-22.1/JestObjectAPI.md @@ -125,9 +125,9 @@ jest.mock( '../moduleName', () => { /* - * Custom implementation of a module that doesn't exist in JS, - * like a generated module or a native module in react-native. - */ + * Custom implementation of a module that doesn't exist in JS, + * like a generated module or a native module in react-native. + */ }, {virtual: true}, ); diff --git a/website/versioned_docs/version-22.2/JestObjectAPI.md b/website/versioned_docs/version-22.2/JestObjectAPI.md index 8991484ca9b2..e2c69594f6b7 100644 --- a/website/versioned_docs/version-22.2/JestObjectAPI.md +++ b/website/versioned_docs/version-22.2/JestObjectAPI.md @@ -126,9 +126,9 @@ jest.mock( '../moduleName', () => { /* - * Custom implementation of a module that doesn't exist in JS, - * like a generated module or a native module in react-native. - */ + * Custom implementation of a module that doesn't exist in JS, + * like a generated module or a native module in react-native. + */ }, {virtual: true}, ); diff --git a/website/versioned_docs/version-22.2/TutorialAsync.md b/website/versioned_docs/version-22.2/TutorialAsync.md index 2c19a5bc4949..4ab1d6c0b749 100644 --- a/website/versioned_docs/version-22.2/TutorialAsync.md +++ b/website/versioned_docs/version-22.2/TutorialAsync.md @@ -51,13 +51,12 @@ const users = { export default function request(url) { return new Promise((resolve, reject) => { const userID = parseInt(url.substr('/users/'.length), 10); - process.nextTick( - () => - users[userID] - ? resolve(users[userID]) - : reject({ - error: 'User with ' + userID + ' not found.', - }), + process.nextTick(() => + users[userID] + ? resolve(users[userID]) + : reject({ + error: 'User with ' + userID + ' not found.', + }), ); }); } diff --git a/website/versioned_docs/version-22.3/JestObjectAPI.md b/website/versioned_docs/version-22.3/JestObjectAPI.md index 8705bac90005..f3b80dd34935 100644 --- a/website/versioned_docs/version-22.3/JestObjectAPI.md +++ b/website/versioned_docs/version-22.3/JestObjectAPI.md @@ -126,9 +126,9 @@ jest.mock( '../moduleName', () => { /* - * Custom implementation of a module that doesn't exist in JS, - * like a generated module or a native module in react-native. - */ + * Custom implementation of a module that doesn't exist in JS, + * like a generated module or a native module in react-native. + */ }, {virtual: true}, ); diff --git a/website/versioned_docs/version-22.3/MongoDB.md b/website/versioned_docs/version-22.3/MongoDB.md index edbbf86e44c3..3a9285891dd2 100644 --- a/website/versioned_docs/version-22.3/MongoDB.md +++ b/website/versioned_docs/version-22.3/MongoDB.md @@ -27,7 +27,7 @@ const {MongoMemoryServer} = require('mongodb-memory-server'); const globalConfigPath = path.join(__dirname, 'globalConfig.json'); -const mongoServer = new MongoMemoryServer({ +const mongod = new MongoMemoryServer({ autoStart: false, }); @@ -38,14 +38,14 @@ module.exports = async () => { const mongoConfig = { mongoDBName: 'jest', - mongoUri: await mongoServer.getConnectionString(), + mongoUri: await mongod.getConnectionString(), }; // Write global config to disk because all tests run in different contexts. fs.writeFileSync(globalConfigPath, JSON.stringify(mongoConfig)); // Set reference to mongod in order to close the server during teardown. - global.__MONGOD__ = mongoServer; + global.__MONGOD__ = mongod; }; ``` diff --git a/website/versioned_docs/version-22.3/TutorialAsync.md b/website/versioned_docs/version-22.3/TutorialAsync.md index b683f8cf552d..4b8ddce1e516 100644 --- a/website/versioned_docs/version-22.3/TutorialAsync.md +++ b/website/versioned_docs/version-22.3/TutorialAsync.md @@ -51,13 +51,12 @@ const users = { export default function request(url) { return new Promise((resolve, reject) => { const userID = parseInt(url.substr('/users/'.length), 10); - process.nextTick( - () => - users[userID] - ? resolve(users[userID]) - : reject({ - error: 'User with ' + userID + ' not found.', - }), + process.nextTick(() => + users[userID] + ? resolve(users[userID]) + : reject({ + error: 'User with ' + userID + ' not found.', + }), ); }); } diff --git a/website/versioned_docs/version-22.4/JestObjectAPI.md b/website/versioned_docs/version-22.4/JestObjectAPI.md index 383c5faab39d..f0f9b8c0fb74 100644 --- a/website/versioned_docs/version-22.4/JestObjectAPI.md +++ b/website/versioned_docs/version-22.4/JestObjectAPI.md @@ -208,9 +208,9 @@ jest.mock( '../moduleName', () => { /* - * Custom implementation of a module that doesn't exist in JS, - * like a generated module or a native module in react-native. - */ + * Custom implementation of a module that doesn't exist in JS, + * like a generated module or a native module in react-native. + */ }, {virtual: true}, ); diff --git a/website/versioned_docs/version-23.0/JestObjectAPI.md b/website/versioned_docs/version-23.0/JestObjectAPI.md index b962638c922c..91305ed295e4 100644 --- a/website/versioned_docs/version-23.0/JestObjectAPI.md +++ b/website/versioned_docs/version-23.0/JestObjectAPI.md @@ -208,9 +208,9 @@ jest.mock( '../moduleName', () => { /* - * Custom implementation of a module that doesn't exist in JS, - * like a generated module or a native module in react-native. - */ + * Custom implementation of a module that doesn't exist in JS, + * like a generated module or a native module in react-native. + */ }, {virtual: true}, ); diff --git a/website/versioned_docs/version-23.0/MongoDB.md b/website/versioned_docs/version-23.0/MongoDB.md index f12a6d257b31..c73ae02b0623 100644 --- a/website/versioned_docs/version-23.0/MongoDB.md +++ b/website/versioned_docs/version-23.0/MongoDB.md @@ -27,7 +27,7 @@ const {MongoMemoryServer} = require('mongodb-memory-server'); const globalConfigPath = path.join(__dirname, 'globalConfig.json'); -const mongoServer = new MongoMemoryServer({ +const mongod = new MongoMemoryServer({ autoStart: false, }); @@ -38,14 +38,14 @@ module.exports = async () => { const mongoConfig = { mongoDBName: 'jest', - mongoUri: await mongoServer.getConnectionString(), + mongoUri: await mongod.getConnectionString(), }; // Write global config to disk because all tests run in different contexts. fs.writeFileSync(globalConfigPath, JSON.stringify(mongoConfig)); // Set reference to mongod in order to close the server during teardown. - global.__MONGOD__ = mongoServer; + global.__MONGOD__ = mongod; }; ``` diff --git a/website/versioned_docs/version-23.1/JestObjectAPI.md b/website/versioned_docs/version-23.1/JestObjectAPI.md index 700bf9bc85a0..843f45835d31 100644 --- a/website/versioned_docs/version-23.1/JestObjectAPI.md +++ b/website/versioned_docs/version-23.1/JestObjectAPI.md @@ -208,9 +208,9 @@ jest.mock( '../moduleName', () => { /* - * Custom implementation of a module that doesn't exist in JS, - * like a generated module or a native module in react-native. - */ + * Custom implementation of a module that doesn't exist in JS, + * like a generated module or a native module in react-native. + */ }, {virtual: true}, ); diff --git a/website/versioned_docs/version-23.2/JestObjectAPI.md b/website/versioned_docs/version-23.2/JestObjectAPI.md index dce067532b37..7607b1aebb9b 100644 --- a/website/versioned_docs/version-23.2/JestObjectAPI.md +++ b/website/versioned_docs/version-23.2/JestObjectAPI.md @@ -208,9 +208,9 @@ jest.mock( '../moduleName', () => { /* - * Custom implementation of a module that doesn't exist in JS, - * like a generated module or a native module in react-native. - */ + * Custom implementation of a module that doesn't exist in JS, + * like a generated module or a native module in react-native. + */ }, {virtual: true}, ); diff --git a/website/versioned_docs/version-23.2/MongoDB.md b/website/versioned_docs/version-23.2/MongoDB.md index e229d0fa0798..a05fbe53a526 100644 --- a/website/versioned_docs/version-23.2/MongoDB.md +++ b/website/versioned_docs/version-23.2/MongoDB.md @@ -27,7 +27,7 @@ const {MongoMemoryServer} = require('mongodb-memory-server'); const globalConfigPath = path.join(__dirname, 'globalConfig.json'); -const mongoServer = new MongoMemoryServer({ +const mongod = new MongoMemoryServer({ autoStart: false, }); @@ -38,14 +38,14 @@ module.exports = async () => { const mongoConfig = { mongoDBName: 'jest', - mongoUri: await mongoServer.getConnectionString(), + mongoUri: await mongod.getConnectionString(), }; // Write global config to disk because all tests run in different contexts. fs.writeFileSync(globalConfigPath, JSON.stringify(mongoConfig)); // Set reference to mongod in order to close the server during teardown. - global.__MONGOD__ = mongoServer; + global.__MONGOD__ = mongod; }; ``` diff --git a/website/versioned_docs/version-23.3/JestObjectAPI.md b/website/versioned_docs/version-23.3/JestObjectAPI.md index f11e24388bb7..f3a8f4be3d9f 100644 --- a/website/versioned_docs/version-23.3/JestObjectAPI.md +++ b/website/versioned_docs/version-23.3/JestObjectAPI.md @@ -209,9 +209,9 @@ jest.mock( '../moduleName', () => { /* - * Custom implementation of a module that doesn't exist in JS, - * like a generated module or a native module in react-native. - */ + * Custom implementation of a module that doesn't exist in JS, + * like a generated module or a native module in react-native. + */ }, {virtual: true}, ); diff --git a/website/versioned_docs/version-23.5/Configuration.md b/website/versioned_docs/version-23.5/Configuration.md index d7876e3f1569..ce8bb1ae41ae 100644 --- a/website/versioned_docs/version-23.5/Configuration.md +++ b/website/versioned_docs/version-23.5/Configuration.md @@ -871,7 +871,7 @@ An example of such function can be found in our default [jasmine2 test runner pa ### `testURL` [string] -Default: `about:blank` +Default: `http://localhost` This option sets the URL for the jsdom environment. It is reflected in properties such as `location.href`. diff --git a/website/versioned_docs/version-23.5/TutorialAsync.md b/website/versioned_docs/version-23.5/TutorialAsync.md index 82ca5daa10c4..b8847230d31e 100644 --- a/website/versioned_docs/version-23.5/TutorialAsync.md +++ b/website/versioned_docs/version-23.5/TutorialAsync.md @@ -51,13 +51,12 @@ const users = { export default function request(url) { return new Promise((resolve, reject) => { const userID = parseInt(url.substr('/users/'.length), 10); - process.nextTick( - () => - users[userID] - ? resolve(users[userID]) - : reject({ - error: 'User with ' + userID + ' not found.', - }), + process.nextTick(() => + users[userID] + ? resolve(users[userID]) + : reject({ + error: 'User with ' + userID + ' not found.', + }), ); }); } diff --git a/website/versioned_docs/version-23.6/Configuration.md b/website/versioned_docs/version-23.6/Configuration.md index 430741801ac9..a687b04524e4 100644 --- a/website/versioned_docs/version-23.6/Configuration.md +++ b/website/versioned_docs/version-23.6/Configuration.md @@ -881,7 +881,7 @@ An example of such function can be found in our default [jasmine2 test runner pa ### `testURL` [string] -Default: `about:blank` +Default: `http://localhost` This option sets the URL for the jsdom environment. It is reflected in properties such as `location.href`. @@ -938,3 +938,17 @@ Default: `[]` An array of RegExp patterns that are matched against all source file paths before re-running tests in watch mode. If the file path matches any of the patterns, when it is updated, it will not trigger a re-run of tests. These patterns match against the full path. Use the `` string token to include the path to your project's root directory to prevent it from accidentally ignoring all of your files in different environments that may have different root directories. Example: `["/node_modules/"]`. + +### `watchPlugins` [array] + +Default: `[]` + +This option allows you to use a custom watch plugins. Read more about watch plugins [here](watch-plugins). + +Examples of watch plugins include: + +- [`jest-watch-master`](https://github.com/rickhanlonii/jest-watch-master) +- [`jest-watch-select-projects`](https://github.com/rogeliog/jest-watch-select-projects) +- [`jest-watch-suspend`](https://github.com/unional/jest-watch-suspend) +- [`jest-watch-typeahead`](https://github.com/jest-community/jest-watch-typeahead) +- [`jest-watch-yarn-workspaces`](https://github.com/cameronhunter/jest-watch-directories/tree/master/packages/jest-watch-yarn-workspaces) diff --git a/yarn.lock b/yarn.lock index 15fe28bd2a1d..e3004bfb8e56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1408,6 +1408,11 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.2.tgz#54c5a964462be3d4d78af631363c18d6fa91ac26" integrity sha512-yprFYuno9FtNsSHVlSWd+nRlmGoAbqbeCwOryP6sC/zoCjhpArcRMYp19EvpSUSizJAlsXEwJv+wcWS9XaXdMw== +"@sheerun/mutationobserver-shim@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#8013f2af54a2b7d735f71560ff360d3a8176a87b" + integrity sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q== + "@types/cheerio@^0.22.8": version "0.22.9" resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.9.tgz#b5990152604c2ada749b7f88cab3476f21f39d7b" @@ -4607,6 +4612,15 @@ dom-serializer@0, dom-serializer@~0.1.0: domelementtype "~1.1.1" entities "~1.1.1" +dom-testing-library@^3.12.0: + version "3.12.3" + resolved "https://registry.yarnpkg.com/dom-testing-library/-/dom-testing-library-3.12.3.tgz#16c2fbd5198e175cb5499dabf9c64a893724015e" + integrity sha512-HvoxiKm8s4Gf+T52MX+gYHKXkGBM4ZiKYElIZxt8zPZtbT3wokrjKHskzryRqRD3oAKeg6chtUe3gchrurhmvA== + dependencies: + "@sheerun/mutationobserver-shim" "^0.3.2" + pretty-format "^23.6.0" + wait-for-expect "^1.0.0" + dom-walk@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" @@ -4980,7 +4994,7 @@ eslint-config-fbjs@^2.1.0: resolved "https://registry.yarnpkg.com/eslint-config-fbjs/-/eslint-config-fbjs-2.1.0.tgz#bfe4f8c2d2282bfe515359553905d830e3a5d12f" integrity sha512-wh7Lveo51V3/SUydWtR2VEU8wNfSHt5V7YzIUKTRkHF3kvkCwFtM6Jgsn+xBNkjxZGpfWgNJN/drk1LLx64Dww== -eslint-config-prettier@^3.0.1: +eslint-config-prettier@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-3.1.0.tgz#2c26d2cdcfa3a05f0642cd7e6e4ef3316cdabfa2" integrity sha512-QYGfmzuc4q4J6XIhlp8vRKdI/fI0tQfQPy1dME3UOLprE+v4ssH/3W9LM2Q7h5qBcy5m0ehCrBDU2YF8q6OY8w== @@ -5044,10 +5058,10 @@ eslint-plugin-import@^2.6.0: read-pkg-up "^2.0.0" resolve "^1.6.0" -eslint-plugin-jest@^21.0.0: - version "21.25.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-21.25.1.tgz#166c6b9d33bd7bc5b8ece62cff878ba207600bb6" - integrity sha512-mmphmAD/WihjFGq1IUHLSZWQPcd8U9w/SeFCHf3p0V3Q3MBxmj1ZKnh41hID44guIACLuwos/LhVWIr4phN4yg== +eslint-plugin-jest@^22.0.0: + version "22.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.0.0.tgz#87dc52bbdd47f37f23bf2b10bb8469458bb3ed68" + integrity sha512-YOj8cYI5ZXEZUrX2kUBLachR1ffjQiicIMBoivN7bXXHnxi8RcwNvmVzwlu3nTmjlvk5AP3kIpC5i8HcinmhPA== eslint-plugin-jsx-a11y@^6.0.2: version "6.1.2" @@ -5072,7 +5086,7 @@ eslint-plugin-markdown@^1.0.0-beta.6: remark-parse "^3.0.0" unified "^6.1.2" -eslint-plugin-prettier@^2.2.0, eslint-plugin-prettier@^2.3.1: +eslint-plugin-prettier@^2.2.0, eslint-plugin-prettier@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.7.0.tgz#b4312dcf2c1d965379d7f9d5b5f8aaadc6a45904" integrity sha512-CStQYJgALoQBw3FsBzH0VOVDRnJ/ZimUlpLm226U8qgqYJfPOY/CPK6wyRInMxh73HSKg5wyRwdS4BVYYHwokA== @@ -5807,10 +5821,10 @@ flatten@^1.0.2: resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I= -flow-bin@^0.80.0: - version "0.80.0" - resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.80.0.tgz#04cc1ee626a6f50786f78170c92ebe1745235403" - integrity sha512-0wRnqvXErQRPrx6GBLB5swgndfWkotd9MgfePgT7Z+VsE046c8Apzl7KKTCypB/pzn0pZF2g5Jurxxb2umET8g== +flow-bin@^0.85.0: + version "0.85.0" + resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.85.0.tgz#a3ca80748a35a071d5bbb2fcd61d64d977fc53a6" + integrity sha512-ougBA2q6Rn9sZrjZQ9r5pTFxCotlGouySpD2yRIuq5AYwwfIT8HHhVMeSwrN5qJayjHINLJyrnsSkkPCZyfMrQ== flow-remove-types@^1.1.0: version "1.2.3" @@ -7501,7 +7515,7 @@ isarray@^2.0.4: resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.4.tgz#38e7bcbb0f3ba1b7933c86ba1894ddfc3781bbb7" integrity sha512-GMxXOiUirWg1xTKRipM0Ek07rX+ubx4nNVElTJdNLYmNO/2YrDkgJGw9CljXn+r4EWiDQg/8lsRdHyg2PJuUaA== -isbinaryfile@^3.0.0: +isbinaryfile@^3.0.0, isbinaryfile@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.3.tgz#5d6def3edebf6e8ca8cae9c30183a804b5f8be80" integrity sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw== @@ -10538,10 +10552,10 @@ preserve@^0.2.0: resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= -prettier@^1.13.3, prettier@^1.13.4: - version "1.14.3" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.3.tgz#90238dd4c0684b7edce5f83b0fb7328e48bd0895" - integrity sha512-qZDVnCrnpsRJJq5nSsiHCE3BYMED2OtsI+cmzIzF1QIfqm5ALf8tEJcO27zV1gKNKRPdhjO0dNWnrzssDQ1tFg== +prettier@^1.13.4, prettier@^1.15.2: + version "1.15.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.2.tgz#d31abe22afa4351efa14c7f8b94b58bb7452205e" + integrity sha512-YgPLFFA0CdKL4Eg2IHtUSjzj/BWgszDHiNQAe0VAIBse34148whfdzLagRL+QiKS+YfK5ftB6X4v/MBw8yCoug== pretty-format@^4.2.1: version "4.3.1" @@ -11003,6 +11017,13 @@ react-test-renderer@*, react-test-renderer@^16.0.0-0: react-is "^16.5.2" schedule "^0.5.0" +react-testing-library@*: + version "5.2.3" + resolved "https://registry.yarnpkg.com/react-testing-library/-/react-testing-library-5.2.3.tgz#c3be44bfa5eb1ba2acc1fb218785c40ebbdfe8ed" + integrity sha512-Bw52++7uORuIQnL55lK/WQfppqAc9+8yFG4lWUp/kmSOvYDnt8J9oI5fNCfAGSQi9iIhAv9aNsI2G5rtid0nrA== + dependencies: + dom-testing-library "^3.12.0" + react-timer-mixin@^0.13.2: version "0.13.4" resolved "https://registry.yarnpkg.com/react-timer-mixin/-/react-timer-mixin-0.13.4.tgz#75a00c3c94c13abe29b43d63b4c65a88fc8264d3" @@ -13495,6 +13516,11 @@ w3c-hr-time@^1.0.1: dependencies: browser-process-hrtime "^0.1.2" +wait-for-expect@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.0.1.tgz#73ab346ed56ed2ef66c380a59fd623755ceac0ce" + integrity sha512-TPZMSxGWUl2DWmqdspLDEy97/S1Mqq0pzbh2A7jTq0WbJurUb5GKli+bai6ayeYdeWTF0rQNWZmUvCVZ9gkrfA== + walker@~1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"