From d8e57bf5b344146d30e1e228e100e625507c40ba Mon Sep 17 00:00:00 2001 From: Martin Feineis Date: Sun, 3 Feb 2019 19:18:41 +0100 Subject: [PATCH 001/107] Check for existence of `Element` when comparing DOM-nody-things (#7786) --- .../__snapshots__/matchers.test.js.snap | 7 +++++++ .../expect/src/__tests__/matchers.test.js | 10 ++++++++++ packages/expect/src/jasmineUtils.js | 20 +++++++++++-------- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap index 1debb403a675..a741beee1b9a 100644 --- a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap @@ -1891,6 +1891,13 @@ Expected: {\\"a\\": 99} Received: {\\"a\\": 99}" `; +exports[`.toEqual() {pass: false} expect({"nodeName": "div", "nodeType": 1}).not.toEqual({"nodeName": "div", "nodeType": 1}) 1`] = ` +"expect(received).not.toEqual(expected) + +Expected: {\\"nodeName\\": \\"div\\", \\"nodeType\\": 1} +Received: {\\"nodeName\\": \\"div\\", \\"nodeType\\": 1}" +`; + exports[`.toEqual() {pass: false} expect({"target": {"nodeType": 1, "value": "a"}}).toEqual({"target": {"nodeType": 1, "value": "b"}}) 1`] = ` "expect(received).toEqual(expected) diff --git a/packages/expect/src/__tests__/matchers.test.js b/packages/expect/src/__tests__/matchers.test.js index bff61b7edc12..11d573ca9bfe 100644 --- a/packages/expect/src/__tests__/matchers.test.js +++ b/packages/expect/src/__tests__/matchers.test.js @@ -518,6 +518,16 @@ describe('.toEqual()', () => { }, }, ], + [ + { + nodeName: 'div', + nodeType: 1, + }, + { + nodeName: 'div', + nodeType: 1, + }, + ], ].forEach(([a, b]) => { test(`{pass: false} expect(${stringify(a)}).not.toEqual(${stringify( b, diff --git a/packages/expect/src/jasmineUtils.js b/packages/expect/src/jasmineUtils.js index a1b88aac8f5a..d9e63fb31900 100644 --- a/packages/expect/src/jasmineUtils.js +++ b/packages/expect/src/jasmineUtils.js @@ -130,14 +130,18 @@ function eq(a, b, aStack, bStack, customTesters, hasKey): boolean { if (a.isEqualNode) { return a.isEqualNode(b); } - // IE8 doesn't support isEqualNode, try to use outerHTML && innerText - var aIsElement = a instanceof Element; - var bIsElement = b instanceof Element; - if (aIsElement && bIsElement) { - return a.outerHTML == b.outerHTML; - } - if (aIsElement || bIsElement) { - return false; + // In some test environments (e.g. "node") there is no `Element` even though + // we might be comparing things that look like DOM nodes + if (typeof Element !== 'undefined') { + // IE8 doesn't support isEqualNode, try to use outerHTML && innerText + var aIsElement = a instanceof Element; + var bIsElement = b instanceof Element; + if (aIsElement && bIsElement) { + return a.outerHTML == b.outerHTML; + } + if (aIsElement || bIsElement) { + return false; + } } return a.innerText == b.innerText && a.textContent == b.textContent; } From 3b1f239bdf7410d5c1259591287a5fe567cdd1b1 Mon Sep 17 00:00:00 2001 From: Martin Feineis Date: Mon, 4 Feb 2019 20:36:39 +0100 Subject: [PATCH 002/107] Fall back to deep equality for presumed DOM nodes if `Element` is not in scope (#7786) --- .../__tests__/__snapshots__/matchers.test.js.snap | 15 +++++++++++++++ packages/expect/src/__tests__/matchers.test.js | 10 ++++++++++ packages/expect/src/jasmineUtils.js | 7 ++++--- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap index a741beee1b9a..73d527e4d0d7 100644 --- a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap @@ -1898,6 +1898,21 @@ Expected: {\\"nodeName\\": \\"div\\", \\"nodeType\\": 1} Received: {\\"nodeName\\": \\"div\\", \\"nodeType\\": 1}" `; +exports[`.toEqual() {pass: false} expect({"nodeName": "div", "nodeType": 1}).toEqual({"nodeName": "p", "nodeType": 1}) 1`] = ` +"expect(received).toEqual(expected) + +Difference: + +- Expected ++ Received + + Object { +- \\"nodeName\\": \\"p\\", ++ \\"nodeName\\": \\"div\\", + \\"nodeType\\": 1, + }" +`; + exports[`.toEqual() {pass: false} expect({"target": {"nodeType": 1, "value": "a"}}).toEqual({"target": {"nodeType": 1, "value": "b"}}) 1`] = ` "expect(received).toEqual(expected) diff --git a/packages/expect/src/__tests__/matchers.test.js b/packages/expect/src/__tests__/matchers.test.js index 11d573ca9bfe..afcce46e1c7e 100644 --- a/packages/expect/src/__tests__/matchers.test.js +++ b/packages/expect/src/__tests__/matchers.test.js @@ -407,6 +407,16 @@ describe('.toEqual()', () => { }, }, ], + [ + { + nodeName: 'div', + nodeType: 1, + }, + { + nodeName: 'p', + nodeType: 1, + }, + ], ].forEach(([a, b]) => { test(`{pass: false} expect(${stringify(a)}).toEqual(${stringify( b, diff --git a/packages/expect/src/jasmineUtils.js b/packages/expect/src/jasmineUtils.js index d9e63fb31900..590d93a25e80 100644 --- a/packages/expect/src/jasmineUtils.js +++ b/packages/expect/src/jasmineUtils.js @@ -125,6 +125,7 @@ function eq(a, b, aStack, bStack, customTesters, hasKey): boolean { var aIsDomNode = isDomNode(a); var bIsDomNode = isDomNode(b); + var hasElementCtor = typeof Element !== 'undefined'; if (aIsDomNode && bIsDomNode) { // At first try to use DOM3 method isEqualNode if (a.isEqualNode) { @@ -132,7 +133,7 @@ function eq(a, b, aStack, bStack, customTesters, hasKey): boolean { } // In some test environments (e.g. "node") there is no `Element` even though // we might be comparing things that look like DOM nodes - if (typeof Element !== 'undefined') { + if (hasElementCtor) { // IE8 doesn't support isEqualNode, try to use outerHTML && innerText var aIsElement = a instanceof Element; var bIsElement = b instanceof Element; @@ -142,10 +143,10 @@ function eq(a, b, aStack, bStack, customTesters, hasKey): boolean { if (aIsElement || bIsElement) { return false; } + return a.innerText == b.innerText && a.textContent == b.textContent; } - return a.innerText == b.innerText && a.textContent == b.textContent; } - if (aIsDomNode || bIsDomNode) { + if (hasElementCtor && (aIsDomNode || bIsDomNode)) { return false; } From 3347089e0cbc8c7383632dd1aa5956402456eac7 Mon Sep 17 00:00:00 2001 From: Martin Feineis Date: Tue, 5 Feb 2019 00:51:39 +0100 Subject: [PATCH 003/107] Remove IE<9 specific fallback DOM node comparison (#7786) --- packages/expect/src/jasmineUtils.js | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/packages/expect/src/jasmineUtils.js b/packages/expect/src/jasmineUtils.js index 590d93a25e80..74b0e106c713 100644 --- a/packages/expect/src/jasmineUtils.js +++ b/packages/expect/src/jasmineUtils.js @@ -125,28 +125,14 @@ function eq(a, b, aStack, bStack, customTesters, hasKey): boolean { var aIsDomNode = isDomNode(a); var bIsDomNode = isDomNode(b); - var hasElementCtor = typeof Element !== 'undefined'; - if (aIsDomNode && bIsDomNode) { - // At first try to use DOM3 method isEqualNode - if (a.isEqualNode) { - return a.isEqualNode(b); - } - // In some test environments (e.g. "node") there is no `Element` even though - // we might be comparing things that look like DOM nodes - if (hasElementCtor) { - // IE8 doesn't support isEqualNode, try to use outerHTML && innerText - var aIsElement = a instanceof Element; - var bIsElement = b instanceof Element; - if (aIsElement && bIsElement) { - return a.outerHTML == b.outerHTML; - } - if (aIsElement || bIsElement) { - return false; - } - return a.innerText == b.innerText && a.textContent == b.textContent; - } + // Use DOM3 method isEqualNode (IE>=9) + if (aIsDomNode && a.isEqualNode && bIsDomNode) { + return a.isEqualNode(b); } - if (hasElementCtor && (aIsDomNode || bIsDomNode)) { + // In some test environments (e.g. "node") there is no `Element` even though + // we might be comparing things that look like DOM nodes. In these cases we + // fall back to deep equality because we just don't know better at this point + if (typeof Element !== 'undefined' && (aIsDomNode || bIsDomNode)) { return false; } From c49a4c9d43bce7d43f7a79475c2f8c9bf8242de1 Mon Sep 17 00:00:00 2001 From: Martin Feineis Date: Tue, 5 Feb 2019 21:12:07 +0100 Subject: [PATCH 004/107] Check for global DOM `Node` in `isDomNode` (#7786) In a real browser and JSDOM the check will use DOM-Level-3 `Node.isEqualNode` and for everything else a deep equality check should do the trick. --- packages/expect/src/jasmineUtils.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/expect/src/jasmineUtils.js b/packages/expect/src/jasmineUtils.js index 74b0e106c713..3feab8f0d068 100644 --- a/packages/expect/src/jasmineUtils.js +++ b/packages/expect/src/jasmineUtils.js @@ -129,12 +129,6 @@ function eq(a, b, aStack, bStack, customTesters, hasKey): boolean { if (aIsDomNode && a.isEqualNode && bIsDomNode) { return a.isEqualNode(b); } - // In some test environments (e.g. "node") there is no `Element` even though - // we might be comparing things that look like DOM nodes. In these cases we - // fall back to deep equality because we just don't know better at this point - if (typeof Element !== 'undefined' && (aIsDomNode || bIsDomNode)) { - return false; - } // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. @@ -243,11 +237,15 @@ export function isA(typeName: string, value: any) { } function isDomNode(obj) { + // In some test environments (e.g. "node") there is no `Node` even though + // we might be comparing things that look like DOM nodes. return ( - obj !== null && - typeof obj === 'object' && - typeof obj.nodeType === 'number' && - typeof obj.nodeName === 'string' + typeof Node !== 'undefined' + ? obj instanceof Node + : obj !== null && + typeof obj === 'object' && + typeof obj.nodeType === 'number' && + typeof obj.nodeName === 'string' ); } From b4d9b34f3b9008ab9a848e39bd8b65e4565eccd6 Mon Sep 17 00:00:00 2001 From: Martin Feineis Date: Wed, 6 Feb 2019 21:08:28 +0100 Subject: [PATCH 005/107] Check that `isEqualNode` is a function and remove duck typing from `isDomNode` (#7786) --- packages/expect/src/jasmineUtils.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/expect/src/jasmineUtils.js b/packages/expect/src/jasmineUtils.js index 3feab8f0d068..b558741ae553 100644 --- a/packages/expect/src/jasmineUtils.js +++ b/packages/expect/src/jasmineUtils.js @@ -126,7 +126,7 @@ function eq(a, b, aStack, bStack, customTesters, hasKey): boolean { var aIsDomNode = isDomNode(a); var bIsDomNode = isDomNode(b); // Use DOM3 method isEqualNode (IE>=9) - if (aIsDomNode && a.isEqualNode && bIsDomNode) { + if (aIsDomNode && typeof a.isEqualNode === 'function' && bIsDomNode) { return a.isEqualNode(b); } @@ -239,14 +239,7 @@ export function isA(typeName: string, value: any) { function isDomNode(obj) { // In some test environments (e.g. "node") there is no `Node` even though // we might be comparing things that look like DOM nodes. - return ( - typeof Node !== 'undefined' - ? obj instanceof Node - : obj !== null && - typeof obj === 'object' && - typeof obj.nodeType === 'number' && - typeof obj.nodeName === 'string' - ); + return typeof Node !== 'undefined' && obj instanceof Node; } export function fnNameFor(func: Function) { From 0ac8fc6f374e7a22848a67483ac351eb3b8300e4 Mon Sep 17 00:00:00 2001 From: Mukul Ashwath Ram Date: Sun, 3 Feb 2019 12:51:59 -0500 Subject: [PATCH 006/107] Addressing malfunctioning code snippet. (#7789) --- docs/Puppeteer.md | 4 ++-- website/versioned_docs/version-22.x/Puppeteer.md | 3 +-- website/versioned_docs/version-23.x/Puppeteer.md | 4 ++-- website/versioned_docs/version-24.0/Puppeteer.md | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/Puppeteer.md b/docs/Puppeteer.md index 1299bb346870..3ae544e56e97 100644 --- a/docs/Puppeteer.md +++ b/docs/Puppeteer.md @@ -31,8 +31,8 @@ describe('Google', () => { await page.goto('https://google.com'); }); - it('should display "google" text on page', async () => { - await expect(page).toMatch('google'); + it('should be titled "Google"', async () => { + await expect(page.title()).resolves.toMatch('Google'); }); }); ``` diff --git a/website/versioned_docs/version-22.x/Puppeteer.md b/website/versioned_docs/version-22.x/Puppeteer.md index a38fed515128..8e51d6fac78c 100644 --- a/website/versioned_docs/version-22.x/Puppeteer.md +++ b/website/versioned_docs/version-22.x/Puppeteer.md @@ -89,8 +89,7 @@ describe( }, timeout); it('should load without error', async () => { - const text = await page.evaluate(() => document.body.textContent); - expect(text).toContain('google'); + await expect(page.title()).resolves.toMatch('Google'); }); }, timeout, diff --git a/website/versioned_docs/version-23.x/Puppeteer.md b/website/versioned_docs/version-23.x/Puppeteer.md index 3a9e51d4c8da..4b084b9b6f82 100644 --- a/website/versioned_docs/version-23.x/Puppeteer.md +++ b/website/versioned_docs/version-23.x/Puppeteer.md @@ -32,8 +32,8 @@ describe('Google', () => { await page.goto('https://google.com'); }); - it('should display "google" text on page', async () => { - await expect(page).toMatch('google'); + it('should be titled "Google"', async () => { + await expect(page.title()).resolves.toMatch('Google'); }); }); ``` diff --git a/website/versioned_docs/version-24.0/Puppeteer.md b/website/versioned_docs/version-24.0/Puppeteer.md index aad7dd28c867..5262e4b2b29c 100644 --- a/website/versioned_docs/version-24.0/Puppeteer.md +++ b/website/versioned_docs/version-24.0/Puppeteer.md @@ -32,8 +32,8 @@ describe('Google', () => { await page.goto('https://google.com'); }); - it('should display "google" text on page', async () => { - await expect(page).toMatch('google'); + it('should be titled "Google"', async () => { + await expect(page.title()).resolves.toMatch('Google'); }); }); ``` From 08762b382b112f4aeecb97afdc797f697c71d28c Mon Sep 17 00:00:00 2001 From: Kornel Dubieniecki Date: Mon, 4 Feb 2019 10:26:38 +0100 Subject: [PATCH 007/107] Exclude setup files from coverage (#7790) --- CHANGELOG.md | 1 + .../__snapshots__/coverageReport.test.js.snap | 44 ++++++++++--------- e2e/__tests__/coverageReport.test.js | 6 +-- e2e/coverage-report/file.js | 8 ++++ e2e/coverage-report/sum.js | 1 + .../src/__tests__/should_instrument.test.js | 38 ++++++++++++++-- packages/jest-runtime/src/shouldInstrument.js | 24 ++++++++++ 7 files changed, 96 insertions(+), 26 deletions(-) create mode 100644 e2e/coverage-report/file.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f0cd29a31e2..86f2904e9ca8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - `[jest-config]` Allow `moduleFileExtensions` without 'js' for custom runners ([#7751](https://github.com/facebook/jest/pull/7751)) - `[jest-cli]` Load transformers before installing require hooks ([#7752](https://github.com/facebook/jest/pull/7752) - `[jest-cli]` Handle missing `numTodoTests` in test results ([#7779](https://github.com/facebook/jest/pull/7779)) +- `[jest-runtime]` Exclude setup/teardown files from coverage report ([#7790](https://github.com/facebook/jest/pull/7790) ### Chore & Maintenance diff --git a/e2e/__tests__/__snapshots__/coverageReport.test.js.snap b/e2e/__tests__/__snapshots__/coverageReport.test.js.snap index ad619cfc44f1..104b7cb67776 100644 --- a/e2e/__tests__/__snapshots__/coverageReport.test.js.snap +++ b/e2e/__tests__/__snapshots__/coverageReport.test.js.snap @@ -17,8 +17,8 @@ exports[`collects coverage only from multiple specified files 1`] = ` File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | --------------|----------|----------|----------|----------|-------------------| All files | 100 | 100 | 100 | 100 | | + file.js | 100 | 100 | 100 | 100 | | otherFile.js | 100 | 100 | 100 | 100 | | - setup.js | 100 | 100 | 100 | 100 | | --------------|----------|----------|----------|----------|-------------------| `; @@ -27,7 +27,7 @@ exports[`collects coverage only from specified file 1`] = ` File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | ----------|----------|----------|----------|----------|-------------------| All files | 100 | 100 | 100 | 100 | | - setup.js | 100 | 100 | 100 | 100 | | + file.js | 100 | 100 | 100 | 100 | | ----------|----------|----------|----------|----------|-------------------| `; @@ -35,8 +35,8 @@ exports[`collects coverage only from specified files avoiding dependencies 1`] = ----------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | ----------|----------|----------|----------|----------|-------------------| -All files | 85.71 | 100 | 50 | 100 | | - sum.js | 85.71 | 100 | 50 | 100 | | +All files | 87.5 | 100 | 50 | 100 | | + sum.js | 87.5 | 100 | 50 | 100 | | ----------|----------|----------|----------|----------|-------------------| `; @@ -46,11 +46,12 @@ exports[`generates coverage when using the testRegex config param 1`] = ` -------------------------------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | -------------------------------------|----------|----------|----------|----------|-------------------| -All files | 56.52 | 0 | 50 | 55.56 | | - coverage-report | 41.18 | 0 | 25 | 42.86 | | +All files | 60 | 0 | 50 | 60 | | + coverage-report | 47.37 | 0 | 25 | 50 | | + file.js | 100 | 100 | 100 | 100 | | notRequiredInTestSuite.js | 0 | 0 | 0 | 0 | 8,15,16,17,19 | otherFile.js | 100 | 100 | 100 | 100 | | - sum.js | 85.71 | 100 | 50 | 100 | | + sum.js | 87.5 | 100 | 50 | 100 | | sumDependency.js | 0 | 0 | 0 | 0 | 8,10,12 | coverage-report/cached-duplicates/a | 100 | 100 | 100 | 100 | | identical.js | 100 | 100 | 100 | 100 | | @@ -71,11 +72,12 @@ exports[`outputs coverage report 1`] = ` -------------------------------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | -------------------------------------|----------|----------|----------|----------|-------------------| -All files | 56.52 | 0 | 50 | 55.56 | | - coverage-report | 41.18 | 0 | 25 | 42.86 | | +All files | 60 | 0 | 50 | 60 | | + coverage-report | 47.37 | 0 | 25 | 50 | | + file.js | 100 | 100 | 100 | 100 | | notRequiredInTestSuite.js | 0 | 0 | 0 | 0 | 8,15,16,17,19 | otherFile.js | 100 | 100 | 100 | 100 | | - sum.js | 85.71 | 100 | 50 | 100 | | + sum.js | 87.5 | 100 | 50 | 100 | | sumDependency.js | 0 | 0 | 0 | 0 | 8,10,12 | coverage-report/cached-duplicates/a | 100 | 100 | 100 | 100 | | identical.js | 100 | 100 | 100 | 100 | | @@ -87,19 +89,20 @@ All files | 56.52 | 0 | 50 | 55.56 exports[`outputs coverage report when text and text-summary is requested 1`] = ` =============================== Coverage summary =============================== -Statements : 56.52% ( 13/23 ) +Statements : 60% ( 15/25 ) Branches : 0% ( 0/4 ) Functions : 50% ( 3/6 ) -Lines : 55.56% ( 10/18 ) +Lines : 60% ( 12/20 ) ================================================================================ -------------------------------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | -------------------------------------|----------|----------|----------|----------|-------------------| -All files | 56.52 | 0 | 50 | 55.56 | | - coverage-report | 41.18 | 0 | 25 | 42.86 | | +All files | 60 | 0 | 50 | 60 | | + coverage-report | 47.37 | 0 | 25 | 50 | | + file.js | 100 | 100 | 100 | 100 | | notRequiredInTestSuite.js | 0 | 0 | 0 | 0 | 8,15,16,17,19 | otherFile.js | 100 | 100 | 100 | 100 | | - sum.js | 85.71 | 100 | 50 | 100 | | + sum.js | 87.5 | 100 | 50 | 100 | | sumDependency.js | 0 | 0 | 0 | 0 | 8,10,12 | coverage-report/cached-duplicates/a | 100 | 100 | 100 | 100 | | identical.js | 100 | 100 | 100 | 100 | | @@ -112,11 +115,12 @@ exports[`outputs coverage report when text is requested 1`] = ` -------------------------------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | -------------------------------------|----------|----------|----------|----------|-------------------| -All files | 56.52 | 0 | 50 | 55.56 | | - coverage-report | 41.18 | 0 | 25 | 42.86 | | +All files | 60 | 0 | 50 | 60 | | + coverage-report | 47.37 | 0 | 25 | 50 | | + file.js | 100 | 100 | 100 | 100 | | notRequiredInTestSuite.js | 0 | 0 | 0 | 0 | 8,15,16,17,19 | otherFile.js | 100 | 100 | 100 | 100 | | - sum.js | 85.71 | 100 | 50 | 100 | | + sum.js | 87.5 | 100 | 50 | 100 | | sumDependency.js | 0 | 0 | 0 | 0 | 8,10,12 | coverage-report/cached-duplicates/a | 100 | 100 | 100 | 100 | | identical.js | 100 | 100 | 100 | 100 | | @@ -128,9 +132,9 @@ All files | 56.52 | 0 | 50 | 55.56 exports[`outputs coverage report when text-summary is requested 1`] = ` =============================== Coverage summary =============================== -Statements : 56.52% ( 13/23 ) +Statements : 60% ( 15/25 ) Branches : 0% ( 0/4 ) Functions : 50% ( 3/6 ) -Lines : 55.56% ( 10/18 ) +Lines : 60% ( 12/20 ) ================================================================================ `; diff --git a/e2e/__tests__/coverageReport.test.js b/e2e/__tests__/coverageReport.test.js index b17fd88e85e4..dc50c3016c08 100644 --- a/e2e/__tests__/coverageReport.test.js +++ b/e2e/__tests__/coverageReport.test.js @@ -39,12 +39,12 @@ test('collects coverage only from specified file', () => { '--no-cache', '--coverage', '--collectCoverageFrom', // overwrites the one in package.json - 'setup.js', + 'file.js', ], {stripAnsi: true}, ); - // Coverage report should only have `setup.js` coverage info + // Coverage report should only have `file.js` coverage info expect(wrap(stdout)).toMatchSnapshot(); }); @@ -55,7 +55,7 @@ test('collects coverage only from multiple specified files', () => { '--no-cache', '--coverage', '--collectCoverageFrom', - 'setup.js', + 'file.js', '--collectCoverageFrom', 'otherFile.js', ], diff --git a/e2e/coverage-report/file.js b/e2e/coverage-report/file.js new file mode 100644 index 000000000000..cae7a2495630 --- /dev/null +++ b/e2e/coverage-report/file.js @@ -0,0 +1,8 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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 = {a: 4}; diff --git a/e2e/coverage-report/sum.js b/e2e/coverage-report/sum.js index 27e339dbed42..13420684e45e 100644 --- a/e2e/coverage-report/sum.js +++ b/e2e/coverage-report/sum.js @@ -7,6 +7,7 @@ require('./sumDependency.js'); require('./otherFile'); +require('./file'); const uncoveredFunction = () => 1 + 'abc'; diff --git a/packages/jest-runtime/src/__tests__/should_instrument.test.js b/packages/jest-runtime/src/__tests__/should_instrument.test.js index 376e8e7d0b4f..dc3ba29e8ce3 100644 --- a/packages/jest-runtime/src/__tests__/should_instrument.test.js +++ b/packages/jest-runtime/src/__tests__/should_instrument.test.js @@ -6,6 +6,7 @@ * */ +import {normalize} from 'jest-config'; import shouldInstrument from '../shouldInstrument'; describe('shouldInstrument', () => { @@ -13,9 +14,12 @@ describe('shouldInstrument', () => { const defaultOptions = { collectCoverage: true, }; - const defaultConfig = { - rootDir: '/', - }; + const defaultConfig = normalize( + { + rootDir: '/', + }, + {}, + ).options; describe('should return true', () => { const testShouldInstrument = ( @@ -198,5 +202,33 @@ describe('shouldInstrument', () => { testShouldInstrument(filename, defaultOptions, defaultConfig); }); + + it('if file is a globalSetup file', () => { + testShouldInstrument('globalSetup.js', defaultOptions, { + globalSetup: 'globalSetup.js', + rootDir: '/', + }); + }); + + it('if file is globalTeardown file', () => { + testShouldInstrument('globalTeardown.js', defaultOptions, { + globalTeardown: 'globalTeardown.js', + rootDir: '/', + }); + }); + + it('if file is in setupFiles', () => { + testShouldInstrument('setupTest.js', defaultOptions, { + rootDir: '/', + setupFiles: ['setupTest.js'], + }); + }); + + it('if file is in setupFilesAfterEnv', () => { + testShouldInstrument('setupTest.js', defaultOptions, { + rootDir: '/', + setupFilesAfterEnv: ['setupTest.js'], + }); + }); }); }); diff --git a/packages/jest-runtime/src/shouldInstrument.js b/packages/jest-runtime/src/shouldInstrument.js index 6717fbea3e52..6333ece7814e 100644 --- a/packages/jest-runtime/src/shouldInstrument.js +++ b/packages/jest-runtime/src/shouldInstrument.js @@ -84,6 +84,30 @@ export default function shouldInstrument( return false; } + if (config.globalSetup === filename) { + return false; + } + + if (config.globalTeardown === filename) { + return false; + } + + if ( + //TODO: Remove additional check when normalized config provided in unit test + config.setupFiles && + config.setupFiles.some(setupFile => setupFile === filename) + ) { + return false; + } + + if ( + //TODO: Remove additional check when normalized config provided in unit test + config.setupFilesAfterEnv && + config.setupFilesAfterEnv.some(setupFile => setupFile === filename) + ) { + return false; + } + if (MOCKS_PATTERN.test(filename)) { return false; } From fdb3acc7e38f467d0c8f62bea6f0643e151286e5 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 4 Feb 2019 16:20:12 +0000 Subject: [PATCH 008/107] fix: make babel-jest warn when file to tarnsform is ignored by babel (#7797) --- CHANGELOG.md | 1 + .../__snapshots__/transform.test.js.snap | 9 +++++++++ e2e/__tests__/transform.test.js | 12 ++++++++++++ .../__tests__/ignoredFile.test.js | 12 ++++++++++++ .../babel-jest-ignored/babel.config.js | 3 +++ e2e/transform/babel-jest-ignored/package.json | 5 +++++ packages/babel-jest/package.json | 4 +++- packages/babel-jest/src/index.js | 19 +++++++++++++++++-- 8 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 e2e/transform/babel-jest-ignored/__tests__/ignoredFile.test.js create mode 100644 e2e/transform/babel-jest-ignored/babel.config.js create mode 100644 e2e/transform/babel-jest-ignored/package.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 86f2904e9ca8..1116fd8a46d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - `[jest-cli]` Load transformers before installing require hooks ([#7752](https://github.com/facebook/jest/pull/7752) - `[jest-cli]` Handle missing `numTodoTests` in test results ([#7779](https://github.com/facebook/jest/pull/7779)) - `[jest-runtime]` Exclude setup/teardown files from coverage report ([#7790](https://github.com/facebook/jest/pull/7790) +- `[babel-jest]` Throw an error if `babel-jest` tries to transform a file ignored by Babel ([#7797](https://github.com/facebook/jest/pull/7797)) ### Chore & Maintenance diff --git a/e2e/__tests__/__snapshots__/transform.test.js.snap b/e2e/__tests__/__snapshots__/transform.test.js.snap index aad749c2f0ad..791e7ec6ae94 100644 --- a/e2e/__tests__/__snapshots__/transform.test.js.snap +++ b/e2e/__tests__/__snapshots__/transform.test.js.snap @@ -1,5 +1,14 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`babel-jest ignored tells user to match ignored files 1`] = ` +FAIL __tests__/ignoredFile.test.js + ● Test suite failed to run + + babel-jest: Babel ignores __tests__/ignoredFile.test.js - make sure to include the file in Jest's transformIgnorePatterns as well. + + at loadBabelConfig (../../../packages/babel-jest/build/index.js:134:13) +`; + exports[`babel-jest instruments only specific files and collects coverage 1`] = ` ------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | diff --git a/e2e/__tests__/transform.test.js b/e2e/__tests__/transform.test.js index 50dba3b10f26..c9f12a0c406a 100644 --- a/e2e/__tests__/transform.test.js +++ b/e2e/__tests__/transform.test.js @@ -12,6 +12,7 @@ import { cleanup, copyDir, createEmptyPackage, + extractSummary, linkJestPackage, run, } from '../Utils'; @@ -45,6 +46,17 @@ describe('babel-jest', () => { }); }); +describe('babel-jest ignored', () => { + const dir = path.resolve(__dirname, '..', 'transform/babel-jest-ignored'); + + it('tells user to match ignored files', () => { + // --no-cache because babel can cache stuff and result in false green + const {status, stderr} = runJest(dir, ['--no-cache']); + expect(status).toBe(1); + expect(wrap(extractSummary(stderr).rest)).toMatchSnapshot(); + }); +}); + // babel-jest is automatically linked at the root because it is a workspace now // a way to test this in isolation is to move the test suite into a temp folder describe('no babel-jest', () => { diff --git a/e2e/transform/babel-jest-ignored/__tests__/ignoredFile.test.js b/e2e/transform/babel-jest-ignored/__tests__/ignoredFile.test.js new file mode 100644 index 000000000000..89e5137d7342 --- /dev/null +++ b/e2e/transform/babel-jest-ignored/__tests__/ignoredFile.test.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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'; + +it('should not run since the file is ignored by babel config', () => { + expect(true).toBe(true); +}); diff --git a/e2e/transform/babel-jest-ignored/babel.config.js b/e2e/transform/babel-jest-ignored/babel.config.js new file mode 100644 index 000000000000..ef0850effc4a --- /dev/null +++ b/e2e/transform/babel-jest-ignored/babel.config.js @@ -0,0 +1,3 @@ +// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +module.exports = {only: ['blablabla']}; diff --git a/e2e/transform/babel-jest-ignored/package.json b/e2e/transform/babel-jest-ignored/package.json new file mode 100644 index 000000000000..148788b25446 --- /dev/null +++ b/e2e/transform/babel-jest-ignored/package.json @@ -0,0 +1,5 @@ +{ + "jest": { + "testEnvironment": "node" + } +} diff --git a/packages/babel-jest/package.json b/packages/babel-jest/package.json index b9e484631a0e..b05b51da1646 100644 --- a/packages/babel-jest/package.json +++ b/packages/babel-jest/package.json @@ -11,7 +11,9 @@ "main": "build/index.js", "dependencies": { "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.0.0" + "babel-preset-jest": "^24.0.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" }, "devDependencies": { "@babel/core": "^7.1.0" diff --git a/packages/babel-jest/src/index.js b/packages/babel-jest/src/index.js index bae5553dbe6c..b659d715588f 100644 --- a/packages/babel-jest/src/index.js +++ b/packages/babel-jest/src/index.js @@ -19,6 +19,8 @@ import crypto from 'crypto'; import fs from 'fs'; import path from 'path'; import {transformSync as babelTransform, loadPartialConfig} from '@babel/core'; +import chalk from 'chalk'; +import slash from 'slash'; const THIS_FILE = fs.readFileSync(__filename); const jestPresetPath = require.resolve('babel-preset-jest'); @@ -40,9 +42,22 @@ const createTransformer = (options: any): Transformer => { delete options.cacheDirectory; delete options.filename; - const loadBabelConfig = (cwd, filename) => + function loadBabelConfig(cwd, filename) { // `cwd` first to allow incoming options to override it - loadPartialConfig({cwd, ...options, filename}); + const babelConfig = loadPartialConfig({cwd, ...options, filename}); + + if (!babelConfig) { + throw new Error( + `babel-jest: Babel ignores ${chalk.bold( + slash(path.relative(cwd, filename)), + )} - make sure to include the file in Jest's ${chalk.bold( + 'transformIgnorePatterns', + )} as well.`, + ); + } + + return babelConfig; + } return { canInstrument: true, From c3873a18110a36bec89aec3c1ff94481cc9f403f Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 4 Feb 2019 16:37:07 +0000 Subject: [PATCH 009/107] fix: ignore TS type references when looking for out-of-scope references (#7799) --- CHANGELOG.md | 1 + e2e/babel-plugin-jest-hoist/__tests__/typescript.test.ts | 2 ++ packages/babel-plugin-jest-hoist/src/index.js | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1116fd8a46d9..8554348f954b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - `[jest-cli]` Handle missing `numTodoTests` in test results ([#7779](https://github.com/facebook/jest/pull/7779)) - `[jest-runtime]` Exclude setup/teardown files from coverage report ([#7790](https://github.com/facebook/jest/pull/7790) - `[babel-jest]` Throw an error if `babel-jest` tries to transform a file ignored by Babel ([#7797](https://github.com/facebook/jest/pull/7797)) +- `[babel-plugin-jest-hoist]` Ignore TS type references when looking for out-of-scope references ([#7799](https://github.com/facebook/jest/pull/7799) ### Chore & Maintenance diff --git a/e2e/babel-plugin-jest-hoist/__tests__/typescript.test.ts b/e2e/babel-plugin-jest-hoist/__tests__/typescript.test.ts index e92369af9684..820294d1973e 100644 --- a/e2e/babel-plugin-jest-hoist/__tests__/typescript.test.ts +++ b/e2e/babel-plugin-jest-hoist/__tests__/typescript.test.ts @@ -12,6 +12,8 @@ import {Color} from '../types'; import {color} from '../entry'; +jest.mock('some-module', () => ({} as Partial<{}>), {virtual: true}); + jest.mock('../entry', () => { const color: Color = 'blue'; return {color}; diff --git a/packages/babel-plugin-jest-hoist/src/index.js b/packages/babel-plugin-jest-hoist/src/index.js index 13f9c2bdcc00..9445cffde661 100644 --- a/packages/babel-plugin-jest-hoist/src/index.js +++ b/packages/babel-plugin-jest-hoist/src/index.js @@ -76,7 +76,7 @@ const IDVisitor = { ReferencedIdentifier(path) { this.ids.add(path); }, - blacklist: ['TypeAnnotation', 'TSTypeAnnotation'], + blacklist: ['TypeAnnotation', 'TSTypeAnnotation', 'TSTypeReference'], }; const FUNCTIONS: Object = Object.create(null); From 34445fa7dfeeeabd36b32269eeef31924144e84d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Tue, 5 Feb 2019 15:08:33 +0000 Subject: [PATCH 010/107] Release 24.1.0 --- CHANGELOG.md | 12 ++++++++++-- lerna.json | 2 +- packages/babel-jest/package.json | 6 +++--- packages/babel-plugin-jest-hoist/package.json | 4 ++-- packages/babel-preset-jest/package.json | 6 +++--- packages/expect/package.json | 4 ++-- packages/jest-circus/package.json | 10 +++++----- packages/jest-cli/package.json | 14 +++++++------- packages/jest-config/package.json | 10 +++++----- packages/jest-jasmine2/package.json | 10 +++++----- packages/jest-repl/package.json | 8 ++++---- packages/jest-resolve-dependencies/package.json | 6 +++--- packages/jest-resolve/package.json | 4 ++-- packages/jest-runner/package.json | 10 +++++----- packages/jest-runtime/package.json | 10 +++++----- packages/jest-snapshot/package.json | 6 +++--- packages/jest/package.json | 6 +++--- 17 files changed, 68 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8554348f954b..ac6595a4dc8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ ### Features +### Fixes + +### Chore & Maintenance + +### Performance + +## 24.1.0 + +### Features + - `[jest-resolve]`: Pass default resolver into custom resolvers ([#7714](https://github.com/facebook/jest/pull/7714)) - `[jest-cli]`: `global{Setup,Teardown}` use default export with es modules ([#7750](https://github.com/facebook/jest/pull/7750)) - `[jest-runtime]` Better error messages when the jest environment is used after teardown by async code ([#7756](https://github.com/facebook/jest/pull/7756)) @@ -32,8 +42,6 @@ - `[website]` Fix broken help link on homepage ([#7706](https://github.com/facebook/jest/pull/7706)) - `[docs]` Changed Babel setup documentation to correctly compile `async/await` ([#7701](https://github.com/facebook/jest/pull/7701)) -### Performance - ## 24.0.0 ### Features diff --git a/lerna.json b/lerna.json index 0b02fdfefe1e..ef98b2713911 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "lerna": "3.10.5", - "version": "24.0.0", + "version": "24.1.0", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/babel-jest/package.json b/packages/babel-jest/package.json index b05b51da1646..979ee04fb46c 100644 --- a/packages/babel-jest/package.json +++ b/packages/babel-jest/package.json @@ -1,7 +1,7 @@ { "name": "babel-jest", "description": "Jest plugin to use babel for transformation.", - "version": "24.0.0", + "version": "24.1.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -11,7 +11,7 @@ "main": "build/index.js", "dependencies": { "babel-plugin-istanbul": "^5.1.0", - "babel-preset-jest": "^24.0.0", + "babel-preset-jest": "^24.1.0", "chalk": "^2.4.2", "slash": "^2.0.0" }, @@ -24,5 +24,5 @@ "engines": { "node": ">= 6" }, - "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } diff --git a/packages/babel-plugin-jest-hoist/package.json b/packages/babel-plugin-jest-hoist/package.json index 4b0c84d0ca0e..b02241e5c3fc 100644 --- a/packages/babel-plugin-jest-hoist/package.json +++ b/packages/babel-plugin-jest-hoist/package.json @@ -1,6 +1,6 @@ { "name": "babel-plugin-jest-hoist", - "version": "24.0.0", + "version": "24.1.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -11,5 +11,5 @@ }, "license": "MIT", "main": "build/index.js", - "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } diff --git a/packages/babel-preset-jest/package.json b/packages/babel-preset-jest/package.json index 437e9e825557..2def73a20e35 100644 --- a/packages/babel-preset-jest/package.json +++ b/packages/babel-preset-jest/package.json @@ -1,6 +1,6 @@ { "name": "babel-preset-jest", - "version": "24.0.0", + "version": "24.1.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -10,10 +10,10 @@ "main": "index.js", "dependencies": { "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "babel-plugin-jest-hoist": "^24.0.0" + "babel-plugin-jest-hoist": "^24.1.0" }, "engines": { "node": ">= 6" }, - "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } diff --git a/packages/expect/package.json b/packages/expect/package.json index 4ae2e66fb1a1..5e5a75e3fbe8 100644 --- a/packages/expect/package.json +++ b/packages/expect/package.json @@ -1,6 +1,6 @@ { "name": "expect", - "version": "24.0.0", + "version": "24.1.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -19,5 +19,5 @@ "engines": { "node": ">= 6" }, - "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } diff --git a/packages/jest-circus/package.json b/packages/jest-circus/package.json index 016ddb9c4272..8298a3252222 100644 --- a/packages/jest-circus/package.json +++ b/packages/jest-circus/package.json @@ -1,6 +1,6 @@ { "name": "jest-circus", - "version": "24.0.0", + "version": "24.1.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -12,12 +12,12 @@ "@babel/traverse": "^7.1.0", "chalk": "^2.0.1", "co": "^4.6.0", - "expect": "^24.0.0", + "expect": "^24.1.0", "is-generator-fn": "^2.0.0", "jest-each": "^24.0.0", "jest-matcher-utils": "^24.0.0", "jest-message-util": "^24.0.0", - "jest-snapshot": "^24.0.0", + "jest-snapshot": "^24.1.0", "jest-util": "^24.0.0", "pretty-format": "^24.0.0", "stack-utils": "^1.0.1", @@ -26,10 +26,10 @@ "devDependencies": { "execa": "^1.0.0", "jest-diff": "^24.0.0", - "jest-runtime": "^24.0.0" + "jest-runtime": "^24.1.0" }, "engines": { "node": ">= 6" }, - "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } diff --git a/packages/jest-cli/package.json b/packages/jest-cli/package.json index 889be6a3f0eb..bc5b2aa3f5a4 100644 --- a/packages/jest-cli/package.json +++ b/packages/jest-cli/package.json @@ -1,7 +1,7 @@ { "name": "jest-cli", "description": "Delightful JavaScript Testing.", - "version": "24.0.0", + "version": "24.1.0", "main": "build/jest.js", "dependencies": { "ansi-escapes": "^3.0.0", @@ -16,16 +16,16 @@ "istanbul-lib-instrument": "^3.0.1", "istanbul-lib-source-maps": "^3.0.1", "jest-changed-files": "^24.0.0", - "jest-config": "^24.0.0", + "jest-config": "^24.1.0", "jest-environment-jsdom": "^24.0.0", "jest-get-type": "^24.0.0", "jest-haste-map": "^24.0.0", "jest-message-util": "^24.0.0", "jest-regex-util": "^24.0.0", - "jest-resolve-dependencies": "^24.0.0", - "jest-runner": "^24.0.0", - "jest-runtime": "^24.0.0", - "jest-snapshot": "^24.0.0", + "jest-resolve-dependencies": "^24.1.0", + "jest-runner": "^24.1.0", + "jest-runtime": "^24.1.0", + "jest-snapshot": "^24.1.0", "jest-util": "^24.0.0", "jest-validate": "^24.0.0", "jest-watcher": "^24.0.0", @@ -85,5 +85,5 @@ "typescript", "watch" ], - "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } diff --git a/packages/jest-config/package.json b/packages/jest-config/package.json index e9a219bf1239..83e5407b2f6b 100644 --- a/packages/jest-config/package.json +++ b/packages/jest-config/package.json @@ -1,6 +1,6 @@ { "name": "jest-config", - "version": "24.0.0", + "version": "24.1.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -10,15 +10,15 @@ "main": "build/index.js", "dependencies": { "@babel/core": "^7.1.0", - "babel-jest": "^24.0.0", + "babel-jest": "^24.1.0", "chalk": "^2.0.1", "glob": "^7.1.1", "jest-environment-jsdom": "^24.0.0", "jest-environment-node": "^24.0.0", "jest-get-type": "^24.0.0", - "jest-jasmine2": "^24.0.0", + "jest-jasmine2": "^24.1.0", "jest-regex-util": "^24.0.0", - "jest-resolve": "^24.0.0", + "jest-resolve": "^24.1.0", "jest-util": "^24.0.0", "jest-validate": "^24.0.0", "micromatch": "^3.1.10", @@ -28,5 +28,5 @@ "engines": { "node": ">= 6" }, - "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } diff --git a/packages/jest-jasmine2/package.json b/packages/jest-jasmine2/package.json index 8c823b93b176..1ed3e1eb7cfe 100644 --- a/packages/jest-jasmine2/package.json +++ b/packages/jest-jasmine2/package.json @@ -1,6 +1,6 @@ { "name": "jest-jasmine2", - "version": "24.0.0", + "version": "24.1.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -12,22 +12,22 @@ "@babel/traverse": "^7.1.0", "chalk": "^2.0.1", "co": "^4.6.0", - "expect": "^24.0.0", + "expect": "^24.1.0", "is-generator-fn": "^2.0.0", "jest-each": "^24.0.0", "jest-matcher-utils": "^24.0.0", "jest-message-util": "^24.0.0", - "jest-snapshot": "^24.0.0", + "jest-snapshot": "^24.1.0", "jest-util": "^24.0.0", "pretty-format": "^24.0.0", "throat": "^4.0.0" }, "devDependencies": { "jest-diff": "^24.0.0", - "jest-runtime": "^24.0.0" + "jest-runtime": "^24.1.0" }, "engines": { "node": ">= 6" }, - "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } diff --git a/packages/jest-repl/package.json b/packages/jest-repl/package.json index 173bad55bac0..4b96382132ab 100644 --- a/packages/jest-repl/package.json +++ b/packages/jest-repl/package.json @@ -1,6 +1,6 @@ { "name": "jest-repl", - "version": "24.0.0", + "version": "24.1.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -9,8 +9,8 @@ "license": "MIT", "main": "build/index.js", "dependencies": { - "jest-config": "^24.0.0", - "jest-runtime": "^24.0.0", + "jest-config": "^24.1.0", + "jest-runtime": "^24.1.0", "jest-validate": "^24.0.0", "repl": "^0.1.3", "yargs": "^12.0.2" @@ -21,5 +21,5 @@ "engines": { "node": ">= 6" }, - "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } diff --git a/packages/jest-resolve-dependencies/package.json b/packages/jest-resolve-dependencies/package.json index b5ae04909f20..3e4bd3dbaa75 100644 --- a/packages/jest-resolve-dependencies/package.json +++ b/packages/jest-resolve-dependencies/package.json @@ -1,6 +1,6 @@ { "name": "jest-resolve-dependencies", - "version": "24.0.0", + "version": "24.1.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -10,10 +10,10 @@ "main": "build/index.js", "dependencies": { "jest-regex-util": "^24.0.0", - "jest-snapshot": "^24.0.0" + "jest-snapshot": "^24.1.0" }, "engines": { "node": ">= 6" }, - "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } diff --git a/packages/jest-resolve/package.json b/packages/jest-resolve/package.json index 09ac490f7c6b..696170cd90ec 100644 --- a/packages/jest-resolve/package.json +++ b/packages/jest-resolve/package.json @@ -1,6 +1,6 @@ { "name": "jest-resolve", - "version": "24.0.0", + "version": "24.1.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -19,5 +19,5 @@ "engines": { "node": ">= 6" }, - "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } diff --git a/packages/jest-runner/package.json b/packages/jest-runner/package.json index 3323e7aeae7f..a8583affb503 100644 --- a/packages/jest-runner/package.json +++ b/packages/jest-runner/package.json @@ -1,6 +1,6 @@ { "name": "jest-runner", - "version": "24.0.0", + "version": "24.1.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -12,13 +12,13 @@ "chalk": "^2.4.2", "exit": "^0.1.2", "graceful-fs": "^4.1.15", - "jest-config": "^24.0.0", + "jest-config": "^24.1.0", "jest-docblock": "^24.0.0", "jest-haste-map": "^24.0.0", - "jest-jasmine2": "^24.0.0", + "jest-jasmine2": "^24.1.0", "jest-leak-detector": "^24.0.0", "jest-message-util": "^24.0.0", - "jest-runtime": "^24.0.0", + "jest-runtime": "^24.1.0", "jest-util": "^24.0.0", "jest-worker": "^24.0.0", "source-map-support": "^0.5.6", @@ -27,5 +27,5 @@ "engines": { "node": ">= 6" }, - "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index 652e342e7e4c..15913207fe44 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -1,6 +1,6 @@ { "name": "jest-runtime", - "version": "24.0.0", + "version": "24.1.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -17,12 +17,12 @@ "fast-json-stable-stringify": "^2.0.0", "glob": "^7.1.3", "graceful-fs": "^4.1.15", - "jest-config": "^24.0.0", + "jest-config": "^24.1.0", "jest-haste-map": "^24.0.0", "jest-message-util": "^24.0.0", "jest-regex-util": "^24.0.0", - "jest-resolve": "^24.0.0", - "jest-snapshot": "^24.0.0", + "jest-resolve": "^24.1.0", + "jest-snapshot": "^24.1.0", "jest-util": "^24.0.0", "jest-validate": "^24.0.0", "micromatch": "^3.1.10", @@ -42,5 +42,5 @@ "engines": { "node": ">= 6" }, - "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } diff --git a/packages/jest-snapshot/package.json b/packages/jest-snapshot/package.json index 7eba9cd83a2d..7740aa3fde00 100644 --- a/packages/jest-snapshot/package.json +++ b/packages/jest-snapshot/package.json @@ -1,6 +1,6 @@ { "name": "jest-snapshot", - "version": "24.0.0", + "version": "24.1.0", "repository": { "type": "git", "url": "https://github.com/facebook/jest.git", @@ -14,7 +14,7 @@ "jest-diff": "^24.0.0", "jest-matcher-utils": "^24.0.0", "jest-message-util": "^24.0.0", - "jest-resolve": "^24.0.0", + "jest-resolve": "^24.1.0", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", "pretty-format": "^24.0.0", @@ -26,5 +26,5 @@ "engines": { "node": ">= 6" }, - "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } diff --git a/packages/jest/package.json b/packages/jest/package.json index 5361f0cb0ddb..b14020ae23a7 100644 --- a/packages/jest/package.json +++ b/packages/jest/package.json @@ -1,11 +1,11 @@ { "name": "jest", "description": "Delightful JavaScript Testing.", - "version": "24.0.0", + "version": "24.1.0", "main": "build/jest.js", "dependencies": { "import-local": "^2.0.0", - "jest-cli": "^24.0.0" + "jest-cli": "^24.1.0" }, "bin": { "jest": "./bin/jest.js" @@ -45,5 +45,5 @@ "typescript", "watch" ], - "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } From 0fe62affd652d4939519a80a6405b10ddfafff5c Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 5 Feb 2019 16:25:48 +0100 Subject: [PATCH 011/107] chore: new version of website --- website/versioned_docs/version-24.1/CLI.md | 327 +++++ .../version-24.1/Configuration.md | 1086 +++++++++++++++++ website/versions.json | 7 +- 3 files changed, 1419 insertions(+), 1 deletion(-) create mode 100644 website/versioned_docs/version-24.1/CLI.md create mode 100644 website/versioned_docs/version-24.1/Configuration.md diff --git a/website/versioned_docs/version-24.1/CLI.md b/website/versioned_docs/version-24.1/CLI.md new file mode 100644 index 000000000000..50b623fe5c1c --- /dev/null +++ b/website/versioned_docs/version-24.1/CLI.md @@ -0,0 +1,327 @@ +--- +id: version-24.1-cli +title: Jest CLI Options +original_id: cli +--- + +The `jest` command line runner has a number of useful options. You can run `jest --help` to view all available options. Many of the options shown below can also be used together to run tests exactly the way you want. Every one of Jest's [Configuration](Configuration.md) options can also be specified through the CLI. + +Here is a brief overview: + +## Running from the command line + +Run all tests (default): + +```bash +jest +``` + +Run only the tests that were specified with a pattern or filename: + +```bash +jest my-test #or +jest path/to/my-test.js +``` + +Run tests related to changed files based on hg/git (uncommitted files): + +```bash +jest -o +``` + +Run tests related to `path/to/fileA.js` and `path/to/fileB.js`: + +```bash +jest --findRelatedTests path/to/fileA.js path/to/fileB.js +``` + +Run tests that match this spec name (match against the name in `describe` or `test`, basically). + +```bash +jest -t name-of-spec +``` + +Run watch mode: + +```bash +jest --watch #runs jest -o by default +jest --watchAll #runs all tests +``` + +Watch mode also enables to specify the name or path to a file to focus on a specific set of tests. + +## Using with yarn + +If you run Jest via `yarn test`, you can pass the command line arguments directly as Jest arguments. + +Instead of: + +```bash +jest -u -t="ColorPicker" +``` + +you can use: + +```bash +yarn test -u -t="ColorPicker" +``` + +## Using with npm scripts + +If you run Jest via `npm test`, you can still use the command line arguments by inserting a `--` between `npm test` and the Jest arguments. + +Instead of: + +```bash +jest -u -t="ColorPicker" +``` + +you can use: + +```bash +npm test -- -u -t="ColorPicker" +``` + +## Camelcase & dashed args support + +Jest supports both camelcase and dashed arg formats. The following examples will have equal result: + +```bash +jest --collect-coverage +jest --collectCoverage +``` + +Arguments can also be mixed: + +```bash +jest --update-snapshot --detectOpenHandles +``` + +## Options + +_Note: CLI options take precedence over values from the [Configuration](Configuration.md)._ + + + +--- + +## Reference + +### `jest ` + +When you run `jest` with an argument, that argument is treated as a regular expression to match against files in your project. It is possible to run test suites by providing a pattern. Only the files that the pattern matches will be picked up and executed. Depending on your terminal, you may need to quote this argument: `jest "my.*(complex)?pattern"`. On Windows, you will need to use `/` as a path separator or escape `\` as `\\`. + +### `--bail` + +Alias: `-b`. Exit the test suite immediately upon `n` number of failing test suite. Defaults to `1`. + +### `--cache` + +Whether to use the cache. Defaults to true. Disable the cache using `--no-cache`. _Note: the cache should only be disabled if you are experiencing caching related problems. On average, disabling the cache makes Jest at least two times slower._ + +If you want to inspect the cache, use `--showConfig` and look at the `cacheDirectory` value. If you need to clear the cache, use `--clearCache`. + +### `--changedFilesWithAncestor` + +Runs tests related to the current changes and the changes made in the last commit. Behaves similarly to `--onlyChanged`. + +### `--changedSince` + +Runs tests related to the changes since the provided branch. If the current branch has diverged from the given branch, then only changes made locally will be tested. Behaves similarly to `--onlyChanged`. + +### `--ci` + +When this option is provided, Jest will assume it is running in a CI environment. This changes the behavior when a new snapshot is encountered. Instead of the regular behavior of storing a new snapshot automatically, it will fail the test and require Jest to be run with `--updateSnapshot`. + +### `--clearCache` + +Deletes the Jest cache directory and then exits without running tests. Will delete `cacheDirectory` if the option is passed, or Jest's default cache directory. The default cache directory can be found by calling `jest --showConfig`. _Note: clearing the cache will reduce performance._ + +### `--collectCoverageFrom=` + +A glob pattern relative to matching the files that coverage info needs to be collected from. + +### `--colors` + +Forces test results output highlighting even if stdout is not a TTY. + +### `--config=` + +Alias: `-c`. The path to a Jest config file specifying how to find and execute tests. If no `rootDir` is set in the config, the directory containing the config file is assumed to be the rootDir for the project. This can also be a JSON-encoded value which Jest will use as configuration. + +### `--coverage` + +Indicates that test coverage information should be collected and reported in the output. This option is also aliased by `--collectCoverage`. + +### `--debug` + +Print debugging info about your Jest config. + +### `--detectOpenHandles` + +Attempt to collect and print open handles preventing Jest from exiting cleanly. Use this in cases where you need to use `--forceExit` in order for Jest to exit to potentially track down the reason. Implemented using [`async_hooks`](https://nodejs.org/api/async_hooks.html), so it only works in Node 8 and newer. + +### `--env=` + +The test environment used for all tests. This can point to any file or node module. Examples: `jsdom`, `node` or `path/to/my-environment.js`. + +### `--errorOnDeprecated` + +Make calling deprecated APIs throw helpful error messages. Useful for easing the upgrade process. + +### `--expand` + +Alias: `-e`. Use this flag to show full diffs and errors instead of a patch. + +### `--findRelatedTests ` + +Find and run the tests that cover a space separated list of source files that were passed in as arguments. Useful for pre-commit hook integration to run the minimal amount of tests necessary. Can be used together with `--coverage` to include a test coverage for the source files, no duplicate `--collectCoverageFrom` arguments needed. + +### `--forceExit` + +Force Jest to exit after all tests have completed running. This is useful when resources set up by test code cannot be adequately cleaned up. _Note: This feature is an escape-hatch. If Jest doesn't exit at the end of a test run, it means external resources are still being held on to or timers are still pending in your code. It is advised to tear down external resources after each test to make sure Jest can shut down cleanly. You can use `--detectOpenHandles` to help track it down._ + +### `--help` + +Show the help information, similar to this page. + +### `--init` + +Generate a basic configuration file. Based on your project, Jest will ask you a few questions that will help to generate a `jest.config.js` file with a short description for each option. + +### `--json` + +Prints the test results in JSON. This mode will send all other test output and user messages to stderr. + +### `--outputFile=` + +Write test results to a file when the `--json` option is also specified. + +### `--lastCommit` + +Run all tests affected by file changes in the last commit made. Behaves similarly to `--onlyChanged`. + +### `--listTests` + +Lists all tests as JSON that Jest will run given the arguments, and exits. This can be used together with `--findRelatedTests` to know which tests Jest will run. + +### `--logHeapUsage` + +Logs the heap usage after every test. Useful to debug memory leaks. Use together with `--runInBand` and `--expose-gc` in node. + +### `--maxConcurrency=` + +Prevents Jest from executing more than the specified amount of tests at the same time. Only affects tests that use `test.concurrent`. + +### `--maxWorkers=|` + +Alias: `-w`. Specifies the maximum number of workers the worker-pool will spawn for running tests. This defaults to the number of the cores available on your machine. It may be useful to adjust this in resource limited environments like CIs but the default should be adequate for most use-cases. + +For environments with variable CPUs available, you can use percentage based configuration: `--maxWorkers=50%` + +### `--noStackTrace` + +Disables stack trace in test results output. + +### `--notify` + +Activates notifications for test results. Good for when you don't want your consciousness to be able to focus on anything except JavaScript testing. + +### `--onlyChanged` + +Alias: `-o`. Attempts to identify which tests to run based on which files have changed in the current repository. Only works if you're running tests in a git/hg repository at the moment and requires a static dependency graph (ie. no dynamic requires). + +### `--passWithNoTests` + +Allows the test suite to pass when no files are found. + +### `--projects ... ` + +Run tests from one or more projects, found in the specified paths; also takes path globs. This option is the CLI equivalent of the [`projects`](configuration#projects-arraystring--projectconfig) configuration option. Note that if configuration files are found in the specified paths, _all_ projects specified within those configuration files will be run. + +### `--reporters` + +Run tests with specified reporters. [Reporter options](configuration#reporters-array-modulename-modulename-options) are not available via CLI. Example with multiple reporters: + +`jest --reporters="default" --reporters="jest-junit"` + +### `--runInBand` + +Alias: `-i`. Run all tests serially in the current process, rather than creating a worker pool of child processes that run tests. This can be useful for debugging. + +### `--runTestsByPath` + +Run only the tests that were specified with their exact paths. + +_Note: The default regex matching works fine on small runs, but becomes slow if provided with multiple patterns and/or against a lot of tests. This option replaces the regex matching logic and by that optimizes the time it takes Jest to filter specific test files_ + +### `--setupTestFrameworkScriptFile=` + +The path to a module that runs some code to configure or set up the testing framework before each test. Beware that files imported by the setup script will not be mocked during testing. + +### `--showConfig` + +Print your Jest config and then exits. + +### `--silent` + +Prevent tests from printing messages through the console. + +### `--testNamePattern=` + +Alias: `-t`. Run only tests with a name that matches the regex. For example, suppose you want to run only tests related to authorization which will have names like `"GET /api/posts with auth"`, then you can use `jest -t=auth`. + +_Note: The regex is matched against the full name, which is a combination of the test name and all its surrounding describe blocks._ + +### `--testLocationInResults` + +Adds a `location` field to test results. Useful if you want to report the location of a test in a reporter. + +Note that `column` is 0-indexed while `line` is not. + +```json +{ + "column": 4, + "line": 5 +} +``` + +### `--testPathPattern=` + +A regexp pattern string that is matched against all tests paths before executing the test. On Windows, you will need to use `/` as a path separator or escape `\` as `\\`. + +### `--testPathIgnorePatterns=[array]` + +An array of regexp pattern strings that is tested against all tests paths before executing the test. Contrary to `--testPathPattern`, it will only run those test with a path that does not match with the provided regexp expressions. + +### `--testRunner=` + +Lets you specify a custom test runner. + +### `--updateSnapshot` + +Alias: `-u`. Use this flag to re-record every snapshot that fails during this test run. Can be used together with a test suite pattern or with `--testNamePattern` to re-record snapshots. + +### `--useStderr` + +Divert all output to stderr. + +### `--verbose` + +Display individual test results with the test suite hierarchy. + +### `--version` + +Alias: `-v`. Print the version and exit. + +### `--watch` + +Watch files for changes and rerun tests related to changed files. If you want to re-run all tests when a file has changed, use the `--watchAll` option instead. + +### `--watchAll` + +Watch files for changes and rerun all tests when something changes. If you want to re-run only the tests that depend on the changed files, use the `--watch` option. + +### `--watchman` + +Whether to use watchman for file crawling. Defaults to true. Disable using `--no-watchman`. diff --git a/website/versioned_docs/version-24.1/Configuration.md b/website/versioned_docs/version-24.1/Configuration.md new file mode 100644 index 000000000000..4366def08088 --- /dev/null +++ b/website/versioned_docs/version-24.1/Configuration.md @@ -0,0 +1,1086 @@ +--- +id: version-24.1-configuration +title: Configuring Jest +original_id: configuration +--- + +Jest's configuration can be defined in the `package.json` file of your project, or through a `jest.config.js` file or through the `--config ` option. If you'd like to use your `package.json` to store Jest's config, the "jest" key should be used on the top level so Jest will know how to find your settings: + +```json +{ + "name": "my-project", + "jest": { + "verbose": true + } +} +``` + +Or through JavaScript: + +```js +// jest.config.js +module.exports = { + verbose: true, +}; +``` + +Please keep in mind that the resulting configuration must be JSON-serializable. + +When using the `--config` option, the JSON file must not contain a "jest" key: + +```json +{ + "bail": 1, + "verbose": true +} +``` + +## Options + +These options let you control Jest's behavior in your `package.json` file. The Jest philosophy is to work great by default, but sometimes you just need more configuration power. + +### Defaults + +You can retrieve Jest's default options to expand them if needed: + +```js +// jest.config.js +const {defaults} = require('jest-config'); +module.exports = { + // ... + moduleFileExtensions: [...defaults.moduleFileExtensions, 'ts', 'tsx'], + // ... +}; +``` + + + +--- + +## Reference + +### `automock` [boolean] + +Default: `false` + +This option tells Jest that all imported modules in your tests should be mocked automatically. All modules used in your tests will have a replacement implementation, keeping the API surface. + +Example: + +```js +// utils.js +export default { + authorize: () => { + return 'token'; + }, + isAuthorized: secret => secret === 'wizard', +}; +``` + +```js +//__tests__/automocking.test.js +import utils from '../utils'; + +test('if utils mocked automatically', () => { + // Public methods of `utils` are now mock functions + expect(utils.authorize.mock).toBeTruthy(); + expect(utils.isAuthorized.mock).toBeTruthy(); + + // You can provide them with your own implementation + // or just pass the expected return value + utils.authorize.mockReturnValue('mocked_token'); + utils.isAuthorized.mockReturnValue(true); + + expect(utils.authorize()).toBe('mocked_token'); + expect(utils.isAuthorized('not_wizard')).toBeTruthy(); +}); +``` + +_Note: Core modules, like `fs`, are not mocked by default. They can be mocked explicitly, like `jest.mock('fs')`._ + +_Note: Automocking has a performance cost most noticeable in large projects. See [here](troubleshooting.html#tests-are-slow-when-leveraging-automocking) for details and a workaround._ + +### `bail` [number | boolean] + +Default: `0` + +By default, Jest runs all tests and produces all errors into the console upon completion. The bail config option can be used here to have Jest stop running tests after `n` failures. Setting bail to `true` is the same as setting bail to `1`. + +### `browser` [boolean] + +Default: `false` + +Respect Browserify's [`"browser"` field](https://github.com/substack/browserify-handbook#browser-field) in `package.json` when resolving modules. Some modules export different versions based on whether they are operating in Node or a browser. + +### `cacheDirectory` [string] + +Default: `"/tmp/"` + +The directory where Jest should store its cached dependency information. + +Jest attempts to scan your dependency tree once (up-front) and cache it in order to ease some of the filesystem raking that needs to happen while running tests. This config option lets you customize where Jest stores that cache data on disk. + +### `clearMocks` [boolean] + +Default: `false` + +Automatically clear mock calls and instances between every test. Equivalent to calling `jest.clearAllMocks()` between each test. This does not remove any mock implementation that may have been provided. + +### `collectCoverage` [boolean] + +Default: `false` + +Indicates whether the coverage information should be collected while executing the test. Because this retrofits all executed files with coverage collection statements, it may significantly slow down your tests. + +### `collectCoverageFrom` [array] + +Default: `undefined` + +An array of [glob patterns](https://github.com/jonschlinkert/micromatch) indicating a set of files for which coverage information should be collected. If a file matches the specified glob pattern, coverage information will be collected for it even if no tests exist for this file and it's never required in the test suite. + +Example: + +```json +{ + "collectCoverageFrom": [ + "**/*.{js,jsx}", + "!**/node_modules/**", + "!**/vendor/**" + ] +} +``` + +This will collect coverage information for all the files inside the project's `rootDir`, except the ones that match `**/node_modules/**` or `**/vendor/**`. + +_Note: This option requires `collectCoverage` to be set to true or Jest to be invoked with `--coverage`._ + +
+ Help: + If you are seeing coverage output such as... + +``` +=============================== Coverage summary =============================== +Statements : Unknown% ( 0/0 ) +Branches : Unknown% ( 0/0 ) +Functions : Unknown% ( 0/0 ) +Lines : Unknown% ( 0/0 ) +================================================================================ +Jest: Coverage data for global was not found. +``` + +Most likely your glob patterns are not matching any files. Refer to the [micromatch](https://github.com/jonschlinkert/micromatch) documentation to ensure your globs are compatible. + +
+ +### `coverageDirectory` [string] + +Default: `undefined` + +The directory where Jest should output its coverage files. + +### `coveragePathIgnorePatterns` [array] + +Default: `["/node_modules/"]` + +An array of regexp pattern strings that are matched against all file paths before executing the test. If the file path matches any of the patterns, coverage information will be skipped. + +These pattern strings 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: `["/build/", "/node_modules/"]`. + +### `coverageReporters` [array] + +Default: `["json", "lcov", "text", "clover"]` + +A list of reporter names that Jest uses when writing coverage reports. Any [istanbul reporter](https://github.com/istanbuljs/istanbuljs/tree/master/packages/istanbul-reports/lib) can be used. + +_Note: Setting this option overwrites the default values. Add `"text"` or `"text-summary"` to see a coverage summary in the console output._ + +### `coverageThreshold` [object] + +Default: `undefined` + +This will be used to configure minimum threshold enforcement for coverage results. Thresholds can be specified as `global`, as a [glob](https://github.com/isaacs/node-glob#glob-primer), and as a directory or file path. If thresholds aren't met, jest will fail. Thresholds specified as a positive number are taken to be the minimum percentage required. Thresholds specified as a negative number represent the maximum number of uncovered entities allowed. + +For example, with the following configuration jest will fail if there is less than 80% branch, line, and function coverage, or if there are more than 10 uncovered statements: + +```json +{ + ... + "jest": { + "coverageThreshold": { + "global": { + "branches": 80, + "functions": 80, + "lines": 80, + "statements": -10 + } + } + } +} +``` + +If globs or paths are specified alongside `global`, coverage data for matching paths will be subtracted from overall coverage and thresholds will be applied independently. Thresholds for globs are applied to all files matching the glob. If the file specified by path is not found, error is returned. + +For example, with the following configuration: + +```json +{ + ... + "jest": { + "coverageThreshold": { + "global": { + "branches": 50, + "functions": 50, + "lines": 50, + "statements": 50 + }, + "./src/components/": { + "branches": 40, + "statements": 40 + }, + "./src/reducers/**/*.js": { + "statements": 90 + }, + "./src/api/very-important-module.js": { + "branches": 100, + "functions": 100, + "lines": 100, + "statements": 100 + } + } + } +} +``` + +Jest will fail if: + +- The `./src/components` directory has less than 40% branch or statement coverage. +- One of the files matching the `./src/reducers/**/*.js` glob has less than 90% statement coverage. +- 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. E.g.: + +```javascript +const fs = require('fs'); +const crypto = require('crypto'); + +module.exports = { + extract(code, filePath, defaultExtract) { + const deps = defaultExtract(code, filePath); + // Scan the file and add dependencies in `deps` (which is a `Set`) + return deps; + }, + getCacheKey() { + return crypto + .createHash('md5') + .update(fs.readFileSync(__filename)) + .digest('hex'); + }, +}; +``` + +The `extract` 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` + +Make calling deprecated APIs throw helpful error messages. Useful for easing the upgrade process. + +### `extraGlobals` [array] + +Default: `undefined` + +Test files run inside a [vm](https://nodejs.org/api/vm.html), which slows calls to global context properties (e.g. `Math`). With this option you can specify extra properties to be defined inside the vm for faster lookups. + +For example, if your tests call `Math` often, you can pass it by setting `extraGlobals`. + +```json +{ + ... + "jest": { + "extraGlobals": ["Math"] + } +} +``` + +### `forceCoverageMatch` [array] + +Default: `['']` + +Test files are normally ignored from collecting code coverage. With this option, you can overwrite this behavior and include otherwise ignored files in code coverage. + +For example, if you have tests in source files named with `.t.js` extension as following: + +```javascript +// sum.t.js + +export function sum(a, b) { + return a + b; +} + +if (process.env.NODE_ENV === 'test') { + test('sum', () => { + expect(sum(1, 2)).toBe(3); + }); +} +``` + +You can collect coverage from those files with setting `forceCoverageMatch`. + +```json +{ + ... + "jest": { + "forceCoverageMatch": ["**/*.t.js"] + } +} +``` + +### `globals` [object] + +Default: `{}` + +A set of global variables that need to be available in all test environments. + +For example, the following would create a global `__DEV__` variable set to `true` in all test environments: + +```json +{ + ... + "jest": { + "globals": { + "__DEV__": true + } + } +} +``` + +Note that, if you specify a global reference value (like an object or array) here, and some code mutates that value in the midst of running a test, that mutation will _not_ be persisted across test runs for other test files. In addition the `globals` object must be json-serializable, so it can't be used to specify global functions. For that you should use `setupFiles`. + +### `globalSetup` [string] + +Default: `undefined` + +This option allows the use of a custom global setup module which exports an async function that is triggered once before all test suites. This function gets Jest's `globalConfig` object as a parameter. + +_Note: A global setup module configured in a project (using multi-project runner) will be triggered only when you run at least one test from this project._ + +_Note: Any global variables that are defined through `globalSetup` can only be read in `globalTeardown`. You cannot retrieve globals defined here in your test suites._ + +_Note: While code transformation is applied to the linked setup-file, Jest will **not** transform any code in `node_modules`. This is due to the need to load the actual transformers (e.g. `babel` or `typescript`) to perform transformation._ + +Example: + +```js +// setup.js +module.exports = async () => { + // ... + // Set reference to mongod in order to close the server during teardown. + global.__MONGOD__ = mongod; +}; +``` + +```js +// teardown.js +module.exports = async function() { + await global.__MONGOD__.stop(); +}; +``` + +### `globalTeardown` [string] + +Default: `undefined` + +This option allows the use of a custom global teardown module which exports an async function that is triggered once after all test suites. This function gets Jest's `globalConfig` object as a parameter. + +_Note: A global teardown module configured in a project (using multi-project runner) will be triggered only when you run at least one test from this project._ + +_Node: The same caveat concerning transformation of `node_modules_ as for `globalSetup` applies to `globalTeardown`. + +### `maxConcurrency` [number] + +Default: `5` + +A number limiting the number of tests that are allowed to run at the same time when using `test.concurrent`. Any test above this limit will be queued and executed once a slot is released. + +### `moduleDirectories` [array] + +Default: `["node_modules"]` + +An array of directory names to be searched recursively up from the requiring module's location. Setting this option will _override_ the default, if you wish to still search `node_modules` for packages include it along with any other options: `["node_modules", "bower_components"]` + +### `moduleFileExtensions` [array] + +Default: `["js", "json", "jsx", "ts", "tsx", "node"]` + +An array of file extensions your modules use. If you require modules without specifying a file extension, these are the extensions Jest will look for, in left-to-right order. + +We recommend placing the extensions most commonly used in your project on the left, so if you are using TypeScript, you may want to consider moving "ts" and/or "tsx" to the beginning of the array. + +### `moduleNameMapper` [object] + +Default: `null` + +A map from regular expressions to module names that allow to stub out resources, like images or styles with a single module. + +Modules that are mapped to an alias are unmocked by default, regardless of whether automocking is enabled or not. + +Use `` string token to refer to [`rootDir`](#rootdir-string) value if you want to use file paths. + +Additionally, you can substitute captured regex groups using numbered backreferences. + +Example: + +```json +{ + "moduleNameMapper": { + "^image![a-zA-Z0-9$_-]+$": "GlobalImageStub", + "^[./a-zA-Z0-9$_-]+\\.png$": "/RelativeImageStub.js", + "module_name_(.*)": "/substituted_module_$1.js" + } +} +``` + +The order in which the mappings are defined matters. Patterns are checked one by one until one fits. The most specific rule should be listed first. + +_Note: If you provide module name without boundaries `^$` it may cause hard to spot errors. E.g. `relay` will replace all modules which contain `relay` as a substring in its name: `relay`, `react-relay` and `graphql-relay` will all be pointed to your stub._ + +### `modulePathIgnorePatterns` [array] + +Default: `[]` + +An array of regexp pattern strings that are matched against all module paths before those paths are to be considered 'visible' to the module loader. If a given module's path matches any of the patterns, it will not be `require()`-able in the test environment. + +These pattern strings 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: `["/build/"]`. + +### `modulePaths` [array] + +Default: `[]` + +An alternative API to setting the `NODE_PATH` env variable, `modulePaths` is an array of absolute paths to additional locations to search when resolving modules. Use the `` string token to include the path to your project's root directory. Example: `["/app/"]`. + +### `notify` [boolean] + +Default: `false` + +Activates notifications for test results. + +### `notifyMode` [string] + +Default: `failure-change` + +Specifies notification mode. Requires `notify: true`. + +#### Modes + +- `always`: always send a notification. +- `failure`: send a notification when tests fail. +- `success`: send a notification when tests pass. +- `change`: send a notification when the status changed. +- `success-change`: send a notification when tests pass or once when it fails. +- `failure-change`: send a notification when tests fail or once when it passes. + +### `preset` [string] + +Default: `undefined` + +A preset that is used as a base for Jest's configuration. A preset should point to an npm module that has a `jest-preset.json` or `jest-preset.js` file at the root. + +For example, this preset `foo-bar/jest-preset.js` will be configured as follows: + +```json +{ + "preset": "foo-bar" +} +``` + +Presets may also be relative filesystem paths. + +```json +{ + "preset": "./node_modules/foo-bar/jest-preset.js" +} +``` + +### `prettierPath` [string] + +Default: `'prettier'` + +Sets the path to the [`prettier`](https://prettier.io/) node module used to update inline snapshots. + +### `projects` [array] + +Default: `undefined` + +When the `projects` configuration is provided with an array of paths or glob patterns, Jest will run tests in all of the specified projects at the same time. This is great for monorepos or when working on multiple projects at the same time. + +```json +{ + "projects": ["", "/examples/*"] +} +``` + +This example configuration will run Jest in the root directory as well as in every folder in the examples directory. You can have an unlimited amount of projects running in the same Jest instance. + +The projects feature can also be used to run multiple configurations or multiple [runners](#runner-string). For this purpose you can pass an array of configuration objects. For example, to run both tests and ESLint (via [jest-runner-eslint](https://github.com/jest-community/jest-runner-eslint)) in the same invocation of Jest: + +```json +{ + "projects": [ + { + "displayName": "test" + }, + { + "displayName": "lint", + "runner": "jest-runner-eslint", + "testMatch": ["/**/*.js"] + } + ] +} +``` + +_Note: When using multi project runner, it's recommended to add a `displayName` for each project. This will show the `displayName` of a project next to its tests._ + +### `reporters` [array] + +Default: `undefined` + +Use this configuration option to add custom reporters to Jest. A custom reporter is a class that implements `onRunStart`, `onTestStart`, `onTestResult`, `onRunComplete` methods that will be called when any of those events occurs. + +If custom reporters are specified, the default Jest reporters will be overridden. To keep default reporters, `default` can be passed as a module name. + +This will override default reporters: + +```json +{ + "reporters": ["/my-custom-reporter.js"] +} +``` + +This will use custom reporter in addition to default reporters that Jest provides: + +```json +{ + "reporters": ["default", "/my-custom-reporter.js"] +} +``` + +Additionally, custom reporters can be configured by passing an `options` object as a second argument: + +```json +{ + "reporters": [ + "default", + ["/my-custom-reporter.js", {"banana": "yes", "pineapple": "no"}] + ] +} +``` + +Custom reporter modules must define a class that takes a `GlobalConfig` and reporter options as constructor arguments: + +Example reporter: + +```js +// my-custom-reporter.js +class MyCustomReporter { + constructor(globalConfig, options) { + this._globalConfig = globalConfig; + this._options = options; + } + + onRunComplete(contexts, results) { + console.log('Custom reporter output:'); + console.log('GlobalConfig: ', this._globalConfig); + console.log('Options: ', this._options); + } +} + +module.exports = MyCustomReporter; +``` + +Custom reporters can also force Jest to exit with non-0 code by returning an Error from `getLastError()` methods + +```js +class MyCustomReporter { + // ... + getLastError() { + if (this._shouldFail) { + return new Error('my-custom-reporter.js reported an error'); + } + } +} +``` + +For the full list of methods and argument types see `Reporter` type in [types/TestRunner.js](https://github.com/facebook/jest/blob/master/types/TestRunner.js) + +### `resetMocks` [boolean] + +Default: `false` + +Automatically reset mock state between every test. Equivalent to calling `jest.resetAllMocks()` between each test. This will lead to any mocks having their fake implementations removed but does not restore their initial implementation. + +### `resetModules` [boolean] + +Default: `false` + +By default, each test file gets its own independent module registry. Enabling `resetModules` goes a step further and resets the module registry before running each individual test. This is useful to isolate modules for every test so that local module state doesn't conflict between tests. This can be done programmatically using [`jest.resetModules()`](#jest-resetmodules). + +### `resolver` [string] + +Default: `undefined` + +This option allows the use of a custom resolver. This resolver must be a node module that exports a function expecting a string as the first argument for the path to resolve and an object with the following structure as the second argument: + +```json +{ + "basedir": string, + "browser": bool, + "extensions": [string], + "moduleDirectory": [string], + "paths": [string], + "rootDir": [string] +} +``` + +The function should either return a path to the module that should be resolved or throw an error if the module can't be found. + +### `restoreMocks` [boolean] + +Default: `false` + +Automatically restore mock state between every test. Equivalent to calling `jest.restoreAllMocks()` between each test. This will lead to any mocks having their fake implementations removed and restores their initial implementation. + +### `rootDir` [string] + +Default: The root of the directory containing your jest's [config file](#) _or_ the `package.json` _or_ the [`pwd`](http://en.wikipedia.org/wiki/Pwd) if no `package.json` is found + +The root directory that Jest should scan for tests and modules within. If you put your Jest config inside your `package.json` and want the root directory to be the root of your repo, the value for this config param will default to the directory of the `package.json`. + +Oftentimes, you'll want to set this to `'src'` or `'lib'`, corresponding to where in your repository the code is stored. + +_Note that using `''` as a string token in any other path-based config settings will refer back to this value. So, for example, if you want your [`setupFiles`](#setupfiles-array) config entry to point at the `env-setup.js` file at the root of your project, you could set its value to `["/env-setup.js"]`._ + +### `roots` [array] + +Default: `[""]` + +A list of paths to directories that Jest should use to search for files in. + +There are times where you only want Jest to search in a single sub-directory (such as cases where you have a `src/` directory in your repo), but prevent it from accessing the rest of the repo. + +_Note: While `rootDir` is mostly used as a token to be re-used in other configuration options, `roots` is used by the internals of Jest to locate **test files and source files**. This applies also when searching for manual mocks for modules from `node_modules` (`__mocks__` will need to live in one of the `roots`)._ + +_Note: By default, `roots` has a single entry `` but there are cases where you may want to have multiple roots within one project, for example `roots: ["/src/", "/tests/"]`._ + +### `runner` [string] + +Default: `"jest-runner"` + +This option allows you to use a custom runner instead of Jest's default test runner. Examples of runners include: + +- [`jest-runner-eslint`](https://github.com/jest-community/jest-runner-eslint) +- [`jest-runner-mocha`](https://github.com/rogeliog/jest-runner-mocha) +- [`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 +async runTests( + tests: Array, + watcher: TestWatcher, + onStart: OnTestStart, + onResult: OnTestSuccess, + onFailure: OnTestFailure, + options: TestRunnerOptions, +): Promise +``` + +If you need to restrict your test-runner to only run in serial rather then being executed in parallel your class should have the property `isSerial` to be set as `true`. + +### `setupFiles` [array] + +Default: `[]` + +A list of paths to modules that run some code to configure or set up the testing environment. Each setupFile will be run once per test file. Since every test runs in its own environment, these scripts will be executed in the testing environment immediately before executing the test code itself. + +It's also worth noting that `setupFiles` will execute _before_ [`setupFilesAfterEnv`](#setupFilesAfterEnv-array). + +### `setupFilesAfterEnv` [array] + +Default: `[]` + +A list of paths to modules that run some code to configure or set up the testing framework before each test. Since [`setupFiles`](#setupfiles-array) executes before the test framework is installed in the environment, this script file presents you the opportunity of running some code immediately after the test framework has been installed in the environment. + +If you want a path to be [relative to the root directory of your project](#rootdir-string), please include `` inside a path's string, like `"/a-configs-folder"`. + +For example, Jest ships with several plug-ins to `jasmine` that work by monkey-patching the jasmine API. If you wanted to add even more jasmine plugins to the mix (or if you wanted some custom, project-wide matchers for example), you could do so in these modules. + +_Note: `setupTestFrameworkScriptFile` is deprecated in favor of `setupFilesAfterEnv`._ + +### `snapshotResolver` [string] + +Default: `undefined` + +The path to a module that can resolve test<->snapshot path. This config option lets you customize where Jest stores snapshot files on disk. + +Example snapshot resolver module: + +```js +module.exports = { + // resolves from test to snapshot path + resolveSnapshotPath: (testPath, snapshotExtension) => + testPath.replace('__tests__', '__snapshots__') + snapshotExtension, + + // resolves from snapshot to test path + resolveTestPath: (snapshotFilePath, snapshotExtension) => + snapshotFilePath + .replace('__snapshots__', '__tests__') + .slice(0, -snapshotExtension.length), + + // Example test path, used for preflight consistency check of the implementation above + testPathForConsistencyCheck: 'some/__tests__/example.test.js', +}; +``` + +### `snapshotSerializers` [array] + +Default: `[]` + +A list of paths to snapshot serializer modules Jest should use for snapshot testing. + +Jest has default serializers for built-in JavaScript types, HTML elements (Jest 20.0.0+), ImmutableJS (Jest 20.0.0+) and for React elements. See [snapshot test tutorial](TutorialReactNative.md#snapshot-test) for more information. + +Example serializer module: + +```js +// my-serializer-module +module.exports = { + print(val, serialize, indent) { + return 'Pretty foo: ' + serialize(val.foo); + }, + + test(val) { + return val && val.hasOwnProperty('foo'); + }, +}; +``` + +`serialize` is a function that serializes a value using existing plugins. + +To use `my-serializer-module` as a serializer, configuration would be as follows: + +```json +{ + ... + "jest": { + "snapshotSerializers": ["my-serializer-module"] + } +} +``` + +Finally tests would look as follows: + +```js +test(() => { + const bar = { + foo: { + x: 1, + y: 2, + }, + }; + + expect(bar).toMatchSnapshot(); +}); +``` + +Rendered snapshot: + +```json +Pretty foo: Object { + "x": 1, + "y": 2, +} +``` + +To make a dependency explicit instead of implicit, you can call [`expect.addSnapshotSerializer`](ExpectAPI.md#expectaddsnapshotserializerserializer) to add a module for an individual test file instead of adding its path to `snapshotSerializers` in Jest configuration. + +### `testEnvironment` [string] + +Default: `"jsdom"` + +The test environment that will be used for testing. The default environment in Jest is a browser-like environment through [jsdom](https://github.com/tmpvar/jsdom). If you are building a node service, you can use the `node` option to use a node-like environment instead. + +By adding a `@jest-environment` docblock at the top of the file, you can specify another environment to be used for all tests in that file: + +```js +/** + * @jest-environment jsdom + */ + +test('use jsdom in this test file', () => { + const element = document.createElement('div'); + expect(element).not.toBeNull(); +}); +``` + +You can create your own module that will be used for setting up the test environment. The module must export a class with `setup`, `teardown` and `runScript` methods. You can also pass variables from this module to your test suites by assigning them to `this.global` object – this will make them available in your test suites as global variables. + +_Note: TestEnvironment is sandboxed. Each test suite will trigger setup/teardown in their own TestEnvironment._ + +Example: + +```js +// my-custom-environment +const NodeEnvironment = require('jest-environment-node'); + +class CustomEnvironment extends NodeEnvironment { + constructor(config, context) { + super(config, context); + this.testPath = context.testPath; + } + + async setup() { + await super.setup(); + await someSetupTasks(this.testPath); + this.global.someGlobalObject = createGlobalObject(); + } + + async teardown() { + this.global.someGlobalObject = destroyGlobalObject(); + await someTeardownTasks(); + await super.teardown(); + } + + runScript(script) { + return super.runScript(script); + } +} + +module.exports = CustomEnvironment; +``` + +```js +// my-test-suite +let someGlobalObject; + +beforeAll(() => { + someGlobalObject = global.someGlobalObject; +}); +``` + +_Note: Jest comes with JSDOM@11 by default. Due to JSDOM 12 and newer dropping support for Node 6, Jest is unable to upgrade for the time being. However, you can install a custom `testEnvironment` with whichever version of JSDOM you want. E.g. [jest-environment-jsdom-thirteen](https://www.npmjs.com/package/jest-environment-jsdom-thirteen), which has JSDOM@13._ + +### `testEnvironmentOptions` [Object] + +Default: `{}` + +Test environment options that will be passed to the `testEnvironment`. The relevant options depend on the environment. For example you can override options given to [jsdom](https://github.com/tmpvar/jsdom) such as `{userAgent: "Agent/007"}`. + +### `testMatch` [array] + +(default: `[ "**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)" ]`) + +The glob patterns Jest uses to detect test files. By default it looks for `.js`, `.jsx`, `.ts` and `.tsx` files inside of `__tests__` folders, as well as any files with a suffix of `.test` or `.spec` (e.g. `Component.test.js` or `Component.spec.js`). It will also find files called `test.js` or `spec.js`. + +See the [micromatch](https://github.com/jonschlinkert/micromatch) package for details of the patterns you can specify. + +See also [`testRegex` [string | Array]](#testregex-string), but note that you cannot specify both options. + +### `testPathIgnorePatterns` [array] + +Default: `["/node_modules/"]` + +An array of regexp pattern strings that are matched against all test paths before executing the test. If the test path matches any of the patterns, it will be skipped. + +These pattern strings 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: `["/build/", "/node_modules/"]`. + +### `testRegex` [string | Array] + +Default: `(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$` + +The pattern or patterns Jest uses to detect test files. By default it looks for `.js`, `.jsx`, `.ts` and `.tsx` files inside of `__tests__` folders, as well as any files with a suffix of `.test` or `.spec` (e.g. `Component.test.js` or `Component.spec.js`). It will also find files called `test.js` or `spec.js`. See also [`testMatch` [array]](#testmatch-array-string), but note that you cannot specify both options. + +The following is a visualization of the default regex: + +```bash +├── __tests__ +│ └── component.spec.js # test +│ └── anything # test +├── package.json # not test +├── foo.test.js # test +├── bar.spec.jsx # test +└── component.js # not test +``` + +_Note: `testRegex` will try to detect test files using the **absolute file path** therefore having a folder with name that match it will run all the files as tests_ + +### `testResultsProcessor` [string] + +Default: `undefined` + +This option allows the use of a custom results processor. This processor must be a node module that exports a function expecting an object with the following structure as the first argument and return it: + +```json +{ + "success": bool, + "startTime": epoch, + "numTotalTestSuites": number, + "numPassedTestSuites": number, + "numFailedTestSuites": number, + "numRuntimeErrorTestSuites": number, + "numTotalTests": number, + "numPassedTests": number, + "numFailedTests": number, + "numPendingTests": number, + "openHandles": Array, + "testResults": [{ + "numFailingTests": number, + "numPassingTests": number, + "numPendingTests": number, + "testResults": [{ + "title": string (message in it block), + "status": "failed" | "pending" | "passed", + "ancestorTitles": [string (message in describe blocks)], + "failureMessages": [string], + "numPassingAsserts": number, + "location": { + "column": number, + "line": number + } + }, + ... + ], + "perfStats": { + "start": epoch, + "end": epoch + }, + "testFilePath": absolute path to test file, + "coverage": {} + }, + ... + ] +} +``` + +### `testRunner` [string] + +Default: `jasmine2` + +This option allows use of a custom test runner. The default is jasmine2. A custom test runner can be provided by specifying a path to a test runner implementation. + +The test runner module must export a function with the following signature: + +```ts +function testRunner( + config: Config, + environment: Environment, + runtime: Runtime, + testPath: string, +): Promise; +``` + +An example of such function can be found in our default [jasmine2 test runner package](https://github.com/facebook/jest/blob/master/packages/jest-jasmine2/src/index.js). + +### `testURL` [string] + +Default: `http://localhost` + +This option sets the URL for the jsdom environment. It is reflected in properties such as `location.href`. + +### `timers` [string] + +Default: `real` + +Setting this value to `fake` allows the use of fake timers for functions such as `setTimeout`. Fake timers are useful when a piece of code sets a long timeout that we don't want to wait for in a test. + +### `transform` [object] + +Default: `undefined` + +A map from regular expressions to paths to transformers. A transformer is a module that provides a synchronous function for transforming source files. For example, if you wanted to be able to use a new language feature in your modules or tests that isn't yet supported by node, you might plug in one of many compilers that compile a future version of JavaScript to a current one. Example: see the [examples/typescript](https://github.com/facebook/jest/blob/master/examples/typescript/package.json#L16) example or the [webpack tutorial](Webpack.md). + +Examples of such compilers include [Babel](https://babeljs.io/), [TypeScript](http://www.typescriptlang.org/) and [async-to-gen](http://github.com/leebyron/async-to-gen#jest). + +_Note: a transformer is only run once per file unless the file has changed. During development of a transformer it can be useful to run Jest with `--no-cache` to frequently [delete Jest's cache](Troubleshooting.md#caching-issues)._ + +_Note: if you are using the `babel-jest` transformer and want to use an additional code preprocessor, keep in mind that when "transform" is overwritten in any way the `babel-jest` is not loaded automatically anymore. If you want to use it to compile JavaScript code it has to be explicitly defined. See [babel-jest plugin](https://github.com/facebook/jest/tree/master/packages/babel-jest#setup)_ + +### `transformIgnorePatterns` [array] + +Default: `["/node_modules/"]` + +An array of regexp pattern strings that are matched against all source file paths before transformation. If the test path matches any of the patterns, it will not be transformed. + +These pattern strings 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: `["/bower_components/", "/node_modules/"]`. + +Sometimes it happens (especially in React Native or TypeScript projects) that 3rd party modules are published as untranspiled. Since all files inside `node_modules` are not transformed by default, Jest will not understand the code in these modules, resulting in syntax errors. To overcome this, you may use `transformIgnorePatterns` to whitelist such modules. You'll find a good example of this use case in [React Native Guide](https://jestjs.io/docs/en/tutorial-react-native#transformignorepatterns-customization). + +### `unmockedModulePathPatterns` [array] + +Default: `[]` + +An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them. If a module's path matches any of the patterns in this list, it will not be automatically mocked by the module loader. + +This is useful for some commonly used 'utility' modules that are almost always used as implementation details almost all the time (like underscore/lo-dash, etc). It's generally a best practice to keep this list as small as possible and always use explicit `jest.mock()`/`jest.unmock()` calls in individual tests. Explicit per-test setup is far easier for other readers of the test to reason about the environment the test will run in. + +It is possible to override this setting in individual tests by explicitly calling `jest.mock()` at the top of the test file. + +### `verbose` [boolean] + +Default: `false` + +Indicates whether each individual test should be reported during the run. All errors will also still be shown on the bottom after execution. + +### `watchPathIgnorePatterns` [array] + +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/website/versions.json b/website/versions.json index 5bc567be87da..5dd08136309e 100644 --- a/website/versions.json +++ b/website/versions.json @@ -1 +1,6 @@ -["24.0", "23.x", "22.x"] +[ + "24.1", + "24.0", + "23.x", + "22.x" +] From 85b9ff3559052dbe80039cfa816c8547317d2601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ramos?= <165856+hramos@users.noreply.github.com> Date: Tue, 5 Feb 2019 10:12:44 -0800 Subject: [PATCH 012/107] Fix typo in question.md issue template (#7806) ## Summary Trivial. Replace "form" with "forum" in issue template. ## Test plan Skipped tests, as this is a minor typo fix in a GitHub issue template. --- .github/ISSUE_TEMPLATE/question.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index bc450db07a13..a4942dc28381 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -8,7 +8,7 @@ about: If you have questions, please check our Discord or StackOverflow ## 💬 Questions and Help -### Please note that this issue tracker is not a help form and this issue will be closed. +### Please note that this issue tracker is not a help forum and this issue will be closed. For questions or help please see: From 625ffec9454722275b1fa7c7b632c9e7d23d5d07 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 6 Feb 2019 10:18:49 +0100 Subject: [PATCH 013/107] Setup building, linting and testing of TypeScript (#7808) --- .eslintrc.js | 14 + CHANGELOG.md | 2 + babel.config.js | 16 +- .../coverageRemapping.test.js.snap | 108 ++--- .../__tests__/typescript.test.ts | 2 - e2e/babel-plugin-jest-hoist/entry.ts | 1 - e2e/coverage-remapping/covered.ts | 2 + e2e/coverage-remapping/package.json | 8 +- e2e/stack-trace-source-maps/package.json | 4 - e2e/typescript-coverage/package.json | 12 +- e2e/typescript-coverage/yarn.lock | 8 +- jest.config.js | 2 +- package.json | 16 +- packages/babel-jest/package.json | 4 +- packages/expect/package.json | 3 + packages/jest-changed-files/package.json | 3 + packages/jest-circus/package.json | 2 + packages/jest-cli/package.json | 17 + packages/jest-config/package.json | 5 + packages/jest-docblock/package.json | 3 + packages/jest-environment-jsdom/package.json | 3 + packages/jest-haste-map/package.json | 7 + packages/jest-jasmine2/package.json | 1 + packages/jest-leak-detector/package.json | 1 + packages/jest-message-util/package.json | 5 + packages/jest-repl/package.json | 3 + packages/jest-resolve/package.json | 1 + packages/jest-runner/package.json | 5 + packages/jest-runtime/package.json | 9 + packages/jest-snapshot/package.json | 2 + packages/jest-util/package.json | 4 + packages/jest-validate/package.json | 4 + packages/jest-watcher/package.json | 4 + packages/jest-worker/package.json | 2 + packages/pretty-format/package.json | 2 + ...lugin-jest-replace-ts-export-assignment.js | 26 ++ scripts/browserBuild.js | 5 +- scripts/build.js | 42 +- scripts/watch.js | 14 +- tsconfig.json | 26 ++ yarn.lock | 402 +++++++++++++++++- 41 files changed, 686 insertions(+), 114 deletions(-) create mode 100644 scripts/babel-plugin-jest-replace-ts-export-assignment.js create mode 100644 tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js index 4a3ba1dcff90..d7ed999831eb 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,6 +15,19 @@ module.exports = { 'prettier/flowtype', ], overrides: [ + { + files: ['*.ts', '*.tsx'], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint/eslint-plugin'], + rules: { + '@typescript-eslint/no-unused-vars': [ + 'error', + {argsIgnorePattern: '^_'}, + ], + 'import/order': 'error', + 'no-unused-vars': 'off', + }, + }, // to make it more suitable for running on code examples in docs/ folder { files: ['*.md'], @@ -122,6 +135,7 @@ module.exports = { '^types/(.*)': './types/$1', }, }, + 'eslint-import-resolver-typescript': true, }, }, }; diff --git a/CHANGELOG.md b/CHANGELOG.md index ac6595a4dc8b..d039d87a4441 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ ### Chore & Maintenance +- `[*]`: Setup building, linting and testing of TypeScript ([#7808](https://github.com/facebook/jest/pull/7808)) + ### Performance ## 24.1.0 diff --git a/babel.config.js b/babel.config.js index cc4688972601..e295ec832457 100644 --- a/babel.config.js +++ b/babel.config.js @@ -2,6 +2,21 @@ module.exports = { babelrcRoots: ['examples/*'], + overrides: [ + { + presets: ['@babel/preset-flow'], + test: '**/*.js', + }, + { + plugins: [ + require.resolve( + './scripts/babel-plugin-jest-replace-ts-export-assignment.js' + ), + ], + presets: ['@babel/preset-typescript'], + test: /\.tsx?$/, + }, + ], plugins: [ ['@babel/plugin-transform-modules-commonjs', {allowTopLevelThis: true}], '@babel/plugin-transform-strict-mode', @@ -14,6 +29,5 @@ module.exports = { targets: {node: 6}, }, ], - '@babel/preset-flow', ], }; diff --git a/e2e/__tests__/__snapshots__/coverageRemapping.test.js.snap b/e2e/__tests__/__snapshots__/coverageRemapping.test.js.snap index 3faf7a0a5362..ee84a8e4d887 100644 --- a/e2e/__tests__/__snapshots__/coverageRemapping.test.js.snap +++ b/e2e/__tests__/__snapshots__/coverageRemapping.test.js.snap @@ -27,32 +27,32 @@ Object { "loc": Object { "end": Object { "column": 35, - "line": 4, + "line": 6, }, "start": Object { "column": 34, - "line": 4, + "line": 6, }, }, "locations": Array [ Object { "end": Object { "column": 35, - "line": 4, + "line": 6, }, "start": Object { "column": 34, - "line": 4, + "line": 6, }, }, Object { "end": Object { "column": 39, - "line": 4, + "line": 6, }, "start": Object { "column": 38, - "line": 4, + "line": 6, }, }, ], @@ -62,32 +62,32 @@ Object { "loc": Object { "end": Object { "column": 35, - "line": 5, + "line": 7, }, "start": Object { "column": 34, - "line": 5, + "line": 7, }, }, "locations": Array [ Object { "end": Object { "column": 35, - "line": 5, + "line": 7, }, "start": Object { "column": 34, - "line": 5, + "line": 7, }, }, Object { "end": Object { "column": 39, - "line": 5, + "line": 7, }, "start": Object { "column": 38, - "line": 5, + "line": 7, }, }, ], @@ -97,42 +97,42 @@ Object { "loc": Object { "end": Object { "column": 31, - "line": 6, + "line": 8, }, "start": Object { "column": 27, - "line": 6, + "line": 8, }, }, "locations": Array [ Object { "end": Object { "column": 31, - "line": 6, + "line": 8, }, "start": Object { "column": 27, - "line": 6, + "line": 8, }, }, Object { "end": Object { "column": 39, - "line": 6, + "line": 8, }, "start": Object { "column": 35, - "line": 6, + "line": 8, }, }, Object { "end": Object { "column": 48, - "line": 6, + "line": 8, }, "start": Object { "column": 43, - "line": 6, + "line": 8, }, }, ], @@ -142,32 +142,32 @@ Object { "loc": Object { "end": Object { "column": 40, - "line": 7, + "line": 9, }, "start": Object { "column": 30, - "line": 7, + "line": 9, }, }, "locations": Array [ Object { "end": Object { "column": 40, - "line": 7, + "line": 9, }, "start": Object { "column": 30, - "line": 7, + "line": 9, }, }, Object { "end": Object { "column": 53, - "line": 7, + "line": 9, }, "start": Object { "column": 43, - "line": 7, + "line": 9, }, }, ], @@ -184,21 +184,21 @@ Object { "decl": Object { "end": Object { "column": 29, - "line": 3, + "line": 5, }, "start": Object { "column": 9, - "line": 3, + "line": 5, }, }, "loc": Object { "end": Object { "column": 1, - "line": 10, + "line": 12, }, "start": Object { "column": 49, - "line": 3, + "line": 5, }, }, "name": "difference", @@ -207,21 +207,21 @@ Object { "decl": Object { "end": Object { "column": 36, - "line": 7, + "line": 9, }, "start": Object { "column": 30, - "line": 7, + "line": 9, }, }, "loc": Object { "end": Object { "column": 40, - "line": 7, + "line": 9, }, "start": Object { "column": 30, - "line": 7, + "line": 9, }, }, "name": "(anonymous_1)", @@ -230,21 +230,21 @@ Object { "decl": Object { "end": Object { "column": 49, - "line": 7, + "line": 9, }, "start": Object { "column": 43, - "line": 7, + "line": 9, }, }, "loc": Object { "end": Object { "column": 53, - "line": 7, + "line": 9, }, "start": Object { "column": 43, - "line": 7, + "line": 9, }, }, "name": "(anonymous_2)", @@ -265,81 +265,81 @@ Object { "0": Object { "end": Object { "column": 2, - "line": 10, + "line": 12, }, "start": Object { "column": 0, - "line": 3, + "line": 5, }, }, "1": Object { "end": Object { "column": 39, - "line": 4, + "line": 6, }, "start": Object { "column": 27, - "line": 4, + "line": 6, }, }, "2": Object { "end": Object { "column": 39, - "line": 5, + "line": 7, }, "start": Object { "column": 27, - "line": 5, + "line": 7, }, }, "3": Object { "end": Object { "column": 48, - "line": 6, + "line": 8, }, "start": Object { "column": 27, - "line": 6, + "line": 8, }, }, "4": Object { "end": Object { "column": 53, - "line": 7, + "line": 9, }, "start": Object { "column": 23, - "line": 7, + "line": 9, }, }, "5": Object { "end": Object { "column": 43, - "line": 7, + "line": 9, }, "start": Object { "column": 36, - "line": 7, + "line": 9, }, }, "6": Object { "end": Object { "column": 54, - "line": 7, + "line": 9, }, "start": Object { "column": 49, - "line": 7, + "line": 9, }, }, "7": Object { "end": Object { "column": 15, - "line": 9, + "line": 11, }, "start": Object { "column": 2, - "line": 9, + "line": 11, }, }, }, diff --git a/e2e/babel-plugin-jest-hoist/__tests__/typescript.test.ts b/e2e/babel-plugin-jest-hoist/__tests__/typescript.test.ts index 820294d1973e..ef772b495891 100644 --- a/e2e/babel-plugin-jest-hoist/__tests__/typescript.test.ts +++ b/e2e/babel-plugin-jest-hoist/__tests__/typescript.test.ts @@ -7,8 +7,6 @@ * @emails oncall+jsinfra */ -/* eslint-disable import/no-unresolved */ - import {Color} from '../types'; import {color} from '../entry'; diff --git a/e2e/babel-plugin-jest-hoist/entry.ts b/e2e/babel-plugin-jest-hoist/entry.ts index 00ed0fce24fa..bafd2d4f8606 100644 --- a/e2e/babel-plugin-jest-hoist/entry.ts +++ b/e2e/babel-plugin-jest-hoist/entry.ts @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -// eslint-disable-next-line import/no-unresolved import {Color} from './types'; export const color: Color = 'red'; diff --git a/e2e/coverage-remapping/covered.ts b/e2e/coverage-remapping/covered.ts index a11ac4f3cfb8..84b8f7ab9c2e 100644 --- a/e2e/coverage-remapping/covered.ts +++ b/e2e/coverage-remapping/covered.ts @@ -1,5 +1,7 @@ // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. +/* eslint-disable @typescript-eslint/no-unused-vars */ + export = function difference(a: number, b: number): number { const branch1: boolean = true ? 1 : 0; const branch2: boolean = true ? 1 : 0; diff --git a/e2e/coverage-remapping/package.json b/e2e/coverage-remapping/package.json index 293ece57d892..4aaa9f2b751e 100644 --- a/e2e/coverage-remapping/package.json +++ b/e2e/coverage-remapping/package.json @@ -4,13 +4,7 @@ "transform": { "^.+\\.(ts|js)$": "/typescriptPreprocessor.js" }, - "testRegex": "/__tests__/.*\\.(ts|tsx|js)$", - "testEnvironment": "node", - "moduleFileExtensions": [ - "ts", - "tsx", - "js" - ] + "testEnvironment": "node" }, "dependencies": { "typescript": "^1.8.10" diff --git a/e2e/stack-trace-source-maps/package.json b/e2e/stack-trace-source-maps/package.json index 5a20e78c8014..a5dbb3f82ea9 100644 --- a/e2e/stack-trace-source-maps/package.json +++ b/e2e/stack-trace-source-maps/package.json @@ -5,10 +5,6 @@ "^.+\\.(ts)$": "/preprocessor.js" }, "testEnvironment": "node", - "moduleFileExtensions": [ - "ts", - "js" - ], "testRegex": "fails" }, "dependencies": { diff --git a/e2e/typescript-coverage/package.json b/e2e/typescript-coverage/package.json index 667e970829c2..ffe9414a2849 100644 --- a/e2e/typescript-coverage/package.json +++ b/e2e/typescript-coverage/package.json @@ -4,17 +4,9 @@ "transform": { "^.+\\.(ts|js)$": "/typescriptPreprocessor.js" }, - "testMatch": [ - "**/__tests__/*.+(ts|tsx|js)" - ], - "testEnvironment": "node", - "moduleFileExtensions": [ - "ts", - "tsx", - "js" - ] + "testEnvironment": "node" }, "dependencies": { - "typescript": "^1.8.10" + "typescript": "^3.3.1" } } diff --git a/e2e/typescript-coverage/yarn.lock b/e2e/typescript-coverage/yarn.lock index ea260b12e78a..1cfa31a1591f 100644 --- a/e2e/typescript-coverage/yarn.lock +++ b/e2e/typescript-coverage/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@^1.8.10: - version "1.8.10" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-1.8.10.tgz#b475d6e0dff0bf50f296e5ca6ef9fbb5c7320f1e" - integrity sha1-tHXW4N/wv1DyluXKbvn7tccyDx4= +typescript@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.1.tgz#6de14e1db4b8a006ac535e482c8ba018c55f750b" + integrity sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA== diff --git a/jest.config.js b/jest.config.js index f0c85d2cdb81..999b16dd01ca 100644 --- a/jest.config.js +++ b/jest.config.js @@ -56,6 +56,6 @@ module.exports = { '/e2e/__tests__/iterator-to-null-test.js', ], transform: { - '^.+\\.js$': '/packages/babel-jest', + '^.+\\.[jt]sx?$': '/packages/babel-jest', }, }; diff --git a/package.json b/package.json index 7b1a06e700a2..6c0042975e2b 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,19 @@ { "private": true, "devDependencies": { - "@babel/core": "^7.1.0", + "@babel/core": "^7.2.2", "@babel/plugin-transform-modules-commonjs": "^7.1.0", "@babel/plugin-transform-strict-mode": "^7.0.0", "@babel/preset-env": "^7.1.0", "@babel/preset-flow": "^7.0.0", "@babel/preset-react": "^7.0.0", + "@babel/preset-typescript": "^7.0.0", "@babel/register": "^7.0.0", + "@types/babel__core": "^7.0.0", + "@types/babel__generator": "^7.0.0", + "@types/babel__template": "^7.0.0", + "@typescript-eslint/eslint-plugin": "^1.2.0", + "@typescript-eslint/parser": "^1.2.0", "ansi-regex": "^4.0.0", "ansi-styles": "^3.2.0", "babel-eslint": "^9.0.0", @@ -18,6 +24,7 @@ "debug": "^4.0.1", "eslint": "^5.6.0", "eslint-config-prettier": "^3.1.0", + "eslint-import-resolver-typescript": "^1.1.1", "eslint-plugin-babel": "^5.1.0", "eslint-plugin-flowtype": "^2.35.0", "eslint-plugin-import": "^2.6.0", @@ -60,7 +67,7 @@ "slash": "^2.0.0", "string-length": "^2.0.0", "strip-ansi": "^5.0.0", - "typescript": "^3.0.3", + "typescript": "^3.3.1", "webpack": "^4.28.4" }, "scripts": { @@ -71,7 +78,7 @@ "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", "jest-coverage": "yarn jest --coverage", - "lint": "eslint . --cache --report-unused-disable-directives --ext js,md", + "lint": "eslint . --cache --report-unused-disable-directives --ext js,jsx,ts,tsx,md", "lint-es5-build": "eslint --no-eslintrc --no-ignore --env=browser packages/*/build-es5", "lint:md": "yarn --silent lint:md:ci --fix", "lint:md:ci": "prettylint '**/*.{md,yml,yaml}' --ignore-path .gitignore", @@ -110,7 +117,8 @@ "e2e/*/**/*", "website/*.js", "website/*/**/*", - "eslintImportResolver.js" + "eslintImportResolver.js", + "babel.config.js" ], "options": { "printWidth": 80, diff --git a/packages/babel-jest/package.json b/packages/babel-jest/package.json index 979ee04fb46c..48c4552b1380 100644 --- a/packages/babel-jest/package.json +++ b/packages/babel-jest/package.json @@ -16,7 +16,9 @@ "slash": "^2.0.0" }, "devDependencies": { - "@babel/core": "^7.1.0" + "@babel/core": "^7.1.0", + "@types/babel__core": "^7.0.4", + "@types/slash": "^2.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0" diff --git a/packages/expect/package.json b/packages/expect/package.json index 5e5a75e3fbe8..3f74a119c97e 100644 --- a/packages/expect/package.json +++ b/packages/expect/package.json @@ -16,6 +16,9 @@ "jest-message-util": "^24.0.0", "jest-regex-util": "^24.0.0" }, + "devDependencies": { + "@types/ansi-styles": "^3.2.1" + }, "engines": { "node": ">= 6" }, diff --git a/packages/jest-changed-files/package.json b/packages/jest-changed-files/package.json index 1ae9580994f3..de3d1e78442a 100644 --- a/packages/jest-changed-files/package.json +++ b/packages/jest-changed-files/package.json @@ -12,6 +12,9 @@ "execa": "^1.0.0", "throat": "^4.0.0" }, + "devDependencies": { + "@types/execa": "^0.9.0" + }, "engines": { "node": ">= 6" }, diff --git a/packages/jest-circus/package.json b/packages/jest-circus/package.json index 8298a3252222..7ce23e74d4a0 100644 --- a/packages/jest-circus/package.json +++ b/packages/jest-circus/package.json @@ -24,6 +24,8 @@ "throat": "^4.0.0" }, "devDependencies": { + "@types/babel__traverse": "^7.0.4", + "@types/stack-utils": "^1.0.1", "execa": "^1.0.0", "jest-diff": "^24.0.0", "jest-runtime": "^24.1.0" diff --git a/packages/jest-cli/package.json b/packages/jest-cli/package.json index bc5b2aa3f5a4..90c8e788397b 100644 --- a/packages/jest-cli/package.json +++ b/packages/jest-cli/package.json @@ -43,6 +43,23 @@ "which": "^1.2.12", "yargs": "^12.0.2" }, + "devDependencies": { + "@types/ansi-escapes": "^3.0.0", + "@types/exit": "^0.1.30", + "@types/glob": "^7.1.1", + "@types/graceful-fs": "^4.1.2", + "@types/is-ci": "^1.1.0", + "@types/istanbul-lib-coverage": "^1.1.0", + "@types/istanbul-lib-instrument": "^1.7.2", + "@types/istanbul-lib-source-maps": "^1.2.1", + "@types/micromatch": "^3.1.0", + "@types/node-notifier": "^0.0.28", + "@types/prompts": "^1.2.0", + "@types/rimraf": "^2.0.2", + "@types/string-length": "^2.0.0", + "@types/which": "^1.3.1", + "@types/yargs": "^12.0.2" + }, "bin": { "jest": "./bin/jest.js" }, diff --git a/packages/jest-config/package.json b/packages/jest-config/package.json index 83e5407b2f6b..c1dc8cde0f46 100644 --- a/packages/jest-config/package.json +++ b/packages/jest-config/package.json @@ -25,6 +25,11 @@ "pretty-format": "^24.0.0", "realpath-native": "^1.0.2" }, + "devDependencies": { + "@types/babel__core": "^7.0.4", + "@types/glob": "^7.1.1", + "@types/micromatch": "^3.1.0" + }, "engines": { "node": ">= 6" }, diff --git a/packages/jest-docblock/package.json b/packages/jest-docblock/package.json index 7c3c5eb93966..464789183d14 100644 --- a/packages/jest-docblock/package.json +++ b/packages/jest-docblock/package.json @@ -11,6 +11,9 @@ "dependencies": { "detect-newline": "^2.1.0" }, + "devDependencies": { + "@types/detect-newline": "^2.1.0" + }, "engines": { "node": ">= 6" }, diff --git a/packages/jest-environment-jsdom/package.json b/packages/jest-environment-jsdom/package.json index 92012d65b2ae..0fb34d1b04f9 100644 --- a/packages/jest-environment-jsdom/package.json +++ b/packages/jest-environment-jsdom/package.json @@ -13,6 +13,9 @@ "jest-util": "^24.0.0", "jsdom": "^11.5.1" }, + "devDependencies": { + "@types/jsdom": "^11.12.0" + }, "engines": { "node": ">= 6" }, diff --git a/packages/jest-haste-map/package.json b/packages/jest-haste-map/package.json index 346c9b395442..1daf9e83bee0 100644 --- a/packages/jest-haste-map/package.json +++ b/packages/jest-haste-map/package.json @@ -18,6 +18,13 @@ "micromatch": "^3.1.10", "sane": "^3.0.0" }, + "devDependencies": { + "@types/fb-watchman": "^2.0.0", + "@types/graceful-fs": "^4.1.2", + "@types/invariant": "^2.2.29", + "@types/micromatch": "^3.1.0", + "@types/sane": "^2.0.0" + }, "engines": { "node": ">= 6" }, diff --git a/packages/jest-jasmine2/package.json b/packages/jest-jasmine2/package.json index 1ed3e1eb7cfe..562d0d5397e5 100644 --- a/packages/jest-jasmine2/package.json +++ b/packages/jest-jasmine2/package.json @@ -23,6 +23,7 @@ "throat": "^4.0.0" }, "devDependencies": { + "@types/babel__traverse": "^7.0.4", "jest-diff": "^24.0.0", "jest-runtime": "^24.1.0" }, diff --git a/packages/jest-leak-detector/package.json b/packages/jest-leak-detector/package.json index 9db8dd393ce4..434f904dcf62 100644 --- a/packages/jest-leak-detector/package.json +++ b/packages/jest-leak-detector/package.json @@ -12,6 +12,7 @@ "pretty-format": "^24.0.0" }, "devDependencies": { + "@types/weak": "^1.0.0", "weak": "^1.0.1" }, "engines": { diff --git a/packages/jest-message-util/package.json b/packages/jest-message-util/package.json index 45e32e2e6f64..e61c38de40c1 100644 --- a/packages/jest-message-util/package.json +++ b/packages/jest-message-util/package.json @@ -18,5 +18,10 @@ "slash": "^2.0.0", "stack-utils": "^1.0.1" }, + "devDependencies": { + "@types/babel__code-frame": "^7.0.0", + "@types/micromatch": "^3.1.0", + "@types/stack-utils": "^1.0.1" + }, "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" } diff --git a/packages/jest-repl/package.json b/packages/jest-repl/package.json index 4b96382132ab..8c9abb3ec472 100644 --- a/packages/jest-repl/package.json +++ b/packages/jest-repl/package.json @@ -15,6 +15,9 @@ "repl": "^0.1.3", "yargs": "^12.0.2" }, + "devDependencies": { + "@types/yargs": "^12.0.2" + }, "bin": { "jest-repl": "./bin/jest-repl.js" }, diff --git a/packages/jest-resolve/package.json b/packages/jest-resolve/package.json index 696170cd90ec..d44ad9c6b183 100644 --- a/packages/jest-resolve/package.json +++ b/packages/jest-resolve/package.json @@ -14,6 +14,7 @@ "realpath-native": "^1.0.0" }, "devDependencies": { + "@types/browser-resolve": "^0.0.5", "jest-haste-map": "^24.0.0" }, "engines": { diff --git a/packages/jest-runner/package.json b/packages/jest-runner/package.json index a8583affb503..98ecf80ca1e7 100644 --- a/packages/jest-runner/package.json +++ b/packages/jest-runner/package.json @@ -24,6 +24,11 @@ "source-map-support": "^0.5.6", "throat": "^4.0.0" }, + "devDependencies": { + "@types/exit": "^0.1.30", + "@types/graceful-fs": "^4.1.2", + "@types/source-map-support": "^0.4.1" + }, "engines": { "node": ">= 6" }, diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index 15913207fe44..2bfa2989ad6d 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -33,6 +33,15 @@ "yargs": "^12.0.2" }, "devDependencies": { + "@types/babel__core": "^7.0.4", + "@types/convert-source-map": "^1.5.1", + "@types/exit": "^0.1.30", + "@types/glob": "^7.1.1", + "@types/graceful-fs": "^4.1.2", + "@types/micromatch": "^3.1.0", + "@types/strip-bom": "3.0.0", + "@types/write-file-atomic": "^2.1.1", + "@types/yargs": "^12.0.2", "jest-environment-jsdom": "^24.0.0", "jest-environment-node": "^24.0.0" }, diff --git a/packages/jest-snapshot/package.json b/packages/jest-snapshot/package.json index 7740aa3fde00..43c27d8bbab0 100644 --- a/packages/jest-snapshot/package.json +++ b/packages/jest-snapshot/package.json @@ -21,6 +21,8 @@ "semver": "^5.5.0" }, "devDependencies": { + "@types/mkdirp": "^0.5.2", + "@types/semver": "^5.5.0", "prettier": "^1.13.4" }, "engines": { diff --git a/packages/jest-util/package.json b/packages/jest-util/package.json index db4a4a311ba5..85f3410824a8 100644 --- a/packages/jest-util/package.json +++ b/packages/jest-util/package.json @@ -19,6 +19,10 @@ "source-map": "^0.6.0" }, "devDependencies": { + "@types/callsites": "^2.0.0", + "@types/graceful-fs": "^4.1.2", + "@types/is-ci": "^1.0.10", + "@types/mkdirp": "^0.5.2", "jest-mock": "^24.0.0" }, "engines": { diff --git a/packages/jest-validate/package.json b/packages/jest-validate/package.json index f3c9508eeaf8..2186f1af66c3 100644 --- a/packages/jest-validate/package.json +++ b/packages/jest-validate/package.json @@ -15,6 +15,10 @@ "leven": "^2.1.0", "pretty-format": "^24.0.0" }, + "devDependencies": { + "@types/camelcase": "^4.1.0", + "@types/leven": "^2.1.1" + }, "engines": { "node": ">= 6" }, diff --git a/packages/jest-watcher/package.json b/packages/jest-watcher/package.json index 3abb4ba0bcda..351a6c7ec3da 100644 --- a/packages/jest-watcher/package.json +++ b/packages/jest-watcher/package.json @@ -9,6 +9,10 @@ "jest-util": "^24.0.0", "string-length": "^2.0.0" }, + "devDependencies": { + "@types/ansi-escapes": "^3.0.0", + "@types/string-length": "^2.0.0" + }, "repository": { "type": "git", "url": "https://github.com/facebook/jest", diff --git a/packages/jest-worker/package.json b/packages/jest-worker/package.json index 7b02f58e07ff..b993e76c65da 100644 --- a/packages/jest-worker/package.json +++ b/packages/jest-worker/package.json @@ -13,6 +13,8 @@ "supports-color": "^6.1.0" }, "devDependencies": { + "@types/merge-stream": "^1.1.2", + "@types/supports-color": "^5.3.0", "worker-farm": "^1.6.0" }, "engines": { diff --git a/packages/pretty-format/package.json b/packages/pretty-format/package.json index 30914be8fa59..c9d7fedc45b7 100644 --- a/packages/pretty-format/package.json +++ b/packages/pretty-format/package.json @@ -16,6 +16,8 @@ "ansi-styles": "^3.2.0" }, "devDependencies": { + "@types/ansi-regex": "^4.0.0", + "@types/ansi-styles": "^3.2.1", "immutable": "4.0.0-rc.9", "react": "*", "react-dom": "*", diff --git a/scripts/babel-plugin-jest-replace-ts-export-assignment.js b/scripts/babel-plugin-jest-replace-ts-export-assignment.js new file mode 100644 index 000000000000..65722500d5cc --- /dev/null +++ b/scripts/babel-plugin-jest-replace-ts-export-assignment.js @@ -0,0 +1,26 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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'; + +// Replace `export =` with `module.exports` which allows us to keep CJS semantics + +module.exports = ({template}) => { + const moduleExportsDeclaration = template(` + module.exports = ASSIGNMENT; + `); + return { + name: 'jest-replace-ts-export-assignment', + visitor: { + TSExportAssignment(path) { + path.replaceWith( + moduleExportsDeclaration({ASSIGNMENT: path.node.expression}) + ); + }, + }, + }; +}; diff --git a/scripts/browserBuild.js b/scripts/browserBuild.js index 60f9c75c65d4..7ca65961a13e 100644 --- a/scripts/browserBuild.js +++ b/scripts/browserBuild.js @@ -11,10 +11,13 @@ const webpack = require('webpack'); const camelCase = require('camelcase'); const rimraf = require('rimraf'); +const transformOptions = require('../babel.config.js'); + const babelEs5Options = { // Dont load other config files babelrc: false, configFile: false, + overrides: transformOptions.overrides, plugins: ['@babel/plugin-transform-strict-mode'], presets: [ [ @@ -27,7 +30,6 @@ const babelEs5Options = { targets: 'IE 11', }, ], - '@babel/preset-flow', ], }; @@ -63,6 +65,7 @@ function browserBuild(pkgName, entryPath, destination) { '../packages/expect/build/fakeChalk.js' ), }, + extensions: ['.js', '.json', '.ts'], }, node: { fs: 'empty', diff --git a/scripts/build.js b/scripts/build.js index 503e8a087ddb..78d67bdbec5f 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -15,7 +15,7 @@ * node ./scripts/build.js * node ./scripts/build.js /users/123/jest/packages/jest-111/src/111.js * - * NOTE: this script is node@4 compatible + * NOTE: this script is node@6 compatible */ 'use strict'; @@ -24,6 +24,7 @@ const fs = require('fs'); const path = require('path'); const glob = require('glob'); const mkdirp = require('mkdirp'); +const execa = require('execa'); const babel = require('@babel/core'); const chalk = require('chalk'); @@ -38,13 +39,13 @@ const SRC_DIR = 'src'; const BUILD_DIR = 'build'; const BUILD_ES5_DIR = 'build-es5'; const JS_FILES_PATTERN = '**/*.js'; +const TS_FILES_PATTERN = '**/*.ts'; const IGNORE_PATTERN = '**/__{tests,mocks}__/**'; const PACKAGES_DIR = path.resolve(__dirname, '../packages'); const INLINE_REQUIRE_BLACKLIST = /packages\/expect|(jest-(circus|diff|get-type|jasmine2|matcher-utils|message-util|regex-util|snapshot))|pretty-format\//; const transformOptions = require('../babel.config.js'); -transformOptions.babelrc = false; const prettierConfig = prettier.resolveConfig.sync(__filename); prettierConfig.trailingComma = 'none'; @@ -73,19 +74,18 @@ function getBuildPath(file, buildFolder) { const pkgSrcPath = path.resolve(PACKAGES_DIR, pkgName, SRC_DIR); const pkgBuildPath = path.resolve(PACKAGES_DIR, pkgName, buildFolder); const relativeToSrcPath = path.relative(pkgSrcPath, file); - return path.resolve(pkgBuildPath, relativeToSrcPath); + return path.resolve(pkgBuildPath, relativeToSrcPath).replace(/\.ts$/, '.js'); } function buildNodePackage(p) { const srcDir = path.resolve(p, SRC_DIR); const pattern = path.resolve(srcDir, '**/*'); - const files = glob.sync(pattern, { - nodir: true, - }); + const files = glob.sync(pattern, {nodir: true}); process.stdout.write(adjustToTerminalWidth(`${path.basename(p)}\n`)); files.forEach(file => buildFile(file, true)); + process.stdout.write(`${OK}\n`); } @@ -104,11 +104,13 @@ function buildBrowserPackage(p) { `browser field for ${pkgJsonPath} should start with "${BUILD_ES5_DIR}"` ); } - browserBuild( - p.split('/').pop(), - path.resolve(srcDir, 'index.js'), - path.resolve(p, browser) - ) + let indexFile = path.resolve(srcDir, 'index.js'); + + if (!fs.existsSync(indexFile)) { + indexFile = indexFile.replace(/\.js$/, '.ts'); + } + + browserBuild(p.split('/').pop(), indexFile, path.resolve(p, browser)) .then(() => { process.stdout.write(adjustToTerminalWidth(`${path.basename(p)}\n`)); process.stdout.write(`${OK}\n`); @@ -134,7 +136,10 @@ function buildFile(file, silent) { } mkdirp.sync(path.dirname(destPath), '777'); - if (!micromatch.isMatch(file, JS_FILES_PATTERN)) { + if ( + !micromatch.isMatch(file, JS_FILES_PATTERN) && + !micromatch.isMatch(file, TS_FILES_PATTERN) + ) { fs.createReadStream(file).pipe(fs.createWriteStream(destPath)); silent || process.stdout.write( @@ -186,10 +191,23 @@ function buildFile(file, silent) { const files = process.argv.slice(2); +function compileTypes(packages) { + const packageWithTs = packages.filter(p => + fs.existsSync(path.resolve(p, 'tsconfig.json')) + ); + + if (packageWithTs.length > 0) { + execa.sync('tsc', ['-b', ...packageWithTs]); + } +} + if (files.length) { files.forEach(buildFile); } else { const packages = getPackages(); + process.stdout.write(chalk.inverse(' Typechecking \n')); + compileTypes(packages); + process.stdout.write(`${OK}\n\n`); process.stdout.write(chalk.inverse(' Building packages \n')); packages.forEach(buildNodePackage); process.stdout.write('\n'); diff --git a/scripts/watch.js b/scripts/watch.js index e26e24241cde..b40d670643b5 100644 --- a/scripts/watch.js +++ b/scripts/watch.js @@ -13,6 +13,7 @@ const fs = require('fs'); const {execSync} = require('child_process'); const path = require('path'); const chalk = require('chalk'); +const execa = require('execa'); const getPackages = require('./getPackages'); const BUILD_CMD = `node ${path.resolve(__dirname, './build.js')}`; @@ -27,11 +28,12 @@ const exists = filename => { }; const rebuild = filename => filesToBuild.set(filename, true); -getPackages().forEach(p => { +const packages = getPackages(); +packages.forEach(p => { const srcDir = path.resolve(p, 'src'); try { fs.accessSync(srcDir, fs.F_OK); - fs.watch(path.resolve(p, 'src'), {recursive: true}, (event, filename) => { + fs.watch(srcDir, {recursive: true}, (event, filename) => { const filePath = path.resolve(srcDir, filename); if ((event === 'change' || event === 'rename') && exists(filePath)) { @@ -55,6 +57,14 @@ getPackages().forEach(p => { } }); +const packageWithTs = packages.filter(p => + fs.existsSync(path.resolve(p, 'tsconfig.json')) +); + +if (packageWithTs.length > 0) { + execa('tsc', ['-b', ...packageWithTs, '--watch']); +} + setInterval(() => { const files = Array.from(filesToBuild.keys()); if (files.length) { diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000000..6b1f7373d1ab --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "es2017", + "module": "commonjs", + "lib": ["dom", "es2017"], + "declaration": true, + "declarationMap": true, + "composite": true, + "emitDeclarationOnly": true, + // "importHelpers": true, + // "isolatedModules": true, + + "strict": true, + + /* Additional Checks */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + + /* Module Resolution Options */ + "moduleResolution": "node", + "esModuleInterop": true + }, + "exclude": ["**/__tests__/**/*", "**/build/**/*", "**/build-es5/**/*"] +} diff --git a/yarn.lock b/yarn.lock index 9dfdd03de271..ffc57a710145 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,7 +9,7 @@ dependencies: "@babel/highlight" "^7.0.0" -"@babel/core@*", "@babel/core@^7.0.0", "@babel/core@^7.1.0": +"@babel/core@*", "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.2.2": version "7.2.2" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.2.2.tgz#07adba6dde27bb5ad8d8672f15fde3e08184a687" integrity sha512-59vB0RWt09cAct5EIe58+NzGP4TFSD3Bz//2/ELy3ZeTeKF6VTD1AXlH8BGGbCX0PuobZBsIzO7IAI9PH67eKw== @@ -232,7 +232,7 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.2.2", "@babel/parser@^7.2.3": +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.2.2", "@babel/parser@^7.2.3": version "7.2.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.2.3.tgz#32f5df65744b70888d17872ec106b02434ba1489" integrity sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA== @@ -786,7 +786,7 @@ "@babel/plugin-transform-react-jsx-self" "^7.0.0" "@babel/plugin-transform-react-jsx-source" "^7.0.0" -"@babel/preset-typescript@*": +"@babel/preset-typescript@*", "@babel/preset-typescript@^7.0.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.1.0.tgz#49ad6e2084ff0bfb5f1f7fb3b5e76c434d442c7f" integrity sha512-LYveByuF9AOM8WrsNne5+N79k1YxjNB6gmpCQsnuSBAcV8QUeB+ZUxQzL7Rz7HksPbahymKkq2qBR+o36ggFZA== @@ -1462,26 +1462,366 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== +"@types/ansi-escapes@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/ansi-escapes/-/ansi-escapes-3.0.0.tgz#619bbc6d46fc75da6d784e53b5a25d2efff07108" + integrity sha512-aamJrX6PdmIO8E9qhZaYmXiMGXwnkF2lcga/VbqLf8g90aaKGZ4cSFP5AabqxAbmp0h69C9yE3a4fUBtVpqtmg== + dependencies: + "@types/node" "*" + +"@types/ansi-regex@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/ansi-regex/-/ansi-regex-4.0.0.tgz#cb20bb66da7700ea9b26f16971f03f0e092eddad" + integrity sha512-r1W316vjsZXn1/csLC4HcCJs6jIHIzksHJd7xx+Dl+PAb0S2Dh9cR8ZsIMEfGmbBtP7JNWlf2KKahSkDP6rg3g== + +"@types/ansi-styles@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@types/ansi-styles/-/ansi-styles-3.2.1.tgz#49e996bb6e0b7957ca831205df31eb9a0702492c" + integrity sha512-UFa7mfKgSutXdT+elzJo8Ulr7FHgLNAyglVIOZYXFNJVQERm8DPrcwPret5BYk66LBE7fwm1XoVGi76MJkQ6ow== + dependencies: + "@types/color-name" "*" + +"@types/babel-types@*": + version "7.0.4" + resolved "https://registry.yarnpkg.com/@types/babel-types/-/babel-types-7.0.4.tgz#bfd5b0d0d1ba13e351dff65b6e52783b816826c8" + integrity sha512-WiZhq3SVJHFRgRYLXvpf65XnV6ipVHhnNaNvE8yCimejrGglkg38kEj0JcizqwSHxmPSjcTlig/6JouxLGEhGw== + +"@types/babel__code-frame@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@types/babel__code-frame/-/babel__code-frame-7.0.0.tgz#d5658827984c1e386c1b4ef30699b344d3f732a2" + integrity sha512-Rt0KuqopAdtpyvxO0JbKbwgtw7B+y0NQpTXns+tiz1lqGXywOESSKIiiQt0wnCreqEKvmrArH7KZ6bQg+w8BoA== + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.0.4": + version "7.0.4" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.0.4.tgz#14b30c11113bad353cabfaea73e327b48edb0f0e" + integrity sha512-2Y2RK1BN5BRFfhneGfQA8mmFmTANbzGgS5uQPluoRqGNWb6uAcefqxzNbqgxPpmPkLqKapQfmYcyyl5iAQV+fA== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__generator@^7.0.0": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.0.1.tgz#533bd37e7be8af27ebf8fcc0a053cfa4c03f4c5c" + integrity sha512-QZ+oM5v3lAh/4DqWDyCL0rVi/BJlKRANDEpShxt3Lg+rhbFA78Ec7N+8lcAIpziA/Mfp/1ShDJj2Ti3HJ/JPxA== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@^7.0.0": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.1.tgz#8de6effa729bf0b0f65eb72399b629a3998a7f10" + integrity sha512-LT1zwkpL0/2oBP+npLaOCYSWv47cxbdAvONutjalMdCIBfjtfzVNnT8rgllBmsr15QAdAZDOmYw1CPhLB2JCtA== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@^7.0.4": + version "7.0.4" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.4.tgz#de652399bd8493ab712e4d6b68031b7a2f72ad5f" + integrity sha512-2vARZYqR4Flf59WtqMfa9GgbOjK04xLZaN9+CMf7Cs+4cAhxZBP3K9LYRzsWxOQe402VvqX9+7DdXxB72ujEOg== + dependencies: + "@babel/types" "^7.0.0" + +"@types/braces@*": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@types/braces/-/braces-2.3.0.tgz#d00ec0a76562b2acb6f29330be33a093e33ed25c" + integrity sha512-A3MV5EsLHgShHoJ/XES/fQAnwNISKLrFuH9eNBZY5OkTQB7JPIwbRoExvRpDsNABvkMojnKqKWS8x0m2rLYi+A== + +"@types/browser-resolve@^0.0.5": + version "0.0.5" + resolved "https://registry.yarnpkg.com/@types/browser-resolve/-/browser-resolve-0.0.5.tgz#4d7ea4babcbfa45752a5cdc016c0a0fc4dc4de7d" + integrity sha512-3XKWKoP/RsU2Ewg9v2rkE1qmAbKIFR6nxFOsDhY2/J1u3zyDP+2vovKI/7tX1b9RqshAwQiiCsLyQr3dIGw9/w== + dependencies: + "@types/resolve" "*" + +"@types/callsites@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/callsites/-/callsites-2.0.0.tgz#df0db68ea34053651caee9975294be902fd04751" + integrity sha512-kUvhIE1ZlV4dYEyFnZ2lJLXMEOc7eLN6ejolVBQm0boUCX2t6p0M2nx+20UkEtDMJYtRQffuhcwO1h/U029QAw== + +"@types/camelcase@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@types/camelcase/-/camelcase-4.1.0.tgz#e054f7986f31658d49936261b5cd4588ef29d1ee" + integrity sha512-nsaprOtNLvUrLyFX5+mRpE9h2Q0d5YzQRr+Lav3fxdYtc1/E/U7G+Ld861NWBDDtWY3MnwKoUOhCrE1nrVxUQA== + "@types/cheerio@^0.22.8": version "0.22.10" resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.10.tgz#780d552467824be4a241b29510a7873a7432c4a6" integrity sha512-fOM/Jhv51iyugY7KOBZz2ThfT1gwvsGCfWxpLpZDgkGjpEO4Le9cld07OdskikLjDUQJ43dzDaVRSFwQlpdqVg== +"@types/color-name@*": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.0.tgz#926f76f7e66f49cc59ad880bb15b030abbf0b66d" + integrity sha512-gZ/Rb+MFXF0pXSEQxdRoPMm5jeO3TycjOdvbpbcpHX/B+n9AqaHFe5q6Ga9CsZ7ir/UgIWPfrBzUzn3F19VH/w== + +"@types/convert-source-map@^1.5.1": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@types/convert-source-map/-/convert-source-map-1.5.1.tgz#d4d180dd6adc5cb68ad99bd56e03d637881f4616" + integrity sha512-laiDIXqqthjJlyAMYAXOtN3N8+UlbM+KvZi4BaY5ZOekmVkBs/UxfK5O0HWeJVG2eW8F+Mu2ww13fTX+kY1FlQ== + +"@types/detect-newline@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@types/detect-newline/-/detect-newline-2.1.0.tgz#1896b5651b473792b6aba2500270fda626bc38d0" + integrity sha512-vcHS4yQkTfy+8QISuAFS+2SCjNcGs37+CWzxrrIOMv7yvPnYd6f1AeUErVEHVCu8dZreAHUqzV8RrjzeBorvtQ== + +"@types/events@*": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" + integrity sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA== + +"@types/execa@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@types/execa/-/execa-0.9.0.tgz#9b025d2755f17e80beaf9368c3f4f319d8b0fb93" + integrity sha512-mgfd93RhzjYBUHHV532turHC2j4l/qxsF/PbfDmprHDEUHmNZGlDn1CEsulGK3AfsPdhkWzZQT/S/k0UGhLGsA== + dependencies: + "@types/node" "*" + +"@types/exit@^0.1.30": + version "0.1.30" + resolved "https://registry.yarnpkg.com/@types/exit/-/exit-0.1.30.tgz#7078b736a7d166c80b6394dc0d9de1577ca76daf" + integrity sha1-cHi3NqfRZsgLY5TcDZ3hV3ynba8= + +"@types/fb-watchman@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/fb-watchman/-/fb-watchman-2.0.0.tgz#ca60ded406baa8c81c65ac1f86763a5d00aa7c55" + integrity sha512-Ao2jlksPEUGCEXBvJz5e2MuDpYUtxXgtUk45cg0g5Mmy4f0j7bQuDlOlqBMgKGRl9dZAK4ZTzFtukuzj2mURlQ== + dependencies: + "@types/events" "*" + +"@types/glob@*", "@types/glob@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" + integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== + dependencies: + "@types/events" "*" + "@types/minimatch" "*" + "@types/node" "*" + +"@types/graceful-fs@^4.1.2": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.2.tgz#fbc9575dbcc6d1d91dd768d30c5fc0c19f6c50bd" + integrity sha512-epDhsJAVxJsWfeqpzEDFhLnhHMbHie/VMFY+2Hvt5p7FemeW5ELM+6gcVYL/ZsUwdu3zrWpDE3VUTddXW+EMYg== + dependencies: + "@types/node" "*" + +"@types/invariant@^2.2.29": + version "2.2.29" + resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.29.tgz#aa845204cd0a289f65d47e0de63a6a815e30cc66" + integrity sha512-lRVw09gOvgviOfeUrKc/pmTiRZ7g7oDOU6OAutyuSHpm1/o2RaBQvRhgK8QEdu+FFuw/wnWb29A/iuxv9i8OpQ== + +"@types/is-ci@^1.0.10", "@types/is-ci@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@types/is-ci/-/is-ci-1.1.0.tgz#583c5fbfcc461be9971106b9558930d67df49227" + integrity sha512-NbyqP5D4hwl5UWnnW4Cz0gIRjhecgx/9OApcCIpp4+tjqjROGf/NBcKKDfbI3YFBTTkD3JBshiNSv5V7VoVJJg== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.0.tgz#2cc2ca41051498382b43157c8227fea60363f94a" + integrity sha512-ohkhb9LehJy+PA40rDtGAji61NCgdtKLAlFoYp4cnuuQEswwdK3vz9SOIkkyc3wrk8dzjphQApNs56yyXLStaQ== + +"@types/istanbul-lib-instrument@^1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.2.tgz#44e61372876597e361148a84537be402f4c9e972" + integrity sha512-SWIpdKneXqThfrKIokt9dXSPeslS2NWcxhtr+/a2+N81aLyOMAsVTMmwaKuCoEahcI0FfhY3/79AR6Vilk9i8A== + dependencies: + "@types/babel-types" "*" + "@types/istanbul-lib-coverage" "*" + source-map "^0.6.1" + +"@types/istanbul-lib-source-maps@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.1.tgz#c6db98b8b9f0b5aea000f7a8922cc075a85eda9f" + integrity sha512-K0IvmTFbI2GjLG0O4AOLPV2hFItE5Bg/TY41IBZIThhLhYthJc3VjpZpM8/sIaIVtnQcX8b2k3muPDvsvhk+Fg== + dependencies: + "@types/istanbul-lib-coverage" "*" + source-map "^0.6.1" + "@types/jest@^23.1.1": version "23.3.12" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.12.tgz#7e0ced251fa94c3bc2d1023d4b84b2992fa06376" integrity sha512-/kQvbVzdEpOq4tEWT79yAHSM4nH4xMlhJv2GrLVQt4Qmo8yYsPdioBM1QpN/2GX1wkfMnyXvdoftvLUr0LBj7Q== +"@types/jsdom@^11.12.0": + version "11.12.0" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-11.12.0.tgz#00ddc6f0a1b04c2f5ff6fb23eb59360ca65f12ae" + integrity sha512-XHMNZFQ0Ih3A4/NTWAO15+OsQafPKnQCanN0FYGbsTM/EoI5EoEAvvkF51/DQC2BT5low4tomp7k2RLMlriA5Q== + dependencies: + "@types/events" "*" + "@types/node" "*" + "@types/tough-cookie" "*" + parse5 "^4.0.0" + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + +"@types/leven@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@types/leven/-/leven-2.1.1.tgz#7cdc02ec636f80dc0bb0a53d8ee7eff2d8e8e1d8" + integrity sha512-f74SsCQnQzm244o5LHZgSLijrwG5e9BgkMHGbDlQThfh42q5RG4c+RNzUvZ347wAlQYD9kwu64qSNylxZdKs6w== + +"@types/merge-stream@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@types/merge-stream/-/merge-stream-1.1.2.tgz#a880ff66b1fbbb5eef4958d015c5947a9334dbb1" + integrity sha512-7faLmaE99g/yX0Y9pF1neh2IUqOf/fXMOWCVzsXjqI1EJ91lrgXmaBKf6bRWM164lLyiHxHt6t/ZO/cIzq61XA== + dependencies: + "@types/node" "*" + +"@types/micromatch@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/micromatch/-/micromatch-3.1.0.tgz#514c8a3d24b2680a9b838eeb80e6d7d724545433" + integrity sha512-06uA9V7v68RTOzA3ky1Oi0HmCPa+YJ050vM+sTECwkxnHUQnO17TAcNCGX400QT6bldUiPb7ux5oKy0j8ccEDw== + dependencies: + "@types/braces" "*" + +"@types/minimatch@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== + +"@types/mkdirp@^0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-0.5.2.tgz#503aacfe5cc2703d5484326b1b27efa67a339c1f" + integrity sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg== + dependencies: + "@types/node" "*" + +"@types/node-notifier@^0.0.28": + version "0.0.28" + resolved "https://registry.yarnpkg.com/@types/node-notifier/-/node-notifier-0.0.28.tgz#86ba3d3aa8d918352cc3191d88de328b20dc93c1" + integrity sha1-hro9OqjZGDUswxkdiN4yiyDck8E= + dependencies: + "@types/node" "*" + "@types/node@*": version "10.12.18" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== +"@types/prompts@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/prompts/-/prompts-1.2.0.tgz#891e73f735ad5e82e8adae3a99424128e105fb62" + integrity sha512-7JXpT2rSd4hqd2oBWU1wfEW6x6gX+qPH+gLzGEx+My3wcb67K9Rc02xNQRVn67phusmXm5Yqn4oTP2OW1G5zdQ== + "@types/q@^1.5.1": version "1.5.1" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.1.tgz#48fd98c1561fe718b61733daed46ff115b496e18" integrity sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA== +"@types/resolve@*": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" + integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== + dependencies: + "@types/node" "*" + +"@types/rimraf@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.2.tgz#7f0fc3cf0ff0ad2a99bb723ae1764f30acaf8b6e" + integrity sha512-Hm/bnWq0TCy7jmjeN5bKYij9vw5GrDFWME4IuxV08278NtU/VdGbzsBohcCUJ7+QMqmUq5hpRKB39HeQWJjztQ== + dependencies: + "@types/glob" "*" + "@types/node" "*" + +"@types/sane@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/sane/-/sane-2.0.0.tgz#891c272b10d1a6f735e915c731756104b5d21374" + integrity sha512-6o/YMfcKcWIK5IAxeo/1DEoVnGKBkPSPj9odxczz2i1gFY78GvguqgtNo28nyKgJUiKEWuFj11acpgdRmRFz7A== + dependencies: + "@types/node" "*" + +"@types/semver@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" + integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ== + +"@types/slash@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/slash/-/slash-2.0.0.tgz#2c37c08f5d2e0dad20aed808de79459e0a35ef9d" + integrity sha512-LTVXHQ+ij5F973ulc7aJEFteAtUvCflhzI3oOdV8VhWUmjy4na4LnK2bZaH8hDBq9eFX+Nb9DT3HVctqLoTy+g== + +"@types/source-map-support@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@types/source-map-support/-/source-map-support-0.4.1.tgz#ad77158e8c6695a16629ef82b9fb9dfe7c610ac0" + integrity sha512-eoyZxYGwaeHq5zCVeoNgY1dQy6dVdm1b7K9k1FRnWkf997Tji3NLBoLAjK5WCobeh1Qs6Q5KUV1rZCmHvzaDBw== + dependencies: + "@types/node" "*" + +"@types/stack-utils@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" + integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== + +"@types/string-length@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/string-length/-/string-length-2.0.0.tgz#358ce3ff2e8c2310270ee192ddd6b79b64fed7b2" + integrity sha512-xFwWZpIWcLsrcVEybzxmxQM/26Snj1gqxmVrelC3We3Nub7q70RCtqTBVQ7nL+315fAcw4BGSFpxvMkhLApKmQ== + +"@types/strip-bom@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" + integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= + +"@types/supports-color@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@types/supports-color/-/supports-color-5.3.0.tgz#eb6a52e9531fb3ebcd401cec774d1bdfb571f793" + integrity sha512-WxwTXnHTIsk7srax1icjLgX+6w1MUAJbhyCpRP/45paEElsPDQUJZDgr1UpKuL2S3Tb+ZyX9MjWwmcSD4bUoOQ== + +"@types/tough-cookie@*": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.4.tgz#821878b81bfab971b93a265a561d54ea61f9059f" + integrity sha512-Set5ZdrAaKI/qHdFlVMgm/GsAv/wkXhSTuZFkJ+JI7HK+wIkIlOaUXSXieIvJ0+OvGIqtREFoE+NHJtEq0gtEw== + +"@types/weak@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/weak/-/weak-1.0.0.tgz#7b3bf891c4b53e2b8a144b7e41f4b0f6e78c2ba8" + integrity sha512-6WXZpeAac3vj5+OfQvlqYEtc88oOgvkcxbrnmBw53Da6gA+MGztL+Hns3BpnyUevgz+4DxsJblgAew1A/tkcng== + dependencies: + "@types/node" "*" + +"@types/which@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/which/-/which-1.3.1.tgz#7802c380887986ca909008afea4e08025b130f8d" + integrity sha512-ZrJDWpvg75LTGX4XwuneY9s6bF3OeZcGTpoGh3zDV9ytzcHMFsRrMIaLBRJZQMBoGyKs6unBQfVdrLZiYfb1zQ== + +"@types/write-file-atomic@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@types/write-file-atomic/-/write-file-atomic-2.1.1.tgz#7f9fcd6c5c8d194dba03472e3fa6cb29a839764c" + integrity sha512-mROQhTxpJsOm/S0eOxDHUy5WJ0yS8fmqsq/s+u5OuAh1TxBFSqVBTkLjbyxDPcKh7DeJXk0OYrCkxXlkf8zu1g== + dependencies: + "@types/node" "*" + +"@types/yargs@^12.0.2": + version "12.0.2" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.2.tgz#7569157b2601d49d4442023c0d22f81d23f62d1e" + integrity sha512-NUyxaQW48vjeDHybmhy7CFx/6of1+PoaEGPMI+0PzBVr5s3BTELT7gV4lNaxUsKllNS1YAjkeTEhBtzIDWs2WQ== + +"@typescript-eslint/eslint-plugin@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.2.0.tgz#a73021a0d5bd1116a88689186844ce4421d52ab0" + integrity sha512-em3q8Gg3euesNohOwaz+SqrQM2Jn1ZWELMM+vgKi4dEk5fC+eVoi05yfubgAi2qPE5ifG4F0SOXM1XTamB0Aig== + dependencies: + "@typescript-eslint/parser" "1.2.0" + requireindex "^1.2.0" + tsutils "^3.7.0" + +"@typescript-eslint/parser@1.2.0", "@typescript-eslint/parser@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.2.0.tgz#256de163b7cb0d79d0388266a5bfb5585bccb1ff" + integrity sha512-IXXiXgs6ocKTmtbzJjGyUvRHZFLuk2mYXyk+ayEql1woh1+rYS/Uct8b4jGtfHG8ZRUBZ12zjzsrDKFYC2pbrQ== + dependencies: + "@typescript-eslint/typescript-estree" "1.2.0" + eslint-scope "^4.0.0" + eslint-visitor-keys "^1.0.0" + +"@typescript-eslint/typescript-estree@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.2.0.tgz#d9043a407d5e0f07db2b4a80d86852f7826c34f0" + integrity sha512-YGh4egbiCfUObvi6fnQNzJAMmScMGCjG5cRHaapW7GpwujWYQymLlids88imnhcTbOx8Mlz1OXFIuxyPBXK6Ig== + dependencies: + lodash.unescape "4.0.1" + semver "5.5.0" + "@webassemblyjs/ast@1.7.11": version "1.7.11" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace" @@ -3971,7 +4311,7 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -deepmerge@^2.1.1: +deepmerge@^2.0.1, deepmerge@^2.1.1: version "2.2.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170" integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA== @@ -4644,6 +4984,15 @@ eslint-import-resolver-node@^0.3.1: debug "^2.6.9" resolve "^1.5.0" +eslint-import-resolver-typescript@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-1.1.1.tgz#e6d42172b95144ef16610fe104ef38340edea591" + integrity sha512-jqSfumQ+H5y3FUJ6NjRkbOQSUOlbBucGTN3ELymOtcDBbPjVdm/luvJuCfCaIXGh8sEF26ma1qVdtDgl9ndhUg== + dependencies: + debug "^4.0.1" + resolve "^1.4.0" + tsconfig-paths "^3.6.0" + eslint-module-utils@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz#b270362cd88b1a48ad308976ce7fa54e98411746" @@ -7890,6 +8239,11 @@ lodash.throttle@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= +lodash.unescape@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" + integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -9506,7 +9860,7 @@ parse-passwd@^1.0.0: resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= -parse5@4.0.0: +parse5@4.0.0, parse5@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== @@ -10935,6 +11289,11 @@ require-main-filename@^1.0.1: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= +requireindex@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef" + integrity sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww== + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -11210,6 +11569,11 @@ semver-truncate@^1.1.2: resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== +semver@5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== + semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -12288,11 +12652,29 @@ truncate-html@^1.0.1: "@types/cheerio" "^0.22.8" cheerio "0.22.0" -tslib@^1.8.0, tslib@^1.9.0: +tsconfig-paths@^3.6.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.7.0.tgz#02ae978db447b22e09dafcd4198be95c4885ceb2" + integrity sha512-7iE+Q/2E1lgvxD+c0Ot+GFFmgmfIjt/zCayyruXkXQ84BLT85gHXy0WSoQSiuFX9+d+keE/jiON7notV74ZY+A== + dependencies: + "@types/json5" "^0.0.29" + deepmerge "^2.0.1" + json5 "^1.0.1" + minimist "^1.2.0" + strip-bom "^3.0.0" + +tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== +tsutils@^3.7.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.8.0.tgz#7a3dbadc88e465596440622b65c04edc8e187ae5" + integrity sha512-XQdPhgcoTbCD8baXC38PQ0vpTZ8T3YrE+vR66YIj/xvDt1//8iAhafpIT/4DmvzzC1QFapEImERu48Pa01dIUA== + dependencies: + tslib "^1.8.1" + tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -12330,10 +12712,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^3.0.0, typescript@^3.0.3: - version "3.2.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.2.tgz#fe8101c46aa123f8353523ebdcf5730c2ae493e5" - integrity sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg== +typescript@^3.0.0, typescript@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.1.tgz#6de14e1db4b8a006ac535e482c8ba018c55f750b" + integrity sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA== ua-parser-js@^0.7.18: version "0.7.19" From 9ab20f62c64f94a1cc7bff3bd2c9545c80af8d4a Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 6 Feb 2019 11:20:36 +0100 Subject: [PATCH 014/107] chore: downgrade yarn on windows on azure See #7760 --- .azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index e28f5b54627b..df022b431dec 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -17,6 +17,7 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true displayName: 'Preserve LF endings and symbolic links on check out' + - script: npm install -g yarn@1.9.4 - template: .azure-pipelines-steps.yml - job: macOS From 830bc747a630c99ecf85862719c219b9a524d90c Mon Sep 17 00:00:00 2001 From: Olivier JM Date: Wed, 6 Feb 2019 18:11:23 +0200 Subject: [PATCH 015/107] docs: link to json output example --- docs/CLI.md | 2 +- website/versioned_docs/version-22.x/CLI.md | 2 +- website/versioned_docs/version-23.x/CLI.md | 2 +- website/versioned_docs/version-24.0/CLI.md | 2 +- website/versioned_docs/version-24.1/CLI.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/CLI.md b/docs/CLI.md index 6c7a70c5bfd3..a681865c01ff 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -194,7 +194,7 @@ Prints the test results in JSON. This mode will send all other test output and u ### `--outputFile=` -Write test results to a file when the `--json` option is also specified. +Write test results to a file when the `--json` option is also specified. The returned JSON structure is documented in [testResultsProcessor](Configuration.md#testResultsProcessor-string). ### `--lastCommit` diff --git a/website/versioned_docs/version-22.x/CLI.md b/website/versioned_docs/version-22.x/CLI.md index 96068d34e534..31ebc57e1b8f 100644 --- a/website/versioned_docs/version-22.x/CLI.md +++ b/website/versioned_docs/version-22.x/CLI.md @@ -168,7 +168,7 @@ Prints the test results in JSON. This mode will send all other test output and u ### `--outputFile=` -Write test results to a file when the `--json` option is also specified. +Write test results to a file when the `--json` option is also specified. The returned JSON structure is documented in [testResultsProcessor](Configuration.md#testResultsProcessor-string). ### `--lastCommit` diff --git a/website/versioned_docs/version-23.x/CLI.md b/website/versioned_docs/version-23.x/CLI.md index 65800e2e127a..9da2e69747da 100644 --- a/website/versioned_docs/version-23.x/CLI.md +++ b/website/versioned_docs/version-23.x/CLI.md @@ -180,7 +180,7 @@ Prints the test results in JSON. This mode will send all other test output and u ### `--outputFile=` -Write test results to a file when the `--json` option is also specified. +Write test results to a file when the `--json` option is also specified. The returned JSON structure is documented in [testResultsProcessor](Configuration.md#testResultsProcessor-string). ### `--lastCommit` diff --git a/website/versioned_docs/version-24.0/CLI.md b/website/versioned_docs/version-24.0/CLI.md index acacfd93dbc6..baad0beead40 100644 --- a/website/versioned_docs/version-24.0/CLI.md +++ b/website/versioned_docs/version-24.0/CLI.md @@ -195,7 +195,7 @@ Prints the test results in JSON. This mode will send all other test output and u ### `--outputFile=` -Write test results to a file when the `--json` option is also specified. +Write test results to a file when the `--json` option is also specified. The returned JSON structure is documented in [testResultsProcessor](Configuration.md#testResultsProcessor-string). ### `--lastCommit` diff --git a/website/versioned_docs/version-24.1/CLI.md b/website/versioned_docs/version-24.1/CLI.md index 50b623fe5c1c..cf142c2cccec 100644 --- a/website/versioned_docs/version-24.1/CLI.md +++ b/website/versioned_docs/version-24.1/CLI.md @@ -195,7 +195,7 @@ Prints the test results in JSON. This mode will send all other test output and u ### `--outputFile=` -Write test results to a file when the `--json` option is also specified. +Write test results to a file when the `--json` option is also specified. The returned JSON structure is documented in [testResultsProcessor](Configuration.md#testResultsProcessor-string). ### `--lastCommit` From 935b295cde446deafd289792ef91e0ad1e279322 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 6 Feb 2019 21:39:09 +0100 Subject: [PATCH 016/107] Migrate pretty-format to TypeScript (#7809) --- CHANGELOG.md | 1 + jest.config.js | 2 +- .../jest-circus/src/formatNodeAssertErrors.js | 1 + packages/jest-circus/src/utils.js | 1 + packages/pretty-format/package.json | 5 + ...cher.test.js => AsymmetricMatcher.test.ts} | 9 +- ...onvertAnsi.test.js => ConvertAnsi.test.ts} | 8 +- ...llection.test.js => DOMCollection.test.ts} | 12 +- ...{DOMElement.test.js => DOMElement.test.ts} | 9 +- .../{Immutable.test.js => Immutable.test.ts} | 25 ++-- ...react.test.js.snap => react.test.tsx.snap} | 0 .../src/__tests__/getPrettyPrint.js | 48 ------- ...ttyFormat.test.js => prettyFormat.test.ts} | 41 +++--- .../{react.test.js => react.test.tsx} | 31 +++-- .../src/__tests__/setPrettyPrint.ts | 56 +++++++++ .../src/{collections.js => collections.ts} | 48 ++++--- .../pretty-format/src/{index.js => index.ts} | 58 +++++---- ...mmetricMatcher.js => AsymmetricMatcher.ts} | 8 +- .../{ConvertAnsi.js => ConvertAnsi.ts} | 17 ++- .../{DOMCollection.js => DOMCollection.ts} | 10 +- .../plugins/{DOMElement.js => DOMElement.ts} | 80 ++++++------ .../plugins/{Immutable.js => Immutable.ts} | 14 +-- .../{ReactElement.js => ReactElement.ts} | 16 +-- ...TestComponent.js => ReactTestComponent.ts} | 28 +++-- .../lib/{escapeHTML.js => escapeHTML.ts} | 2 - .../src/plugins/lib/{markup.js => markup.ts} | 6 +- packages/pretty-format/src/types.ts | 117 ++++++++++++++++++ packages/pretty-format/tsconfig.json | 7 ++ scripts/build.js | 4 +- scripts/watch.js | 4 +- yarn.lock | 30 +++++ 31 files changed, 433 insertions(+), 265 deletions(-) rename packages/pretty-format/src/__tests__/{AsymmetricMatcher.test.js => AsymmetricMatcher.test.ts} (98%) rename packages/pretty-format/src/__tests__/{ConvertAnsi.test.js => ConvertAnsi.test.ts} (94%) rename packages/pretty-format/src/__tests__/{DOMCollection.test.js => DOMCollection.test.ts} (94%) rename packages/pretty-format/src/__tests__/{DOMElement.test.js => DOMElement.test.ts} (98%) rename packages/pretty-format/src/__tests__/{Immutable.test.js => Immutable.test.ts} (98%) rename packages/pretty-format/src/__tests__/__snapshots__/{react.test.js.snap => react.test.tsx.snap} (100%) delete mode 100644 packages/pretty-format/src/__tests__/getPrettyPrint.js rename packages/pretty-format/src/__tests__/{prettyFormat.test.js => prettyFormat.test.ts} (97%) rename packages/pretty-format/src/__tests__/{react.test.js => react.test.tsx} (97%) create mode 100644 packages/pretty-format/src/__tests__/setPrettyPrint.ts rename packages/pretty-format/src/{collections.js => collections.ts} (81%) rename packages/pretty-format/src/{index.js => index.ts} (91%) rename packages/pretty-format/src/plugins/{AsymmetricMatcher.js => AsymmetricMatcher.ts} (93%) rename packages/pretty-format/src/plugins/{ConvertAnsi.js => ConvertAnsi.ts} (84%) rename packages/pretty-format/src/plugins/{DOMCollection.js => DOMCollection.ts} (89%) rename packages/pretty-format/src/plugins/{DOMElement.js => DOMElement.ts} (58%) rename packages/pretty-format/src/plugins/{Immutable.js => Immutable.ts} (94%) rename packages/pretty-format/src/plugins/{ReactElement.js => ReactElement.ts} (90%) rename packages/pretty-format/src/plugins/{ReactTestComponent.js => ReactTestComponent.ts} (73%) rename packages/pretty-format/src/plugins/lib/{escapeHTML.js => escapeHTML.ts} (96%) rename packages/pretty-format/src/plugins/lib/{markup.js => markup.ts} (97%) create mode 100644 packages/pretty-format/src/types.ts create mode 100644 packages/pretty-format/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index d039d87a4441..fa44400ddb21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Chore & Maintenance - `[*]`: Setup building, linting and testing of TypeScript ([#7808](https://github.com/facebook/jest/pull/7808)) +- `[pretty-format]`: Migrate to TypeScript ([#7809](https://github.com/facebook/jest/pull/7809)) ### Performance diff --git a/jest.config.js b/jest.config.js index 999b16dd01ca..db041ad091ae 100644 --- a/jest.config.js +++ b/jest.config.js @@ -38,7 +38,7 @@ module.exports = { '\\.snap$', '/packages/.*/build', '/packages/.*/build-es5', - '/packages/.*/src/__tests__/getPrettyPrint.js', + '/packages/.*/src/__tests__/setPrettyPrint.ts', '/packages/jest-cli/src/__tests__/test_root', '/packages/jest-cli/src/__tests__/__fixtures__/', '/packages/jest-cli/src/lib/__tests__/fixtures/', diff --git a/packages/jest-circus/src/formatNodeAssertErrors.js b/packages/jest-circus/src/formatNodeAssertErrors.js index a13caf678687..6434eee318a5 100644 --- a/packages/jest-circus/src/formatNodeAssertErrors.js +++ b/packages/jest-circus/src/formatNodeAssertErrors.js @@ -12,6 +12,7 @@ import type {Event, State} from 'types/Circus'; import {diff, printExpected, printReceived} from 'jest-matcher-utils'; import chalk from 'chalk'; +// $FlowFixMe: Converted to TS import prettyFormat from 'pretty-format'; type AssertionError = {| diff --git a/packages/jest-circus/src/utils.js b/packages/jest-circus/src/utils.js index e8c515478694..4f079ece8c6e 100644 --- a/packages/jest-circus/src/utils.js +++ b/packages/jest-circus/src/utils.js @@ -28,6 +28,7 @@ import co from 'co'; import StackUtils from 'stack-utils'; +// $FlowFixMe: Converted to TS import prettyFormat from 'pretty-format'; import {getState} from './state'; diff --git a/packages/pretty-format/package.json b/packages/pretty-format/package.json index c9d7fedc45b7..c90ad0715fcd 100644 --- a/packages/pretty-format/package.json +++ b/packages/pretty-format/package.json @@ -9,6 +9,7 @@ "license": "MIT", "description": "Stringify any JavaScript value.", "main": "build/index.js", + "types": "build/index.d.ts", "browser": "build-es5/index.js", "author": "James Kyle ", "dependencies": { @@ -18,7 +19,11 @@ "devDependencies": { "@types/ansi-regex": "^4.0.0", "@types/ansi-styles": "^3.2.1", + "@types/jest": "^24.0.0", + "@types/react": "*", + "@types/react-test-renderer": "*", "immutable": "4.0.0-rc.9", + "jest-diff": "^24.0.0", "react": "*", "react-dom": "*", "react-test-renderer": "*" diff --git a/packages/pretty-format/src/__tests__/AsymmetricMatcher.test.js b/packages/pretty-format/src/__tests__/AsymmetricMatcher.test.ts similarity index 98% rename from packages/pretty-format/src/__tests__/AsymmetricMatcher.test.js rename to packages/pretty-format/src/__tests__/AsymmetricMatcher.test.ts index 7e670e57e9d2..b46d96ba5565 100644 --- a/packages/pretty-format/src/__tests__/AsymmetricMatcher.test.js +++ b/packages/pretty-format/src/__tests__/AsymmetricMatcher.test.ts @@ -3,17 +3,16 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {OptionsReceived} from 'types/PrettyFormat'; +import {OptionsReceived} from '../types'; + +import prettyFormat from '../'; -const prettyFormat = require('../'); const {AsymmetricMatcher} = prettyFormat.plugins; let options: OptionsReceived; -function fnNameFor(func) { +function fnNameFor(func: (...any: any[]) => any) { if (func.name) { return func.name; } diff --git a/packages/pretty-format/src/__tests__/ConvertAnsi.test.js b/packages/pretty-format/src/__tests__/ConvertAnsi.test.ts similarity index 94% rename from packages/pretty-format/src/__tests__/ConvertAnsi.test.js rename to packages/pretty-format/src/__tests__/ConvertAnsi.test.ts index f17208681dc1..c562f67a8123 100644 --- a/packages/pretty-format/src/__tests__/ConvertAnsi.test.js +++ b/packages/pretty-format/src/__tests__/ConvertAnsi.test.ts @@ -3,12 +3,12 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -const ansiStyle = require('ansi-styles'); -const prettyFormat = require('../'); +import ansiStyle from 'ansi-styles'; + +import prettyFormat from '../'; + const {ConvertAnsi} = prettyFormat.plugins; const prettyFormatResult = (val: string) => diff --git a/packages/pretty-format/src/__tests__/DOMCollection.test.js b/packages/pretty-format/src/__tests__/DOMCollection.test.ts similarity index 94% rename from packages/pretty-format/src/__tests__/DOMCollection.test.js rename to packages/pretty-format/src/__tests__/DOMCollection.test.ts index 6c08ba00308a..8cba5748f95b 100644 --- a/packages/pretty-format/src/__tests__/DOMCollection.test.js +++ b/packages/pretty-format/src/__tests__/DOMCollection.test.ts @@ -5,21 +5,15 @@ * LICENSE file in the root directory of this source tree. * * @jest-environment jsdom - * @flow */ /* eslint-env browser*/ -'use strict'; +import prettyFormat from '../'; +import setPrettyPrint from './setPrettyPrint'; -const prettyFormat = require('../'); const {DOMCollection, DOMElement} = prettyFormat.plugins; -const toPrettyPrintTo = require('./getPrettyPrint').default([ - DOMCollection, - DOMElement, -]); -const expect: any = global.expect; -expect.extend({toPrettyPrintTo}); +setPrettyPrint([DOMCollection, DOMElement]); describe('DOMCollection plugin for object properties', () => { it('supports DOMStringMap', () => { diff --git a/packages/pretty-format/src/__tests__/DOMElement.test.js b/packages/pretty-format/src/__tests__/DOMElement.test.ts similarity index 98% rename from packages/pretty-format/src/__tests__/DOMElement.test.js rename to packages/pretty-format/src/__tests__/DOMElement.test.ts index 074252ea0f77..b5e22f549c40 100644 --- a/packages/pretty-format/src/__tests__/DOMElement.test.js +++ b/packages/pretty-format/src/__tests__/DOMElement.test.ts @@ -5,18 +5,15 @@ * LICENSE file in the root directory of this source tree. * * @jest-environment jsdom - * @flow */ /* eslint-env browser*/ -'use strict'; +import prettyFormat from '../'; +import setPrettyPrint from './setPrettyPrint'; -const prettyFormat = require('../'); const {DOMElement} = prettyFormat.plugins; -const toPrettyPrintTo = require('./getPrettyPrint').default([DOMElement]); -const expect: any = global.expect; -expect.extend({toPrettyPrintTo}); +setPrettyPrint([DOMElement]); describe('pretty-format', () => { // Test is not related to plugin but is related to jsdom testing environment. diff --git a/packages/pretty-format/src/__tests__/Immutable.test.js b/packages/pretty-format/src/__tests__/Immutable.test.ts similarity index 98% rename from packages/pretty-format/src/__tests__/Immutable.test.js rename to packages/pretty-format/src/__tests__/Immutable.test.ts index e03b69dcc860..05c51241f804 100644 --- a/packages/pretty-format/src/__tests__/Immutable.test.js +++ b/packages/pretty-format/src/__tests__/Immutable.test.ts @@ -3,27 +3,22 @@ * * 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 React from 'react'; import Immutable from 'immutable'; -import getPrettyPrint from './getPrettyPrint'; -const {Immutable: ImmutablePlugin, ReactElement} = require('..').plugins; +import setPrettyPrint from './setPrettyPrint'; +import prettyFormat from '..'; -const toPrettyPrintTo = getPrettyPrint([ReactElement, ImmutablePlugin]); +const {Immutable: ImmutablePlugin, ReactElement} = prettyFormat.plugins; -const expect = global.expect; -expect.extend({toPrettyPrintTo}); +setPrettyPrint([ReactElement, ImmutablePlugin]); it('does not incorrectly match identity-obj-proxy as Immutable object', () => { // SENTINEL constant is from https://github.com/facebook/immutable-js const IS_ITERABLE_SENTINEL = '@@__IMMUTABLE_ITERABLE__@@'; - const val = {}; + const val: any = {}; val[IS_ITERABLE_SENTINEL] = IS_ITERABLE_SENTINEL; // mock the mock object :) const expected = `{"${IS_ITERABLE_SENTINEL}": "${IS_ITERABLE_SENTINEL}"}`; expect(val).toPrettyPrintTo(expected, {min: true}); @@ -507,7 +502,7 @@ describe('Immutable.OrderedMap', () => { }); it('supports non-string keys', () => { - const val = Immutable.OrderedMap([ + const val = Immutable.OrderedMap([ [false, 'boolean'], ['false', 'string'], [0, 'number'], @@ -871,7 +866,7 @@ describe('Immutable.Seq', () => { }); it('supports a non-empty sequence from arguments', () => { - function returnArguments(...args) { + function returnArguments(..._args: Array) { return arguments; } expect(Immutable.Seq(returnArguments(0, 1, 2))).toPrettyPrintTo( @@ -933,7 +928,7 @@ describe('Immutable.Seq', () => { describe('Immutable.Seq lazy entries', () => { const expected = 'Immutable.Seq {…}'; const object = {key0: '', key1: '1'}; - const filterer = value => value.length !== 0; + const filterer = (value: string) => value.length !== 0; // undefined size confirms correct criteria for lazy Seq test('from object properties', () => { @@ -951,7 +946,7 @@ describe('Immutable.Seq lazy entries', () => { describe('Immutable.Seq lazy values', () => { const expected = 'Immutable.Seq […]'; const array = ['', '1', '22']; - const filterer = item => item.length !== 0; + const filterer = (item: string) => item.length !== 0; test('from Immutable.Range', () => { const val = Immutable.Range(1, Infinity); @@ -961,7 +956,7 @@ describe('Immutable.Seq lazy values', () => { // undefined size confirms correct criteria for lazy Seq test('from iterator', () => { - function returnIterator(values) { + function returnIterator(values: Array) { let i = 0; return { next() { diff --git a/packages/pretty-format/src/__tests__/__snapshots__/react.test.js.snap b/packages/pretty-format/src/__tests__/__snapshots__/react.test.tsx.snap similarity index 100% rename from packages/pretty-format/src/__tests__/__snapshots__/react.test.js.snap rename to packages/pretty-format/src/__tests__/__snapshots__/react.test.tsx.snap diff --git a/packages/pretty-format/src/__tests__/getPrettyPrint.js b/packages/pretty-format/src/__tests__/getPrettyPrint.js deleted file mode 100644 index 3eae13b07dbc..000000000000 --- a/packages/pretty-format/src/__tests__/getPrettyPrint.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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 type {OptionsReceived, Plugins} from 'types/PrettyFormat'; - -const diff = require('jest-diff'); -const prettyFormat = require('../'); - -const getPrettyPrint = (plugins: Plugins) => - function(received: any, expected: any, options?: OptionsReceived) { - const prettyFormatted = prettyFormat(received, {plugins, ...options}); - const pass = prettyFormatted === expected; - - const message = pass - ? () => - this.utils.matcherHint('.not.toBe') + - '\n\n' + - `Expected value to not be:\n` + - ` ${this.utils.printExpected(expected)}\n` + - `Received:\n` + - ` ${this.utils.printReceived(prettyFormatted)}` - : () => { - const diffString = diff(expected, prettyFormatted, { - expand: this.expand, - }); - return ( - this.utils.matcherHint('.toBe') + - '\n\n' + - `Expected value to be:\n` + - ` ${this.utils.printExpected(expected)}\n` + - `Received:\n` + - ` ${this.utils.printReceived(prettyFormatted)}` + - (diffString ? `\n\nDifference:\n\n${diffString}` : '') - ); - }; - - return {actual: prettyFormatted, message, pass}; - }; - -export default getPrettyPrint; diff --git a/packages/pretty-format/src/__tests__/prettyFormat.test.js b/packages/pretty-format/src/__tests__/prettyFormat.test.ts similarity index 97% rename from packages/pretty-format/src/__tests__/prettyFormat.test.js rename to packages/pretty-format/src/__tests__/prettyFormat.test.ts index 339252ee9be2..779d3c45a754 100644 --- a/packages/pretty-format/src/__tests__/prettyFormat.test.js +++ b/packages/pretty-format/src/__tests__/prettyFormat.test.ts @@ -3,21 +3,18 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -'use strict'; - -const prettyFormat = require('../'); +import prettyFormat from '../'; -function returnArguments(...args) { +function returnArguments(..._args: Array) { return arguments; } class MyArray extends Array {} -function MyObject(value) { +function MyObject(value: unknown) { + // @ts-ignore this.name = value; } @@ -33,7 +30,7 @@ describe('prettyFormat()', () => { }); it('prints an empty array', () => { - const val = []; + const val: never[] = []; expect(prettyFormat(val)).toEqual('Array []'); }); @@ -94,7 +91,7 @@ describe('prettyFormat()', () => { it('prints an anonymous callback function', () => { let val; - function f(cb) { + function f(cb: () => void) { val = cb; } f(() => {}); @@ -159,7 +156,7 @@ describe('prettyFormat()', () => { }); it('prints a map with non-string keys', () => { - const val = new Map([ + const val = new Map([ [false, 'boolean'], ['false', 'string'], [0, 'number'], @@ -416,7 +413,7 @@ describe('prettyFormat()', () => { }); it('prints circular references', () => { - const val = {}; + const val: any = {}; val.prop = val; expect(prettyFormat(val)).toEqual('Object {\n "prop": [Circular],\n}'); }); @@ -488,6 +485,7 @@ describe('prettyFormat()', () => { 'map non-empty': new Map([['name', 'value']]), 'object literal empty': {}, 'object literal non-empty': {name: 'value'}, + // @ts-ignore 'object with constructor': new MyObject('value'), 'object without constructor': Object.create(null), 'set empty': new Set(), @@ -519,13 +517,13 @@ describe('prettyFormat()', () => { it('throws on invalid options', () => { expect(() => { - // $FlowFixMe + // @ts-ignore prettyFormat({}, {invalidOption: true}); }).toThrow(); }); it('supports plugins', () => { - function Foo() {} + class Foo {} expect( prettyFormat(new Foo(), { @@ -548,10 +546,10 @@ describe('prettyFormat()', () => { const options = { plugins: [ { - print(val) { + print(val: any) { return val.payload; }, - test(val) { + test(val: any) { return val && typeof val.payload === 'string'; }, }, @@ -565,7 +563,7 @@ describe('prettyFormat()', () => { const options = { plugins: [ { - print(val) { + print(val: any) { return val; }, test() { @@ -646,7 +644,7 @@ describe('prettyFormat()', () => { plugins: [ { print(val, print) { - return val.map(item => print(item)).join(' - '); + return val.map((item: any) => print(item)).join(' - '); }, test(val) { return Array.isArray(val); @@ -663,7 +661,7 @@ describe('prettyFormat()', () => { prettyFormat(val, { plugins: [ { - print(val, print) { + print(_val, _print) { return '[called]'; }, test(val) { @@ -728,7 +726,7 @@ describe('prettyFormat()', () => { it('calls toJSON on Sets', () => { const set = new Set([1]); - (set: Object).toJSON = () => 'map'; + (set as any).toJSON = () => 'map'; expect(prettyFormat(set)).toEqual('"map"'); }); @@ -736,7 +734,7 @@ describe('prettyFormat()', () => { const value = {apple: 'banana', toJSON: jest.fn(() => '1')}; const name = value.toJSON.name || 'anonymous'; const set = new Set([value]); - (set: Object).toJSON = jest.fn(() => 'map'); + (set as any).toJSON = jest.fn(() => 'map'); expect( prettyFormat(set, { callToJSON: false, @@ -746,7 +744,7 @@ describe('prettyFormat()', () => { name + '],\n },\n}', ); - expect((set: Object).toJSON).not.toBeCalled(); + expect((set as any).toJSON).not.toBeCalled(); expect(value.toJSON).not.toBeCalled(); }); @@ -787,6 +785,7 @@ describe('prettyFormat()', () => { 'map non-empty': new Map([['name', 'value']]), 'object literal empty': {}, 'object literal non-empty': {name: 'value'}, + // @ts-ignore 'object with constructor': new MyObject('value'), 'object without constructor': Object.create(null), 'set empty': new Set(), diff --git a/packages/pretty-format/src/__tests__/react.test.js b/packages/pretty-format/src/__tests__/react.test.tsx similarity index 97% rename from packages/pretty-format/src/__tests__/react.test.js rename to packages/pretty-format/src/__tests__/react.test.tsx index 6a268319cf99..44d33d4b083e 100644 --- a/packages/pretty-format/src/__tests__/react.test.js +++ b/packages/pretty-format/src/__tests__/react.test.tsx @@ -3,20 +3,17 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {OptionsReceived} from 'types/PrettyFormat'; +import React from 'react'; +import renderer from 'react-test-renderer'; -const React = require('react'); -const renderer = require('react-test-renderer'); +import prettyFormat from '..'; +import {OptionsReceived} from '../types'; const elementSymbol = Symbol.for('react.element'); const fragmentSymbol = Symbol.for('react.fragment'); const testSymbol = Symbol.for('react.test.json'); - -const prettyFormat = require('..'); const {ReactElement, ReactTestComponent} = prettyFormat.plugins; const formatElement = (element: any, options?: OptionsReceived) => @@ -116,7 +113,9 @@ test('supports props with numbers', () => { test('supports a single element with a function prop', () => { assertPrintedJSX( - React.createElement('Mouse', {onclick: function onclick() {}}), + React.createElement<{onclick: any}>('Mouse', { + onclick: function onclick() {}, + }), '', ); }); @@ -141,7 +140,7 @@ test('supports an element with and object prop and children', () => { test('supports an element with complex props and mixed children', () => { assertPrintedJSX( - React.createElement( + React.createElement<{customProp: any; onclick: any}>( 'Mouse', {customProp: {one: '1', two: 2}, onclick: function onclick() {}}, 'HELLO', @@ -167,11 +166,11 @@ test('escapes children properly', () => { test('supports everything all together', () => { assertPrintedJSX( - React.createElement( + React.createElement<{customProp: any; onclick: any}>( 'Mouse', {customProp: {one: '1', two: 2}, onclick: function onclick() {}}, 'HELLO', - React.createElement( + React.createElement<{customProp: any; onclick: any}>( 'Mouse', {customProp: {one: '1', two: 2}, onclick: function onclick() {}}, 'HELLO', @@ -266,7 +265,7 @@ test('supports a single element with custom React elements with props (using ano }); test('supports a single element with custom React elements with a child', () => { - function Cat(props) { + function Cat(props: any) { return React.createElement('div', props); } assertPrintedJSX( @@ -565,7 +564,7 @@ describe('maxDepth option', () => { test('min option', () => { assertPrintedJSX( - React.createElement( + React.createElement<{customProp: any; onclick: any}>( 'Mouse', {customProp: {one: '1', two: 2}, onclick: function onclick() {}}, 'HELLO', @@ -622,9 +621,9 @@ test('throws if theme option is null', () => { 'Hello, Mouse!', ); expect(() => { + // @ts-ignore formatElement(jsx, { highlight: true, - // $FlowFixMe theme: null, }); }).toThrow('pretty-format: Option "theme" must not be null.'); @@ -637,9 +636,9 @@ test('throws if theme option is not of type "object"', () => { {style: 'color:red'}, 'Hello, Mouse!', ); + // @ts-ignore formatElement(jsx, { highlight: true, - // $FlowFixMe theme: 'beautiful', }); }).toThrow( @@ -701,7 +700,7 @@ test('ReactTestComponent plugin highlights syntax with color from theme option', }); test('supports forwardRef with a child', () => { - function Cat(props) { + function Cat(props: any) { return React.createElement('div', props, props.children); } diff --git a/packages/pretty-format/src/__tests__/setPrettyPrint.ts b/packages/pretty-format/src/__tests__/setPrettyPrint.ts new file mode 100644 index 000000000000..4a8a562bf52b --- /dev/null +++ b/packages/pretty-format/src/__tests__/setPrettyPrint.ts @@ -0,0 +1,56 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +// @ts-ignore +import diff from 'jest-diff'; + +import prettyFormat from '../'; +import {OptionsReceived, Plugins} from '../types'; + +declare global { + namespace jest { + interface Matchers { + toPrettyPrintTo(expected: any, options?: OptionsReceived): R; + } + } +} + +const setPrettyPrint = (plugins: Plugins) => { + expect.extend({ + toPrettyPrintTo(received: any, expected: any, options?: OptionsReceived) { + const prettyFormatted = prettyFormat(received, {plugins, ...options}); + const pass = prettyFormatted === expected; + + const message = pass + ? () => + this.utils.matcherHint('.not.toBe') + + '\n\n' + + `Expected value to not be:\n` + + ` ${this.utils.printExpected(expected)}\n` + + `Received:\n` + + ` ${this.utils.printReceived(prettyFormatted)}` + : () => { + const diffString = diff(expected, prettyFormatted, { + expand: this.expand, + }); + return ( + this.utils.matcherHint('.toBe') + + '\n\n' + + `Expected value to be:\n` + + ` ${this.utils.printExpected(expected)}\n` + + `Received:\n` + + ` ${this.utils.printReceived(prettyFormatted)}` + + (diffString ? `\n\nDifference:\n\n${diffString}` : '') + ); + }; + + return {actual: prettyFormatted, message, pass}; + }, + }); +}; + +export default setPrettyPrint; diff --git a/packages/pretty-format/src/collections.js b/packages/pretty-format/src/collections.ts similarity index 81% rename from packages/pretty-format/src/collections.js rename to packages/pretty-format/src/collections.ts index ec0478b488f3..305e5c0da3d6 100644 --- a/packages/pretty-format/src/collections.js +++ b/packages/pretty-format/src/collections.ts @@ -4,18 +4,16 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ -import type {Config, Printer, Refs} from 'types/PrettyFormat'; +import {Config, Printer, Refs} from './types'; const getKeysOfEnumerableProperties = (object: Object) => { - const keys = Object.keys(object).sort(); + const keys: Array = Object.keys(object).sort(); if (Object.getOwnPropertySymbols) { Object.getOwnPropertySymbols(object).forEach(symbol => { - //$FlowFixMe because property enumerable is missing in undefined - if (Object.getOwnPropertyDescriptor(object, symbol).enumerable) { + if (Object.getOwnPropertyDescriptor(object, symbol)!.enumerable) { keys.push(symbol); } }); @@ -24,9 +22,11 @@ const getKeysOfEnumerableProperties = (object: Object) => { return keys; }; -// Return entries (for example, of a map) -// with spacing, indentation, and comma -// without surrounding punctuation (for example, braces) +/** + * Return entries (for example, of a map) + * with spacing, indentation, and comma + * without surrounding punctuation (for example, braces) + */ export function printIteratorEntries( // Flow 0.51.0: property `@@iterator` of $Iterator not found in Object // To allow simplistic getRecordIterator in immutable.js @@ -83,9 +83,11 @@ export function printIteratorEntries( return result; } -// Return values (for example, of a set) -// with spacing, indentation, and comma -// without surrounding punctuation (braces or brackets) +/** + * Return values (for example, of a set) + * with spacing, indentation, and comma + * without surrounding punctuation (braces or brackets) + */ export function printIteratorValues( iterator: Iterator, config: Config, @@ -122,9 +124,11 @@ export function printIteratorValues( return result; } -// Return items (for example, of an array) -// with spacing, indentation, and comma -// without surrounding punctuation (for example, brackets) +/** + * Return items (for example, of an array) + * with spacing, indentation, and comma + * without surrounding punctuation (for example, brackets) + **/ export function printListItems( list: any, config: Config, @@ -158,9 +162,11 @@ export function printListItems( return result; } -// Return properties of an object -// with spacing, indentation, and comma -// without surrounding punctuation (for example, braces) +/** + * Return properties of an object + * with spacing, indentation, and comma + * without surrounding punctuation (for example, braces) + */ export function printObjectProperties( val: Object, config: Config, @@ -180,7 +186,13 @@ export function printObjectProperties( for (let i = 0; i < keys.length; i++) { const key = keys[i]; const name = printer(key, config, indentationNext, depth, refs); - const value = printer(val[key], config, indentationNext, depth, refs); + const value = printer( + (val as any)[key], + config, + indentationNext, + depth, + refs, + ); result += indentationNext + name + ': ' + value; diff --git a/packages/pretty-format/src/index.js b/packages/pretty-format/src/index.ts similarity index 91% rename from packages/pretty-format/src/index.js rename to packages/pretty-format/src/index.ts index bea132820a4e..9ed723356e42 100644 --- a/packages/pretty-format/src/index.js +++ b/packages/pretty-format/src/index.ts @@ -3,23 +3,20 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type { +import style from 'ansi-styles'; +import { Colors, Config, Options, OptionsReceived, + NewPlugin, Plugin, Plugins, Refs, - StringOrNull, Theme, -} from 'types/PrettyFormat'; - -import style from 'ansi-styles'; +} from './types'; import { printIteratorEntries, @@ -42,20 +39,22 @@ const errorToString = Error.prototype.toString; const regExpToString = RegExp.prototype.toString; const symbolToString = Symbol.prototype.toString; -// Explicitly comparing typeof constructor to function avoids undefined as name -// when mock identity-obj-proxy returns the key as the value for any key. -const getConstructorName = val => +/** + * Explicitly comparing typeof constructor to function avoids undefined as name + * when mock identity-obj-proxy returns the key as the value for any key. + */ +const getConstructorName = (val: new (...args: any[]) => any) => (typeof val.constructor === 'function' && val.constructor.name) || 'Object'; -// Is val is equal to global window object? Works even if it does not exist :) /* global window */ -const isWindow = val => typeof window !== 'undefined' && val === window; +/** Is val is equal to global window object? Works even if it does not exist :) */ +const isWindow = (val: any) => typeof window !== 'undefined' && val === window; const SYMBOL_REGEXP = /^Symbol\((.*)\)(.*)$/; const NEWLINE_REGEXP = /\n/gi; class PrettyFormatPluginError extends Error { - constructor(message, stack) { + constructor(message: string, stack: string) { super(message); this.stack = stack; this.name = this.constructor.name; @@ -98,12 +97,16 @@ function printError(val: Error): string { return '[' + errorToString.call(val) + ']'; } +/** + * The first port of call for printing an object, handles most of the + * data-types in JS. + */ function printBasicValue( val: any, printFunctionName: boolean, escapeRegex: boolean, escapeString: boolean, -): StringOrNull { +): string | null { if (val === true || val === false) { return '' + val; } @@ -170,6 +173,10 @@ function printBasicValue( return null; } +/** + * Handles more complex objects ( such as objects with circular references. + * maps and sets etc ) + */ function printComplexValue( val: any, config: Config, @@ -254,6 +261,10 @@ function printComplexValue( '}'; } +function isNewPlugin(plugin: Plugin): plugin is NewPlugin { + return (plugin as NewPlugin).serialize != null; +} + function printPlugin( plugin: Plugin, val: any, @@ -265,7 +276,7 @@ function printPlugin( let printed; try { - printed = plugin.serialize + printed = isNewPlugin(plugin) ? plugin.serialize(val, config, indentation, depth, refs, printer) : plugin.print( val, @@ -392,13 +403,12 @@ function validateOptions(options: OptionsReceived) { } const getColorsHighlight = (options: OptionsReceived): Colors => - // $FlowFixMe: Flow thinks keys from `Colors` are missing from `DEFAULT_THEME_KEYS` DEFAULT_THEME_KEYS.reduce((colors, key) => { const value = - options.theme && options.theme[key] !== undefined - ? options.theme[key] - : DEFAULT_THEME[key]; - const color = style[value]; + options.theme && (options.theme as any)[key] !== undefined + ? (options.theme as any)[key] + : (DEFAULT_THEME as any)[key]; + const color = (style as any)[value]; if ( color && typeof color.close === 'string' && @@ -414,7 +424,6 @@ const getColorsHighlight = (options: OptionsReceived): Colors => }, Object.create(null)); const getColorsEmpty = (): Colors => - // $FlowFixMe: Flow thinks keys from `Colors` are missing from `DEFAULT_THEME_KEYS` DEFAULT_THEME_KEYS.reduce((colors, key) => { colors[key] = {close: '', open: ''}; return colors; @@ -472,6 +481,11 @@ function createIndent(indent: number): string { return new Array(indent + 1).join(' '); } +/** + * Returns a presentation string of your `val` object + * @param val any potential JavaScript object + * @param options Custom settings + */ function prettyFormat(val: any, options?: OptionsReceived): string { if (options) { validateOptions(options); @@ -506,4 +520,4 @@ prettyFormat.plugins = { ReactTestComponent, }; -module.exports = prettyFormat; +export = prettyFormat; diff --git a/packages/pretty-format/src/plugins/AsymmetricMatcher.js b/packages/pretty-format/src/plugins/AsymmetricMatcher.ts similarity index 93% rename from packages/pretty-format/src/plugins/AsymmetricMatcher.js rename to packages/pretty-format/src/plugins/AsymmetricMatcher.ts index 3d6376c732fe..83fe8a7cf4d6 100644 --- a/packages/pretty-format/src/plugins/AsymmetricMatcher.js +++ b/packages/pretty-format/src/plugins/AsymmetricMatcher.ts @@ -3,11 +3,9 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Config, NewPlugin, Printer, Refs} from 'types/PrettyFormat'; +import {Config, NewPlugin, Printer, Refs} from '../types'; import {printListItems, printObjectProperties} from '../collections'; @@ -90,4 +88,6 @@ export const serialize = ( export const test = (val: any) => val && val.$$typeof === asymmetricMatcher; -export default ({serialize, test}: NewPlugin); +const plugin: NewPlugin = {serialize, test}; + +export default plugin; diff --git a/packages/pretty-format/src/plugins/ConvertAnsi.js b/packages/pretty-format/src/plugins/ConvertAnsi.ts similarity index 84% rename from packages/pretty-format/src/plugins/ConvertAnsi.js rename to packages/pretty-format/src/plugins/ConvertAnsi.ts index b974b47aef88..5a673342ce0d 100644 --- a/packages/pretty-format/src/plugins/ConvertAnsi.js +++ b/packages/pretty-format/src/plugins/ConvertAnsi.ts @@ -3,17 +3,14 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Config, Printer, NewPlugin, Refs} from 'types/PrettyFormat'; - import ansiRegex from 'ansi-regex'; import style from 'ansi-styles'; +import {Config, Printer, NewPlugin, Refs} from '../types'; -const toHumanReadableAnsi = text => - text.replace(ansiRegex(), (match, offset, string) => { +const toHumanReadableAnsi = (text: string) => + text.replace(ansiRegex(), match => { switch (match) { case style.red.close: case style.green.close: @@ -59,8 +56,8 @@ const toHumanReadableAnsi = text => } }); -export const test = (val: any) => - typeof val === 'string' && val.match(ansiRegex()); +export const test = (val: any): boolean => + typeof val === 'string' && !!val.match(ansiRegex()); export const serialize = ( val: string, @@ -71,4 +68,6 @@ export const serialize = ( printer: Printer, ) => printer(toHumanReadableAnsi(val), config, indentation, depth, refs); -export default ({serialize, test}: NewPlugin); +const plugin: NewPlugin = {serialize, test}; + +export default plugin; diff --git a/packages/pretty-format/src/plugins/DOMCollection.js b/packages/pretty-format/src/plugins/DOMCollection.ts similarity index 89% rename from packages/pretty-format/src/plugins/DOMCollection.js rename to packages/pretty-format/src/plugins/DOMCollection.ts index b45975022286..e922cc1bd9f9 100644 --- a/packages/pretty-format/src/plugins/DOMCollection.js +++ b/packages/pretty-format/src/plugins/DOMCollection.ts @@ -3,11 +3,9 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Config, NewPlugin, Printer, Refs} from 'types/PrettyFormat'; +import {Config, NewPlugin, Printer, Refs} from '../types'; import {printListItems, printObjectProperties} from '../collections'; @@ -26,7 +24,7 @@ export const test = (val: any) => testName(val.constructor.name); // Convert array of attribute objects to props object. -const propsReducer = (props, attribute) => { +const propsReducer = (props: any, attribute: any) => { props[attribute.name] = attribute.value; return props; }; @@ -72,4 +70,6 @@ export const serialize = ( ); }; -export default ({serialize, test}: NewPlugin); +const plugin: NewPlugin = {serialize, test}; + +export default plugin; diff --git a/packages/pretty-format/src/plugins/DOMElement.js b/packages/pretty-format/src/plugins/DOMElement.ts similarity index 58% rename from packages/pretty-format/src/plugins/DOMElement.js rename to packages/pretty-format/src/plugins/DOMElement.ts index f9e86164ad15..03546fb210fb 100644 --- a/packages/pretty-format/src/plugins/DOMElement.js +++ b/packages/pretty-format/src/plugins/DOMElement.ts @@ -3,11 +3,9 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Config, NewPlugin, Printer, Refs} from 'types/PrettyFormat'; +import {Config, NewPlugin, Printer, Refs} from '../types'; import { printChildren, @@ -18,30 +16,6 @@ import { printText, } from './lib/markup'; -type Attribute = { - name: string, - value: string, -}; - -type Element = { - attributes: Array, - childNodes: Array, - nodeType: 1, - tagName: string, -}; -type Text = { - data: string, - nodeType: 3, -}; -type Comment = { - data: string, - nodeType: 8, -}; -type DocumentFragment = { - children: Array, - nodeType: 11, -}; - const ELEMENT_NODE = 1; const TEXT_NODE = 3; const COMMENT_NODE = 8; @@ -61,33 +35,39 @@ export const test = (val: any) => val.constructor.name && testNode(val.nodeType, val.constructor.name); -// Convert array of attribute objects to keys array and props object. -const keysMapper = attribute => attribute.name; -const propsReducer = (props, attribute) => { - props[attribute.name] = attribute.value; - return props; -}; +type HandledType = Element | Text | Comment | DocumentFragment; + +function nodeIsText(node: HandledType): node is Text { + return node.nodeType === TEXT_NODE; +} + +function nodeIsComment(node: HandledType): node is Comment { + return node.nodeType === COMMENT_NODE; +} + +function nodeIsFragment(node: HandledType): node is DocumentFragment { + return node.nodeType === FRAGMENT_NODE; +} export const serialize = ( - node: Element | Text | Comment | DocumentFragment, + node: HandledType, config: Config, indentation: string, depth: number, refs: Refs, printer: Printer, ): string => { - if (node.nodeType === TEXT_NODE) { + if (nodeIsText(node)) { return printText(node.data, config); } - if (node.nodeType === COMMENT_NODE) { + if (nodeIsComment(node)) { return printComment(node.data, config); } - const type = - node.nodeType === FRAGMENT_NODE - ? `DocumentFragment` - : node.tagName.toLowerCase(); + const type = nodeIsFragment(node) + ? `DocumentFragment` + : node.tagName.toLowerCase(); if (++depth > config.maxDepth) { return printElementAsLeaf(type, config); @@ -96,8 +76,20 @@ export const serialize = ( return printElement( type, printProps( - Array.prototype.map.call(node.attributes || [], keysMapper).sort(), - Array.prototype.reduce.call(node.attributes || [], propsReducer, {}), + nodeIsFragment(node) + ? [] + : Array.from(node.attributes) + .map(attr => attr.name) + .sort(), + nodeIsFragment(node) + ? [] + : Array.from(node.attributes).reduce( + (props, attribute) => { + props[attribute.name] = attribute.value; + return props; + }, + {} as any, + ), config, indentation + config.indent, depth, @@ -117,4 +109,6 @@ export const serialize = ( ); }; -export default ({serialize, test}: NewPlugin); +const plugin: NewPlugin = {serialize, test}; + +export default plugin; diff --git a/packages/pretty-format/src/plugins/Immutable.js b/packages/pretty-format/src/plugins/Immutable.ts similarity index 94% rename from packages/pretty-format/src/plugins/Immutable.js rename to packages/pretty-format/src/plugins/Immutable.ts index 3814b7d832a0..85a415731a49 100644 --- a/packages/pretty-format/src/plugins/Immutable.js +++ b/packages/pretty-format/src/plugins/Immutable.ts @@ -3,11 +3,9 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Config, Printer, NewPlugin, Refs} from 'types/PrettyFormat'; +import {Config, Printer, NewPlugin, Refs} from '../types'; import {printIteratorEntries, printIteratorValues} from '../collections'; // SENTINEL constants are from https://github.com/facebook/immutable-js @@ -21,8 +19,8 @@ const IS_SEQ_SENTINEL = '@@__IMMUTABLE_SEQ__@@'; const IS_SET_SENTINEL = '@@__IMMUTABLE_SET__@@'; const IS_STACK_SENTINEL = '@@__IMMUTABLE_STACK__@@'; -const getImmutableName = name => 'Immutable.' + name; -const printAsLeaf = name => '[' + name + ']'; +const getImmutableName = (name: string) => 'Immutable.' + name; +const printAsLeaf = (name: string) => '[' + name + ']'; const SPACE = ' '; const LAZY = '…'; // Seq is lazy if it calls a method like filter @@ -52,7 +50,7 @@ const printImmutableEntries = ( // Record has an entries method because it is a collection in immutable v3. // Return an iterator for Immutable Record from version v3 or v4. -const getRecordEntries = val => { +const getRecordEntries = (val: any) => { let i = 0; return { next() { @@ -239,4 +237,6 @@ export const test = (val: any) => val && (val[IS_ITERABLE_SENTINEL] === true || val[IS_RECORD_SENTINEL] === true); -export default ({serialize, test}: NewPlugin); +const plugin: NewPlugin = {serialize, test}; + +export default plugin; diff --git a/packages/pretty-format/src/plugins/ReactElement.js b/packages/pretty-format/src/plugins/ReactElement.ts similarity index 90% rename from packages/pretty-format/src/plugins/ReactElement.js rename to packages/pretty-format/src/plugins/ReactElement.ts index eef452ce9525..aa05d4ebf72e 100644 --- a/packages/pretty-format/src/plugins/ReactElement.js +++ b/packages/pretty-format/src/plugins/ReactElement.ts @@ -3,11 +3,9 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Config, NewPlugin, Printer, Refs} from 'types/PrettyFormat'; +import {Config, NewPlugin, Printer, Refs} from '../types'; import { printChildren, @@ -24,7 +22,7 @@ const contextSymbol = Symbol.for('react.context'); // Given element.props.children, or subtree during recursive traversal, // return flattened array of children. -const getChildren = (arg, children = []) => { +const getChildren = (arg: any[], children = []) => { if (Array.isArray(arg)) { arg.forEach(item => { getChildren(item, children); @@ -35,7 +33,7 @@ const getChildren = (arg, children = []) => { return children; }; -const getType = element => { +const getType = (element: any) => { const type = element.type; if (typeof type === 'string') { return type; @@ -66,7 +64,7 @@ const getType = element => { return 'UNDEFINED'; }; -const getPropKeys = element => { +const getPropKeys = (element: any) => { const {props} = element; return Object.keys(props) @@ -75,7 +73,7 @@ const getPropKeys = element => { }; export const serialize = ( - element: React$Element, + element: any, config: Config, indentation: string, depth: number, @@ -109,4 +107,6 @@ export const serialize = ( export const test = (val: any) => val && val.$$typeof === elementSymbol; -export default ({serialize, test}: NewPlugin); +const plugin: NewPlugin = {serialize, test}; + +export default plugin; diff --git a/packages/pretty-format/src/plugins/ReactTestComponent.js b/packages/pretty-format/src/plugins/ReactTestComponent.ts similarity index 73% rename from packages/pretty-format/src/plugins/ReactTestComponent.js rename to packages/pretty-format/src/plugins/ReactTestComponent.ts index 159602bd79b8..c380fbfc3e81 100644 --- a/packages/pretty-format/src/plugins/ReactTestComponent.js +++ b/packages/pretty-format/src/plugins/ReactTestComponent.ts @@ -3,17 +3,19 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type { - Config, - Printer, - NewPlugin, - ReactTestObject, - Refs, -} from 'types/PrettyFormat'; +import {Config, Printer, NewPlugin, Refs} from '../types'; + +export type ReactTestObject = { + $$typeof: Symbol; + type: string; + props?: Object; + children?: null | Array; +}; + +// Child can be `number` in Stack renderer but not in Fiber renderer. +type ReactTestChild = ReactTestObject | string | number; import { printChildren, @@ -24,12 +26,12 @@ import { const testSymbol = Symbol.for('react.test.json'); -const getPropKeys = object => { +const getPropKeys = (object: ReactTestObject) => { const {props} = object; return props ? Object.keys(props) - .filter(key => props[key] !== undefined) + .filter(key => (props as any)[key] !== undefined) .sort() : []; }; @@ -73,4 +75,6 @@ export const serialize = ( export const test = (val: any) => val && val.$$typeof === testSymbol; -export default ({serialize, test}: NewPlugin); +const plugin: NewPlugin = {serialize, test}; + +export default plugin; diff --git a/packages/pretty-format/src/plugins/lib/escapeHTML.js b/packages/pretty-format/src/plugins/lib/escapeHTML.ts similarity index 96% rename from packages/pretty-format/src/plugins/lib/escapeHTML.js rename to packages/pretty-format/src/plugins/lib/escapeHTML.ts index 2fd80a074e32..e4c17104269e 100644 --- a/packages/pretty-format/src/plugins/lib/escapeHTML.js +++ b/packages/pretty-format/src/plugins/lib/escapeHTML.ts @@ -3,8 +3,6 @@ * * 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 escapeHTML(str: string): string { diff --git a/packages/pretty-format/src/plugins/lib/markup.js b/packages/pretty-format/src/plugins/lib/markup.ts similarity index 97% rename from packages/pretty-format/src/plugins/lib/markup.js rename to packages/pretty-format/src/plugins/lib/markup.ts index ee346d73c6ef..c5ab6a786091 100644 --- a/packages/pretty-format/src/plugins/lib/markup.js +++ b/packages/pretty-format/src/plugins/lib/markup.ts @@ -3,18 +3,16 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Config, Printer, Refs} from 'types/PrettyFormat'; +import {Config, Printer, Refs} from '../../types'; import escapeHTML from './escapeHTML'; // Return empty string if keys is empty. export const printProps = ( keys: Array, - props: Object, + props: any, config: Config, indentation: string, depth: number, diff --git a/packages/pretty-format/src/types.ts b/packages/pretty-format/src/types.ts new file mode 100644 index 000000000000..89d06d5bd418 --- /dev/null +++ b/packages/pretty-format/src/types.ts @@ -0,0 +1,117 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +export type Colors = { + comment: {close: string; open: string}; + content: {close: string; open: string}; + prop: {close: string; open: string}; + tag: {close: string; open: string}; + value: {close: string; open: string}; +}; +type Indent = (arg0: string) => string; +export type Refs = Array; +type Print = (arg0: any) => string; + +export type Theme = { + comment: string; + content: string; + prop: string; + tag: string; + value: string; +}; + +type ThemeReceived = { + comment?: string; + content?: string; + prop?: string; + tag?: string; + value?: string; +}; + +export type Options = { + callToJSON: boolean; + escapeRegex: boolean; + escapeString: boolean; + highlight: boolean; + indent: number; + maxDepth: number; + min: boolean; + plugins: Plugins; + printFunctionName: boolean; + theme: Theme; +}; + +export type OptionsReceived = { + callToJSON?: boolean; + escapeRegex?: boolean; + escapeString?: boolean; + highlight?: boolean; + indent?: number; + maxDepth?: number; + min?: boolean; + plugins?: Plugins; + printFunctionName?: boolean; + theme?: ThemeReceived; +}; + +export type Config = { + callToJSON: boolean; + colors: Colors; + escapeRegex: boolean; + escapeString: boolean; + indent: string; + maxDepth: number; + min: boolean; + plugins: Plugins; + printFunctionName: boolean; + spacingInner: string; + spacingOuter: string; +}; + +export type Printer = ( + val: any, + config: Config, + indentation: string, + depth: number, + refs: Refs, + hasCalledToJSON?: boolean, +) => string; + +type Test = (arg0: any) => boolean; + +export type NewPlugin = { + serialize: ( + val: any, + config: Config, + indentation: string, + depth: number, + refs: Refs, + printer: Printer, + ) => string; + test: Test; +}; + +type PluginOptions = { + edgeSpacing: string; + min: boolean; + spacing: string; +}; + +type OldPlugin = { + print: ( + val: any, + print: Print, + indent: Indent, + options: PluginOptions, + colors: Colors, + ) => string; + test: Test; +}; + +export type Plugin = NewPlugin | OldPlugin; + +export type Plugins = Array; diff --git a/packages/pretty-format/tsconfig.json b/packages/pretty-format/tsconfig.json new file mode 100644 index 000000000000..7bb06bce6d20 --- /dev/null +++ b/packages/pretty-format/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + } +} diff --git a/scripts/build.js b/scripts/build.js index 78d67bdbec5f..9afc50653ad6 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -196,9 +196,7 @@ function compileTypes(packages) { fs.existsSync(path.resolve(p, 'tsconfig.json')) ); - if (packageWithTs.length > 0) { - execa.sync('tsc', ['-b', ...packageWithTs]); - } + execa.sync('tsc', ['-b', ...packageWithTs]); } if (files.length) { diff --git a/scripts/watch.js b/scripts/watch.js index b40d670643b5..d2ab88c9791a 100644 --- a/scripts/watch.js +++ b/scripts/watch.js @@ -61,9 +61,7 @@ const packageWithTs = packages.filter(p => fs.existsSync(path.resolve(p, 'tsconfig.json')) ); -if (packageWithTs.length > 0) { - execa('tsc', ['-b', ...packageWithTs, '--watch']); -} +execa('tsc', ['-b', ...packageWithTs, '--watch']); setInterval(() => { const files = Array.from(filesToBuild.keys()); diff --git a/yarn.lock b/yarn.lock index ffc57a710145..6b1c4ffc6978 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1640,6 +1640,11 @@ resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.12.tgz#7e0ced251fa94c3bc2d1023d4b84b2992fa06376" integrity sha512-/kQvbVzdEpOq4tEWT79yAHSM4nH4xMlhJv2GrLVQt4Qmo8yYsPdioBM1QpN/2GX1wkfMnyXvdoftvLUr0LBj7Q== +"@types/jest@^24.0.0": + version "24.0.0" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.0.tgz#848492026c327b3548d92be0352a545c36a21e8a" + integrity sha512-kOafJnUTnMd7/OfEO/x3I47EHswNjn+dbz9qk3mtonr1RvKT+1FGVxnxAx08I9K8Tl7j9hpoJRE7OCf+t10fng== + "@types/jsdom@^11.12.0": version "11.12.0" resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-11.12.0.tgz#00ddc6f0a1b04c2f5ff6fb23eb59360ca65f12ae" @@ -1703,11 +1708,31 @@ resolved "https://registry.yarnpkg.com/@types/prompts/-/prompts-1.2.0.tgz#891e73f735ad5e82e8adae3a99424128e105fb62" integrity sha512-7JXpT2rSd4hqd2oBWU1wfEW6x6gX+qPH+gLzGEx+My3wcb67K9Rc02xNQRVn67phusmXm5Yqn4oTP2OW1G5zdQ== +"@types/prop-types@*": + version "15.5.8" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.8.tgz#8ae4e0ea205fe95c3901a5a1df7f66495e3a56ce" + integrity sha512-3AQoUxQcQtLHsK25wtTWIoIpgYjH3vSDroZOUr7PpCHw/jLY1RB9z9E8dBT/OSmwStVgkRNvdh+ZHNiomRieaw== + "@types/q@^1.5.1": version "1.5.1" resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.1.tgz#48fd98c1561fe718b61733daed46ff115b496e18" integrity sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA== +"@types/react-test-renderer@*": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.0.3.tgz#cce5c983d66cc5c3582e7c2f44b274ab635a8acc" + integrity sha512-NWOAxVQeJxpXuNKgw83Hah0nquiw1nUexM9qY/Hk3a+XhZwgMtaa6GLA9E1TKMT75Odb3/KE/jiBO4enTuEJjQ== + dependencies: + "@types/react" "*" + +"@types/react@*": + version "16.8.1" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.1.tgz#b431b104ecc6febda170b718caa9f50be66a6750" + integrity sha512-tD1ETKJcuhANOejRc/p7OgQ16DKnbGi0M3LccelKlPnUCDp2a5koVxZFoRN9HN+A+m84HB5VGN7I+r3nNhS3PA== + dependencies: + "@types/prop-types" "*" + csstype "^2.2.0" + "@types/resolve@*": version "0.0.8" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" @@ -4115,6 +4140,11 @@ cssstyle@^1.0.0: dependencies: cssom "0.3.x" +csstype@^2.2.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.2.tgz#3043d5e065454579afc7478a18de41909c8a2f01" + integrity sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow== + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" From dbcfbb081ef422029d7dc4b2607b4e5c5381f167 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 6 Feb 2019 22:12:13 +0100 Subject: [PATCH 017/107] chore move @types/jest dep to the root of the project --- package.json | 1 + packages/pretty-format/package.json | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6c0042975e2b..1e05d1cbbe5a 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@types/babel__core": "^7.0.0", "@types/babel__generator": "^7.0.0", "@types/babel__template": "^7.0.0", + "@types/jest": "^24.0.0", "@typescript-eslint/eslint-plugin": "^1.2.0", "@typescript-eslint/parser": "^1.2.0", "ansi-regex": "^4.0.0", diff --git a/packages/pretty-format/package.json b/packages/pretty-format/package.json index c90ad0715fcd..e2eb759c6e2c 100644 --- a/packages/pretty-format/package.json +++ b/packages/pretty-format/package.json @@ -19,7 +19,6 @@ "devDependencies": { "@types/ansi-regex": "^4.0.0", "@types/ansi-styles": "^3.2.1", - "@types/jest": "^24.0.0", "@types/react": "*", "@types/react-test-renderer": "*", "immutable": "4.0.0-rc.9", From 9425c0d29d879d26783012c0a0d0dd478e8ef5bf Mon Sep 17 00:00:00 2001 From: Lorenzo Rapetti Date: Wed, 6 Feb 2019 22:24:42 +0100 Subject: [PATCH 018/107] Migrate diff-sequences to Typescript (#7820) --- CHANGELOG.md | 1 + packages/diff-sequences/package.json | 1 + ...{index.test.js.snap => index.test.ts.snap} | 0 .../{index.test.js => index.test.ts} | 7 ++-- .../diff-sequences/src/{index.js => index.ts} | 37 +++++++++---------- packages/diff-sequences/tsconfig.json | 7 ++++ 6 files changed, 30 insertions(+), 23 deletions(-) rename packages/diff-sequences/src/__tests__/__snapshots__/{index.test.js.snap => index.test.ts.snap} (100%) rename packages/diff-sequences/src/__tests__/{index.test.js => index.test.ts} (99%) rename packages/diff-sequences/src/{index.js => index.ts} (97%) create mode 100644 packages/diff-sequences/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index fa44400ddb21..6fd0b87aefb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - `[*]`: Setup building, linting and testing of TypeScript ([#7808](https://github.com/facebook/jest/pull/7808)) - `[pretty-format]`: Migrate to TypeScript ([#7809](https://github.com/facebook/jest/pull/7809)) +- `[diff-sequences]`: Migrate to Typescript ([#7820](https://github.com/facebook/jest/pull/7820)) ### Performance diff --git a/packages/diff-sequences/package.json b/packages/diff-sequences/package.json index 176da42680e1..33fc60579975 100644 --- a/packages/diff-sequences/package.json +++ b/packages/diff-sequences/package.json @@ -19,6 +19,7 @@ "node": ">= 6" }, "main": "build/index.js", + "types": "build/index.d.ts", "scripts": { "perf": "node --expose-gc perf/index.js" }, diff --git a/packages/diff-sequences/src/__tests__/__snapshots__/index.test.js.snap b/packages/diff-sequences/src/__tests__/__snapshots__/index.test.ts.snap similarity index 100% rename from packages/diff-sequences/src/__tests__/__snapshots__/index.test.js.snap rename to packages/diff-sequences/src/__tests__/__snapshots__/index.test.ts.snap diff --git a/packages/diff-sequences/src/__tests__/index.test.js b/packages/diff-sequences/src/__tests__/index.test.ts similarity index 99% rename from packages/diff-sequences/src/__tests__/index.test.js rename to packages/diff-sequences/src/__tests__/index.test.ts index 7362a8de6b00..6652c756b7fb 100644 --- a/packages/diff-sequences/src/__tests__/index.test.js +++ b/packages/diff-sequences/src/__tests__/index.test.ts @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ import diff from '../'; @@ -16,7 +15,7 @@ describe('invalid arg', () => { describe('length', () => { test('is not a number', () => { expect(() => { - diff(('0': any), 0, isCommon, foundSubsequence); + diff('0' as any, 0, isCommon, foundSubsequence); }).toThrow(/aLength/); }); test('Infinity is not a safe integer', () => { @@ -50,12 +49,12 @@ describe('invalid arg', () => { describe('callback', () => { test('null is not a function', () => { expect(() => { - diff(0, 0, (null: any), foundSubsequence); + diff(0, 0, null as any, foundSubsequence); }).toThrow(/isCommon/); }); test('undefined is not a function', () => { expect(() => { - diff(0, 0, isCommon, (undefined: any)); + diff(0, 0, isCommon, undefined as any); }).toThrow(/foundSubsequence/); }); }); diff --git a/packages/diff-sequences/src/index.js b/packages/diff-sequences/src/index.ts similarity index 97% rename from packages/diff-sequences/src/index.js rename to packages/diff-sequences/src/index.ts index 9d7d4bb3488c..3a676e0575dc 100644 --- a/packages/diff-sequences/src/index.js +++ b/packages/diff-sequences/src/index.ts @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ // This diff-sequences package implements the linear space variation in @@ -66,10 +65,10 @@ type FoundSubsequence = ( ) => void; // Either original functions or wrapped to swap indexes if graph is transposed. -type Callbacks = {| - foundSubsequence: FoundSubsequence, - isCommon: IsCommon, -|}; +type Callbacks = { + foundSubsequence: FoundSubsequence; + isCommon: IsCommon; +}; // Indexes in sequence a of last point of forward or reverse paths in graph. // Myers algorithm indexes by diagonal k which for negative is bad deopt in V8. @@ -81,25 +80,25 @@ type Indexes = Array; // Division of index intervals in sequences a and b at the middle change. // Invariant: intervals do not have common items at the start or end. -type Division = {| +type Division = { // The end of interval preceding division is open like array slice method. - nChangePreceding: number, // number of change items - aEndPreceding: number, - bEndPreceding: number, + nChangePreceding: number; // number of change items + aEndPreceding: number; + bEndPreceding: number; - nCommonPreceding: number, // 0 if no common items preceding middle change - aCommonPreceding: number, // ignore prop value if nCommonPreceding === 0 - bCommonPreceding: number, // ignore prop value if nCommonPreceding === 0 + nCommonPreceding: number; // 0 if no common items preceding middle change + aCommonPreceding: number; // ignore prop value if nCommonPreceding === 0 + bCommonPreceding: number; // ignore prop value if nCommonPreceding === 0 - nCommonFollowing: number, // 0 if no common items following middle change - aCommonFollowing: number, // ignore prop value if nCommonFollowing === 0 - bCommonFollowing: number, // ignore prop value if nCommonFollowing === 0 + nCommonFollowing: number; // 0 if no common items following middle change + aCommonFollowing: number; // ignore prop value if nCommonFollowing === 0 + bCommonFollowing: number; // ignore prop value if nCommonFollowing === 0 // The start of interval following division is closed like array slice method. - nChangeFollowing: number, // number of change items - aStartFollowing: number, - bStartFollowing: number, -|}; + nChangeFollowing: number; // number of change items + aStartFollowing: number; + bStartFollowing: number; +}; const pkg = 'diff-sequences'; // for error messages const NOT_YET_SET = 0; // small int instead of undefined to avoid deopt in V8 diff --git a/packages/diff-sequences/tsconfig.json b/packages/diff-sequences/tsconfig.json new file mode 100644 index 000000000000..7bb06bce6d20 --- /dev/null +++ b/packages/diff-sequences/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + } +} From f37360d9c9849f1393496e981b160fecff5f5c46 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 6 Feb 2019 22:25:48 +0100 Subject: [PATCH 019/107] Migrate jest-get-type to TypeScript (#7818) --- CHANGELOG.md | 1 + packages/jest-get-type/package.json | 1 + .../{index.test.js => index.test.ts} | 4 +-- .../jest-get-type/src/{index.js => index.ts} | 29 +++++++++---------- packages/jest-get-type/tsconfig.json | 7 +++++ 5 files changed, 23 insertions(+), 19 deletions(-) rename packages/jest-get-type/src/__tests__/{index.test.js => index.test.ts} (95%) rename packages/jest-get-type/src/{index.js => index.ts} (71%) create mode 100644 packages/jest-get-type/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fd0b87aefb8..8d01ec2a25d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - `[*]`: Setup building, linting and testing of TypeScript ([#7808](https://github.com/facebook/jest/pull/7808)) - `[pretty-format]`: Migrate to TypeScript ([#7809](https://github.com/facebook/jest/pull/7809)) - `[diff-sequences]`: Migrate to Typescript ([#7820](https://github.com/facebook/jest/pull/7820)) +- `[jest-get-type]`: Migrate to TypeScript ([#7818](https://github.com/facebook/jest/pull/7818)) ### Performance diff --git a/packages/jest-get-type/package.json b/packages/jest-get-type/package.json index 422b6d8f2ab8..2b055dd2d218 100644 --- a/packages/jest-get-type/package.json +++ b/packages/jest-get-type/package.json @@ -12,5 +12,6 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" } diff --git a/packages/jest-get-type/src/__tests__/index.test.js b/packages/jest-get-type/src/__tests__/index.test.ts similarity index 95% rename from packages/jest-get-type/src/__tests__/index.test.js rename to packages/jest-get-type/src/__tests__/index.test.ts index 8390b9a4bfd6..8a34a0cb947d 100644 --- a/packages/jest-get-type/src/__tests__/index.test.js +++ b/packages/jest-get-type/src/__tests__/index.test.ts @@ -6,9 +6,7 @@ * */ -'use strict'; - -const getType = require('..'); +import getType from '..'; describe('.getType()', () => { test('null', () => expect(getType(null)).toBe('null')); diff --git a/packages/jest-get-type/src/index.js b/packages/jest-get-type/src/index.ts similarity index 71% rename from packages/jest-get-type/src/index.js rename to packages/jest-get-type/src/index.ts index 3a444dd6f84d..dd15b71be771 100644 --- a/packages/jest-get-type/src/index.js +++ b/packages/jest-get-type/src/index.ts @@ -3,13 +3,9 @@ * * 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'; - -export type ValueType = +type ValueType = | 'array' | 'boolean' | 'function' @@ -26,7 +22,7 @@ export type ValueType = // get the type of a value with handling the edge cases like `typeof []` // and `typeof null` -const getType = (value: any): ValueType => { +const getType = (value: unknown): ValueType => { if (value === undefined) { return 'undefined'; } else if (value === null) { @@ -42,17 +38,18 @@ const getType = (value: any): ValueType => { } else if (typeof value === 'string') { return 'string'; } else if (typeof value === 'object') { - if (value.constructor === RegExp) { - return 'regexp'; - } else if (value.constructor === Map) { - return 'map'; - } else if (value.constructor === Set) { - return 'set'; - } else if (value.constructor === Date) { - return 'date'; + if (value != null) { + if (value.constructor === RegExp) { + return 'regexp'; + } else if (value.constructor === Map) { + return 'map'; + } else if (value.constructor === Set) { + return 'set'; + } else if (value.constructor === Date) { + return 'date'; + } } return 'object'; - // $FlowFixMe https://github.com/facebook/flow/issues/1015 } else if (typeof value === 'symbol') { return 'symbol'; } @@ -60,4 +57,4 @@ const getType = (value: any): ValueType => { throw new Error(`value of unknown type: ${value}`); }; -module.exports = getType; +export = getType; diff --git a/packages/jest-get-type/tsconfig.json b/packages/jest-get-type/tsconfig.json new file mode 100644 index 000000000000..7bb06bce6d20 --- /dev/null +++ b/packages/jest-get-type/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + } +} From 6acbd0128d39d8127ec896c98ec1906150e10bb4 Mon Sep 17 00:00:00 2001 From: Lorenzo Rapetti Date: Wed, 6 Feb 2019 23:55:54 +0100 Subject: [PATCH 020/107] Migrate jest-regex-util to Typescript (#7822) --- CHANGELOG.md | 1 + packages/jest-regex-util/package.json | 1 + .../src/__tests__/{index.test.js => index.test.ts} | 8 ++++---- packages/jest-regex-util/src/{index.js => index.ts} | 3 +-- packages/jest-regex-util/tsconfig.json | 7 +++++++ 5 files changed, 14 insertions(+), 6 deletions(-) rename packages/jest-regex-util/src/__tests__/{index.test.js => index.test.ts} (90%) rename packages/jest-regex-util/src/{index.js => index.ts} (91%) create mode 100644 packages/jest-regex-util/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d01ec2a25d4..db7f53290797 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - `[pretty-format]`: Migrate to TypeScript ([#7809](https://github.com/facebook/jest/pull/7809)) - `[diff-sequences]`: Migrate to Typescript ([#7820](https://github.com/facebook/jest/pull/7820)) - `[jest-get-type]`: Migrate to TypeScript ([#7818](https://github.com/facebook/jest/pull/7818)) +- `[jest-regex-util]`: Migrate to TypeScript ([#7822](https://github.com/facebook/jest/pull/7822)) ### Performance diff --git a/packages/jest-regex-util/package.json b/packages/jest-regex-util/package.json index 56618fc89c5a..446f7ec5fbab 100644 --- a/packages/jest-regex-util/package.json +++ b/packages/jest-regex-util/package.json @@ -11,5 +11,6 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" } diff --git a/packages/jest-regex-util/src/__tests__/index.test.js b/packages/jest-regex-util/src/__tests__/index.test.ts similarity index 90% rename from packages/jest-regex-util/src/__tests__/index.test.js rename to packages/jest-regex-util/src/__tests__/index.test.ts index 4efcbf465df9..828d6601c194 100644 --- a/packages/jest-regex-util/src/__tests__/index.test.js +++ b/packages/jest-regex-util/src/__tests__/index.test.ts @@ -2,21 +2,21 @@ jest.mock('path'); -import {replacePathSepForRegex} from '../index'; import path from 'path'; +import {replacePathSepForRegex} from '../index'; describe('replacePathSepForRegex()', () => { describe('posix', () => { - beforeEach(() => (path.sep = '/')); + beforeEach(() => ((path as any).sep = '/')); it('should return the path', () => { const expected = {}; - expect(replacePathSepForRegex(expected)).toBe(expected); + expect(replacePathSepForRegex(expected as any)).toBe(expected); }); }); describe('win32', () => { - beforeEach(() => (path.sep = '\\')); + beforeEach(() => ((path as any).sep = '\\')); it('should replace POSIX path separators', () => { expect(replacePathSepForRegex('a/b/c')).toBe('a\\\\b\\\\c'); diff --git a/packages/jest-regex-util/src/index.js b/packages/jest-regex-util/src/index.ts similarity index 91% rename from packages/jest-regex-util/src/index.js rename to packages/jest-regex-util/src/index.ts index 2564df4eae41..a33228598959 100644 --- a/packages/jest-regex-util/src/index.js +++ b/packages/jest-regex-util/src/index.ts @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ import path from 'path'; @@ -25,7 +24,7 @@ export const replacePathSepForRegex = (string: string) => { if (path.sep === '\\') { return string.replace( /(\/|(.)?\\(?![[\]{}()*+?.^$|\\]))/g, - (_match, p1, p2) => (p2 && p2 !== '\\' ? p2 + '\\\\' : '\\\\'), + (_match, _, p2) => (p2 && p2 !== '\\' ? p2 + '\\\\' : '\\\\'), ); } return string; diff --git a/packages/jest-regex-util/tsconfig.json b/packages/jest-regex-util/tsconfig.json new file mode 100644 index 000000000000..7bb06bce6d20 --- /dev/null +++ b/packages/jest-regex-util/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + } +} From a417f983b2620870819beaee441b122a2bde581b Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 7 Feb 2019 11:43:40 +0100 Subject: [PATCH 021/107] chore: migrate jest-diff to TypeScript (#7824) --- CHANGELOG.md | 1 + packages/diff-sequences/src/index.ts | 2 +- .../jest-circus/src/formatNodeAssertErrors.js | 3 +- packages/jest-diff/package.json | 5 ++ .../{diff.test.js.snap => diff.test.ts.snap} | 0 .../__tests__/{diff.test.js => diff.test.ts} | 11 ++-- .../src/{constants.js => constants.ts} | 2 - .../src/{diffStrings.js => diffStrings.ts} | 51 ++++++++++--------- packages/jest-diff/src/{index.js => index.ts} | 17 +++---- packages/jest-diff/src/types.ts | 13 +++++ packages/jest-diff/tsconfig.json | 12 +++++ .../src/assertionErrorMessage.js | 3 +- yarn.lock | 5 ++ 13 files changed, 80 insertions(+), 45 deletions(-) rename packages/jest-diff/src/__tests__/__snapshots__/{diff.test.js.snap => diff.test.ts.snap} (100%) rename packages/jest-diff/src/__tests__/{diff.test.js => diff.test.ts} (99%) rename packages/jest-diff/src/{constants.js => constants.ts} (97%) rename packages/jest-diff/src/{diffStrings.js => diffStrings.ts} (92%) rename packages/jest-diff/src/{index.js => index.ts} (92%) create mode 100644 packages/jest-diff/src/types.ts create mode 100644 packages/jest-diff/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index db7f53290797..eed385502916 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - `[diff-sequences]`: Migrate to Typescript ([#7820](https://github.com/facebook/jest/pull/7820)) - `[jest-get-type]`: Migrate to TypeScript ([#7818](https://github.com/facebook/jest/pull/7818)) - `[jest-regex-util]`: Migrate to TypeScript ([#7822](https://github.com/facebook/jest/pull/7822)) +- `[jest-diff]`: Migrate to TypeScript ([#7824](https://github.com/facebook/jest/pull/7824)) ### Performance diff --git a/packages/diff-sequences/src/index.ts b/packages/diff-sequences/src/index.ts index 3a676e0575dc..5dbbc4c6bd65 100644 --- a/packages/diff-sequences/src/index.ts +++ b/packages/diff-sequences/src/index.ts @@ -65,7 +65,7 @@ type FoundSubsequence = ( ) => void; // Either original functions or wrapped to swap indexes if graph is transposed. -type Callbacks = { +export type Callbacks = { foundSubsequence: FoundSubsequence; isCommon: IsCommon; }; diff --git a/packages/jest-circus/src/formatNodeAssertErrors.js b/packages/jest-circus/src/formatNodeAssertErrors.js index 6434eee318a5..e7f685f852f7 100644 --- a/packages/jest-circus/src/formatNodeAssertErrors.js +++ b/packages/jest-circus/src/formatNodeAssertErrors.js @@ -7,7 +7,8 @@ * @flow strict-local */ -import type {DiffOptions} from 'jest-diff/src/diffStrings'; +// $FlowFixMe: Converted to TS. It's also not exported, but should be imported from `matcher-utils` +import type {DiffOptions} from 'jest-diff'; import type {Event, State} from 'types/Circus'; import {diff, printExpected, printReceived} from 'jest-matcher-utils'; diff --git a/packages/jest-diff/package.json b/packages/jest-diff/package.json index b705af90783d..08db396fd77a 100644 --- a/packages/jest-diff/package.json +++ b/packages/jest-diff/package.json @@ -8,12 +8,17 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { "chalk": "^2.0.1", "diff-sequences": "^24.0.0", "jest-get-type": "^24.0.0", "pretty-format": "^24.0.0" }, + "devDependencies": { + "@types/strip-ansi": "^3.0.0", + "strip-ansi": "^5.0.0" + }, "engines": { "node": ">= 6" }, diff --git a/packages/jest-diff/src/__tests__/__snapshots__/diff.test.js.snap b/packages/jest-diff/src/__tests__/__snapshots__/diff.test.ts.snap similarity index 100% rename from packages/jest-diff/src/__tests__/__snapshots__/diff.test.js.snap rename to packages/jest-diff/src/__tests__/__snapshots__/diff.test.ts.snap diff --git a/packages/jest-diff/src/__tests__/diff.test.js b/packages/jest-diff/src/__tests__/diff.test.ts similarity index 99% rename from packages/jest-diff/src/__tests__/diff.test.js rename to packages/jest-diff/src/__tests__/diff.test.ts index 50df67c274f7..090813e61f70 100644 --- a/packages/jest-diff/src/__tests__/diff.test.js +++ b/packages/jest-diff/src/__tests__/diff.test.ts @@ -3,16 +3,17 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -const stripAnsi = require('strip-ansi'); -const diff = require('../'); +import stripAnsi from 'strip-ansi'; + +import diff from '../'; +import {DiffOptions} from '../types'; const NO_DIFF_MESSAGE = 'Compared values have no visual difference.'; -const stripped = (a, b, options) => stripAnsi(diff(a, b, options)); +const stripped = (a: unknown, b: unknown, options?: DiffOptions) => + stripAnsi(diff(a, b, options) || ''); const unexpanded = {expand: false}; const expanded = {expand: true}; diff --git a/packages/jest-diff/src/constants.js b/packages/jest-diff/src/constants.ts similarity index 97% rename from packages/jest-diff/src/constants.js rename to packages/jest-diff/src/constants.ts index 83a4b905929a..9f3f45e7c63c 100644 --- a/packages/jest-diff/src/constants.js +++ b/packages/jest-diff/src/constants.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import chalk from 'chalk'; diff --git a/packages/jest-diff/src/diffStrings.js b/packages/jest-diff/src/diffStrings.ts similarity index 92% rename from packages/jest-diff/src/diffStrings.js rename to packages/jest-diff/src/diffStrings.ts index 474278d63fe4..b71259c40485 100644 --- a/packages/jest-diff/src/diffStrings.js +++ b/packages/jest-diff/src/diffStrings.ts @@ -3,28 +3,19 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import chalk from 'chalk'; -import type {Chalk} from 'chalk'; -import diff from 'diff-sequences'; -import {NO_DIFF_MESSAGE} from './constants.js'; +import chalk, {Chalk} from 'chalk'; +import diff, {Callbacks} from 'diff-sequences'; +import {NO_DIFF_MESSAGE} from './constants'; +import {DiffOptions} from './types'; const DIFF_CONTEXT_DEFAULT = 5; -export type DiffOptions = {| - aAnnotation?: string, - bAnnotation?: string, - expand?: boolean, - contextLines?: number, -|}; - -type Original = {| - a: string, - b: string, -|}; +type Original = { + a: string; + b: string; +}; const fgPatchMark = chalk.yellow; const fgDelete = chalk.green; @@ -52,7 +43,7 @@ type Highlight = (line: string, bgColor: Chalk) => string; const getHighlightSpaces = (bothEdges: boolean): Highlight => bothEdges ? highlightLeadingTrailingSpaces : highlightTrailingSpaces; -const getAnnotation = (options: ?DiffOptions): string => +const getAnnotation = (options?: DiffOptions): string => fgDelete('- ' + ((options && options.aAnnotation) || 'Expected')) + '\n' + fgInsert('+ ' + ((options && options.bAnnotation) || 'Received')) + @@ -134,9 +125,10 @@ const diffExpand = ( aLinesIn: Array, bLinesIn: Array, ): string => { - const isCommon = (aIndex, bIndex) => aLinesUn[aIndex] === bLinesUn[bIndex]; + const isCommon: Callbacks['isCommon'] = (aIndex, bIndex) => + aLinesUn[aIndex] === bLinesUn[bIndex]; - const array = []; + const array: string[] = []; const put = (line: string) => { array.push(line); }; @@ -144,7 +136,11 @@ const diffExpand = ( let aStart = 0; let bStart = 0; - const foundSubsequence = (nCommon, aCommon, bCommon) => { + const foundSubsequence: Callbacks['foundSubsequence'] = ( + nCommon, + aCommon, + bCommon, + ) => { formatDelete(aStart, aCommon, aLinesUn, aLinesIn, put); formatInsert(bStart, bCommon, bLinesUn, bLinesIn, put); formatCommon(nCommon, aCommon, bCommon, aLinesIn, bLinesUn, bLinesIn, put); @@ -175,7 +171,7 @@ const createPatchMark = ( `@@ -${aStart + 1},${aEnd - aStart} +${bStart + 1},${bEnd - bStart} @@`, ); -const getContextLines = (options: ?DiffOptions): number => +const getContextLines = (options?: DiffOptions): number => options && typeof options.contextLines === 'number' && options.contextLines >= 0 @@ -193,7 +189,8 @@ const diffNoExpand = ( bLinesIn: Array, nContextLines: number, ): string => { - const isCommon = (aIndex, bIndex) => aLinesUn[aIndex] === bLinesUn[bIndex]; + const isCommon: Callbacks['isCommon'] = (aIndex, bIndex) => + aLinesUn[aIndex] === bLinesUn[bIndex]; let iPatchMark = 0; // index of placeholder line for patch mark const array = ['']; @@ -215,7 +212,11 @@ const diffNoExpand = ( // Given the number of items and starting indexes of each common subsequence, // format any preceding change lines, and then common context lines. - const foundSubsequence = (nCommon, aStartCommon, bStartCommon) => { + const foundSubsequence: Callbacks['foundSubsequence'] = ( + nCommon, + aStartCommon, + bStartCommon, + ) => { const aEndCommon = aStartCommon + nCommon; const bEndCommon = bStartCommon + nCommon; isAtEnd = aEndCommon === aLength && bEndCommon === bLength; @@ -294,7 +295,7 @@ const diffNoExpand = ( export default ( a: string, b: string, - options: ?DiffOptions, + options?: DiffOptions, original?: Original, ): string => { if (a === b) { diff --git a/packages/jest-diff/src/index.js b/packages/jest-diff/src/index.ts similarity index 92% rename from packages/jest-diff/src/index.js rename to packages/jest-diff/src/index.ts index a52fa1c1b524..176a8cbd5d38 100644 --- a/packages/jest-diff/src/index.js +++ b/packages/jest-diff/src/index.ts @@ -3,17 +3,14 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {DiffOptions} from './diffStrings'; - import prettyFormat from 'pretty-format'; import chalk from 'chalk'; import getType from 'jest-get-type'; import diffStrings from './diffStrings'; import {NO_DIFF_MESSAGE, SIMILAR_MESSAGE} from './constants'; +import {DiffOptions} from './types'; const { AsymmetricMatcher, @@ -45,7 +42,7 @@ const FALLBACK_FORMAT_OPTIONS_0 = {...FALLBACK_FORMAT_OPTIONS, indent: 0}; // Generate a string that will highlight the difference between two values // with green and red. (similar to how github does code diffing) -function diff(a: any, b: any, options: ?DiffOptions): ?string { +function diff(a: any, b: any, options?: DiffOptions): string | null { if (Object.is(a, b)) { return NO_DIFF_MESSAGE; } @@ -98,7 +95,7 @@ function diff(a: any, b: any, options: ?DiffOptions): ?string { function comparePrimitive( a: number | boolean, b: number | boolean, - options: ?DiffOptions, + options?: DiffOptions, ) { return diffStrings( prettyFormat(a, FORMAT_OPTIONS), @@ -107,15 +104,15 @@ function comparePrimitive( ); } -function sortMap(map) { +function sortMap(map: Map) { return new Map(Array.from(map.entries()).sort()); } -function sortSet(set) { +function sortSet(set: Set) { return new Set(Array.from(set.values()).sort()); } -function compareObjects(a: Object, b: Object, options: ?DiffOptions) { +function compareObjects(a: Object, b: Object, options?: DiffOptions) { let diffMessage; let hasThrown = false; @@ -153,4 +150,4 @@ function compareObjects(a: Object, b: Object, options: ?DiffOptions) { return diffMessage; } -module.exports = diff; +export = diff; diff --git a/packages/jest-diff/src/types.ts b/packages/jest-diff/src/types.ts new file mode 100644 index 000000000000..66edf604d0c0 --- /dev/null +++ b/packages/jest-diff/src/types.ts @@ -0,0 +1,13 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +export type DiffOptions = { + aAnnotation?: string; + bAnnotation?: string; + expand?: boolean; + contextLines?: number; +}; diff --git a/packages/jest-diff/tsconfig.json b/packages/jest-diff/tsconfig.json new file mode 100644 index 000000000000..15ec4c0eff44 --- /dev/null +++ b/packages/jest-diff/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [ + {"path": "../diff-sequences"}, + {"path": "../jest-get-type"}, + {"path": "../pretty-format"} + ] +} diff --git a/packages/jest-jasmine2/src/assertionErrorMessage.js b/packages/jest-jasmine2/src/assertionErrorMessage.js index 3e066ae8377d..dd83b4cee4a8 100644 --- a/packages/jest-jasmine2/src/assertionErrorMessage.js +++ b/packages/jest-jasmine2/src/assertionErrorMessage.js @@ -7,7 +7,8 @@ * @flow */ -import type {DiffOptions} from 'jest-diff/src/diffStrings'; +// TODO: Converted to TS. It's also not exported, but should be imported from `matcher-utils` +import type {DiffOptions} from 'jest-diff'; import {diff, printReceived, printExpected} from 'jest-matcher-utils'; import chalk from 'chalk'; diff --git a/yarn.lock b/yarn.lock index 6b1c4ffc6978..093b3ce1e7d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1782,6 +1782,11 @@ resolved "https://registry.yarnpkg.com/@types/string-length/-/string-length-2.0.0.tgz#358ce3ff2e8c2310270ee192ddd6b79b64fed7b2" integrity sha512-xFwWZpIWcLsrcVEybzxmxQM/26Snj1gqxmVrelC3We3Nub7q70RCtqTBVQ7nL+315fAcw4BGSFpxvMkhLApKmQ== +"@types/strip-ansi@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/strip-ansi/-/strip-ansi-3.0.0.tgz#9b63d453a6b54aa849182207711a08be8eea48ae" + integrity sha1-m2PUU6a1SqhJGCIHcRoIvo7qSK4= + "@types/strip-bom@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" From 1573eca1e5ccdc4382d8795c20f8a33ab79f1aea Mon Sep 17 00:00:00 2001 From: Andrew M Date: Thu, 7 Feb 2019 14:14:40 +0300 Subject: [PATCH 022/107] Migrate jest-leak-detector to TypeScript (#7825) --- CHANGELOG.md | 1 + packages/jest-leak-detector/package.json | 1 + .../{index.test.js.snap => index.test.ts.snap} | 0 .../src/__tests__/{index.test.js => index.test.ts} | 4 ++-- .../jest-leak-detector/src/{index.js => index.ts} | 14 +++++--------- packages/jest-leak-detector/tsconfig.json | 10 ++++++++++ 6 files changed, 19 insertions(+), 11 deletions(-) rename packages/jest-leak-detector/src/__tests__/__snapshots__/{index.test.js.snap => index.test.ts.snap} (100%) rename packages/jest-leak-detector/src/__tests__/{index.test.js => index.test.ts} (98%) rename packages/jest-leak-detector/src/{index.js => index.ts} (91%) create mode 100644 packages/jest-leak-detector/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index eed385502916..cef9079de837 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - `[jest-get-type]`: Migrate to TypeScript ([#7818](https://github.com/facebook/jest/pull/7818)) - `[jest-regex-util]`: Migrate to TypeScript ([#7822](https://github.com/facebook/jest/pull/7822)) - `[jest-diff]`: Migrate to TypeScript ([#7824](https://github.com/facebook/jest/pull/7824)) +- `[jest-leak-detector]`: Migrate to TypeScript ([#7825](https://github.com/facebook/jest/pull/7825)) ### Performance diff --git a/packages/jest-leak-detector/package.json b/packages/jest-leak-detector/package.json index 434f904dcf62..eaeb90409094 100644 --- a/packages/jest-leak-detector/package.json +++ b/packages/jest-leak-detector/package.json @@ -8,6 +8,7 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { "pretty-format": "^24.0.0" }, diff --git a/packages/jest-leak-detector/src/__tests__/__snapshots__/index.test.js.snap b/packages/jest-leak-detector/src/__tests__/__snapshots__/index.test.ts.snap similarity index 100% rename from packages/jest-leak-detector/src/__tests__/__snapshots__/index.test.js.snap rename to packages/jest-leak-detector/src/__tests__/__snapshots__/index.test.ts.snap diff --git a/packages/jest-leak-detector/src/__tests__/index.test.js b/packages/jest-leak-detector/src/__tests__/index.test.ts similarity index 98% rename from packages/jest-leak-detector/src/__tests__/index.test.js rename to packages/jest-leak-detector/src/__tests__/index.test.ts index b4499ec20d2e..188aaf4cf920 100644 --- a/packages/jest-leak-detector/src/__tests__/index.test.js +++ b/packages/jest-leak-detector/src/__tests__/index.test.ts @@ -73,8 +73,8 @@ it('tests different objects', () => { }); it('correctly checks more complex leaks', () => { - let ref1 = {}; - let ref2 = {}; + let ref1: any = {}; + let ref2: any = {}; // Create a circular dependency between ref1 and ref2. ref1.ref2 = ref2; diff --git a/packages/jest-leak-detector/src/index.js b/packages/jest-leak-detector/src/index.ts similarity index 91% rename from packages/jest-leak-detector/src/index.js rename to packages/jest-leak-detector/src/index.ts index d9e97788a406..007b907fad6e 100644 --- a/packages/jest-leak-detector/src/index.js +++ b/packages/jest-leak-detector/src/index.ts @@ -3,20 +3,16 @@ * * 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 prettyFormat from 'pretty-format'; import v8 from 'v8'; import vm from 'vm'; +import prettyFormat from 'pretty-format'; export default class { - _isReferenceBeingHeld: boolean; + private _isReferenceBeingHeld: boolean; - constructor(value: ?Object) { + constructor(value: unknown) { if (this._isPrimitive(value)) { throw new TypeError( [ @@ -55,7 +51,7 @@ export default class { return this._isReferenceBeingHeld; } - _runGarbageCollector() { + private _runGarbageCollector() { const isGarbageCollectorHidden = !global.gc; // GC is usually hidden, so we have to expose it before running. @@ -68,7 +64,7 @@ export default class { } } - _isPrimitive(value: any): boolean { + private _isPrimitive(value: unknown): boolean { return value !== Object(value); } } diff --git a/packages/jest-leak-detector/tsconfig.json b/packages/jest-leak-detector/tsconfig.json new file mode 100644 index 000000000000..3e70b10372b1 --- /dev/null +++ b/packages/jest-leak-detector/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [ + {"path": "../pretty-format"} + ] +} From 5c5599459db3ae4863687b349a34a703f22a9987 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 7 Feb 2019 12:17:32 +0100 Subject: [PATCH 023/107] chore: correct type errors in leak-detector unit test --- packages/jest-leak-detector/src/__tests__/index.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/jest-leak-detector/src/__tests__/index.test.ts b/packages/jest-leak-detector/src/__tests__/index.test.ts index 188aaf4cf920..a135b1637ad8 100644 --- a/packages/jest-leak-detector/src/__tests__/index.test.ts +++ b/packages/jest-leak-detector/src/__tests__/index.test.ts @@ -26,6 +26,7 @@ it('complains if the value is a primitive', () => { it('does not show the GC if hidden', () => { const detector = new LeakDetector({}); + // @ts-ignore: purposefully removed global.gc = undefined; detector.isLeaking(); expect(global.gc).not.toBeDefined(); @@ -40,7 +41,7 @@ it('does not hide the GC if visible', () => { }); it('correctly checks simple leaks', () => { - let reference = {}; + let reference: unknown = {}; const detector = new LeakDetector(reference); From 59d9f28af85081d977ef063f5068b690fd368793 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 7 Feb 2019 14:36:54 +0100 Subject: [PATCH 024/107] chore: bump typescript eslint parser --- examples/typescript/package.json | 2 +- package.json | 4 ++-- yarn.lock | 33 ++++++++++++++------------------ 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/examples/typescript/package.json b/examples/typescript/package.json index 08915984fd34..2b1e8d4c4a79 100644 --- a/examples/typescript/package.json +++ b/examples/typescript/package.json @@ -12,7 +12,7 @@ "@babel/preset-env": "*", "@babel/preset-react": "*", "@babel/preset-typescript": "*", - "@types/jest": "^23.1.1", + "@types/jest": "^24.0.0", "babel-jest": "*", "jest": "*" }, diff --git a/package.json b/package.json index 1e05d1cbbe5a..2b98f9bfca09 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,8 @@ "@types/babel__generator": "^7.0.0", "@types/babel__template": "^7.0.0", "@types/jest": "^24.0.0", - "@typescript-eslint/eslint-plugin": "^1.2.0", - "@typescript-eslint/parser": "^1.2.0", + "@typescript-eslint/eslint-plugin": "^1.3.0", + "@typescript-eslint/parser": "^1.3.0", "ansi-regex": "^4.0.0", "ansi-styles": "^3.2.0", "babel-eslint": "^9.0.0", diff --git a/yarn.lock b/yarn.lock index 093b3ce1e7d0..dffaf5b9881c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1635,11 +1635,6 @@ "@types/istanbul-lib-coverage" "*" source-map "^0.6.1" -"@types/jest@^23.1.1": - version "23.3.12" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.12.tgz#7e0ced251fa94c3bc2d1023d4b84b2992fa06376" - integrity sha512-/kQvbVzdEpOq4tEWT79yAHSM4nH4xMlhJv2GrLVQt4Qmo8yYsPdioBM1QpN/2GX1wkfMnyXvdoftvLUr0LBj7Q== - "@types/jest@^24.0.0": version "24.0.0" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.0.tgz#848492026c327b3548d92be0352a545c36a21e8a" @@ -1826,28 +1821,28 @@ resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.2.tgz#7569157b2601d49d4442023c0d22f81d23f62d1e" integrity sha512-NUyxaQW48vjeDHybmhy7CFx/6of1+PoaEGPMI+0PzBVr5s3BTELT7gV4lNaxUsKllNS1YAjkeTEhBtzIDWs2WQ== -"@typescript-eslint/eslint-plugin@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.2.0.tgz#a73021a0d5bd1116a88689186844ce4421d52ab0" - integrity sha512-em3q8Gg3euesNohOwaz+SqrQM2Jn1ZWELMM+vgKi4dEk5fC+eVoi05yfubgAi2qPE5ifG4F0SOXM1XTamB0Aig== +"@typescript-eslint/eslint-plugin@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.3.0.tgz#e64c859a3eec10d96731eb5f72b5f48796a2f9ad" + integrity sha512-s+vjO9+PvYS2A6FnQC/imyEDOkrEKIzSpPf2OEGnkKqa5+1d0cuXgCi/oROtuBht2/u/iK22nrYcO/Ei4R8F/g== dependencies: - "@typescript-eslint/parser" "1.2.0" + "@typescript-eslint/parser" "1.3.0" requireindex "^1.2.0" tsutils "^3.7.0" -"@typescript-eslint/parser@1.2.0", "@typescript-eslint/parser@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.2.0.tgz#256de163b7cb0d79d0388266a5bfb5585bccb1ff" - integrity sha512-IXXiXgs6ocKTmtbzJjGyUvRHZFLuk2mYXyk+ayEql1woh1+rYS/Uct8b4jGtfHG8ZRUBZ12zjzsrDKFYC2pbrQ== +"@typescript-eslint/parser@1.3.0", "@typescript-eslint/parser@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.3.0.tgz#e61e795aa050351b7312a757e0864b6d90b84078" + integrity sha512-Q5cz9nyEQyRrtItRElvQXdNs0Xja1xvOdphDQR7N6MqUdi4juWVNxHKypdHQCx9OcEBev6pWHOda8lwg/2W9/g== dependencies: - "@typescript-eslint/typescript-estree" "1.2.0" + "@typescript-eslint/typescript-estree" "1.3.0" eslint-scope "^4.0.0" eslint-visitor-keys "^1.0.0" -"@typescript-eslint/typescript-estree@1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.2.0.tgz#d9043a407d5e0f07db2b4a80d86852f7826c34f0" - integrity sha512-YGh4egbiCfUObvi6fnQNzJAMmScMGCjG5cRHaapW7GpwujWYQymLlids88imnhcTbOx8Mlz1OXFIuxyPBXK6Ig== +"@typescript-eslint/typescript-estree@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.3.0.tgz#1d1f36680e5c32c3af38c1180153f018152f65f9" + integrity sha512-h6UxHSmBUopFcxHg/eryrA+GwHMbh7PxotMbkq9p2f3nX60CKm5Zc0VN8krBD3IS5KqsK0iOz24VpEWrP+JZ2Q== dependencies: lodash.unescape "4.0.1" semver "5.5.0" From 3a90e47c7e8885e8df3b4687b5dd0ed61da94182 Mon Sep 17 00:00:00 2001 From: Marco Scabbiolo Date: Thu, 7 Feb 2019 12:43:46 -0300 Subject: [PATCH 025/107] Refactor -o & --coverage implementation | Fixes #6859 (#7611) --- CHANGELOG.md | 2 + e2e/__tests__/onlyChanged.test.js | 24 ++++ packages/jest-cli/src/SearchSource.js | 108 +++++++++------- packages/jest-cli/src/TestScheduler.js | 16 ++- .../src/__tests__/SearchSource.test.js | 14 +++ .../src/__tests__/runJestWithCoverage.test.js | 117 ------------------ .../jest-cli/src/generateEmptyCoverage.js | 2 + .../__tests__/coverage_worker.test.js | 1 + .../src/reporters/coverage_reporter.js | 11 +- .../jest-cli/src/reporters/coverage_worker.js | 4 + packages/jest-cli/src/runJest.js | 53 ++------ .../jest-resolve-dependencies/src/index.js | 32 +++-- .../src/__tests__/testRunner.test.js | 17 ++- packages/jest-runner/src/index.js | 7 +- packages/jest-runner/src/runTest.js | 6 +- packages/jest-runner/src/testWorker.js | 4 + .../jest-runtime/src/ScriptTransformer.js | 1 + packages/jest-runtime/src/index.js | 4 + packages/jest-runtime/src/shouldInstrument.js | 4 + types/ChangedFiles.js | 5 +- types/Resolve.js | 5 + types/TestRunner.js | 4 + 22 files changed, 220 insertions(+), 221 deletions(-) delete mode 100644 packages/jest-cli/src/__tests__/runJestWithCoverage.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index cef9079de837..0a516378ae79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Fixes +- `[jest-cli]` Refactor `-o` and `--coverage` combined ([#7611](https://github.com/facebook/jest/pull/7611)) + ### Chore & Maintenance - `[*]`: Setup building, linting and testing of TypeScript ([#7808](https://github.com/facebook/jest/pull/7808)) diff --git a/e2e/__tests__/onlyChanged.test.js b/e2e/__tests__/onlyChanged.test.js index 82282142fb8a..1f351e28d81d 100644 --- a/e2e/__tests__/onlyChanged.test.js +++ b/e2e/__tests__/onlyChanged.test.js @@ -139,6 +139,30 @@ test('report test coverage for only changed files', () => { expect(stdout).not.toMatch('b.js'); }); +test('do not pickup non-tested files when reporting coverage on only changed files', () => { + writeFiles(DIR, { + 'a.js': 'module.exports = {}', + 'b.test.js': 'module.exports = {}', + 'package.json': JSON.stringify({name: 'original name'}), + }); + + run(`${GIT} init`, DIR); + run(`${GIT} add .`, DIR); + run(`${GIT} commit --no-gpg-sign -m "first"`, DIR); + + writeFiles(DIR, { + 'b.test.js': 'require("./package.json"); it("passes", () => {})', + 'package.json': JSON.stringify({name: 'new name'}), + }); + + const {stderr, stdout} = runJest(DIR, ['-o', '--coverage']); + + expect(stderr).toEqual( + expect.not.stringContaining('Failed to collect coverage from'), + ); + expect(stdout).toEqual(expect.not.stringContaining('package.json')); +}); + test('onlyChanged in config is overwritten by --all or testPathPattern', () => { writeFiles(DIR, { '.watchmanconfig': '', diff --git a/packages/jest-cli/src/SearchSource.js b/packages/jest-cli/src/SearchSource.js index 3d004d8ef140..52d83bdb3f5a 100644 --- a/packages/jest-cli/src/SearchSource.js +++ b/packages/jest-cli/src/SearchSource.js @@ -10,7 +10,7 @@ import type {Context} from 'types/Context'; import type {Glob, GlobalConfig, Path} from 'types/Config'; import type {Test} from 'types/TestRunner'; -import type {ChangedFilesPromise} from 'types/ChangedFiles'; +import type {ChangedFilesInfo} from 'types/ChangedFiles'; import path from 'path'; import micromatch from 'micromatch'; @@ -24,7 +24,7 @@ import {replacePathSepForGlob} from 'jest-util'; type SearchResult = {| noSCM?: boolean, stats?: {[key: string]: number}, - collectCoverageFrom?: Array, + collectCoverageFrom?: Set, tests: Array, total?: number, |}; @@ -158,29 +158,55 @@ export default class SearchSource { buildSnapshotResolver(this._context.config), ); - const tests = toTests( - this._context, - dependencyResolver.resolveInverse( - allPaths, - this.isTestFilePath.bind(this), - { - skipNodeResolution: this._context.config.skipNodeResolution, - }, - ), - ); - let collectCoverageFrom; - - // If we are collecting coverage, also return collectCoverageFrom patterns - if (collectCoverage) { - collectCoverageFrom = Array.from(allPaths).map(filename => { - filename = replaceRootDirInPath(this._context.config.rootDir, filename); - return path.isAbsolute(filename) - ? path.relative(this._context.config.rootDir, filename) - : filename; - }); + if (!collectCoverage) { + return { + tests: toTests( + this._context, + dependencyResolver.resolveInverse( + allPaths, + this.isTestFilePath.bind(this), + {skipNodeResolution: this._context.config.skipNodeResolution}, + ), + ), + }; } - return {collectCoverageFrom, tests}; + const testModulesMap = dependencyResolver.resolveInverseModuleMap( + allPaths, + this.isTestFilePath.bind(this), + {skipNodeResolution: this._context.config.skipNodeResolution}, + ); + + const allPathsAbsolute = Array.from(allPaths).map(p => path.resolve(p)); + + const collectCoverageFrom = new Set(); + + testModulesMap.forEach(testModule => { + if (!testModule.dependencies) { + return; + } + + testModule.dependencies + .filter(p => allPathsAbsolute.includes(p)) + .map(filename => { + filename = replaceRootDirInPath( + this._context.config.rootDir, + filename, + ); + return path.isAbsolute(filename) + ? path.relative(this._context.config.rootDir, filename) + : filename; + }) + .forEach(filename => collectCoverageFrom.add(filename)); + }); + + return { + collectCoverageFrom, + tests: toTests( + this._context, + testModulesMap.map(testModule => testModule.file), + ), + }; } findTestsByPaths(paths: Array): SearchResult { @@ -207,11 +233,11 @@ export default class SearchSource { return {tests: []}; } - async findTestRelatedToChangedFiles( - changedFilesPromise: ChangedFilesPromise, + findTestRelatedToChangedFiles( + changedFilesInfo: ChangedFilesInfo, collectCoverage: boolean, ) { - const {repos, changedFiles} = await changedFilesPromise; + const {repos, changedFiles} = changedFilesInfo; // no SCM (git/hg/...) is found in any of the roots. const noSCM = Object.keys(repos).every(scm => repos[scm].size === 0); return noSCM @@ -221,42 +247,38 @@ export default class SearchSource { _getTestPaths( globalConfig: GlobalConfig, - changedFilesPromise: ?ChangedFilesPromise, - ): Promise { + changedFiles: ?ChangedFilesInfo, + ): SearchResult { const paths = globalConfig.nonFlagArgs; if (globalConfig.onlyChanged) { - if (!changedFilesPromise) { - throw new Error('This promise must be present when running with -o.'); + if (!changedFiles) { + throw new Error('Changed files must be set when running with -o.'); } return this.findTestRelatedToChangedFiles( - changedFilesPromise, + changedFiles, globalConfig.collectCoverage, ); } else if (globalConfig.runTestsByPath && paths && paths.length) { - return Promise.resolve(this.findTestsByPaths(paths)); + return this.findTestsByPaths(paths); } else if (globalConfig.findRelatedTests && paths && paths.length) { - return Promise.resolve( - this.findRelatedTestsFromPattern(paths, globalConfig.collectCoverage), + return this.findRelatedTestsFromPattern( + paths, + globalConfig.collectCoverage, ); } else if (globalConfig.testPathPattern != null) { - return Promise.resolve( - this.findMatchingTests(globalConfig.testPathPattern), - ); + return this.findMatchingTests(globalConfig.testPathPattern); } else { - return Promise.resolve({tests: []}); + return {tests: []}; } } async getTestPaths( globalConfig: GlobalConfig, - changedFilesPromise: ?ChangedFilesPromise, + changedFiles: ?ChangedFilesInfo, ): Promise { - const searchResult = await this._getTestPaths( - globalConfig, - changedFilesPromise, - ); + const searchResult = this._getTestPaths(globalConfig, changedFiles); const filterPath = globalConfig.filter; diff --git a/packages/jest-cli/src/TestScheduler.js b/packages/jest-cli/src/TestScheduler.js index f183ab33b867..28511803998a 100644 --- a/packages/jest-cli/src/TestScheduler.js +++ b/packages/jest-cli/src/TestScheduler.js @@ -8,7 +8,7 @@ */ import type {AggregatedResult, TestResult} from 'types/TestResult'; -import type {GlobalConfig, ReporterConfig} from 'types/Config'; +import type {GlobalConfig, ReporterConfig, Path} from 'types/Config'; import type {Context} from 'types/Context'; import type {Reporter, Test} from 'types/TestRunner'; @@ -41,6 +41,7 @@ export type TestSchedulerOptions = {| export type TestSchedulerContext = {| firstRun: boolean, previousSuccess: boolean, + changedFiles?: Set, |}; export default class TestScheduler { _dispatcher: ReporterDispatcher; @@ -173,6 +174,7 @@ export default class TestScheduler { // $FlowFixMe testRunners[config.runner] = new (require(config.runner): TestRunner)( this._globalConfig, + {changedFiles: this._context && this._context.changedFiles}, ); } }); @@ -262,7 +264,11 @@ export default class TestScheduler { } if (!isDefault && collectCoverage) { - this.addReporter(new CoverageReporter(this._globalConfig)); + this.addReporter( + new CoverageReporter(this._globalConfig, { + changedFiles: this._context && this._context.changedFiles, + }), + ); } if (notify) { @@ -288,7 +294,11 @@ export default class TestScheduler { ); if (collectCoverage) { - this.addReporter(new CoverageReporter(this._globalConfig)); + this.addReporter( + new CoverageReporter(this._globalConfig, { + changedFiles: this._context && this._context.changedFiles, + }), + ); } this.addReporter(new SummaryReporter(this._globalConfig)); diff --git a/packages/jest-cli/src/__tests__/SearchSource.test.js b/packages/jest-cli/src/__tests__/SearchSource.test.js index 0a5ddef370dc..0f010a136e3d 100644 --- a/packages/jest-cli/src/__tests__/SearchSource.test.js +++ b/packages/jest-cli/src/__tests__/SearchSource.test.js @@ -419,6 +419,20 @@ describe('SearchSource', () => { rootPath, ]); }); + + it('excludes untested files from coverage', () => { + const unrelatedFile = path.join(rootDir, 'JSONFile.json'); + const regular = path.join(rootDir, 'RegularModule.js'); + const requireRegular = path.join(rootDir, 'RequireRegularMode.js'); + + const data = searchSource.findRelatedTests( + new Set([regular, requireRegular, unrelatedFile]), + true, + ); + expect(Array.from(data.collectCoverageFrom)).toEqual([ + 'RegularModule.js', + ]); + }); }); describe('findRelatedTestsFromPattern', () => { diff --git a/packages/jest-cli/src/__tests__/runJestWithCoverage.test.js b/packages/jest-cli/src/__tests__/runJestWithCoverage.test.js deleted file mode 100644 index e0ceb5266755..000000000000 --- a/packages/jest-cli/src/__tests__/runJestWithCoverage.test.js +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. - -import runJest from '../runJest'; - -jest.mock('jest-util', () => { - const util = jest.requireActual('jest-util'); - return { - ...jest.genMockFromModule('jest-util'), - replacePathSepForGlob: util.replacePathSepForGlob, - }; -}); - -jest.mock( - '../TestScheduler', - () => - class { - constructor(globalConfig) { - this._globalConfig = globalConfig; - } - - scheduleTests() { - return {_globalConfig: this._globalConfig}; - } - }, -); - -jest.mock( - '../TestSequencer', - () => - class { - sort(allTests) { - return allTests; - } - cacheResults() {} - }, -); - -jest.mock( - '../SearchSource', - () => - class { - constructor(context) { - this._context = context; - } - - async getTestPaths(globalConfig, changedFilesPromise) { - const {files} = await changedFilesPromise; - const paths = files.filter(path => path.match(/__tests__/)); - - return { - collectCoverageFrom: files.filter(path => !path.match(/__tests__/)), - tests: paths.map(path => ({ - context: this._context, - duration: null, - path, - })), - }; - } - }, -); - -const config = {roots: [], testPathIgnorePatterns: [], testRegex: []}; -let globalConfig; -const defaults = { - changedFilesPromise: Promise.resolve({ - files: ['foo.js', '__tests__/foo-test.js', 'dont/cover.js'], - }), - contexts: [{config}], - onComplete: runResults => (globalConfig = runResults._globalConfig), - outputStream: {}, - startRun: {}, - testWatcher: {isInterrupted: () => false}, -}; - -describe('collectCoverageFrom patterns', () => { - it('should apply collectCoverageFrom patterns coming from SearchSource', async () => { - expect.assertions(1); - - await runJest({ - ...defaults, - globalConfig: { - rootDir: '', - }, - }); - expect(globalConfig.collectCoverageFrom).toEqual([ - 'foo.js', - 'dont/cover.js', - ]); - }); - - it('excludes coverage from files outside the global collectCoverageFrom config', async () => { - expect.assertions(1); - - await runJest({ - ...defaults, - globalConfig: { - collectCoverageFrom: ['**/dont/*.js'], - rootDir: '', - }, - }); - expect(globalConfig.collectCoverageFrom).toEqual(['dont/cover.js']); - }); - - it('respects coveragePathIgnorePatterns', async () => { - expect.assertions(1); - - await runJest({ - ...defaults, - globalConfig: { - collectCoverageFrom: ['**/*.js'], - coveragePathIgnorePatterns: ['dont'], - rootDir: '', - }, - }); - expect(globalConfig.collectCoverageFrom).toEqual(['foo.js']); - }); -}); diff --git a/packages/jest-cli/src/generateEmptyCoverage.js b/packages/jest-cli/src/generateEmptyCoverage.js index 65c29fcfb1c9..d43f2de65dab 100644 --- a/packages/jest-cli/src/generateEmptyCoverage.js +++ b/packages/jest-cli/src/generateEmptyCoverage.js @@ -25,8 +25,10 @@ export default function( filename: Path, globalConfig: GlobalConfig, config: ProjectConfig, + changedFiles: ?Set, ): ?CoverageWorkerResult { const coverageOptions = { + changedFiles, collectCoverage: globalConfig.collectCoverage, collectCoverageFrom: globalConfig.collectCoverageFrom, collectCoverageOnlyFrom: globalConfig.collectCoverageOnlyFrom, diff --git a/packages/jest-cli/src/reporters/__tests__/coverage_worker.test.js b/packages/jest-cli/src/reporters/__tests__/coverage_worker.test.js index 8f0fe1c0e7d7..fe9d1f1bb5db 100644 --- a/packages/jest-cli/src/reporters/__tests__/coverage_worker.test.js +++ b/packages/jest-cli/src/reporters/__tests__/coverage_worker.test.js @@ -40,6 +40,7 @@ test('resolves to the result of generateEmptyCoverage upon success', async () => 'banana.js', globalConfig, config, + undefined, ); expect(result).toEqual(42); diff --git a/packages/jest-cli/src/reporters/coverage_reporter.js b/packages/jest-cli/src/reporters/coverage_reporter.js index 81e45ec08a1e..f2fad46f9d27 100644 --- a/packages/jest-cli/src/reporters/coverage_reporter.js +++ b/packages/jest-cli/src/reporters/coverage_reporter.js @@ -16,7 +16,7 @@ import type { } from 'types/TestResult'; import typeof {worker} from './coverage_worker'; -import type {GlobalConfig} from 'types/Config'; +import type {GlobalConfig, Path} from 'types/Config'; import type {Context} from 'types/Context'; import type {Test} from 'types/TestRunner'; @@ -35,16 +35,22 @@ const RUNNING_TEST_COLOR = chalk.bold.dim; type CoverageWorker = {worker: worker}; +export type CoverageReporterOptions = { + changedFiles?: Set, +}; + export default class CoverageReporter extends BaseReporter { _coverageMap: CoverageMap; _globalConfig: GlobalConfig; _sourceMapStore: any; + _options: CoverageReporterOptions; - constructor(globalConfig: GlobalConfig) { + constructor(globalConfig: GlobalConfig, options?: CoverageReporterOptions) { super(); this._coverageMap = istanbulCoverage.createCoverageMap({}); this._globalConfig = globalConfig; this._sourceMapStore = libSourceMaps.createSourceMapStore(); + this._options = options || {}; } onTestResult( @@ -170,6 +176,7 @@ export default class CoverageReporter extends BaseReporter { const result = await worker.worker({ config, globalConfig, + options: this._options, path: filename, }); diff --git a/packages/jest-cli/src/reporters/coverage_worker.js b/packages/jest-cli/src/reporters/coverage_worker.js index c3d53016f248..6a7feded80e1 100644 --- a/packages/jest-cli/src/reporters/coverage_worker.js +++ b/packages/jest-cli/src/reporters/coverage_worker.js @@ -8,6 +8,7 @@ */ import type {GlobalConfig, ProjectConfig, Path} from 'types/Config'; +import type {CoverageReporterOptions} from './coverage_reporter'; import exit from 'exit'; import fs from 'fs'; @@ -18,6 +19,7 @@ export type CoverageWorkerData = {| globalConfig: GlobalConfig, config: ProjectConfig, path: Path, + options?: CoverageReporterOptions, |}; export type {CoverageWorkerResult}; @@ -32,11 +34,13 @@ export function worker({ config, globalConfig, path, + options, }: CoverageWorkerData): ?CoverageWorkerResult { return generateEmptyCoverage( fs.readFileSync(path, 'utf8'), path, globalConfig, config, + options && options.changedFiles, ); } diff --git a/packages/jest-cli/src/runJest.js b/packages/jest-cli/src/runJest.js index 7b4894e6ac0a..a403ac57ca8a 100644 --- a/packages/jest-cli/src/runJest.js +++ b/packages/jest-cli/src/runJest.js @@ -14,12 +14,12 @@ import type {AggregatedResult} from 'types/TestResult'; import type {TestRunData} from 'types/TestRunner'; import type {JestHookEmitter} from 'types/JestHooks'; import type TestWatcher from './TestWatcher'; +import type {TestSchedulerContext} from './TestScheduler'; -import micromatch from 'micromatch'; import chalk from 'chalk'; import path from 'path'; import {sync as realpath} from 'realpath-native'; -import {Console, formatTestResults, replacePathSepForGlob} from 'jest-util'; +import {Console, formatTestResults} from 'jest-util'; import exit from 'exit'; import fs from 'graceful-fs'; import getNoTestsFoundMessage from './getNoTestsFoundMessage'; @@ -36,11 +36,11 @@ const getTestPaths = async ( globalConfig, context, outputStream, - changedFilesPromise, + changedFiles, jestHooks, ) => { const source = new SearchSource(context); - const data = await source.getTestPaths(globalConfig, changedFilesPromise); + const data = await source.getTestPaths(globalConfig, changedFiles); if (!data.tests.length && globalConfig.onlyChanged && data.noSCM) { new Console(outputStream, outputStream).log( @@ -104,7 +104,7 @@ const processResults = (runResults, options) => { return onComplete && onComplete(runResults); }; -const testSchedulerContext = { +const testSchedulerContext: TestSchedulerContext = { firstRun: true, previousSuccess: true, }; @@ -135,6 +135,7 @@ export default (async function runJest({ if (changedFilesPromise && globalConfig.watch) { const {repos} = await changedFilesPromise; + const noSCM = Object.keys(repos).every(scm => repos[scm].size === 0); if (noSCM) { process.stderr.write( @@ -147,57 +148,21 @@ export default (async function runJest({ } } - let collectCoverageFrom = []; - const testRunData: TestRunData = await Promise.all( contexts.map(async context => { const matches = await getTestPaths( globalConfig, context, outputStream, - changedFilesPromise, + changedFilesPromise && (await changedFilesPromise), jestHooks, ); allTests = allTests.concat(matches.tests); - if (matches.collectCoverageFrom) { - collectCoverageFrom = collectCoverageFrom.concat( - matches.collectCoverageFrom.filter(filename => { - if ( - globalConfig.collectCoverageFrom && - !micromatch.some( - replacePathSepForGlob( - path.relative(globalConfig.rootDir, filename), - ), - globalConfig.collectCoverageFrom, - ) - ) { - return false; - } - - if ( - globalConfig.coveragePathIgnorePatterns && - globalConfig.coveragePathIgnorePatterns.some(pattern => - filename.match(pattern), - ) - ) { - return false; - } - - return true; - }), - ); - } - return {context, matches}; }), ); - if (collectCoverageFrom.length) { - const newConfig: GlobalConfig = {...globalConfig, collectCoverageFrom}; - globalConfig = Object.freeze(newConfig); - } - allTests = sequencer.sort(allTests); if (globalConfig.listTests) { @@ -256,6 +221,10 @@ export default (async function runJest({ await runGlobalHook({allTests, globalConfig, moduleName: 'globalSetup'}); } + if (changedFilesPromise) { + testSchedulerContext.changedFiles = (await changedFilesPromise).changedFiles; + } + const results = await new TestScheduler( globalConfig, { diff --git a/packages/jest-resolve-dependencies/src/index.js b/packages/jest-resolve-dependencies/src/index.js index ff1dce7bc612..bebb7a76e212 100644 --- a/packages/jest-resolve-dependencies/src/index.js +++ b/packages/jest-resolve-dependencies/src/index.js @@ -9,7 +9,11 @@ import type {HasteFS} from 'types/HasteMap'; import type {Path} from 'types/Config'; -import type {Resolver, ResolveModuleConfig} from 'types/Resolve'; +import type { + Resolver, + ResolveModuleConfig, + ResolvedModule, +} from 'types/Resolve'; import type {SnapshotResolver} from 'types/SnapshotResolver'; import {isSnapshotPath} from 'jest-snapshot'; @@ -61,17 +65,18 @@ class DependencyResolver { }, []); } - resolveInverse( + resolveInverseModuleMap( paths: Set, filter: (file: Path) => boolean, options?: ResolveModuleConfig, - ): Array { + ): Array { if (!paths.size) { return []; } - const collectModules = (relatedPaths, moduleMap, changed) => { + const collectModules = (related, moduleMap, changed) => { const visitedModules = new Set(); + const result: Array = []; while (changed.size) { changed = new Set( moduleMap.reduce((acc, module) => { @@ -84,7 +89,8 @@ class DependencyResolver { const file = module.file; if (filter(file)) { - relatedPaths.add(file); + result.push(module); + related.delete(file); } visitedModules.add(file); acc.push(module.file); @@ -92,10 +98,10 @@ class DependencyResolver { }, []), ); } - return relatedPaths; + return result.concat(Array.from(related).map(file => ({file}))); }; - const relatedPaths = new Set(); + const relatedPaths = new Set(); const changed = new Set(); for (const path of paths) { if (this._hasteFS.exists(path)) { @@ -115,7 +121,17 @@ class DependencyResolver { file, }); } - return Array.from(collectModules(relatedPaths, modules, changed)); + return collectModules(relatedPaths, modules, changed); + } + + resolveInverse( + paths: Set, + filter: (file: Path) => boolean, + options?: ResolveModuleConfig, + ): Array { + return this.resolveInverseModuleMap(paths, filter, options).map( + module => module.file, + ); } } diff --git a/packages/jest-runner/src/__tests__/testRunner.test.js b/packages/jest-runner/src/__tests__/testRunner.test.js index d375d91a43a3..c4ce341398cd 100644 --- a/packages/jest-runner/src/__tests__/testRunner.test.js +++ b/packages/jest-runner/src/__tests__/testRunner.test.js @@ -31,6 +31,7 @@ test('injects the serializable module map into each worker in watch mode', () => const globalConfig = {maxWorkers: 2, watch: true}; const config = {rootDir: '/path/'}; const serializableModuleMap = jest.fn(); + const runContext = {}; const context = { config, moduleMap: {toJSON: () => serializableModuleMap}, @@ -46,10 +47,19 @@ test('injects the serializable module map into each worker in watch mode', () => ) .then(() => { expect(mockWorkerFarm.worker.mock.calls).toEqual([ - [{config, globalConfig, path: './file.test.js', serializableModuleMap}], [ { config, + context: runContext, + globalConfig, + path: './file.test.js', + serializableModuleMap, + }, + ], + [ + { + config, + context: runContext, globalConfig, path: './file2.test.js', serializableModuleMap, @@ -63,8 +73,9 @@ test('does not inject the serializable module map in serial mode', () => { const globalConfig = {maxWorkers: 1, watch: false}; const config = {rootDir: '/path/'}; const context = {config}; + const runContext = {}; - return new TestRunner(globalConfig) + return new TestRunner(globalConfig, runContext) .runTests( [{context, path: './file.test.js'}, {context, path: './file2.test.js'}], new TestWatcher({isWatchMode: globalConfig.watch}), @@ -78,6 +89,7 @@ test('does not inject the serializable module map in serial mode', () => { [ { config, + context: runContext, globalConfig, path: './file.test.js', serializableModuleMap: null, @@ -86,6 +98,7 @@ test('does not inject the serializable module map in serial mode', () => { [ { config, + context: runContext, globalConfig, path: './file2.test.js', serializableModuleMap: null, diff --git a/packages/jest-runner/src/index.js b/packages/jest-runner/src/index.js index ca4ccc141895..cd339cd37e5c 100644 --- a/packages/jest-runner/src/index.js +++ b/packages/jest-runner/src/index.js @@ -13,6 +13,7 @@ import type { OnTestStart, OnTestSuccess, Test, + TestRunnerContext, TestRunnerOptions, TestWatcher, } from 'types/TestRunner'; @@ -30,9 +31,11 @@ type WorkerInterface = Worker & {worker: worker}; class TestRunner { _globalConfig: GlobalConfig; + _context: TestRunnerContext; - constructor(globalConfig: GlobalConfig) { + constructor(globalConfig: GlobalConfig, context?: TestRunnerContext) { this._globalConfig = globalConfig; + this._context = context || {}; } async runTests( @@ -78,6 +81,7 @@ class TestRunner { this._globalConfig, test.context.config, test.context.resolver, + this._context, ); }) .then(result => onResult(test, result)) @@ -119,6 +123,7 @@ class TestRunner { return worker.worker({ config: test.context.config, + context: this._context, globalConfig: this._globalConfig, path: test.path, serializableModuleMap: watcher.isWatchMode() diff --git a/packages/jest-runner/src/runTest.js b/packages/jest-runner/src/runTest.js index 2ff916f45abd..f609e09d4c8d 100644 --- a/packages/jest-runner/src/runTest.js +++ b/packages/jest-runner/src/runTest.js @@ -10,7 +10,7 @@ import type {EnvironmentClass} from 'types/Environment'; import type {GlobalConfig, Path, ProjectConfig} from 'types/Config'; import type {Resolver} from 'types/Resolve'; -import type {TestFramework} from 'types/TestRunner'; +import type {TestFramework, TestRunnerContext} from 'types/TestRunner'; import type {TestResult} from 'types/TestResult'; import type RuntimeClass from 'jest-runtime'; @@ -78,6 +78,7 @@ async function runTestInternal( globalConfig: GlobalConfig, config: ProjectConfig, resolver: Resolver, + context: ?TestRunnerContext, ): Promise { const testSource = fs.readFileSync(path, 'utf8'); const parsedDocblock = docblock.parse(docblock.extract(testSource)); @@ -143,6 +144,7 @@ async function runTestInternal( setGlobal(environment.global, 'console', testConsole); runtime = new Runtime(config, environment, resolver, cacheFS, { + changedFiles: context && context.changedFiles, collectCoverage: globalConfig.collectCoverage, collectCoverageFrom: globalConfig.collectCoverageFrom, collectCoverageOnlyFrom: globalConfig.collectCoverageOnlyFrom, @@ -268,12 +270,14 @@ export default async function runTest( globalConfig: GlobalConfig, config: ProjectConfig, resolver: Resolver, + context: ?TestRunnerContext, ): Promise { const {leakDetector, result} = await runTestInternal( path, globalConfig, config, resolver, + context, ); if (leakDetector) { diff --git a/packages/jest-runner/src/testWorker.js b/packages/jest-runner/src/testWorker.js index c16f2f749a37..1651803861e6 100644 --- a/packages/jest-runner/src/testWorker.js +++ b/packages/jest-runner/src/testWorker.js @@ -11,6 +11,7 @@ import type {GlobalConfig, Path, ProjectConfig} from 'types/Config'; import type {SerializableError, TestResult} from 'types/TestResult'; import type {SerializableModuleMap} from 'types/HasteMap'; import type {ErrorWithCode} from 'types/Errors'; +import type {TestRunnerContext} from 'types/TestRunner'; import exit from 'exit'; import HasteMap from 'jest-haste-map'; @@ -23,6 +24,7 @@ export type WorkerData = {| globalConfig: GlobalConfig, path: Path, serializableModuleMap: ?SerializableModuleMap, + context?: TestRunnerContext, |}; // Make sure uncaught errors are logged before we exit. @@ -74,6 +76,7 @@ export async function worker({ globalConfig, path, serializableModuleMap, + context, }: WorkerData): Promise { try { const moduleMap = serializableModuleMap @@ -84,6 +87,7 @@ export async function worker({ globalConfig, config, getResolver(config, moduleMap), + context, ); } catch (error) { throw formatError(error); diff --git a/packages/jest-runtime/src/ScriptTransformer.js b/packages/jest-runtime/src/ScriptTransformer.js index 92d0cb46714b..72ba88f9fabd 100644 --- a/packages/jest-runtime/src/ScriptTransformer.js +++ b/packages/jest-runtime/src/ScriptTransformer.js @@ -33,6 +33,7 @@ import {sync as realpath} from 'realpath-native'; import {enhanceUnexpectedTokenMessage} from './helpers'; export type Options = {| + changedFiles: ?Set, collectCoverage: boolean, collectCoverageFrom: Array, collectCoverageOnlyFrom: ?{[key: string]: boolean, __proto__: null}, diff --git a/packages/jest-runtime/src/index.js b/packages/jest-runtime/src/index.js index 7ff87fbf2827..2f7290c73711 100644 --- a/packages/jest-runtime/src/index.js +++ b/packages/jest-runtime/src/index.js @@ -56,6 +56,7 @@ type InternalModuleOptions = {| |}; type CoverageOptions = { + changedFiles: ?Set, collectCoverage: boolean, collectCoverageFrom: Array, collectCoverageOnlyFrom: ?{[key: string]: boolean, __proto__: null}, @@ -122,6 +123,7 @@ class Runtime { this._cacheFS = cacheFS || Object.create(null); this._config = config; this._coverageOptions = coverageOptions || { + changedFiles: null, collectCoverage: false, collectCoverageFrom: [], collectCoverageOnlyFrom: null, @@ -185,6 +187,7 @@ class Runtime { return shouldInstrument( filename, { + changedFiles: options.changedFiles, collectCoverage: options.collectCoverage, collectCoverageFrom: options.collectCoverageFrom, collectCoverageOnlyFrom: options.collectCoverageOnlyFrom, @@ -671,6 +674,7 @@ class Runtime { const transformedFile = this._scriptTransformer.transform( filename, { + changedFiles: this._coverageOptions.changedFiles, collectCoverage: this._coverageOptions.collectCoverage, collectCoverageFrom: this._coverageOptions.collectCoverageFrom, collectCoverageOnlyFrom: this._coverageOptions.collectCoverageOnlyFrom, diff --git a/packages/jest-runtime/src/shouldInstrument.js b/packages/jest-runtime/src/shouldInstrument.js index 6333ece7814e..ecfe24fb14f1 100644 --- a/packages/jest-runtime/src/shouldInstrument.js +++ b/packages/jest-runtime/src/shouldInstrument.js @@ -112,5 +112,9 @@ export default function shouldInstrument( return false; } + if (options.changedFiles && !options.changedFiles.has(filename)) { + return false; + } + return true; } diff --git a/types/ChangedFiles.js b/types/ChangedFiles.js index 53b116b8783a..9e0914c01016 100644 --- a/types/ChangedFiles.js +++ b/types/ChangedFiles.js @@ -18,10 +18,11 @@ export type Options = {| export type ChangedFiles = Set; export type Repos = {|git: Set, hg: Set|}; -export type ChangedFilesPromise = Promise<{| +export type ChangedFilesInfo = {| repos: Repos, changedFiles: ChangedFiles, -|}>; +|}; +export type ChangedFilesPromise = Promise; export type SCMAdapter = {| findChangedFiles: (cwd: Path, options: Options) => Promise>, diff --git a/types/Resolve.js b/types/Resolve.js index 1d849797cbc4..facd55e4fc56 100644 --- a/types/Resolve.js +++ b/types/Resolve.js @@ -16,4 +16,9 @@ export type ResolveModuleConfig = {| paths?: Path[], |}; +export type ResolvedModule = { + file: string, + dependencies?: string[], +}; + export type Resolver = _Resolver; diff --git a/types/TestRunner.js b/types/TestRunner.js index 2eac8afcd349..2aa7005c6f26 100644 --- a/types/TestRunner.js +++ b/types/TestRunner.js @@ -61,6 +61,10 @@ export type TestRunnerOptions = { serial: boolean, }; +export type TestRunnerContext = { + changedFiles?: Set, +}; + export type TestRunData = Array<{ context: Context, matches: {allTests: number, tests: Array, total: number}, From e37f1a9ff9dc20cf00a9390a92e59546c1ec7801 Mon Sep 17 00:00:00 2001 From: Lorenzo Rapetti Date: Thu, 7 Feb 2019 18:25:41 +0100 Subject: [PATCH 026/107] Migrate jest-changed-files to Typescript (#7827) * Migrate jest-changed-files to Typescript * Add changelog entry * Update e2e/__tests__/jestChangedFiles.test.js Co-Authored-By: loryman * Stricter typings Merge remote-tracking branch 'origin/jest-changed-files-typescript' into jest-changed-files-typescript * Make prettier happy --- CHANGELOG.md | 1 + e2e/__tests__/jestChangedFiles.test.js | 7 ++--- packages/jest-changed-files/package.json | 1 + .../jest-changed-files/src/{git.js => git.ts} | 10 +++---- .../jest-changed-files/src/{hg.js => hg.ts} | 10 +++---- .../src/{index.js => index.ts} | 28 +++++++++++-------- packages/jest-changed-files/src/types.ts | 28 +++++++++++++++++++ packages/jest-changed-files/tsconfig.json | 7 +++++ 8 files changed, 63 insertions(+), 29 deletions(-) rename packages/jest-changed-files/src/{git.js => git.ts} (91%) rename packages/jest-changed-files/src/{hg.js => hg.ts} (87%) rename packages/jest-changed-files/src/{index.js => index.ts} (65%) create mode 100644 packages/jest-changed-files/src/types.ts create mode 100644 packages/jest-changed-files/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a516378ae79..34b0e950f185 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - `[jest-regex-util]`: Migrate to TypeScript ([#7822](https://github.com/facebook/jest/pull/7822)) - `[jest-diff]`: Migrate to TypeScript ([#7824](https://github.com/facebook/jest/pull/7824)) - `[jest-leak-detector]`: Migrate to TypeScript ([#7825](https://github.com/facebook/jest/pull/7825)) +- `[jest-changed-files]`: Migrate to TypeScript ([#7827](https://github.com/facebook/jest/pull/7827)) ### Performance diff --git a/e2e/__tests__/jestChangedFiles.test.js b/e2e/__tests__/jestChangedFiles.test.js index 6020514f44f3..d4efe1838964 100644 --- a/e2e/__tests__/jestChangedFiles.test.js +++ b/e2e/__tests__/jestChangedFiles.test.js @@ -9,14 +9,11 @@ import os from 'os'; import path from 'path'; -import { - findRepos, - getChangedFilesForRoots, -} from '../../packages/jest-changed-files/src'; +import {wrap} from 'jest-snapshot-serializer-raw'; +import {findRepos, getChangedFilesForRoots} from 'jest-changed-files'; import {skipSuiteOnWindows} from '../../scripts/ConditionalTest'; import {cleanup, run, writeFiles} from '../Utils'; import runJest from '../runJest'; -import {wrap} from 'jest-snapshot-serializer-raw'; skipSuiteOnWindows(); diff --git a/packages/jest-changed-files/package.json b/packages/jest-changed-files/package.json index de3d1e78442a..6aafc1373de5 100644 --- a/packages/jest-changed-files/package.json +++ b/packages/jest-changed-files/package.json @@ -8,6 +8,7 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { "execa": "^1.0.0", "throat": "^4.0.0" diff --git a/packages/jest-changed-files/src/git.js b/packages/jest-changed-files/src/git.ts similarity index 91% rename from packages/jest-changed-files/src/git.js rename to packages/jest-changed-files/src/git.ts index 8d405ce593a4..27124ef57c33 100644 --- a/packages/jest-changed-files/src/git.js +++ b/packages/jest-changed-files/src/git.ts @@ -4,15 +4,13 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ -import type {Path} from 'types/Config'; -import type {Options, SCMAdapter} from 'types/ChangedFiles'; - import path from 'path'; import execa from 'execa'; +import {Path, Options, SCMAdapter} from './types'; + const findChangedFilesUsingCommand = async ( args: Array, cwd: Path, @@ -30,7 +28,7 @@ const adapter: SCMAdapter = { cwd: string, options?: Options, ): Promise> => { - const changedSince: ?string = + const changedSince: string | undefined = options && (options.withAncestor ? 'HEAD^' : options.changedSince); const includePaths: Array = (options && options.includePaths) || []; @@ -72,7 +70,7 @@ const adapter: SCMAdapter = { } }, - getRoot: async (cwd: string): Promise => { + getRoot: async (cwd: string): Promise => { const options = ['rev-parse', '--show-toplevel']; try { diff --git a/packages/jest-changed-files/src/hg.js b/packages/jest-changed-files/src/hg.ts similarity index 87% rename from packages/jest-changed-files/src/hg.js rename to packages/jest-changed-files/src/hg.ts index 9459619fcc6c..0b3b3ecf7963 100644 --- a/packages/jest-changed-files/src/hg.js +++ b/packages/jest-changed-files/src/hg.ts @@ -4,16 +4,14 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ -import type {Path} from 'types/Config'; -import type {Options, SCMAdapter} from 'types/ChangedFiles'; - import path from 'path'; import execa from 'execa'; -const env = {...process.env, HGPLAIN: 1}; +import {Path, Options, SCMAdapter} from './types'; + +const env = {...process.env, HGPLAIN: '1'}; const ANCESTORS = [ // Parent commit to this one. @@ -51,7 +49,7 @@ const adapter: SCMAdapter = { .map(changedPath => path.resolve(cwd, changedPath)); }, - getRoot: async (cwd: Path): Promise => { + getRoot: async (cwd: Path): Promise => { try { const result = await execa('hg', ['root'], {cwd, env}); diff --git a/packages/jest-changed-files/src/index.js b/packages/jest-changed-files/src/index.ts similarity index 65% rename from packages/jest-changed-files/src/index.js rename to packages/jest-changed-files/src/index.ts index f7ecf3d8fb1d..d3c160e683ee 100644 --- a/packages/jest-changed-files/src/index.js +++ b/packages/jest-changed-files/src/index.ts @@ -4,25 +4,23 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ -import type {Path} from 'types/Config'; -import type {ChangedFilesPromise, Options, Repos} from 'types/ChangedFiles'; +import throat from 'throat'; +import {Path, ChangedFilesPromise, Options, Repos} from './types'; import git from './git'; import hg from './hg'; -import throat from 'throat'; // This is an arbitrary number. The main goal is to prevent projects with // many roots (50+) from spawning too many processes at once. const mutex = throat(5); -const findGitRoot = dir => mutex(() => git.getRoot(dir)); -const findHgRoot = dir => mutex(() => hg.getRoot(dir)); +const findGitRoot = (dir: string) => mutex(() => git.getRoot(dir)); +const findHgRoot = (dir: string) => mutex(() => hg.getRoot(dir)); export const getChangedFilesForRoots = async ( - roots: Array, + roots: Path[], options: Options, ): ChangedFilesPromise => { const repos = await findRepos(roots); @@ -50,16 +48,22 @@ export const getChangedFilesForRoots = async ( return {changedFiles, repos}; }; -export const findRepos = async (roots: Array): Promise => { +export const findRepos = async (roots: Path[]): Promise => { const gitRepos = await Promise.all( - roots.reduce((promises, root) => promises.concat(findGitRoot(root)), []), + roots.reduce[]>( + (promises, root) => promises.concat(findGitRoot(root)), + [], + ), ); const hgRepos = await Promise.all( - roots.reduce((promises, root) => promises.concat(findHgRoot(root)), []), + roots.reduce[]>( + (promises, root) => promises.concat(findHgRoot(root)), + [], + ), ); return { - git: new Set(gitRepos.filter(Boolean)), - hg: new Set(hgRepos.filter(Boolean)), + git: new Set(gitRepos.filter(Boolean) as string[]), + hg: new Set(hgRepos.filter(Boolean) as string[]), }; }; diff --git a/packages/jest-changed-files/src/types.ts b/packages/jest-changed-files/src/types.ts new file mode 100644 index 000000000000..36bde410b70b --- /dev/null +++ b/packages/jest-changed-files/src/types.ts @@ -0,0 +1,28 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + * + */ + +export type Path = string; + +export type Options = { + lastCommit?: boolean; + withAncestor?: boolean; + changedSince?: string; + includePaths?: Array; +}; + +export type ChangedFiles = Set; +export type Repos = {git: Set; hg: Set}; +export type ChangedFilesPromise = Promise<{ + repos: Repos; + changedFiles: ChangedFiles; +}>; + +export type SCMAdapter = { + findChangedFiles: (cwd: Path, options: Options) => Promise>; + getRoot: (cwd: Path) => Promise; +}; diff --git a/packages/jest-changed-files/tsconfig.json b/packages/jest-changed-files/tsconfig.json new file mode 100644 index 000000000000..7bb06bce6d20 --- /dev/null +++ b/packages/jest-changed-files/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + } +} From 1aa82f9dd240e3cbfe273e9f34c584f2fb1518d9 Mon Sep 17 00:00:00 2001 From: Viktor Karpov Date: Fri, 8 Feb 2019 11:39:55 +0300 Subject: [PATCH 027/107] chore: Remove redundant stripAnsi from test (#7833) ## Summary It doesn't make sense to wrap a `runJust` call into `stripAnsi` since the latter works only with strings. `runJest` returns an object so stripAnsi just passes it along. Materials: - Commit where the code was added in the first place: https://github.com/facebook/jest/commit/e12cab63f06cabaf4e38c18fac18cf463fc10e1f#diff-4cf7d6c79ff377b63522b3af20e34e8eR307 - stripAnsi source code: https://github.com/chalk/strip-ansi/blob/097894423fedb6b4dca3005ad45608b893fcdcf8/index.js --- e2e/__tests__/multiProjectRunner.test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/e2e/__tests__/multiProjectRunner.test.js b/e2e/__tests__/multiProjectRunner.test.js index 7fc070a53c32..5850adf05af8 100644 --- a/e2e/__tests__/multiProjectRunner.test.js +++ b/e2e/__tests__/multiProjectRunner.test.js @@ -10,7 +10,6 @@ import runJest from '../runJest'; import os from 'os'; import path from 'path'; -import stripAnsi from 'strip-ansi'; import {cleanup, extractSummary, sortLines, writeFiles} from '../Utils'; import {wrap} from 'jest-snapshot-serializer-raw'; @@ -322,7 +321,7 @@ test('resolves projects and their properly', () => { }), }); - ({stderr} = stripAnsi(runJest(DIR, ['--no-watchman']))); + ({stderr} = runJest(DIR, ['--no-watchman'])); expect(stderr).toMatch( /Whoops! Two projects resolved to the same config path/, ); From ecc8518ffa3d94fb3672830e5283e4a81018cffc Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Fri, 8 Feb 2019 10:39:43 +0100 Subject: [PATCH 028/107] chore: remove cyclic dev dep on jest-diff from pretty-format --- packages/pretty-format/package.json | 1 - packages/pretty-format/src/__tests__/setPrettyPrint.ts | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/pretty-format/package.json b/packages/pretty-format/package.json index e2eb759c6e2c..ae89d57ddda0 100644 --- a/packages/pretty-format/package.json +++ b/packages/pretty-format/package.json @@ -22,7 +22,6 @@ "@types/react": "*", "@types/react-test-renderer": "*", "immutable": "4.0.0-rc.9", - "jest-diff": "^24.0.0", "react": "*", "react-dom": "*", "react-test-renderer": "*" diff --git a/packages/pretty-format/src/__tests__/setPrettyPrint.ts b/packages/pretty-format/src/__tests__/setPrettyPrint.ts index 4a8a562bf52b..98fe6e9fb894 100644 --- a/packages/pretty-format/src/__tests__/setPrettyPrint.ts +++ b/packages/pretty-format/src/__tests__/setPrettyPrint.ts @@ -5,9 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -// @ts-ignore -import diff from 'jest-diff'; - import prettyFormat from '../'; import {OptionsReceived, Plugins} from '../types'; @@ -34,7 +31,8 @@ const setPrettyPrint = (plugins: Plugins) => { `Received:\n` + ` ${this.utils.printReceived(prettyFormatted)}` : () => { - const diffString = diff(expected, prettyFormatted, { + // @ts-ignore: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/32891 + const diffString = this.utils.diff(expected, prettyFormatted, { expand: this.expand, }); return ( From 7a7cad797b5bd999db2261ed82b97a93d469c8ae Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Fri, 8 Feb 2019 11:08:28 +0100 Subject: [PATCH 029/107] chore: migrate jest-matcher-util to TypeScript (#7835) * chore: migrate jest-matcher-util to TypeScript * link to PR * unknown and fix flow --- CHANGELOG.md | 1 + .../jest-circus/src/formatNodeAssertErrors.js | 1 + ...{index.test.js.snap => index.test.ts.snap} | 0 .../{index.test.js => index.test.ts} | 10 +++-- .../src/{index.js => index.ts} | 39 +++++++++++-------- packages/jest-matcher-utils/tsconfig.json | 12 ++++++ 6 files changed, 44 insertions(+), 19 deletions(-) rename packages/jest-matcher-utils/src/__tests__/__snapshots__/{index.test.js.snap => index.test.ts.snap} (100%) rename packages/jest-matcher-utils/src/__tests__/{index.test.js => index.test.ts} (97%) rename packages/jest-matcher-utils/src/{index.js => index.ts} (90%) create mode 100644 packages/jest-matcher-utils/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 34b0e950f185..0050a9f04365 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - `[jest-diff]`: Migrate to TypeScript ([#7824](https://github.com/facebook/jest/pull/7824)) - `[jest-leak-detector]`: Migrate to TypeScript ([#7825](https://github.com/facebook/jest/pull/7825)) - `[jest-changed-files]`: Migrate to TypeScript ([#7827](https://github.com/facebook/jest/pull/7827)) +- `[jest-matcher-utils]`: Migrate to TypeScript ([#7835](https://github.com/facebook/jest/pull/7835)) ### Performance diff --git a/packages/jest-circus/src/formatNodeAssertErrors.js b/packages/jest-circus/src/formatNodeAssertErrors.js index e7f685f852f7..ef84986451b6 100644 --- a/packages/jest-circus/src/formatNodeAssertErrors.js +++ b/packages/jest-circus/src/formatNodeAssertErrors.js @@ -11,6 +11,7 @@ import type {DiffOptions} from 'jest-diff'; import type {Event, State} from 'types/Circus'; +// $FlowFixMe: Converted to TS import {diff, printExpected, printReceived} from 'jest-matcher-utils'; import chalk from 'chalk'; // $FlowFixMe: Converted to TS diff --git a/packages/jest-matcher-utils/src/__tests__/__snapshots__/index.test.js.snap b/packages/jest-matcher-utils/src/__tests__/__snapshots__/index.test.ts.snap similarity index 100% rename from packages/jest-matcher-utils/src/__tests__/__snapshots__/index.test.js.snap rename to packages/jest-matcher-utils/src/__tests__/__snapshots__/index.test.ts.snap diff --git a/packages/jest-matcher-utils/src/__tests__/index.test.js b/packages/jest-matcher-utils/src/__tests__/index.test.ts similarity index 97% rename from packages/jest-matcher-utils/src/__tests__/index.test.js rename to packages/jest-matcher-utils/src/__tests__/index.test.ts index 703ab50fa0d0..a78976c11a3c 100644 --- a/packages/jest-matcher-utils/src/__tests__/index.test.js +++ b/packages/jest-matcher-utils/src/__tests__/index.test.ts @@ -37,7 +37,7 @@ describe('.stringify()', () => { }); test('circular references', () => { - const a = {}; + const a: any = {}; a.a = a; expect(stringify(a)).toBe('{"a": [Circular]}'); }); @@ -75,8 +75,8 @@ describe('.stringify()', () => { }); test('reduces maxDepth if stringifying very large objects', () => { - const big = {a: 1, b: {}}; - const small = {a: 1, b: {}}; + const big: any = {a: 1, b: {}}; + const small: any = {a: 1, b: {}}; for (let i = 0; i < 10000; i += 1) { big.b[i] = 'test'; } @@ -93,18 +93,21 @@ describe('.stringify()', () => { describe('.ensureNumbers()', () => { test('dont throw error when variables are numbers', () => { expect(() => { + // @ts-ignore ensureNumbers(1, 2); }).not.toThrow(); }); test('throws error when expected is not a number', () => { expect(() => { + // @ts-ignore ensureNumbers(1, 'not_a_number'); }).toThrowErrorMatchingSnapshot(); }); test('throws error when received is not a number', () => { expect(() => { + // @ts-ignore ensureNumbers('not_a_number', 3); }).toThrowErrorMatchingSnapshot(); }); @@ -113,6 +116,7 @@ describe('.ensureNumbers()', () => { describe('.ensureNoExpected()', () => { test('dont throw error when undefined', () => { expect(() => { + // @ts-ignore ensureNoExpected(undefined); }).not.toThrow(); }); diff --git a/packages/jest-matcher-utils/src/index.js b/packages/jest-matcher-utils/src/index.ts similarity index 90% rename from packages/jest-matcher-utils/src/index.js rename to packages/jest-matcher-utils/src/index.ts index a7b040e867a5..ef5121d7210a 100644 --- a/packages/jest-matcher-utils/src/index.js +++ b/packages/jest-matcher-utils/src/index.ts @@ -3,12 +3,8 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {MatcherHintOptions} from 'types/Matchers'; - import chalk from 'chalk'; import jestDiff from 'jest-diff'; import getType from 'jest-get-type'; @@ -31,6 +27,14 @@ const PLUGINS = [ AsymmetricMatcher, ]; +export type MatcherHintOptions = { + comment?: string; + isDirectExpectCall?: boolean; + isNot?: boolean; + promise?: string; + secondArgument?: string; +}; + export const EXPECTED_COLOR = chalk.green; export const RECEIVED_COLOR = chalk.red; const DIM_COLOR = chalk.dim; @@ -60,7 +64,7 @@ export const SUGGEST_TO_CONTAIN_EQUAL = chalk.dim( 'Looks like you wanted to test for object/array equality with the stricter `toContain` matcher. You probably need to use `toContainEqual` instead.', ); -export const stringify = (object: any, maxDepth?: number = 10): string => { +export const stringify = (object: unknown, maxDepth: number = 10): string => { const MAX_LENGTH = 10000; let result; @@ -87,15 +91,15 @@ export const stringify = (object: any, maxDepth?: number = 10): string => { export const highlightTrailingWhitespace = (text: string): string => text.replace(/\s+$/gm, chalk.inverse('$&')); -export const printReceived = (object: any) => +export const printReceived = (object: unknown) => RECEIVED_COLOR(highlightTrailingWhitespace(stringify(object))); -export const printExpected = (value: any) => +export const printExpected = (value: unknown) => EXPECTED_COLOR(highlightTrailingWhitespace(stringify(value))); export const printWithType = ( name: string, // 'Expected' or 'Received' - value: any, - print: (value: any) => string, // printExpected or printReceived + value: unknown, + print: (value: unknown) => string, // printExpected or printReceived ) => { const type = getType(value); const hasType = @@ -107,7 +111,7 @@ export const printWithType = ( }; export const ensureNoExpected = ( - expected: any, + expected: unknown, matcherName: string, options?: MatcherHintOptions, ) => { @@ -126,7 +130,7 @@ export const ensureNoExpected = ( } }; -export const ensureActualIsNumber = (actual: any, matcherName: string) => { +export const ensureActualIsNumber = (actual: unknown, matcherName: string) => { matcherName || (matcherName = 'This matcher'); if (typeof actual !== 'number') { throw new Error( @@ -139,7 +143,10 @@ export const ensureActualIsNumber = (actual: any, matcherName: string) => { } }; -export const ensureExpectedIsNumber = (expected: any, matcherName: string) => { +export const ensureExpectedIsNumber = ( + expected: unknown, + matcherName: string, +) => { matcherName || (matcherName = 'This matcher'); if (typeof expected !== 'number') { throw new Error( @@ -153,8 +160,8 @@ export const ensureExpectedIsNumber = (expected: any, matcherName: string) => { }; export const ensureNumbers = ( - actual: any, - expected: any, + actual: unknown, + expected: unknown, matcherName: string, ) => { ensureActualIsNumber(actual, matcherName); @@ -164,7 +171,7 @@ export const ensureNumbers = ( // Sometimes, e.g. when comparing two numbers, the output from jest-diff // does not contain more information than the `Expected:` / `Received:` already gives. // In those cases, we do not print a diff to make the output shorter and not redundant. -const shouldPrintDiff = (actual: any, expected: any) => { +const shouldPrintDiff = (actual: unknown, expected: unknown) => { if (typeof actual === 'number' && typeof expected === 'number') { return false; } @@ -184,7 +191,7 @@ export const pluralize = (word: string, count: number) => // return function which given each string, returns the label: // string, colon, space, and enough padding spaces to align the value. -type PrintLabel = string => string; +type PrintLabel = (string: string) => string; export const getLabelPrinter = (...strings: Array): PrintLabel => { const maxLength = strings.reduce( diff --git a/packages/jest-matcher-utils/tsconfig.json b/packages/jest-matcher-utils/tsconfig.json new file mode 100644 index 000000000000..2248b5ff7fa1 --- /dev/null +++ b/packages/jest-matcher-utils/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [ + {"path": "../jest-diff"}, + {"path": "../jest-get-type"}, + {"path": "../pretty-format"} + ] +} From ce29b7285c96c4a434866e09f8e964800d417da1 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Fri, 8 Feb 2019 12:53:36 +0100 Subject: [PATCH 030/107] chore: migrate jest-docblock to TypeScript (#7836) --- CHANGELOG.md | 1 + .../{index.test.js => index.test.ts} | 4 ---- .../jest-docblock/src/{index.js => index.ts} | 21 +++++++++---------- packages/jest-docblock/tsconfig.json | 7 +++++++ 4 files changed, 18 insertions(+), 15 deletions(-) rename packages/jest-docblock/src/__tests__/{index.test.js => index.test.ts} (99%) rename packages/jest-docblock/src/{index.js => index.ts} (88%) create mode 100644 packages/jest-docblock/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 0050a9f04365..4fb53e9eb963 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - `[jest-leak-detector]`: Migrate to TypeScript ([#7825](https://github.com/facebook/jest/pull/7825)) - `[jest-changed-files]`: Migrate to TypeScript ([#7827](https://github.com/facebook/jest/pull/7827)) - `[jest-matcher-utils]`: Migrate to TypeScript ([#7835](https://github.com/facebook/jest/pull/7835)) +- `[jest-docblock]`: Migrate to TypeScript ([#7836](https://github.com/facebook/jest/pull/7836)) ### Performance diff --git a/packages/jest-docblock/src/__tests__/index.test.js b/packages/jest-docblock/src/__tests__/index.test.ts similarity index 99% rename from packages/jest-docblock/src/__tests__/index.test.js rename to packages/jest-docblock/src/__tests__/index.test.ts index aba7bf160607..5df6ce09c552 100644 --- a/packages/jest-docblock/src/__tests__/index.test.js +++ b/packages/jest-docblock/src/__tests__/index.test.ts @@ -4,12 +4,8 @@ * * 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 os from 'os'; import * as docblock from '..'; diff --git a/packages/jest-docblock/src/index.js b/packages/jest-docblock/src/index.ts similarity index 88% rename from packages/jest-docblock/src/index.js rename to packages/jest-docblock/src/index.ts index 408f790d765c..892a8a32cac3 100644 --- a/packages/jest-docblock/src/index.js +++ b/packages/jest-docblock/src/index.ts @@ -3,14 +3,12 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import detectNewline from 'detect-newline'; import {EOL} from 'os'; +import detectNewline from 'detect-newline'; -type Pragmas = {[key: string]: string | string[], __proto__: null}; +type Pragmas = {[key: string]: string | string[]}; const commentEndRe = /\*\/$/; const commentStartRe = /^\/\*\*/; @@ -37,7 +35,7 @@ export function parse(docblock: string): Pragmas { export function parseWithComments( docblock: string, -): {comments: string, pragmas: Pragmas} { +): {comments: string; pragmas: Pragmas} { const line = detectNewline(docblock) || EOL; docblock = docblock @@ -67,7 +65,7 @@ export function parseWithComments( typeof result[match[1]] === 'string' || Array.isArray(result[match[1]]) ) { - result[match[1]] = [].concat(result[match[1]], nextPragma); + result[match[1]] = ([] as string[]).concat(result[match[1]], nextPragma); } else { result[match[1]] = nextPragma; } @@ -79,9 +77,8 @@ export function print({ comments = '', pragmas = {}, }: { - comments?: string, - pragmas?: Pragmas, - __proto__: null, + comments?: string; + pragmas?: Pragmas; }): string { const line = detectNewline(comments) || EOL; const head = '/**'; @@ -122,6 +119,8 @@ export function print({ ); } -function printKeyValues(key, valueOrArray) { - return [].concat(valueOrArray).map(value => `@${key} ${value}`.trim()); +function printKeyValues(key: string, valueOrArray: string | string[]) { + return ([] as string[]) + .concat(valueOrArray) + .map(value => `@${key} ${value}`.trim()); } diff --git a/packages/jest-docblock/tsconfig.json b/packages/jest-docblock/tsconfig.json new file mode 100644 index 000000000000..7bb06bce6d20 --- /dev/null +++ b/packages/jest-docblock/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + } +} From d05105a46a0f13445fcd51d5034a2cdde517e014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Sat, 9 Feb 2019 10:39:34 +0100 Subject: [PATCH 031/107] chore: migrate jest-serializer to TypeScript (#7841) --- CHANGELOG.md | 1 + packages/jest-serializer/package.json | 1 + .../{index.test.js => index.test.ts} | 1 + packages/jest-serializer/src/global.d.ts | 11 +++++ .../src/{index.js => index.ts} | 40 ++++--------------- packages/jest-serializer/tsconfig.json | 7 ++++ 6 files changed, 29 insertions(+), 32 deletions(-) rename packages/jest-serializer/src/__tests__/{index.test.js => index.test.ts} (98%) create mode 100644 packages/jest-serializer/src/global.d.ts rename packages/jest-serializer/src/{index.js => index.ts} (83%) create mode 100644 packages/jest-serializer/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fb53e9eb963..5133b092ef0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - `[jest-changed-files]`: Migrate to TypeScript ([#7827](https://github.com/facebook/jest/pull/7827)) - `[jest-matcher-utils]`: Migrate to TypeScript ([#7835](https://github.com/facebook/jest/pull/7835)) - `[jest-docblock]`: Migrate to TypeScript ([#7836](https://github.com/facebook/jest/pull/7836)) +- `[jest-serializer]`: Migrate to TypeScript ([#7841](https://github.com/facebook/jest/pull/7841)) ### Performance diff --git a/packages/jest-serializer/package.json b/packages/jest-serializer/package.json index 0355c4e2c235..73386346ee8d 100644 --- a/packages/jest-serializer/package.json +++ b/packages/jest-serializer/package.json @@ -11,5 +11,6 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" } diff --git a/packages/jest-serializer/src/__tests__/index.test.js b/packages/jest-serializer/src/__tests__/index.test.ts similarity index 98% rename from packages/jest-serializer/src/__tests__/index.test.js rename to packages/jest-serializer/src/__tests__/index.test.ts index 5546ca0addc7..f23cf3651525 100644 --- a/packages/jest-serializer/src/__tests__/index.test.js +++ b/packages/jest-serializer/src/__tests__/index.test.ts @@ -34,6 +34,7 @@ const objs = [ {key1: 'foo', key2: 'bar', key3: {array: [null, {}]}}, {minusInf: -Infinity, nan: NaN, plusInf: +Infinity}, {date: new Date(1234567890), re: /foo/gi}, + // @ts-ignore - testing NaN {map: new Map([[NaN, 4], [undefined, 'm']]), set: new Set([undefined, NaN])}, {buf: Buffer.from([0, 255, 127])}, ]; diff --git a/packages/jest-serializer/src/global.d.ts b/packages/jest-serializer/src/global.d.ts new file mode 100644 index 000000000000..809c49911f31 --- /dev/null +++ b/packages/jest-serializer/src/global.d.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +declare module 'v8' { + function serialize(value: unknown): Buffer; + function deserialize(value: Buffer): unknown; +} diff --git a/packages/jest-serializer/src/index.js b/packages/jest-serializer/src/index.ts similarity index 83% rename from packages/jest-serializer/src/index.js rename to packages/jest-serializer/src/index.ts index 357e8c2f50be..c9b3525ea613 100644 --- a/packages/jest-serializer/src/index.js +++ b/packages/jest-serializer/src/index.ts @@ -3,16 +3,12 @@ * * 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 fs from 'fs'; import v8 from 'v8'; -import type {Path} from 'types/Config'; +type Path = string; // JSON and V8 serializers are both stable when it comes to compatibility. The // current JSON specification is well defined in RFC 8259, and V8 ensures that @@ -23,7 +19,7 @@ const JS_TYPE = '__$t__'; const JS_VALUE = '__$v__'; const JS_VF = '__$f__'; -function replacer(key: string, value: any): any { +function replacer(_key: string, value: any): any { // NaN cannot be in a switch statement, because NaN !== NaN. if (Number.isNaN(value)) { return {[JS_TYPE]: 'n'}; @@ -32,10 +28,8 @@ function replacer(key: string, value: any): any { switch (value) { case undefined: return {[JS_TYPE]: 'u'}; - case +Infinity: return {[JS_TYPE]: '+'}; - case -Infinity: return {[JS_TYPE]: '-'}; } @@ -43,16 +37,12 @@ function replacer(key: string, value: any): any { switch (value && value.constructor) { case Date: return {[JS_TYPE]: 'd', [JS_VALUE]: value.getTime()}; - case RegExp: return {[JS_TYPE]: 'r', [JS_VALUE]: value.source, [JS_VF]: value.flags}; - case Set: return {[JS_TYPE]: 's', [JS_VALUE]: Array.from(value)}; - case Map: return {[JS_TYPE]: 'm', [JS_VALUE]: Array.from(value)}; - case Buffer: return {[JS_TYPE]: 'b', [JS_VALUE]: value.toString('latin1')}; } @@ -60,7 +50,7 @@ function replacer(key: string, value: any): any { return value; } -function reviver(key: string, value: any): any { +function reviver(_key: string, value: any): any { if (!value || (typeof value !== 'object' && !value.hasOwnProperty(JS_TYPE))) { return value; } @@ -68,28 +58,20 @@ function reviver(key: string, value: any): any { switch (value[JS_TYPE]) { case 'u': return undefined; - case 'n': return NaN; - case '+': return +Infinity; - case '-': return -Infinity; - case 'd': return new Date(value[JS_VALUE]); - case 'r': return new RegExp(value[JS_VALUE], value[JS_VF]); - case 's': return new Set(value[JS_VALUE]); - case 'm': return new Map(value[JS_VALUE]); - case 'b': return Buffer.from(value[JS_VALUE], 'latin1'); } @@ -97,7 +79,7 @@ function reviver(key: string, value: any): any { return value; } -function jsonStringify(content) { +function jsonStringify(content: unknown) { // Not pretty, but the ES JSON spec says that "toJSON" will be called before // getting into your replacer, so we have to remove them beforehand. See // https://www.ecma-international.org/ecma-262/#sec-serializejsonproperty @@ -109,37 +91,33 @@ function jsonStringify(content) { /* eslint-disable no-extend-native */ try { - // $FlowFixMe: intentional removal of "toJSON" property. + // @ts-ignore intentional removal of "toJSON" property. Date.prototype.toJSON = undefined; - // $FlowFixMe: intentional removal of "toJSON" property. + // @ts-ignore intentional removal of "toJSON" property. Buffer.prototype.toJSON = undefined; return JSON.stringify(content, replacer); } finally { - // $FlowFixMe: intentional assignment of "toJSON" property. Date.prototype.toJSON = dateToJSON; - // $FlowFixMe: intentional assignment of "toJSON" property. Buffer.prototype.toJSON = bufferToJSON; } /* eslint-enable no-extend-native */ } -function jsonParse(content) { +function jsonParse(content: string) { return JSON.parse(content, reviver); } // In memory functions. export function deserialize(buffer: Buffer): any { - // $FlowFixMe - Node 8+ only return v8.deserialize ? v8.deserialize(buffer) : jsonParse(buffer.toString('utf8')); } -export function serialize(content: any): Buffer { - // $FlowFixMe - Node 8+ only +export function serialize(content: unknown): Buffer { return v8.serialize ? v8.serialize(content) : Buffer.from(jsonStringify(content)); @@ -148,14 +126,12 @@ export function serialize(content: any): Buffer { // Synchronous filesystem functions. export function readFileSync(filePath: Path): any { - // $FlowFixMe - Node 8+ only return v8.deserialize ? v8.deserialize(fs.readFileSync(filePath)) : jsonParse(fs.readFileSync(filePath, 'utf8')); } export function writeFileSync(filePath: Path, content: any) { - // $FlowFixMe - Node 8+ only return v8.serialize ? fs.writeFileSync(filePath, v8.serialize(content)) : fs.writeFileSync(filePath, jsonStringify(content), 'utf8'); diff --git a/packages/jest-serializer/tsconfig.json b/packages/jest-serializer/tsconfig.json new file mode 100644 index 000000000000..7bb06bce6d20 --- /dev/null +++ b/packages/jest-serializer/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + } +} From 413a15bfbb6b43614fb0853ce59d75502ddd3191 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sat, 9 Feb 2019 17:13:16 +0100 Subject: [PATCH 032/107] chore: migrate jest-message-util to TypeScript (#7834) --- CHANGELOG.md | 2 + packages/jest-message-util/package.json | 5 +- ...ges.test.js.snap => messages.test.ts.snap} | 0 .../{messages.test.js => messages.test.ts} | 26 +- .../src/{index.js => index.ts} | 67 ++-- packages/jest-message-util/tsconfig.json | 10 + packages/jest-types/.npmignore | 3 + packages/jest-types/package.json | 15 + packages/jest-types/src/Config.ts | 349 ++++++++++++++++++ packages/jest-types/src/Console.ts | 38 ++ packages/jest-types/src/TestResult.ts | 251 +++++++++++++ packages/jest-types/src/index.ts | 12 + packages/jest-types/tsconfig.json | 7 + 13 files changed, 752 insertions(+), 33 deletions(-) rename packages/jest-message-util/src/__tests__/__snapshots__/{messages.test.js.snap => messages.test.ts.snap} (100%) rename packages/jest-message-util/src/__tests__/{messages.test.js => messages.test.ts} (87%) rename packages/jest-message-util/src/{index.js => index.ts} (88%) create mode 100644 packages/jest-message-util/tsconfig.json create mode 100644 packages/jest-types/.npmignore create mode 100644 packages/jest-types/package.json create mode 100644 packages/jest-types/src/Config.ts create mode 100644 packages/jest-types/src/Console.ts create mode 100644 packages/jest-types/src/TestResult.ts create mode 100644 packages/jest-types/src/index.ts create mode 100644 packages/jest-types/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 5133b092ef0f..817d95ea1333 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ - `[jest-matcher-utils]`: Migrate to TypeScript ([#7835](https://github.com/facebook/jest/pull/7835)) - `[jest-docblock]`: Migrate to TypeScript ([#7836](https://github.com/facebook/jest/pull/7836)) - `[jest-serializer]`: Migrate to TypeScript ([#7841](https://github.com/facebook/jest/pull/7841)) +- `[jest-message-util]`: Migrate to TypeScript ([#7834](https://github.com/facebook/jest/pull/7834)) +- `[@jest/types]`: New package to handle shared types ([#7834](https://github.com/facebook/jest/pull/7834)) ### Performance diff --git a/packages/jest-message-util/package.json b/packages/jest-message-util/package.json index e61c38de40c1..97a00e60466d 100644 --- a/packages/jest-message-util/package.json +++ b/packages/jest-message-util/package.json @@ -11,8 +11,11 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { "@babel/code-frame": "^7.0.0", + "@jest/types": "^24.1.0", + "@types/stack-utils": "^1.0.1", "chalk": "^2.0.1", "micromatch": "^3.1.10", "slash": "^2.0.0", @@ -21,7 +24,7 @@ "devDependencies": { "@types/babel__code-frame": "^7.0.0", "@types/micromatch": "^3.1.0", - "@types/stack-utils": "^1.0.1" + "@types/slash": "^2.0.0" }, "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" } diff --git a/packages/jest-message-util/src/__tests__/__snapshots__/messages.test.js.snap b/packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap similarity index 100% rename from packages/jest-message-util/src/__tests__/__snapshots__/messages.test.js.snap rename to packages/jest-message-util/src/__tests__/__snapshots__/messages.test.ts.snap diff --git a/packages/jest-message-util/src/__tests__/messages.test.js b/packages/jest-message-util/src/__tests__/messages.test.ts similarity index 87% rename from packages/jest-message-util/src/__tests__/messages.test.js rename to packages/jest-message-util/src/__tests__/messages.test.ts index a26c4dab944e..667038690713 100644 --- a/packages/jest-message-util/src/__tests__/messages.test.js +++ b/packages/jest-message-util/src/__tests__/messages.test.ts @@ -6,9 +6,7 @@ * */ -'use strict'; - -const {formatResultsErrors, formatExecError} = require('..'); +import {formatExecError, formatResultsErrors} from '..'; const unixStackTrace = ` ` + @@ -77,11 +75,16 @@ it('should exclude jasmine from stack trace for Unix paths.', () => { { ancestorTitles: [], failureMessages: [unixStackTrace], + fullName: 'full name', + location: null, + numPassingAsserts: 0, + status: 'failed', title: 'Unix test', }, ], { rootDir: '', + testMatch: [], }, { noStackTrace: false, @@ -95,9 +98,11 @@ it('.formatExecError()', () => { const message = formatExecError( { message: 'Whoops!', + stack: '', }, { rootDir: '', + testMatch: [], }, { noStackTrace: false, @@ -114,11 +119,16 @@ it('formatStackTrace should strip node internals', () => { { ancestorTitles: [], failureMessages: [assertionStack], + fullName: 'full name', + location: null, + numPassingAsserts: 0, + status: 'failed', title: 'Unix test', }, ], { rootDir: '', + testMatch: [], }, { noStackTrace: false, @@ -134,11 +144,16 @@ it('should not exclude vendor from stack trace', () => { { ancestorTitles: [], failureMessages: [vendorStack], + fullName: 'full name', + location: null, + numPassingAsserts: 0, + status: 'failed', title: 'Vendor test', }, ], { rootDir: '', + testMatch: [], }, { noStackTrace: false, @@ -154,11 +169,16 @@ it('retains message in babel code frame error', () => { { ancestorTitles: [], failureMessages: [babelStack], + fullName: 'full name', + location: null, + numPassingAsserts: 0, + status: 'failed', title: 'Babel test', }, ], { rootDir: '', + testMatch: [], }, { noStackTrace: false, diff --git a/packages/jest-message-util/src/index.js b/packages/jest-message-util/src/index.ts similarity index 88% rename from packages/jest-message-util/src/index.js rename to packages/jest-message-util/src/index.ts index 48afb9ec2a31..a95be08b6332 100644 --- a/packages/jest-message-util/src/index.js +++ b/packages/jest-message-util/src/index.ts @@ -3,27 +3,28 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Glob, Path} from 'types/Config'; -import type {AssertionResult, SerializableError} from 'types/TestResult'; - import fs from 'fs'; import path from 'path'; +import {Config, TestResult} from '@jest/types'; import chalk from 'chalk'; import micromatch from 'micromatch'; import slash from 'slash'; import {codeFrameColumns} from '@babel/code-frame'; import StackUtils from 'stack-utils'; +type Glob = Config.Glob; +type Path = Config.Path; +type AssertionResult = TestResult.AssertionResult; +type SerializableError = TestResult.SerializableError; + // stack utils tries to create pretty stack by making paths relative. const stackUtils = new StackUtils({ cwd: 'something which does not exist', }); -let nodeInternals = []; +let nodeInternals: RegExp[] = []; try { nodeInternals = StackUtils.nodeInternals(); @@ -33,12 +34,12 @@ try { } type StackTraceConfig = { - rootDir: string, - testMatch: Array, + rootDir: string; + testMatch: Array; }; type StackTraceOptions = { - noStackTrace: boolean, + noStackTrace: boolean; }; const PATH_NODE_MODULES = `${path.sep}node_modules${path.sep}`; @@ -64,13 +65,13 @@ const NOT_EMPTY_LINE_REGEXP = /^(?!$)/gm; const indentAllLines = (lines: string, indent: string) => lines.replace(NOT_EMPTY_LINE_REGEXP, indent); -const trim = string => (string || '').trim(); +const trim = (string: string) => (string || '').trim(); // Some errors contain not only line numbers in stack traces // e.g. SyntaxErrors can contain snippets of code, and we don't // want to trim those, because they may have pointers to the column/character // which will get misaligned. -const trimPaths = string => +const trimPaths = (string: string) => string.match(STACK_PATH_REGEXP) ? trim(string) : string; const getRenderedCallsite = ( @@ -94,11 +95,11 @@ const getRenderedCallsite = ( // `before/after each` hooks). If it's thrown, none of the tests in the file // are executed. export const formatExecError = ( - error?: Error | SerializableError | string, + error: Error | SerializableError | string | undefined, config: StackTraceConfig, options: StackTraceOptions, - testPath: ?Path, - reuseMessage: ?boolean, + testPath?: Path, + reuseMessage?: boolean, ) => { if (!error || typeof error === 'number') { error = new Error(`Expected an Error, but "${String(error)}" was thrown`); @@ -198,7 +199,11 @@ const removeInternalStackEntries = ( }); }; -const formatPaths = (config: StackTraceConfig, relativeTestPath, line) => { +const formatPaths = ( + config: StackTraceConfig, + relativeTestPath: Path | null, + line: string, +) => { // Extract the file path from the trace line. const match = line.match(/(^\s*at .*?\(?)([^()]+)(:[0-9]+:[0-9]+\)?.*$)/); if (!match) { @@ -243,7 +248,7 @@ export const formatStackTrace = ( stack: string, config: StackTraceConfig, options: StackTraceOptions, - testPath: ?Path, + testPath?: Path, ) => { const lines = getStackTraceLines(stack, options); const topFrame = getTopFrame(lines); @@ -253,19 +258,15 @@ export const formatStackTrace = ( : null; if (topFrame) { - const filename = topFrame.file; + const {column, file: filename, line} = topFrame; - if (path.isAbsolute(filename)) { + if (line && filename && path.isAbsolute(filename)) { let fileContent; try { // TODO: check & read HasteFS instead of reading the filesystem: // see: https://github.com/facebook/jest/pull/5405#discussion_r164281696 fileContent = fs.readFileSync(filename, 'utf8'); - renderedCallsite = getRenderedCallsite( - fileContent, - topFrame.line, - topFrame.column, - ); + renderedCallsite = getRenderedCallsite(fileContent, line, column); } catch (e) { // the file does not exist or is inaccessible, we ignore } @@ -287,12 +288,20 @@ export const formatResultsErrors = ( testResults: Array, config: StackTraceConfig, options: StackTraceOptions, - testPath: ?Path, -): ?string => { - const failedResults = testResults.reduce((errors, result) => { - result.failureMessages.forEach(content => errors.push({content, result})); - return errors; - }, []); + testPath?: Path, +): string | null => { + type FailedResults = Array<{ + content: string; + result: AssertionResult; + }>; + + const failedResults: FailedResults = testResults.reduce( + (errors, result) => { + result.failureMessages.forEach(content => errors.push({content, result})); + return errors; + }, + [] as FailedResults, + ); if (!failedResults.length) { return null; diff --git a/packages/jest-message-util/tsconfig.json b/packages/jest-message-util/tsconfig.json new file mode 100644 index 000000000000..b802dbf5ce24 --- /dev/null +++ b/packages/jest-message-util/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [ + {"path": "../jest-types"} + ] +} diff --git a/packages/jest-types/.npmignore b/packages/jest-types/.npmignore new file mode 100644 index 000000000000..85e48fe7b0a4 --- /dev/null +++ b/packages/jest-types/.npmignore @@ -0,0 +1,3 @@ +**/__mocks__/** +**/__tests__/** +src diff --git a/packages/jest-types/package.json b/packages/jest-types/package.json new file mode 100644 index 000000000000..86018bd9b822 --- /dev/null +++ b/packages/jest-types/package.json @@ -0,0 +1,15 @@ +{ + "name": "@jest/types", + "version": "24.1.0", + "repository": { + "type": "git", + "url": "https://github.com/facebook/jest.git", + "directory": "packages/jest-types" + }, + "engines": { + "node": ">= 6" + }, + "license": "MIT", + "main": "build/index.js", + "types": "build/index.d.ts" +} diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts new file mode 100644 index 000000000000..bd74d87c6b8b --- /dev/null +++ b/packages/jest-types/src/Config.ts @@ -0,0 +1,349 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +export type Path = string; + +export type Glob = string; + +export type HasteConfig = { + computeSha1?: boolean; + defaultPlatform?: string | null | undefined; + hasteImplModulePath?: string; + platforms?: Array; + providesModuleNodeModules: Array; +}; + +export type ReporterConfig = [string, Object]; + +export type ConfigGlobals = Object; + +export type DefaultOptions = { + automock: boolean; + bail: number; + browser: boolean; + cache: boolean; + cacheDirectory: Path; + changedFilesWithAncestor: boolean; + clearMocks: boolean; + collectCoverage: boolean; + collectCoverageFrom: Array | null | undefined; + coverageDirectory: string | null | undefined; + coveragePathIgnorePatterns: Array; + coverageReporters: Array; + coverageThreshold: + | { + global: { + [key: string]: number; + }; + } + | null + | undefined; + cwd: Path; + dependencyExtractor: string | null | undefined; + errorOnDeprecated: boolean; + expand: boolean; + filter: Path | null | undefined; + forceCoverageMatch: Array; + globals: ConfigGlobals; + globalSetup: string | null | undefined; + globalTeardown: string | null | undefined; + haste: HasteConfig; + maxConcurrency: number; + moduleDirectories: Array; + moduleFileExtensions: Array; + moduleNameMapper: { + [key: string]: string; + }; + modulePathIgnorePatterns: Array; + noStackTrace: boolean; + notify: boolean; + notifyMode: string; + preset: string | null | undefined; + prettierPath: string | null | undefined; + projects: Array | null | undefined; + resetMocks: boolean; + resetModules: boolean; + resolver: Path | null | undefined; + restoreMocks: boolean; + rootDir: Path | null | undefined; + roots: Array | null | undefined; + runner: string; + runTestsByPath: boolean; + setupFiles: Array; + setupFilesAfterEnv: Array; + skipFilter: boolean; + snapshotSerializers: Array; + testEnvironment: string; + testEnvironmentOptions: Object; + testFailureExitCode: string | number; + testLocationInResults: boolean; + testMatch: Array; + testPathIgnorePatterns: Array; + testRegex: Array; + testResultsProcessor: string | null | undefined; + testRunner: string | null | undefined; + testURL: string; + timers: 'real' | 'fake'; + transform: + | { + [key: string]: string; + } + | null + | undefined; + transformIgnorePatterns: Array; + watchPathIgnorePatterns: Array; + useStderr: boolean; + verbose: boolean | null | undefined; + watch: boolean; + watchman: boolean; +}; + +export type InitialOptions = { + automock?: boolean; + bail?: boolean | number; + browser?: boolean; + cache?: boolean; + cacheDirectory?: Path; + clearMocks?: boolean; + changedFilesWithAncestor?: boolean; + changedSince?: string; + collectCoverage?: boolean; + collectCoverageFrom?: Array; + collectCoverageOnlyFrom?: { + [key: string]: boolean; + }; + coverageDirectory?: string; + coveragePathIgnorePatterns?: Array; + coverageReporters?: Array; + coverageThreshold?: { + global: { + [key: string]: number; + }; + }; + dependencyExtractor?: string; + detectLeaks?: boolean; + detectOpenHandles?: boolean; + displayName?: string; + expand?: boolean; + extraGlobals?: Array; + filter?: Path; + findRelatedTests?: boolean; + forceCoverageMatch?: Array; + forceExit?: boolean; + json?: boolean; + globals?: ConfigGlobals; + globalSetup?: string | null | undefined; + globalTeardown?: string | null | undefined; + haste?: HasteConfig; + reporters?: Array; + logHeapUsage?: boolean; + lastCommit?: boolean; + listTests?: boolean; + mapCoverage?: boolean; + maxConcurrency?: number; + moduleDirectories?: Array; + moduleFileExtensions?: Array; + moduleLoader?: Path; + moduleNameMapper?: { + [key: string]: string; + }; + modulePathIgnorePatterns?: Array; + modulePaths?: Array; + name?: string; + noStackTrace?: boolean; + notify?: boolean; + notifyMode?: string; + onlyChanged?: boolean; + outputFile?: Path; + passWithNoTests?: boolean; + preprocessorIgnorePatterns?: Array; + preset?: string | null | undefined; + prettierPath?: string | null | undefined; + projects?: Array; + replname?: string | null | undefined; + resetMocks?: boolean; + resetModules?: boolean; + resolver?: Path | null | undefined; + restoreMocks?: boolean; + rootDir: Path; + roots?: Array; + runner?: string; + runTestsByPath?: boolean; + scriptPreprocessor?: string; + setupFiles?: Array; + setupTestFrameworkScriptFile?: Path; + setupFilesAfterEnv?: Array; + silent?: boolean; + skipFilter?: boolean; + skipNodeResolution?: boolean; + snapshotResolver?: Path; + snapshotSerializers?: Array; + errorOnDeprecated?: boolean; + testEnvironment?: string; + testEnvironmentOptions?: Object; + testFailureExitCode?: string | number; + testLocationInResults?: boolean; + testMatch?: Array; + testNamePattern?: string; + testPathDirs?: Array; + testPathIgnorePatterns?: Array; + testRegex?: string | Array; + testResultsProcessor?: string | null | undefined; + testRunner?: string; + testURL?: string; + timers?: 'real' | 'fake'; + transform?: { + [key: string]: string; + }; + transformIgnorePatterns?: Array; + watchPathIgnorePatterns?: Array; + unmockedModulePathPatterns?: Array; + updateSnapshot?: boolean; + useStderr?: boolean; + verbose?: boolean | null | undefined; + watch?: boolean; + watchAll?: boolean; + watchman?: boolean; + watchPlugins?: Array; +}; + +export type SnapshotUpdateState = 'all' | 'new' | 'none'; + +export type GlobalConfig = { + bail: number; + changedSince: string; + changedFilesWithAncestor: boolean; + collectCoverage: boolean; + collectCoverageFrom: Array; + collectCoverageOnlyFrom: + | { + [key: string]: boolean; + } + | null + | undefined; + coverageDirectory: string; + coveragePathIgnorePatterns?: Array; + coverageReporters: Array; + coverageThreshold: { + global: { + [key: string]: number; + }; + }; + detectLeaks: boolean; + detectOpenHandles: boolean; + enabledTestsMap: + | { + [key: string]: { + [key: string]: boolean; + }; + } + | null + | undefined; + expand: boolean; + extraGlobals: Array; + filter: Path | null | undefined; + findRelatedTests: boolean; + forceExit: boolean; + json: boolean; + globalSetup: string | null | undefined; + globalTeardown: string | null | undefined; + lastCommit: boolean; + logHeapUsage: boolean; + listTests: boolean; + maxConcurrency: number; + maxWorkers: number; + noStackTrace: boolean; + nonFlagArgs: Array; + noSCM: boolean | null | undefined; + notify: boolean; + notifyMode: string; + outputFile: Path | null | undefined; + onlyChanged: boolean; + onlyFailures: boolean; + passWithNoTests: boolean; + projects: Array; + replname: string | null | undefined; + reporters: Array; + runTestsByPath: boolean; + rootDir: Path; + silent: boolean; + skipFilter: boolean; + errorOnDeprecated: boolean; + testFailureExitCode: number; + testNamePattern: string; + testPathPattern: string; + testResultsProcessor: string | null | undefined; + updateSnapshot: SnapshotUpdateState; + useStderr: boolean; + verbose: boolean | null | undefined; + watch: boolean; + watchAll: boolean; + watchman: boolean; + watchPlugins: + | Array<{ + path: string; + config: Object; + }> + | null + | undefined; +}; + +export type ProjectConfig = { + automock: boolean; + browser: boolean; + cache: boolean; + cacheDirectory: Path; + clearMocks: boolean; + coveragePathIgnorePatterns: Array; + cwd: Path; + dependencyExtractor?: string; + detectLeaks: boolean; + detectOpenHandles: boolean; + displayName: string | null | undefined; + errorOnDeprecated: boolean; + extraGlobals: Array; + filter: Path | null | undefined; + forceCoverageMatch: Array; + globalSetup: string | null | undefined; + globalTeardown: string | null | undefined; + globals: ConfigGlobals; + haste: HasteConfig; + moduleDirectories: Array; + moduleFileExtensions: Array; + moduleLoader: Path; + moduleNameMapper: Array<[string, string]>; + modulePathIgnorePatterns: Array; + modulePaths: Array; + name: string; + prettierPath: string; + resetMocks: boolean; + resetModules: boolean; + resolver: Path | null | undefined; + restoreMocks: boolean; + rootDir: Path; + roots: Array; + runner: string; + setupFiles: Array; + setupFilesAfterEnv: Array; + skipFilter: boolean; + skipNodeResolution: boolean; + snapshotResolver: Path | null | undefined; + snapshotSerializers: Array; + testEnvironment: string; + testEnvironmentOptions: Object; + testMatch: Array; + testLocationInResults: boolean; + testPathIgnorePatterns: Array; + testRegex: Array; + testRunner: string; + testURL: string; + timers: 'real' | 'fake'; + transform: Array<[string, Path]>; + transformIgnorePatterns: Array; + watchPathIgnorePatterns: Array; + unmockedModulePathPatterns: Array | null | undefined; +}; diff --git a/packages/jest-types/src/Console.ts b/packages/jest-types/src/Console.ts new file mode 100644 index 000000000000..c85bef6639d3 --- /dev/null +++ b/packages/jest-types/src/Console.ts @@ -0,0 +1,38 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +export type LogMessage = string; + +export type LogEntry = { + message: LogMessage; + origin: string; + type: LogType; +}; + +export type LogCounters = { + [label: string]: number; +}; + +export type LogTimers = { + [label: string]: Date; +}; + +export type LogType = + | 'assert' + | 'count' + | 'debug' + | 'dir' + | 'dirxml' + | 'error' + | 'group' + | 'groupCollapsed' + | 'info' + | 'log' + | 'time' + | 'warn'; + +export type ConsoleBuffer = Array; diff --git a/packages/jest-types/src/TestResult.ts b/packages/jest-types/src/TestResult.ts new file mode 100644 index 000000000000..7c3b082836fd --- /dev/null +++ b/packages/jest-types/src/TestResult.ts @@ -0,0 +1,251 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import {ConsoleBuffer} from './Console'; + +export type RawFileCoverage = { + path: string; + s: { + [statementId: number]: number; + }; + b: { + [branchId: number]: number; + }; + f: { + [functionId: number]: number; + }; + l: { + [lineId: number]: number; + }; + fnMap: { + [functionId: number]: any; + }; + statementMap: { + [statementId: number]: any; + }; + branchMap: { + [branchId: number]: any; + }; + inputSourceMap?: Object; +}; + +export type RawCoverage = { + [filePath: string]: RawFileCoverage; +}; +type FileCoverageTotal = { + total: number; + covered: number; + skipped: number; + pct: number; +}; + +export type CoverageSummary = { + lines: FileCoverageTotal; + statements: FileCoverageTotal; + branches: FileCoverageTotal; + functions: FileCoverageTotal; + merge: (other: CoverageSummary) => undefined; +}; + +export type FileCoverage = { + getLineCoverage: () => Object; + getUncoveredLines: () => Array; + getBranchCoverageByLine: () => Object; + toJSON: () => Object; + merge: (other: Object) => undefined; + computeSimpleTotals: (property: string) => FileCoverageTotal; + computeBranchTotals: () => FileCoverageTotal; + resetHits: () => undefined; + toSummary: () => CoverageSummary; +}; + +export type CoverageMap = { + merge: (data: Object) => undefined; + getCoverageSummary: () => FileCoverage; + data: RawCoverage; + addFileCoverage: (fileCoverage: RawFileCoverage) => undefined; + files: () => Array; + fileCoverageFor: (file: string) => FileCoverage; +}; + +export type SerializableError = { + code?: unknown; + message: string; + stack: string | null | undefined; + type?: string; +}; + +export type FailedAssertion = { + matcherName: string; + message?: string; + actual?: any; + pass?: boolean; + expected?: any; + isNot?: boolean; + stack?: string; +}; + +export type AssertionLocation = { + fullName: string; + path: string; +}; + +export type Status = + | 'passed' + | 'failed' + | 'skipped' + | 'pending' + | 'todo' + | 'disabled'; + +export type Bytes = number; + +export type Milliseconds = number; +type Callsite = { + column: number; + line: number; +}; + +export type AssertionResult = { + ancestorTitles: Array; + duration?: Milliseconds | null | undefined; + failureMessages: Array; + fullName: string; + invocations?: number; + location: Callsite | null | undefined; + numPassingAsserts: number; + status: Status; + title: string; +}; + +export type FormattedAssertionResult = { + failureMessages: Array | null; + fullName: string; + location: Callsite | null | undefined; + status: Status; + title: string; +}; + +export type AggregatedResultWithoutCoverage = { + numFailedTests: number; + numFailedTestSuites: number; + numPassedTests: number; + numPassedTestSuites: number; + numPendingTests: number; + numTodoTests: number; + numPendingTestSuites: number; + numRuntimeErrorTestSuites: number; + numTotalTests: number; + numTotalTestSuites: number; + openHandles: Array; + snapshot: SnapshotSummary; + startTime: number; + success: boolean; + testResults: Array; + wasInterrupted: boolean; +}; + +export type AggregatedResult = AggregatedResultWithoutCoverage & { + coverageMap?: CoverageMap | null | undefined; +}; + +export type Suite = { + title: string; + suites: Array; + tests: Array; +}; + +export type TestResult = { + console: ConsoleBuffer | null | undefined; + coverage?: RawCoverage; + displayName: string | null | undefined; + failureMessage: string | null | undefined; + leaks: boolean; + memoryUsage?: Bytes; + numFailingTests: number; + numPassingTests: number; + numPendingTests: number; + numTodoTests: number; + openHandles: Array; + perfStats: { + end: Milliseconds; + start: Milliseconds; + }; + skipped: boolean; + snapshot: { + added: number; + fileDeleted: boolean; + matched: number; + unchecked: number; + uncheckedKeys: Array; + unmatched: number; + updated: number; + }; + sourceMaps: { + [sourcePath: string]: string; + }; + testExecError?: SerializableError; + testFilePath: string; + testResults: Array; +}; + +export type FormattedTestResult = { + message: string; + name: string; + summary: string; + status: 'failed' | 'passed'; + startTime: number; + endTime: number; + coverage: any; + assertionResults: Array; +}; + +export type FormattedTestResults = { + coverageMap?: CoverageMap | null | undefined; + numFailedTests: number; + numFailedTestSuites: number; + numPassedTests: number; + numPassedTestSuites: number; + numPendingTests: number; + numPendingTestSuites: number; + numRuntimeErrorTestSuites: number; + numTotalTests: number; + numTotalTestSuites: number; + snapshot: SnapshotSummary; + startTime: number; + success: boolean; + testResults: Array; + wasInterrupted: boolean; +}; + +export type CodeCoverageReporter = any; + +export type CodeCoverageFormatter = ( + coverage: RawCoverage | null | undefined, + reporter: CodeCoverageReporter, +) => Object | null | undefined; + +export type UncheckedSnapshot = { + filePath: string; + keys: Array; +}; + +export type SnapshotSummary = { + added: number; + didUpdate: boolean; + failure: boolean; + filesAdded: number; + filesRemoved: number; + filesUnmatched: number; + filesUpdated: number; + matched: number; + total: number; + unchecked: number; + uncheckedKeysByFile: Array; + unmatched: number; + updated: number; +}; diff --git a/packages/jest-types/src/index.ts b/packages/jest-types/src/index.ts new file mode 100644 index 000000000000..60df27cb7dfd --- /dev/null +++ b/packages/jest-types/src/index.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import * as Config from './Config'; +import * as Console from './Console'; +import * as TestResult from './TestResult'; + +export {Config, Console, TestResult}; diff --git a/packages/jest-types/tsconfig.json b/packages/jest-types/tsconfig.json new file mode 100644 index 000000000000..7bb06bce6d20 --- /dev/null +++ b/packages/jest-types/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + } +} From 4a66dd0091931990b182258ceeca05305975f420 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sun, 10 Feb 2019 10:26:23 +0100 Subject: [PATCH 033/107] chore: migrate jest-util to TypeScript (#7844) --- CHANGELOG.md | 1 + .../consoleAfterTeardown.test.js.snap | 2 +- packages/jest-circus/src/index.js | 1 + packages/jest-circus/src/utils.js | 1 + packages/jest-message-util/src/index.ts | 11 +- packages/jest-runner/src/runTest.js | 2 - .../src/SourceMaps.ts} | 7 +- packages/jest-types/src/TestResult.ts | 9 +- packages/jest-types/src/index.ts | 3 +- packages/jest-util/package.json | 9 +- packages/jest-util/src/BufferedConsole.js | 167 ------ packages/jest-util/src/BufferedConsole.ts | 161 ++++++ packages/jest-util/src/CustomConsole.js | 143 ------ packages/jest-util/src/CustomConsole.ts | 143 ++++++ .../{ErrorWithStack.js => ErrorWithStack.ts} | 4 +- .../src/{FakeTimers.js => FakeTimers.ts} | 133 ++--- .../src/{NullConsole.js => NullConsole.ts} | 2 - ...s.test.js.snap => fakeTimers.test.ts.snap} | 0 ...onsole.test.js => bufferedConsole.test.ts} | 4 +- .../{console.test.js => console.test.ts} | 2 - ...ct.test.js => createProcessObject.test.ts} | 3 + ...licCopy.test.js => deepCyclicCopy.test.ts} | 21 +- ...thStack.test.js => errorWithStack.test.ts} | 2 - ...{fakeTimers.test.js => fakeTimers.test.ts} | 479 +++++++++++++----- ...ults.test.js => formatTestResults.test.ts} | 7 +- ...etCallsite.test.js => getCallsite.test.ts} | 7 +- ...shot.test.js => getFailedSnapshot.test.ts} | 5 + ...s.test.js => installCommonGlobals.test.ts} | 7 +- ...eractive.test.js => isInteractive.test.ts} | 10 +- .../src/{clearLine.js => clearLine.ts} | 5 +- ...String.js => convertDescriptorToString.ts} | 7 +- ...{createDirectory.js => createDirectory.ts} | 7 +- ...rocessObject.js => createProcessObject.ts} | 12 +- .../{deepCyclicCopy.js => deepCyclicCopy.ts} | 47 +- ...matTestResults.js => formatTestResults.ts} | 41 +- .../src/{getCallsite.js => getCallsite.ts} | 26 +- ...etConsoleOutput.js => getConsoleOutput.ts} | 11 +- ...shotTests.js => getFailedSnapshotTests.ts} | 8 +- packages/jest-util/src/{index.js => index.ts} | 4 +- ...mmonGlobals.js => installCommonGlobals.ts} | 17 +- .../{isInteractive.js => isInteractive.ts} | 2 +- ...SepForGlob.js => replacePathSepForGlob.ts} | 6 +- packages/jest-util/src/setGlobal.ts | 15 + .../src/{specialChars.js => specialChars.ts} | 2 - packages/jest-util/tsconfig.json | 8 + yarn.lock | 20 +- 46 files changed, 889 insertions(+), 695 deletions(-) rename packages/{jest-util/src/setGlobal.js => jest-types/src/SourceMaps.ts} (60%) delete mode 100644 packages/jest-util/src/BufferedConsole.js create mode 100644 packages/jest-util/src/BufferedConsole.ts delete mode 100644 packages/jest-util/src/CustomConsole.js create mode 100644 packages/jest-util/src/CustomConsole.ts rename packages/jest-util/src/{ErrorWithStack.js => ErrorWithStack.ts} (85%) rename packages/jest-util/src/{FakeTimers.js => FakeTimers.ts} (83%) rename packages/jest-util/src/{NullConsole.js => NullConsole.ts} (97%) rename packages/jest-util/src/__tests__/__snapshots__/{fakeTimers.test.js.snap => fakeTimers.test.ts.snap} (100%) rename packages/jest-util/src/__tests__/{bufferedConsole.test.js => bufferedConsole.test.ts} (99%) rename packages/jest-util/src/__tests__/{console.test.js => console.test.ts} (99%) rename packages/jest-util/src/__tests__/{createProcessObject.test.js => createProcessObject.test.ts} (98%) rename packages/jest-util/src/__tests__/{deepCyclicCopy.test.js => deepCyclicCopy.test.ts} (94%) rename packages/jest-util/src/__tests__/{errorWithStack.test.js => errorWithStack.test.ts} (98%) rename packages/jest-util/src/__tests__/{fakeTimers.test.js => fakeTimers.test.ts} (73%) rename packages/jest-util/src/__tests__/{formatTestResults.test.js => formatTestResults.test.ts} (88%) rename packages/jest-util/src/__tests__/{getCallsite.test.js => getCallsite.test.ts} (90%) rename packages/jest-util/src/__tests__/{getFailedSnapshot.test.js => getFailedSnapshot.test.ts} (95%) rename packages/jest-util/src/__tests__/{installCommonGlobals.test.js => installCommonGlobals.test.ts} (90%) rename packages/jest-util/src/__tests__/{isInteractive.test.js => isInteractive.test.ts} (90%) rename packages/jest-util/src/{clearLine.js => clearLine.ts} (73%) rename packages/jest-util/src/{convertDescriptorToString.js => convertDescriptorToString.ts} (89%) rename packages/jest-util/src/{createDirectory.js => createDirectory.ts} (76%) rename packages/jest-util/src/{createProcessObject.js => createProcessObject.ts} (92%) rename packages/jest-util/src/{deepCyclicCopy.js => deepCyclicCopy.ts} (71%) rename packages/jest-util/src/{formatTestResults.js => formatTestResults.ts} (68%) rename packages/jest-util/src/{getCallsite.js => getCallsite.ts} (72%) rename packages/jest-util/src/{getConsoleOutput.js => getConsoleOutput.ts} (88%) rename packages/jest-util/src/{getFailedSnapshotTests.js => getFailedSnapshotTests.ts} (76%) rename packages/jest-util/src/{index.js => index.ts} (97%) rename packages/jest-util/src/{installCommonGlobals.js => installCommonGlobals.ts} (83%) rename packages/jest-util/src/{isInteractive.js => isInteractive.ts} (56%) rename packages/jest-util/src/{replacePathSepForGlob.js => replacePathSepForGlob.ts} (68%) create mode 100644 packages/jest-util/src/setGlobal.ts rename packages/jest-util/src/{specialChars.js => specialChars.ts} (97%) create mode 100644 packages/jest-util/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 817d95ea1333..ea78999740ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - `[jest-serializer]`: Migrate to TypeScript ([#7841](https://github.com/facebook/jest/pull/7841)) - `[jest-message-util]`: Migrate to TypeScript ([#7834](https://github.com/facebook/jest/pull/7834)) - `[@jest/types]`: New package to handle shared types ([#7834](https://github.com/facebook/jest/pull/7834)) +- `[jest-util]`: Migrate to TypeScript ([#7844](https://github.com/facebook/jest/pull/7844)) ### Performance diff --git a/e2e/__tests__/__snapshots__/consoleAfterTeardown.test.js.snap b/e2e/__tests__/__snapshots__/consoleAfterTeardown.test.js.snap index 635de99d73cd..07ff6d62433e 100644 --- a/e2e/__tests__/__snapshots__/consoleAfterTeardown.test.js.snap +++ b/e2e/__tests__/__snapshots__/consoleAfterTeardown.test.js.snap @@ -15,6 +15,6 @@ PASS __tests__/console.test.js 15 | }); 16 | - at BufferedConsole.log (../../packages/jest-util/build/BufferedConsole.js:169:10) + at BufferedConsole.log (../../packages/jest-util/build/BufferedConsole.js:182:10) at log (__tests__/console.test.js:13:13) `; diff --git a/packages/jest-circus/src/index.js b/packages/jest-circus/src/index.js index 150fd8b6cb87..ca770f8f07ca 100644 --- a/packages/jest-circus/src/index.js +++ b/packages/jest-circus/src/index.js @@ -18,6 +18,7 @@ import type { TestMode, } from 'types/Circus'; import {bind as bindEach} from 'jest-each'; +// $FlowFixMe: Converted to TS import {ErrorWithStack} from 'jest-util'; import {dispatch} from './state'; diff --git a/packages/jest-circus/src/utils.js b/packages/jest-circus/src/utils.js index 4f079ece8c6e..80a58fc2d9cd 100644 --- a/packages/jest-circus/src/utils.js +++ b/packages/jest-circus/src/utils.js @@ -22,6 +22,7 @@ import type { TestName, TestResults, } from 'types/Circus'; +// $FlowFixMe: Converted to TS import {convertDescriptorToString} from 'jest-util'; import isGeneratorFn from 'is-generator-fn'; import co from 'co'; diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index a95be08b6332..4e1b969f5d23 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -14,7 +14,6 @@ import slash from 'slash'; import {codeFrameColumns} from '@babel/code-frame'; import StackUtils from 'stack-utils'; -type Glob = Config.Glob; type Path = Config.Path; type AssertionResult = TestResult.AssertionResult; type SerializableError = TestResult.SerializableError; @@ -33,12 +32,12 @@ try { // node internals in the browser though, so no issue. } -type StackTraceConfig = { - rootDir: string; - testMatch: Array; -}; +export type StackTraceConfig = Pick< + Config.ProjectConfig, + 'rootDir' | 'testMatch' +>; -type StackTraceOptions = { +export type StackTraceOptions = { noStackTrace: boolean; }; diff --git a/packages/jest-runner/src/runTest.js b/packages/jest-runner/src/runTest.js index f609e09d4c8d..88fe237666cb 100644 --- a/packages/jest-runner/src/runTest.js +++ b/packages/jest-runner/src/runTest.js @@ -39,7 +39,6 @@ function freezeConsole( testConsole: BufferedConsole | Console | NullConsole, config: ProjectConfig, ) { - // $FlowFixMe: overwrite it for pretty errors testConsole._log = function fakeConsolePush(_type, message) { const error = new ErrorWithStack( `${chalk.red( @@ -89,7 +88,6 @@ async function runTestInternal( if (customEnvironment) { testEnvironment = getTestEnvironment({ ...config, - // $FlowFixMe testEnvironment: customEnvironment, }); } diff --git a/packages/jest-util/src/setGlobal.js b/packages/jest-types/src/SourceMaps.ts similarity index 60% rename from packages/jest-util/src/setGlobal.js rename to packages/jest-types/src/SourceMaps.ts index c9c836aaddbd..e7a298633114 100644 --- a/packages/jest-util/src/setGlobal.js +++ b/packages/jest-types/src/SourceMaps.ts @@ -3,11 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Global} from 'types/Global'; - -export default (global: Global, key: string, value: any) => - (global[key] = value); +export type SourceMapRegistry = {[key: string]: string}; diff --git a/packages/jest-types/src/TestResult.ts b/packages/jest-types/src/TestResult.ts index 7c3b082836fd..841064692ff1 100644 --- a/packages/jest-types/src/TestResult.ts +++ b/packages/jest-types/src/TestResult.ts @@ -123,6 +123,7 @@ export type AssertionResult = { }; export type FormattedAssertionResult = { + ancestorTitles: Array; failureMessages: Array | null; fullName: string; location: Callsite | null | undefined; @@ -150,7 +151,7 @@ export type AggregatedResultWithoutCoverage = { }; export type AggregatedResult = AggregatedResultWithoutCoverage & { - coverageMap?: CoverageMap | null | undefined; + coverageMap?: CoverageMap | null; }; export type Suite = { @@ -160,10 +161,10 @@ export type Suite = { }; export type TestResult = { - console: ConsoleBuffer | null | undefined; + console?: ConsoleBuffer | null; coverage?: RawCoverage; - displayName: string | null | undefined; - failureMessage: string | null | undefined; + displayName?: string | null; + failureMessage?: string | null; leaks: boolean; memoryUsage?: Bytes; numFailingTests: number; diff --git a/packages/jest-types/src/index.ts b/packages/jest-types/src/index.ts index 60df27cb7dfd..61dfddbf9407 100644 --- a/packages/jest-types/src/index.ts +++ b/packages/jest-types/src/index.ts @@ -7,6 +7,7 @@ import * as Config from './Config'; import * as Console from './Console'; +import * as SourceMaps from './SourceMaps'; import * as TestResult from './TestResult'; -export {Config, Console, TestResult}; +export {Config, Console, SourceMaps, TestResult}; diff --git a/packages/jest-util/package.json b/packages/jest-util/package.json index 85f3410824a8..13e9e9f6c706 100644 --- a/packages/jest-util/package.json +++ b/packages/jest-util/package.json @@ -8,22 +8,27 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { + "@jest/types": "^24.1.0", + "@types/node": "*", "callsites": "^3.0.0", "chalk": "^2.0.1", "graceful-fs": "^4.1.15", "is-ci": "^2.0.0", "jest-message-util": "^24.0.0", + "jest-mock": "^24.0.0", "mkdirp": "^0.5.1", + "readable-stream": "^3.1.1", "slash": "^2.0.0", "source-map": "^0.6.0" }, "devDependencies": { - "@types/callsites": "^2.0.0", "@types/graceful-fs": "^4.1.2", "@types/is-ci": "^1.0.10", "@types/mkdirp": "^0.5.2", - "jest-mock": "^24.0.0" + "@types/readable-stream": "^2.3.0", + "@types/slash": "^2.0.0" }, "engines": { "node": ">= 6" diff --git a/packages/jest-util/src/BufferedConsole.js b/packages/jest-util/src/BufferedConsole.js deleted file mode 100644 index 53695331c7f3..000000000000 --- a/packages/jest-util/src/BufferedConsole.js +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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 type { - ConsoleBuffer, - LogMessage, - LogType, - LogCounters, - LogTimers, -} from 'types/Console'; - -import type {SourceMapRegistry} from 'types/SourceMaps'; - -import assert from 'assert'; -import {Console} from 'console'; -import {format} from 'util'; -import chalk from 'chalk'; -import getCallsite from './getCallsite'; - -export default class BufferedConsole extends Console { - _buffer: ConsoleBuffer; - _counters: LogCounters; - _timers: LogTimers; - _groupDepth: number; - _getSourceMaps: () => ?SourceMapRegistry; - - constructor(getSourceMaps: () => ?SourceMapRegistry) { - const buffer = []; - super({ - write: message => - BufferedConsole.write(buffer, 'log', message, null, getSourceMaps()), - }); - this._getSourceMaps = getSourceMaps; - this._buffer = buffer; - this._counters = {}; - this._timers = {}; - this._groupDepth = 0; - } - - static write( - buffer: ConsoleBuffer, - type: LogType, - message: LogMessage, - level: ?number, - sourceMaps: ?SourceMapRegistry, - ) { - const callsite = getCallsite(level != null ? level : 2, sourceMaps); - const origin = callsite.getFileName() + ':' + callsite.getLineNumber(); - - buffer.push({ - message, - origin, - type, - }); - - return buffer; - } - - _log(type: LogType, message: LogMessage) { - BufferedConsole.write( - this._buffer, - type, - ' '.repeat(this._groupDepth) + message, - 3, - this._getSourceMaps(), - ); - } - - assert(...args: Array) { - try { - assert(...args); - } catch (error) { - this._log('assert', error.toString()); - } - } - - count(label: string = 'default') { - if (!this._counters[label]) { - this._counters[label] = 0; - } - - this._log('count', format(`${label}: ${++this._counters[label]}`)); - } - - countReset(label: string = 'default') { - this._counters[label] = 0; - } - - debug(...args: Array) { - this._log('debug', format(...args)); - } - - dir(...args: Array) { - this._log('dir', format(...args)); - } - - dirxml(...args: Array) { - this._log('dirxml', format(...args)); - } - - error(...args: Array) { - this._log('error', format(...args)); - } - - group(...args: Array) { - this._groupDepth++; - - if (args.length > 0) { - this._log('group', chalk.bold(format(...args))); - } - } - - groupCollapsed(...args: Array) { - this._groupDepth++; - - if (args.length > 0) { - this._log('groupCollapsed', chalk.bold(format(...args))); - } - } - - groupEnd() { - if (this._groupDepth > 0) { - this._groupDepth--; - } - } - - info(...args: Array) { - this._log('info', format(...args)); - } - - log(...args: Array) { - this._log('log', format(...args)); - } - - time(label: string = 'default') { - if (this._timers[label]) { - return; - } - - this._timers[label] = new Date(); - } - - timeEnd(label: string = 'default') { - const startTime = this._timers[label]; - - if (startTime) { - const endTime = new Date(); - const time = endTime - startTime; - this._log('time', format(`${label}: ${time}ms`)); - delete this._timers[label]; - } - } - - warn(...args: Array) { - this._log('warn', format(...args)); - } - - getBuffer() { - return this._buffer; - } -} diff --git a/packages/jest-util/src/BufferedConsole.ts b/packages/jest-util/src/BufferedConsole.ts new file mode 100644 index 000000000000..8312b314444f --- /dev/null +++ b/packages/jest-util/src/BufferedConsole.ts @@ -0,0 +1,161 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import assert from 'assert'; +import {Console} from 'console'; +import {format} from 'util'; +import {Writable} from 'readable-stream'; +import chalk from 'chalk'; +import {Console as ConsoleType, SourceMaps} from '@jest/types'; +import getCallsite from './getCallsite'; + +export default class BufferedConsole extends Console { + private _buffer: ConsoleType.ConsoleBuffer; + private _counters: ConsoleType.LogCounters; + private _timers: ConsoleType.LogTimers; + private _groupDepth: number; + private _getSourceMaps: () => SourceMaps.SourceMapRegistry | null | undefined; + + constructor( + getSourceMaps: () => SourceMaps.SourceMapRegistry | null | undefined, + ) { + const buffer: ConsoleType.ConsoleBuffer = []; + super( + new Writable({ + write: message => + BufferedConsole.write(buffer, 'log', message, null, getSourceMaps()), + }), + ); + this._getSourceMaps = getSourceMaps; + this._buffer = buffer; + this._counters = {}; + this._timers = {}; + this._groupDepth = 0; + } + + static write( + buffer: ConsoleType.ConsoleBuffer, + type: ConsoleType.LogType, + message: ConsoleType.LogMessage, + level?: number | null, + sourceMaps?: SourceMaps.SourceMapRegistry | null, + ) { + const callsite = getCallsite(level != null ? level : 2, sourceMaps); + const origin = callsite.getFileName() + ':' + callsite.getLineNumber(); + + buffer.push({ + message, + origin, + type, + }); + + return buffer; + } + + private _log(type: ConsoleType.LogType, message: ConsoleType.LogMessage) { + BufferedConsole.write( + this._buffer, + type, + ' '.repeat(this._groupDepth) + message, + 3, + this._getSourceMaps(), + ); + } + + assert(value: any, message?: string | Error) { + try { + assert(value, message); + } catch (error) { + this._log('assert', error.toString()); + } + } + + count(label: string = 'default') { + if (!this._counters[label]) { + this._counters[label] = 0; + } + + this._log('count', format(`${label}: ${++this._counters[label]}`)); + } + + countReset(label: string = 'default') { + this._counters[label] = 0; + } + + debug(firstArg: any, ...rest: Array) { + this._log('debug', format(firstArg, ...rest)); + } + + dir(firstArg: any, ...rest: Array) { + this._log('dir', format(firstArg, ...rest)); + } + + dirxml(firstArg: any, ...rest: Array) { + this._log('dirxml', format(firstArg, ...rest)); + } + + error(firstArg: any, ...rest: Array) { + this._log('error', format(firstArg, ...rest)); + } + + group(title?: string, ...rest: Array) { + this._groupDepth++; + + if (title || rest.length > 0) { + this._log('group', chalk.bold(format(title, ...rest))); + } + } + + groupCollapsed(title?: string, ...rest: Array) { + this._groupDepth++; + + if (title || rest.length > 0) { + this._log('groupCollapsed', chalk.bold(format(title, ...rest))); + } + } + + groupEnd() { + if (this._groupDepth > 0) { + this._groupDepth--; + } + } + + info(firstArg: any, ...rest: Array) { + this._log('info', format(firstArg, ...rest)); + } + + log(firstArg: any, ...rest: Array) { + this._log('log', format(firstArg, ...rest)); + } + + time(label: string = 'default') { + if (this._timers[label]) { + return; + } + + this._timers[label] = new Date(); + } + + timeEnd(label: string = 'default') { + const startTime = this._timers[label]; + + if (startTime) { + const endTime = new Date(); + const time = endTime.getTime() - startTime.getTime(); + this._log('time', format(`${label}: ${time}ms`)); + delete this._timers[label]; + } + } + + warn(firstArg: any, ...rest: Array) { + this._log('warn', format(firstArg, ...rest)); + } + + getBuffer(): ConsoleType.ConsoleBuffer { + return this._buffer; + } +} diff --git a/packages/jest-util/src/CustomConsole.js b/packages/jest-util/src/CustomConsole.js deleted file mode 100644 index 3084dc3f38c7..000000000000 --- a/packages/jest-util/src/CustomConsole.js +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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 - */ -/* global stream$Writable */ - -import type {LogType, LogMessage, LogCounters, LogTimers} from 'types/Console'; - -import assert from 'assert'; -import {format} from 'util'; -import {Console} from 'console'; -import chalk from 'chalk'; -import clearLine from './clearLine'; - -type Formatter = (type: LogType, message: LogMessage) => string; - -export default class CustomConsole extends Console { - _stdout: stream$Writable; - _formatBuffer: Formatter; - _counters: LogCounters; - _timers: LogTimers; - _groupDepth: number; - - constructor( - stdout: stream$Writable, - stderr: stream$Writable, - formatBuffer: ?Formatter, - ) { - super(stdout, stderr); - this._formatBuffer = formatBuffer || ((type, message) => message); - this._counters = {}; - this._timers = {}; - this._groupDepth = 0; - } - - _logToParentConsole(message: string) { - super.log(message); - } - - _log(type: LogType, message: string) { - clearLine(this._stdout); - this._logToParentConsole( - this._formatBuffer(type, ' '.repeat(this._groupDepth) + message), - ); - } - - assert(...args: Array) { - try { - assert(...args); - } catch (error) { - this._log('assert', error.toString()); - } - } - - count(label: string = 'default') { - if (!this._counters[label]) { - this._counters[label] = 0; - } - - this._log('count', format(`${label}: ${++this._counters[label]}`)); - } - - countReset(label: string = 'default') { - this._counters[label] = 0; - } - - debug(...args: Array) { - this._log('debug', format(...args)); - } - - dir(...args: Array) { - this._log('dir', format(...args)); - } - - dirxml(...args: Array) { - this._log('dirxml', format(...args)); - } - - error(...args: Array) { - this._log('error', format(...args)); - } - - group(...args: Array) { - this._groupDepth++; - - if (args.length > 0) { - this._log('group', chalk.bold(format(...args))); - } - } - - groupCollapsed(...args: Array) { - this._groupDepth++; - - if (args.length > 0) { - this._log('groupCollapsed', chalk.bold(format(...args))); - } - } - - groupEnd() { - if (this._groupDepth > 0) { - this._groupDepth--; - } - } - - info(...args: Array) { - this._log('info', format(...args)); - } - - log(...args: Array) { - this._log('log', format(...args)); - } - - time(label: string = 'default') { - if (this._timers[label]) { - return; - } - - this._timers[label] = new Date(); - } - - timeEnd(label: string = 'default') { - const startTime = this._timers[label]; - - if (startTime) { - const endTime = new Date(); - const time = endTime - startTime; - this._log('time', format(`${label}: ${time}ms`)); - delete this._timers[label]; - } - } - - warn(...args: Array) { - this._log('warn', format(...args)); - } - - getBuffer() { - return null; - } -} diff --git a/packages/jest-util/src/CustomConsole.ts b/packages/jest-util/src/CustomConsole.ts new file mode 100644 index 000000000000..a2a3fb9a8950 --- /dev/null +++ b/packages/jest-util/src/CustomConsole.ts @@ -0,0 +1,143 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import assert from 'assert'; +import {format} from 'util'; +import {Console} from 'console'; +import chalk from 'chalk'; +import {Console as ConsoleType} from '@jest/types'; +import clearLine from './clearLine'; + +type Formatter = ( + type: ConsoleType.LogType, + message: ConsoleType.LogMessage, +) => string; + +export default class CustomConsole extends Console { + private _stdout: NodeJS.WritableStream; + private _formatBuffer: Formatter; + private _counters: ConsoleType.LogCounters; + private _timers: ConsoleType.LogTimers; + private _groupDepth: number; + + constructor( + stdout: NodeJS.WritableStream, + stderr: NodeJS.WritableStream, + formatBuffer: Formatter = (_type, message) => message, + ) { + super(stdout, stderr); + this._stdout = stdout; + this._formatBuffer = formatBuffer; + this._counters = {}; + this._timers = {}; + this._groupDepth = 0; + } + + private _logToParentConsole(message: string) { + super.log(message); + } + + private _log(type: ConsoleType.LogType, message: string) { + clearLine(this._stdout); + this._logToParentConsole( + this._formatBuffer(type, ' '.repeat(this._groupDepth) + message), + ); + } + + assert(value: any, message?: string | Error) { + try { + assert(value, message); + } catch (error) { + this._log('assert', error.toString()); + } + } + + count(label: string = 'default') { + if (!this._counters[label]) { + this._counters[label] = 0; + } + + this._log('count', format(`${label}: ${++this._counters[label]}`)); + } + + countReset(label: string = 'default') { + this._counters[label] = 0; + } + + debug(firstArg: any, ...args: Array) { + this._log('debug', format(firstArg, ...args)); + } + + dir(firstArg: any, ...args: Array) { + this._log('dir', format(firstArg, ...args)); + } + + dirxml(firstArg: any, ...args: Array) { + this._log('dirxml', format(firstArg, ...args)); + } + + error(firstArg: any, ...args: Array) { + this._log('error', format(firstArg, ...args)); + } + + group(title?: string, ...args: Array) { + this._groupDepth++; + + if (title || args.length > 0) { + this._log('group', chalk.bold(format(title, ...args))); + } + } + + groupCollapsed(title?: string, ...args: Array) { + this._groupDepth++; + + if (title || args.length > 0) { + this._log('groupCollapsed', chalk.bold(format(title, ...args))); + } + } + + groupEnd() { + if (this._groupDepth > 0) { + this._groupDepth--; + } + } + + info(firstArg: any, ...args: Array) { + this._log('info', format(firstArg, ...args)); + } + + log(firstArg: any, ...args: Array) { + this._log('log', format(firstArg, ...args)); + } + + time(label: string = 'default') { + if (this._timers[label]) { + return; + } + + this._timers[label] = new Date(); + } + + timeEnd(label: string = 'default') { + const startTime = this._timers[label]; + + if (startTime) { + const endTime = new Date().getTime(); + const time = endTime - startTime.getTime(); + this._log('time', format(`${label}: ${time}ms`)); + delete this._timers[label]; + } + } + + warn(firstArg: any, ...args: Array) { + this._log('warn', format(firstArg, ...args)); + } + + getBuffer() { + return null; + } +} diff --git a/packages/jest-util/src/ErrorWithStack.js b/packages/jest-util/src/ErrorWithStack.ts similarity index 85% rename from packages/jest-util/src/ErrorWithStack.js rename to packages/jest-util/src/ErrorWithStack.ts index 73015ce15c6f..871f44498c22 100644 --- a/packages/jest-util/src/ErrorWithStack.js +++ b/packages/jest-util/src/ErrorWithStack.ts @@ -3,12 +3,10 @@ * * 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 class ErrorWithStack extends Error { - constructor(message: ?string, callsite: Function) { + constructor(message: string | undefined, callsite: Function) { super(message); if (Error.captureStackTrace) { Error.captureStackTrace(this, callsite); diff --git a/packages/jest-util/src/FakeTimers.js b/packages/jest-util/src/FakeTimers.ts similarity index 83% rename from packages/jest-util/src/FakeTimers.js rename to packages/jest-util/src/FakeTimers.ts index c00a3c30da25..d8cfac38d266 100644 --- a/packages/jest-util/src/FakeTimers.js +++ b/packages/jest-util/src/FakeTimers.ts @@ -3,15 +3,14 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {ProjectConfig} from 'types/Config'; -import type {Global} from 'types/Global'; -import type {ModuleMocker} from 'types/Mock'; +// not yet migrated to TS +// import {ModuleMocker} from 'jest-mock'; + +type ModuleMocker = any; -import {formatStackTrace} from 'jest-message-util'; +import {formatStackTrace, StackTraceConfig} from 'jest-message-util'; import setGlobal from './setGlobal'; /** @@ -23,57 +22,50 @@ type Callback = (...args: any) => void; type TimerID = string; -type Tick = {| - uuid: string, - callback: Callback, -|}; +type Tick = { + uuid: string; + callback: Callback; +}; -type Timer = {| - type: string, - callback: Callback, - expiry: number, - interval: ?number, -|}; +type Timer = { + type: string; + callback: Callback; + expiry: number; + interval?: number; +}; type TimerAPI = { - clearImmediate(timeoutId?: number): void, - clearInterval(intervalId?: number): void, - clearTimeout(timeoutId?: number): void, - nextTick: (callback: Callback) => void, - - /** - * The additional arguments in the following methods are passed to the - * callback and thus we don't know their types ahead of time as they can be - * anything, which is why we are disabling the flowtype/no-weak-types rule - * here. - */ - - setImmediate(callback: Callback, ms?: number, ...args: Array): number, - setInterval(callback: Callback, ms?: number, ...args: Array): number, - setTimeout(callback: Callback, ms?: number, ...args: Array): number, + clearImmediate: typeof global.clearImmediate; + clearInterval: typeof global.clearInterval; + clearTimeout: typeof global.clearTimeout; + nextTick: typeof process.nextTick; + + setImmediate: typeof global.setImmediate; + setInterval: typeof global.setInterval; + setTimeout: typeof global.setTimeout; }; -type TimerConfig = {| - idToRef: (id: number) => Ref, - refToId: (ref: Ref) => ?number, -|}; +type TimerConfig = { + idToRef: (id: number) => Ref; + refToId: (ref: Ref) => number | void; +}; const MS_IN_A_YEAR = 31536000000; export default class FakeTimers { - _cancelledImmediates: {[key: TimerID]: boolean}; - _cancelledTicks: {[key: TimerID]: boolean}; - _config: ProjectConfig; - _disposed: boolean; - _fakeTimerAPIs: TimerAPI; - _global: Global; - _immediates: Array; + _cancelledImmediates!: {[key: string]: boolean}; + _cancelledTicks!: {[key: string]: boolean}; + _config: StackTraceConfig; + _disposed?: boolean; + _fakeTimerAPIs!: TimerAPI; + _global: NodeJS.Global; + _immediates!: Array; _maxLoops: number; _moduleMocker: ModuleMocker; - _now: number; - _ticks: Array; + _now!: number; + _ticks!: Array; _timerAPIs: TimerAPI; - _timers: {[key: TimerID]: Timer}; + _timers!: {[key: string]: Timer}; _uuidCounter: number; _timerConfig: TimerConfig; @@ -84,11 +76,11 @@ export default class FakeTimers { config, maxLoops, }: { - global: Global, - moduleMocker: ModuleMocker, - timerConfig: TimerConfig, - config: ProjectConfig, - maxLoops?: number, + global: NodeJS.Global; + moduleMocker: ModuleMocker; + timerConfig: TimerConfig; + config: StackTraceConfig; + maxLoops?: number; }) { this._global = global; this._timerConfig = timerConfig; @@ -355,7 +347,7 @@ export default class FakeTimers { `default configuration change in Jest 15.\n\n` + `Release Blog Post: https://jestjs.io/blog/2016/09/01/jest-15.html\n` + `Stack Trace:\n` + - formatStackTrace(new Error().stack, this._config, { + formatStackTrace(new Error().stack!, this._config, { noStackTrace: false, }), ); @@ -363,7 +355,8 @@ export default class FakeTimers { } _createMocks() { - const fn = impl => this._moduleMocker.fn().mockImplementation(impl); + const fn = (impl: Function) => + this._moduleMocker.fn().mockImplementation(impl); this._fakeTimerAPIs = { clearImmediate: fn(this._fakeClearImmediate.bind(this)), @@ -388,16 +381,11 @@ export default class FakeTimers { this._cancelledImmediates[uuid] = true; } - _fakeNextTick(callback: Callback) { + _fakeNextTick(callback: Callback, ...args: Array) { if (this._disposed) { return; } - const args = []; - for (let ii = 1, ll = arguments.length; ii < ll; ii++) { - args.push(arguments[ii]); - } - const uuid = String(this._uuidCounter++); this._ticks.push({ @@ -415,16 +403,11 @@ export default class FakeTimers { }); } - _fakeSetImmediate(callback: Callback) { + _fakeSetImmediate(callback: Callback, ...args: Array) { if (this._disposed) { return null; } - const args = []; - for (let ii = 1, ll = arguments.length; ii < ll; ii++) { - args.push(arguments[ii]); - } - const uuid = this._uuidCounter++; this._immediates.push({ @@ -444,7 +427,11 @@ export default class FakeTimers { return uuid; } - _fakeSetInterval(callback: Callback, intervalDelay?: number) { + _fakeSetInterval( + callback: Callback, + intervalDelay?: number, + ...args: Array + ) { if (this._disposed) { return null; } @@ -453,11 +440,6 @@ export default class FakeTimers { intervalDelay = 0; } - const args = []; - for (let ii = 2, ll = arguments.length; ii < ll; ii++) { - args.push(arguments[ii]); - } - const uuid = this._uuidCounter++; this._timers[String(uuid)] = { @@ -470,7 +452,7 @@ export default class FakeTimers { return this._timerConfig.idToRef(uuid); } - _fakeSetTimeout(callback: Callback, delay?: number) { + _fakeSetTimeout(callback: Callback, delay?: number, ...args: Array) { if (this._disposed) { return null; } @@ -478,17 +460,12 @@ export default class FakeTimers { // eslint-disable-next-line no-bitwise delay = Number(delay) | 0; - const args = []; - for (let ii = 2, ll = arguments.length; ii < ll; ii++) { - args.push(arguments[ii]); - } - const uuid = this._uuidCounter++; this._timers[String(uuid)] = { callback: () => callback.apply(null, args), expiry: this._now + delay, - interval: null, + interval: undefined, type: 'timeout', }; @@ -526,7 +503,7 @@ export default class FakeTimers { break; case 'interval': - timer.expiry = this._now + timer.interval; + timer.expiry = this._now + (timer.interval || 0); timer.callback(); break; diff --git a/packages/jest-util/src/NullConsole.js b/packages/jest-util/src/NullConsole.ts similarity index 97% rename from packages/jest-util/src/NullConsole.js rename to packages/jest-util/src/NullConsole.ts index 7c073abd2948..7f9f59ebad78 100644 --- a/packages/jest-util/src/NullConsole.js +++ b/packages/jest-util/src/NullConsole.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import CustomConsole from './CustomConsole'; diff --git a/packages/jest-util/src/__tests__/__snapshots__/fakeTimers.test.js.snap b/packages/jest-util/src/__tests__/__snapshots__/fakeTimers.test.ts.snap similarity index 100% rename from packages/jest-util/src/__tests__/__snapshots__/fakeTimers.test.js.snap rename to packages/jest-util/src/__tests__/__snapshots__/fakeTimers.test.ts.snap diff --git a/packages/jest-util/src/__tests__/bufferedConsole.test.js b/packages/jest-util/src/__tests__/bufferedConsole.test.ts similarity index 99% rename from packages/jest-util/src/__tests__/bufferedConsole.test.js rename to packages/jest-util/src/__tests__/bufferedConsole.test.ts index 1e8c7c3b1680..dbd3ce39d2eb 100644 --- a/packages/jest-util/src/__tests__/bufferedConsole.test.js +++ b/packages/jest-util/src/__tests__/bufferedConsole.test.ts @@ -3,15 +3,13 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import chalk from 'chalk'; import BufferedConsole from '../BufferedConsole'; describe('CustomConsole', () => { - let _console; + let _console: BufferedConsole; const stdout = () => _console .getBuffer() diff --git a/packages/jest-util/src/__tests__/console.test.js b/packages/jest-util/src/__tests__/console.test.ts similarity index 99% rename from packages/jest-util/src/__tests__/console.test.js rename to packages/jest-util/src/__tests__/console.test.ts index da8fdea7fddd..4b994fc6e888 100644 --- a/packages/jest-util/src/__tests__/console.test.js +++ b/packages/jest-util/src/__tests__/console.test.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import chalk from 'chalk'; diff --git a/packages/jest-util/src/__tests__/createProcessObject.test.js b/packages/jest-util/src/__tests__/createProcessObject.test.ts similarity index 98% rename from packages/jest-util/src/__tests__/createProcessObject.test.js rename to packages/jest-util/src/__tests__/createProcessObject.test.ts index b64cdc9796ed..4eca00c977a2 100644 --- a/packages/jest-util/src/__tests__/createProcessObject.test.js +++ b/packages/jest-util/src/__tests__/createProcessObject.test.ts @@ -18,7 +18,9 @@ it('creates a process object that looks like the original one', () => { // "_events" property is checked to ensure event emitter properties are // properly copied. ['argv', 'env', '_events'].forEach(key => { + // @ts-ignore expect(fakeProcess[key]).toEqual(process[key]); + // @ts-ignore expect(fakeProcess[key]).not.toBe(process[key]); }); @@ -36,6 +38,7 @@ it('checks that process.env works as expected on Linux platforms', () => { // Existing properties inside process.env are copied to the fake environment. process.env.PROP_STRING = 'foo'; + // @ts-ignore process.env.PROP_NUMBER = 3; process.env.PROP_UNDEFINED = undefined; diff --git a/packages/jest-util/src/__tests__/deepCyclicCopy.test.js b/packages/jest-util/src/__tests__/deepCyclicCopy.test.ts similarity index 94% rename from packages/jest-util/src/__tests__/deepCyclicCopy.test.js rename to packages/jest-util/src/__tests__/deepCyclicCopy.test.ts index 43b4645211a2..41bb93570f17 100644 --- a/packages/jest-util/src/__tests__/deepCyclicCopy.test.js +++ b/packages/jest-util/src/__tests__/deepCyclicCopy.test.ts @@ -23,6 +23,7 @@ it('returns the same value for primitive or function values', () => { it('does not execute getters/setters, but copies them', () => { const fn = jest.fn(); const obj = { + // @ts-ignore get foo() { fn(); }, @@ -48,7 +49,7 @@ it('copies arrays as array objects', () => { }); it('handles cyclic dependencies', () => { - const cyclic = {a: 42, subcycle: {}}; + const cyclic: any = {a: 42, subcycle: {}}; cyclic.subcycle.baz = cyclic; cyclic.bar = cyclic; @@ -83,9 +84,13 @@ it('uses the blacklist to avoid copying properties on the first level', () => { }); it('does not keep the prototype by default when top level is object', () => { + // @ts-ignore const sourceObject = new function() {}(); + // @ts-ignore sourceObject.nestedObject = new function() {}(); + // @ts-ignore sourceObject.nestedArray = new function() { + // @ts-ignore this.length = 0; }(); @@ -119,7 +124,9 @@ it('does not keep the prototype by default when top level is object', () => { it('does not keep the prototype by default when top level is array', () => { const spy = jest.spyOn(Array, 'isArray').mockImplementation(() => true); + // @ts-ignore const sourceArray = new function() { + // @ts-ignore this.length = 0; }(); @@ -135,7 +142,9 @@ it('does not keep the prototype by default when top level is array', () => { it('does not keep the prototype of arrays when keepPrototype = false', () => { const spy = jest.spyOn(Array, 'isArray').mockImplementation(() => true); + // @ts-ignore const sourceArray = new function() { + // @ts-ignore this.length = 0; }(); @@ -151,7 +160,9 @@ it('does not keep the prototype of arrays when keepPrototype = false', () => { it('keeps the prototype of arrays when keepPrototype = true', () => { const spy = jest.spyOn(Array, 'isArray').mockImplementation(() => true); + // @ts-ignore const sourceArray = new function() { + // @ts-ignore this.length = 0; }(); @@ -162,9 +173,13 @@ it('keeps the prototype of arrays when keepPrototype = true', () => { }); it('does not keep the prototype for objects when keepPrototype = false', () => { + // @ts-ignore const sourceobject = new function() {}(); + // @ts-ignore sourceobject.nestedObject = new function() {}(); + // @ts-ignore sourceobject.nestedArray = new function() { + // @ts-ignore this.length = 0; }(); @@ -195,9 +210,13 @@ it('does not keep the prototype for objects when keepPrototype = false', () => { }); it('keeps the prototype for objects when keepPrototype = true', () => { + // @ts-ignore const sourceObject = new function() {}(); + // @ts-ignore sourceObject.nestedObject = new function() {}(); + // @ts-ignore sourceObject.nestedArray = new function() { + // @ts-ignore this.length = 0; }(); diff --git a/packages/jest-util/src/__tests__/errorWithStack.test.js b/packages/jest-util/src/__tests__/errorWithStack.test.ts similarity index 98% rename from packages/jest-util/src/__tests__/errorWithStack.test.js rename to packages/jest-util/src/__tests__/errorWithStack.test.ts index 9c86c46f4003..e2c79e4de9eb 100644 --- a/packages/jest-util/src/__tests__/errorWithStack.test.js +++ b/packages/jest-util/src/__tests__/errorWithStack.test.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import ErrorWithStack from '../ErrorWithStack'; diff --git a/packages/jest-util/src/__tests__/fakeTimers.test.js b/packages/jest-util/src/__tests__/fakeTimers.test.ts similarity index 73% rename from packages/jest-util/src/__tests__/fakeTimers.test.js rename to packages/jest-util/src/__tests__/fakeTimers.test.ts index c39fb0c2a4e4..3a14fed27b93 100644 --- a/packages/jest-util/src/__tests__/fakeTimers.test.js +++ b/packages/jest-util/src/__tests__/fakeTimers.test.ts @@ -3,76 +3,109 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * */ -'use strict'; +import vm from 'vm'; +// @ts-ignore: not yet migrated +import mock, {ModuleMocker} from 'jest-mock'; +import FakeTimers from '../FakeTimers'; + +const timerConfig = { + idToRef: (id: number) => id, + refToId: (ref: number) => ref, +}; -const vm = require('vm'); +const config = { + rootDir: '/', + testMatch: [], +}; describe('FakeTimers', () => { - let FakeTimers, moduleMocker, timerConfig; + let moduleMocker: ModuleMocker; beforeEach(() => { - FakeTimers = require('../FakeTimers').default; - const mock = require('jest-mock'); const global = vm.runInNewContext('this'); moduleMocker = new mock.ModuleMocker(global); - - timerConfig = { - idToRef: (id: number) => id, - refToId: (ref: number) => ref, - }; }); describe('construction', () => { it('installs setTimeout mock', () => { - const global = {process}; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const global = ({process} as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); expect(global.setTimeout).not.toBe(undefined); }); it('installs clearTimeout mock', () => { - const global = {process}; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const global = ({process} as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); expect(global.clearTimeout).not.toBe(undefined); }); it('installs setInterval mock', () => { - const global = {process}; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const global = ({process} as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); expect(global.setInterval).not.toBe(undefined); }); it('installs clearInterval mock', () => { - const global = {process}; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const global = ({process} as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); expect(global.clearInterval).not.toBe(undefined); }); it('mocks process.nextTick if it exists on global', () => { const origNextTick = () => {}; - const global = { + const global = ({ process: { nextTick: origNextTick, }, - }; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + } as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); expect(global.process.nextTick).not.toBe(origNextTick); }); it('mocks setImmediate if it exists on global', () => { const origSetImmediate = () => {}; - const global = { + const global = ({ process, setImmediate: origSetImmediate, - }; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + } as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); expect(global.setImmediate).not.toBe(origSetImmediate); }); @@ -80,12 +113,17 @@ describe('FakeTimers', () => { it('mocks clearImmediate if setImmediate is on global', () => { const origSetImmediate = () => {}; const origClearImmediate = () => {}; - const global = { + const global = ({ clearImmediate: origClearImmediate, process, setImmediate: origSetImmediate, - }; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + } as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); expect(global.clearImmediate).not.toBe(origClearImmediate); }); @@ -93,16 +131,21 @@ describe('FakeTimers', () => { describe('runAllTicks', () => { it('runs all ticks, in order', () => { - const global = { + const global = ({ process: { nextTick: () => {}, }, - }; + } as unknown) as NodeJS.Global; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); - const runOrder = []; + const runOrder: Array = []; const mock1 = jest.fn(() => runOrder.push('mock1')); const mock2 = jest.fn(() => runOrder.push('mock2')); @@ -121,13 +164,18 @@ describe('FakeTimers', () => { it('does nothing when no ticks have been scheduled', () => { const nextTick = jest.fn(); - const global = { + const global = ({ process: { nextTick, }, - }; + } as unknown) as NodeJS.Global; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); timers.runAllTicks(); @@ -135,13 +183,18 @@ describe('FakeTimers', () => { }); it('only runs a scheduled callback once', () => { - const global = { + const global = ({ process: { nextTick: () => {}, }, - }; + } as unknown) as NodeJS.Global; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -158,13 +211,18 @@ describe('FakeTimers', () => { it('cancels a callback even from native nextTick', () => { const nativeNextTick = jest.fn(); - const global = { + const global = ({ process: { nextTick: nativeNextTick, }, - }; + } as unknown) as NodeJS.Global; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -182,12 +240,17 @@ describe('FakeTimers', () => { it('cancels a callback even from native setImmediate', () => { const nativeSetImmediate = jest.fn(); - const global = { + const global = ({ process, setImmediate: nativeSetImmediate, - }; + } as unknown) as NodeJS.Global; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -204,13 +267,18 @@ describe('FakeTimers', () => { it('doesnt run a tick callback if native nextTick already did', () => { const nativeNextTick = jest.fn(); - const global = { + const global = ({ process: { nextTick: nativeNextTick, }, - }; + } as unknown) as NodeJS.Global; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -228,12 +296,17 @@ describe('FakeTimers', () => { it('doesnt run immediate if native setImmediate already did', () => { const nativeSetImmediate = jest.fn(); - const global = { + const global = ({ process, setImmediate: nativeSetImmediate, - }; + } as unknown) as NodeJS.Global; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -251,12 +324,17 @@ describe('FakeTimers', () => { it('native doesnt run immediate if fake already did', () => { const nativeSetImmediate = jest.fn(); - const global = { + const global = ({ process, setImmediate: nativeSetImmediate, - }; + } as unknown) as NodeJS.Global; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -273,13 +351,14 @@ describe('FakeTimers', () => { }); it('throws before allowing infinite recursion', () => { - const global = { + const global = ({ process: { nextTick: () => {}, }, - }; + } as unknown) as NodeJS.Global; const timers = new FakeTimers({ + config, global, maxLoops: 100, moduleMocker, @@ -305,11 +384,16 @@ describe('FakeTimers', () => { describe('runAllTimers', () => { it('runs all timers in order', () => { - const global = {process}; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const global = ({process} as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); - const runOrder = []; + const runOrder: Array = []; const mock1 = jest.fn(() => runOrder.push('mock1')); const mock2 = jest.fn(() => runOrder.push('mock2')); const mock3 = jest.fn(() => runOrder.push('mock3')); @@ -339,11 +423,13 @@ describe('FakeTimers', () => { }); it('warns when trying to advance timers while real timers are used', () => { - const consoleWarn = console.warn; - console.warn = jest.fn(); + const consoleWarn = jest + .spyOn(console, 'warn') + .mockImplementation(() => {}); const timers = new FakeTimers({ config: { rootDir: __dirname, + testMatch: [], }, global, moduleMocker, @@ -351,26 +437,36 @@ describe('FakeTimers', () => { }); timers.runAllTimers(); expect( - console.warn.mock.calls[0][0].split('\nStack Trace')[0], + consoleWarn.mock.calls[0][0].split('\nStack Trace')[0], ).toMatchSnapshot(); - console.warn = consoleWarn; + consoleWarn.mockRestore(); }); it('does nothing when no timers have been scheduled', () => { const nativeSetTimeout = jest.fn(); - const global = { + const global = ({ process, setTimeout: nativeSetTimeout, - }; + } as unknown) as NodeJS.Global; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); timers.runAllTimers(); }); it('only runs a setTimeout callback once (ever)', () => { - const global = {process}; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const global = ({process} as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); const fn = jest.fn(); @@ -385,8 +481,13 @@ describe('FakeTimers', () => { }); it('runs callbacks with arguments after the interval', () => { - const global = {process}; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const global = ({process} as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); const fn = jest.fn(); @@ -400,12 +501,17 @@ describe('FakeTimers', () => { it('doesnt pass the callback to native setTimeout', () => { const nativeSetTimeout = jest.fn(); - const global = { + const global = ({ process, setTimeout: nativeSetTimeout, - }; + } as unknown) as NodeJS.Global; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -417,8 +523,9 @@ describe('FakeTimers', () => { }); it('throws before allowing infinite recursion', () => { - const global = {process}; + const global = ({process} as unknown) as NodeJS.Global; const timers = new FakeTimers({ + config, global, maxLoops: 100, moduleMocker, @@ -441,8 +548,13 @@ describe('FakeTimers', () => { }); it('also clears ticks', () => { - const global = {process}; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const global = ({process} as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); const fn = jest.fn(); @@ -458,11 +570,16 @@ describe('FakeTimers', () => { describe('advanceTimersByTime', () => { it('runs timers in order', () => { - const global = {process}; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const global = ({process} as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); - const runOrder = []; + const runOrder: Array = []; const mock1 = jest.fn(() => runOrder.push('mock1')); const mock2 = jest.fn(() => runOrder.push('mock2')); const mock3 = jest.fn(() => runOrder.push('mock3')); @@ -497,16 +614,22 @@ describe('FakeTimers', () => { }); it('does nothing when no timers have been scheduled', () => { - const global = {process}; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const global = ({process} as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); timers.advanceTimersByTime(100); }); it('throws before allowing infinite recursion', () => { - const global = {process}; + const global = ({process} as unknown) as NodeJS.Global; const timers = new FakeTimers({ + config, global, maxLoops: 100, moduleMocker, @@ -531,8 +654,13 @@ describe('FakeTimers', () => { describe('reset', () => { it('resets all pending setTimeouts', () => { - const global = {process}; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const global = ({process} as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -544,8 +672,13 @@ describe('FakeTimers', () => { }); it('resets all pending setIntervals', () => { - const global = {process}; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const global = ({process} as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -557,13 +690,18 @@ describe('FakeTimers', () => { }); it('resets all pending ticks callbacks & immediates', () => { - const global = { + const global = ({ process: { nextTick: () => {}, }, setImmediate: () => {}, - }; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + } as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -577,8 +715,13 @@ describe('FakeTimers', () => { }); it('resets current advanceTimersByTime time cursor', () => { - const global = {process}; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const global = ({process} as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); const mock1 = jest.fn(); @@ -597,15 +740,20 @@ describe('FakeTimers', () => { it('runs all timers in order', () => { const nativeSetImmediate = jest.fn(); - const global = { + const global = ({ process, setImmediate: nativeSetImmediate, - }; + } as unknown) as NodeJS.Global; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); - const runOrder = []; + const runOrder: Array = []; global.setTimeout(function cb() { runOrder.push('mock1'); @@ -649,8 +797,13 @@ describe('FakeTimers', () => { }); it('does not run timers that were cleared in another timer', () => { - const global = {process}; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const global = ({process} as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); const fn = jest.fn(); @@ -671,40 +824,45 @@ describe('FakeTimers', () => { const nativeSetInterval = jest.fn(); const nativeSetTimeout = jest.fn(); - const global = { + const global = ({ clearInterval: nativeClearInterval, clearTimeout: nativeClearTimeout, process, setInterval: nativeSetInterval, setTimeout: nativeSetTimeout, - }; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + } as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); // clearInterval() timers.runWithRealTimers(() => { - global.clearInterval(); + (global as any).clearInterval(); }); expect(nativeClearInterval).toHaveBeenCalledTimes(1); expect(global.clearInterval).toHaveBeenCalledTimes(0); // clearTimeout() timers.runWithRealTimers(() => { - global.clearTimeout(); + (global as any).clearTimeout(); }); expect(nativeClearTimeout).toHaveBeenCalledTimes(1); expect(global.clearTimeout).toHaveBeenCalledTimes(0); // setInterval() timers.runWithRealTimers(() => { - global.setInterval(); + (global as any).setInterval(); }); expect(nativeSetInterval).toHaveBeenCalledTimes(1); expect(global.setInterval).toHaveBeenCalledTimes(0); // setTimeout() timers.runWithRealTimers(() => { - global.setTimeout(); + (global as any).setTimeout(); }); expect(nativeSetTimeout).toHaveBeenCalledTimes(1); expect(global.setTimeout).toHaveBeenCalledTimes(0); @@ -716,80 +874,90 @@ describe('FakeTimers', () => { const nativeSetInterval = jest.fn(); const nativeSetTimeout = jest.fn(); - const global = { + const global = ({ clearInterval: nativeClearInterval, clearTimeout: nativeClearTimeout, process, setInterval: nativeSetInterval, setTimeout: nativeSetTimeout, - }; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + } as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); // clearInterval() timers.runWithRealTimers(() => { - global.clearInterval(); + (global as any).clearInterval(); }); expect(nativeClearInterval).toHaveBeenCalledTimes(1); expect(global.clearInterval).toHaveBeenCalledTimes(0); - global.clearInterval(); + (global as any).clearInterval(); expect(nativeClearInterval).toHaveBeenCalledTimes(1); expect(global.clearInterval).toHaveBeenCalledTimes(1); // clearTimeout() timers.runWithRealTimers(() => { - global.clearTimeout(); + (global as any).clearTimeout(); }); expect(nativeClearTimeout).toHaveBeenCalledTimes(1); expect(global.clearTimeout).toHaveBeenCalledTimes(0); - global.clearTimeout(); + (global as any).clearTimeout(); expect(nativeClearTimeout).toHaveBeenCalledTimes(1); expect(global.clearTimeout).toHaveBeenCalledTimes(1); // setInterval() timers.runWithRealTimers(() => { - global.setInterval(); + (global as any).setInterval(); }); expect(nativeSetInterval).toHaveBeenCalledTimes(1); expect(global.setInterval).toHaveBeenCalledTimes(0); - global.setInterval(); + (global as any).setInterval(); expect(nativeSetInterval).toHaveBeenCalledTimes(1); expect(global.setInterval).toHaveBeenCalledTimes(1); // setTimeout() timers.runWithRealTimers(() => { - global.setTimeout(); + (global as any).setTimeout(); }); expect(nativeSetTimeout).toHaveBeenCalledTimes(1); expect(global.setTimeout).toHaveBeenCalledTimes(0); - global.setTimeout(); + (global as any).setTimeout(); expect(nativeSetTimeout).toHaveBeenCalledTimes(1); expect(global.setTimeout).toHaveBeenCalledTimes(1); }); it('resets mock timer functions even if callback throws', () => { const nativeSetTimeout = jest.fn(); - const global = { + const global = ({ process, setTimeout: nativeSetTimeout, - }; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + } as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); expect(() => { timers.runWithRealTimers(() => { - global.setTimeout(); + (global as any).setTimeout(); throw new Error('test'); }); }).toThrow(new Error('test')); expect(nativeSetTimeout).toHaveBeenCalledTimes(1); expect(global.setTimeout).toHaveBeenCalledTimes(0); - global.setTimeout(); + (global as any).setTimeout(); expect(nativeSetTimeout).toHaveBeenCalledTimes(1); expect(global.setTimeout).toHaveBeenCalledTimes(1); }); @@ -802,14 +970,19 @@ describe('FakeTimers', () => { const nativeClearTimeout = jest.fn(); const nativeClearInterval = jest.fn(); - const global = { + const global = ({ clearInterval: nativeClearInterval, clearTimeout: nativeClearTimeout, process, setInterval: nativeSetInterval, setTimeout: nativeSetTimeout, - }; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + } as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); // Ensure that timers has overridden the native timer APIs @@ -830,10 +1003,15 @@ describe('FakeTimers', () => { it('resets native process.nextTick when present', () => { const nativeProcessNextTick = jest.fn(); - const global = { + const global = ({ process: {nextTick: nativeProcessNextTick}, - }; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + } as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); // Ensure that timers has overridden the native timer APIs @@ -849,12 +1027,17 @@ describe('FakeTimers', () => { const nativeSetImmediate = jest.fn(); const nativeClearImmediate = jest.fn(); - const global = { + const global = ({ clearImmediate: nativeClearImmediate, process, setImmediate: nativeSetImmediate, - }; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + } as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); // Ensure that timers has overridden the native timer APIs @@ -876,14 +1059,19 @@ describe('FakeTimers', () => { const nativeClearTimeout = jest.fn(); const nativeClearInterval = jest.fn(); - const global = { + const global = ({ clearInterval: nativeClearInterval, clearTimeout: nativeClearTimeout, process, setInterval: nativeSetInterval, setTimeout: nativeSetTimeout, - }; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + } as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useRealTimers(); // Ensure that the real timers are installed at this point @@ -904,10 +1092,15 @@ describe('FakeTimers', () => { it('resets mock process.nextTick when present', () => { const nativeProcessNextTick = jest.fn(); - const global = { + const global = ({ process: {nextTick: nativeProcessNextTick}, - }; - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + } as unknown) as NodeJS.Global; + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useRealTimers(); // Ensure that the real timers are installed at this point @@ -923,12 +1116,17 @@ describe('FakeTimers', () => { const nativeSetImmediate = jest.fn(); const nativeClearImmediate = jest.fn(); - const global = { + const global = ({ clearImmediate: nativeClearImmediate, process, setImmediate: nativeSetImmediate, - }; - const fakeTimers = new FakeTimers({global, moduleMocker, timerConfig}); + } as unknown) as NodeJS.Global; + const fakeTimers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); fakeTimers.useRealTimers(); // Ensure that the real timers are installed at this point @@ -945,7 +1143,12 @@ describe('FakeTimers', () => { describe('getTimerCount', () => { it('returns the correct count', () => { - const timers = new FakeTimers({global, moduleMocker, timerConfig}); + const timers = new FakeTimers({ + config, + global, + moduleMocker, + timerConfig, + }); timers.useFakeTimers(); diff --git a/packages/jest-util/src/__tests__/formatTestResults.test.js b/packages/jest-util/src/__tests__/formatTestResults.test.ts similarity index 88% rename from packages/jest-util/src/__tests__/formatTestResults.test.js rename to packages/jest-util/src/__tests__/formatTestResults.test.ts index 83f7987538de..09bd58b9a786 100644 --- a/packages/jest-util/src/__tests__/formatTestResults.test.js +++ b/packages/jest-util/src/__tests__/formatTestResults.test.ts @@ -3,11 +3,9 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * */ -'use strict'; - +import {TestResult} from '@jest/types'; import formatTestResults from '../formatTestResults'; describe('formatTestResults', () => { @@ -17,11 +15,12 @@ describe('formatTestResults', () => { title: 'returns true', }; - const results = { + const results: TestResult.AggregatedResult = { testResults: [ { numFailingTests: 0, perfStats: {end: 2, start: 1}, + // @ts-ignore testResults: [assertion], }, ], diff --git a/packages/jest-util/src/__tests__/getCallsite.test.js b/packages/jest-util/src/__tests__/getCallsite.test.ts similarity index 90% rename from packages/jest-util/src/__tests__/getCallsite.test.js rename to packages/jest-util/src/__tests__/getCallsite.test.ts index d73c7c80dfae..f375645d679d 100644 --- a/packages/jest-util/src/__tests__/getCallsite.test.js +++ b/packages/jest-util/src/__tests__/getCallsite.test.ts @@ -22,7 +22,7 @@ describe('getCallsite', () => { }); test('ignores errors when fs throws', () => { - fs.readFileSync.mockImplementation(() => { + (fs.readFileSync as jest.Mock).mockImplementation(() => { throw new Error('Mock error'); }); @@ -35,12 +35,13 @@ describe('getCallsite', () => { }); test('reads source map file to determine line and column', () => { - fs.readFileSync.mockImplementation(() => 'file data'); + (fs.readFileSync as jest.Mock).mockImplementation(() => 'file data'); const sourceMapColumn = 1; const sourceMapLine = 2; + // @ts-ignore SourceMap.SourceMapConsumer = class { - originalPositionFor(params) { + originalPositionFor(params: Object) { expect(params).toMatchObject({ column: expect.any(Number), line: expect.any(Number), diff --git a/packages/jest-util/src/__tests__/getFailedSnapshot.test.js b/packages/jest-util/src/__tests__/getFailedSnapshot.test.ts similarity index 95% rename from packages/jest-util/src/__tests__/getFailedSnapshot.test.js rename to packages/jest-util/src/__tests__/getFailedSnapshot.test.ts index 4afa658efe6e..d1dbcd19d10b 100644 --- a/packages/jest-util/src/__tests__/getFailedSnapshot.test.js +++ b/packages/jest-util/src/__tests__/getFailedSnapshot.test.ts @@ -20,6 +20,7 @@ test('return a list of path', () => { }, ], }; + // @ts-ignore expect(getFailedSnapshotTests(param)).toEqual([targetFilename]); }); @@ -33,6 +34,7 @@ test('handle missing snapshot object', () => { }, ], }; + // @ts-ignore expect(getFailedSnapshotTests(param)).toEqual([]); }); @@ -40,6 +42,7 @@ test('handle missing testResults object', () => { const param = { numFailedTests: 1, }; + // @ts-ignore expect(getFailedSnapshotTests(param)).toEqual([]); }); @@ -47,6 +50,7 @@ test('return empty if not failed tests', () => { const param = { numFailedTests: 0, }; + // @ts-ignore expect(getFailedSnapshotTests(param)).toEqual([]); }); @@ -63,5 +67,6 @@ test('return empty if not failed snapshot tests', () => { }, ], }; + // @ts-ignore expect(getFailedSnapshotTests(param)).toEqual([]); }); diff --git a/packages/jest-util/src/__tests__/installCommonGlobals.test.js b/packages/jest-util/src/__tests__/installCommonGlobals.test.ts similarity index 90% rename from packages/jest-util/src/__tests__/installCommonGlobals.test.js rename to packages/jest-util/src/__tests__/installCommonGlobals.test.ts index 2aa6c0b131e3..3dc16aa63536 100644 --- a/packages/jest-util/src/__tests__/installCommonGlobals.test.js +++ b/packages/jest-util/src/__tests__/installCommonGlobals.test.ts @@ -7,15 +7,16 @@ import vm from 'vm'; -let installCommonGlobals; -let fake; +let installCommonGlobals: any; +let fake: jest.Mock; -function getGlobal() { +function getGlobal(): NodeJS.Global { return vm.runInContext('this', vm.createContext()); } beforeEach(() => { fake = jest.fn(); + // @ts-ignore global.DTRACE_NET_SERVER_CONNECTION = fake; installCommonGlobals = require('../installCommonGlobals').default; diff --git a/packages/jest-util/src/__tests__/isInteractive.test.js b/packages/jest-util/src/__tests__/isInteractive.test.ts similarity index 90% rename from packages/jest-util/src/__tests__/isInteractive.test.js rename to packages/jest-util/src/__tests__/isInteractive.test.ts index 4a65cbc2f071..79201ed801b6 100644 --- a/packages/jest-util/src/__tests__/isInteractive.test.js +++ b/packages/jest-util/src/__tests__/isInteractive.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. -let oldIsTTY; -let oldTERM; +let oldIsTTY: typeof process.stdout.isTTY; +let oldTERM: string | undefined; beforeEach(() => { oldIsTTY = process.stdout.isTTY; @@ -29,7 +29,7 @@ it('Returns false when running on a non-interactive environment', () => { // Test with is-ci being true and isTTY false jest.doMock('is-ci', () => true); - process.stdout.isTTY = false; + process.stdout.isTTY = undefined; process.env.TERM = 'xterm-256color'; isInteractive = require('../isInteractive').default; expect(isInteractive).toBe(expectedResult); @@ -37,7 +37,7 @@ it('Returns false when running on a non-interactive environment', () => { // Test with is-ci being false and isTTY false jest.resetModules(); jest.doMock('is-ci', () => false); - process.stdout.isTTY = false; + process.stdout.isTTY = undefined; process.env.TERM = 'xterm-256color'; isInteractive = require('../isInteractive').default; expect(isInteractive).toBe(expectedResult); @@ -53,7 +53,7 @@ it('Returns false when running on a non-interactive environment', () => { // Test with dumb terminal jest.resetModules(); jest.doMock('is-ci', () => false); - process.stdout.isTTY = false; + process.stdout.isTTY = undefined; process.env.TERM = 'dumb'; isInteractive = require('../isInteractive').default; expect(isInteractive).toBe(expectedResult); diff --git a/packages/jest-util/src/clearLine.js b/packages/jest-util/src/clearLine.ts similarity index 73% rename from packages/jest-util/src/clearLine.js rename to packages/jest-util/src/clearLine.ts index 65f0f77ac98a..c16f99c7c3fc 100644 --- a/packages/jest-util/src/clearLine.js +++ b/packages/jest-util/src/clearLine.ts @@ -3,12 +3,9 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -/* global stream$Writable */ -export default (stream: stream$Writable | tty$WriteStream) => { +export default (stream: NodeJS.WritableStream) => { if (process.stdout.isTTY) { stream.write('\x1b[999D\x1b[K'); } diff --git a/packages/jest-util/src/convertDescriptorToString.js b/packages/jest-util/src/convertDescriptorToString.ts similarity index 89% rename from packages/jest-util/src/convertDescriptorToString.js rename to packages/jest-util/src/convertDescriptorToString.ts index 447284c5d442..fb36658fde85 100644 --- a/packages/jest-util/src/convertDescriptorToString.js +++ b/packages/jest-util/src/convertDescriptorToString.ts @@ -3,14 +3,12 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ // See: https://github.com/facebook/jest/pull/5154 export default function convertDescriptorToString( descriptor: string | Function, -) { +): string { if ( typeof descriptor === 'string' || typeof descriptor === 'number' || @@ -31,8 +29,9 @@ export default function convertDescriptorToString( const stringified = descriptor.toString(); const typeDescriptorMatch = stringified.match(/class|function/); const indexOfNameSpace = + // @ts-ignore: typeDescriptorMatch exists typeDescriptorMatch.index + typeDescriptorMatch[0].length; - const indexOfNameAfterSpace = stringified.search(/\(|\{/, indexOfNameSpace); + const indexOfNameAfterSpace = stringified.search(/\(|\{/); const name = stringified.substring(indexOfNameSpace, indexOfNameAfterSpace); return name.trim(); } diff --git a/packages/jest-util/src/createDirectory.js b/packages/jest-util/src/createDirectory.ts similarity index 76% rename from packages/jest-util/src/createDirectory.js rename to packages/jest-util/src/createDirectory.ts index 929eabb66cd0..7da0783bbb44 100644 --- a/packages/jest-util/src/createDirectory.js +++ b/packages/jest-util/src/createDirectory.ts @@ -3,15 +3,12 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Path} from 'types/Config'; - import mkdirp from 'mkdirp'; +import {Config} from '@jest/types'; -export default function createDirectory(path: Path) { +export default function createDirectory(path: Config.Path) { try { mkdirp.sync(path, '777'); } catch (e) { diff --git a/packages/jest-util/src/createProcessObject.js b/packages/jest-util/src/createProcessObject.ts similarity index 92% rename from packages/jest-util/src/createProcessObject.js rename to packages/jest-util/src/createProcessObject.ts index b816d32a78ad..b5be6442fe8b 100644 --- a/packages/jest-util/src/createProcessObject.js +++ b/packages/jest-util/src/createProcessObject.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import deepCyclicCopy from './deepCyclicCopy'; @@ -16,17 +14,17 @@ const BLACKLIST = new Set(['env', 'mainModule', '_events']); // string; and third, it is case-insensitive in Windows. We use a proxy here to // mimic it (see https://nodejs.org/api/process.html#process_process_env). -function createProcessEnv() { +function createProcessEnv(): NodeJS.ProcessEnv { if (typeof Proxy === 'undefined') { return deepCyclicCopy(process.env); } const proto: Object = Object.getPrototypeOf(process.env); const real = Object.create(proto); - const lookup = {}; + const lookup: typeof process.env = {}; const proxy = new Proxy(real, { - deleteProperty(target, key) { + deleteProperty(_target, key) { for (const name in real) { if (real.hasOwnProperty(name)) { if (typeof key === 'string' && process.platform === 'win32') { @@ -46,7 +44,7 @@ function createProcessEnv() { return true; }, - get(target, key) { + get(_target, key) { if (typeof key === 'string' && process.platform === 'win32') { return lookup[key in proto ? key : key.toLowerCase()]; } else { @@ -54,7 +52,7 @@ function createProcessEnv() { } }, - set(target, key, value) { + set(_target, key, value) { const strValue = '' + value; if (typeof key === 'string') { diff --git a/packages/jest-util/src/deepCyclicCopy.js b/packages/jest-util/src/deepCyclicCopy.ts similarity index 71% rename from packages/jest-util/src/deepCyclicCopy.js rename to packages/jest-util/src/deepCyclicCopy.ts index 79a3674a91a7..465ce9f3f1e0 100644 --- a/packages/jest-util/src/deepCyclicCopy.js +++ b/packages/jest-util/src/deepCyclicCopy.ts @@ -3,37 +3,37 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ const EMPTY = new Set(); -export type DeepCyclicCopyOptions = {| - blacklist: Set, - keepPrototype: boolean, -|}; +export type DeepCyclicCopyOptions = { + blacklist?: Set; + keepPrototype?: boolean; +}; -// $FlowFixMe: Node 6 does not have gOPDs, so we define a simple polyfill for it. +// Node 6 does not have gOPDs, so we define a simple polyfill for it. if (!Object.getOwnPropertyDescriptors) { - // $FlowFixMe: polyfill + // @ts-ignore: polyfill Object.getOwnPropertyDescriptors = obj => { - const list = {}; + const list: {[key: string]: PropertyDescriptor | undefined} = {}; - Object.getOwnPropertyNames(obj) + (Object.getOwnPropertyNames(obj) as Array) .concat(Object.getOwnPropertySymbols(obj)) - // $FlowFixMe: assignment with a Symbol is OK. - .forEach(key => (list[key] = Object.getOwnPropertyDescriptor(obj, key))); + .forEach(key => { + // @ts-ignore: assignment with a Symbol is OK. + list[key] = Object.getOwnPropertyDescriptor(obj, key); + }); return list; }; } -export default function deepCyclicCopy( - value: any, - options?: DeepCyclicCopyOptions = {blacklist: EMPTY, keepPrototype: false}, +export default function deepCyclicCopy( + value: T, + options: DeepCyclicCopyOptions = {blacklist: EMPTY, keepPrototype: false}, cycles: WeakMap = new WeakMap(), -): any { +): T { if (typeof value !== 'object' || value === null) { return value; } else if (cycles.has(value)) { @@ -45,11 +45,11 @@ export default function deepCyclicCopy( } } -function deepCyclicCopyObject( - object: Object, +function deepCyclicCopyObject( + object: T, options: DeepCyclicCopyOptions, cycles: WeakMap, -): Object { +): T { const newObject = options.keepPrototype ? Object.create(Object.getPrototypeOf(object)) : {}; @@ -80,14 +80,13 @@ function deepCyclicCopyObject( return Object.defineProperties(newObject, descriptors); } -function deepCyclicCopyArray( - array: Array, +function deepCyclicCopyArray( + array: Array, options: DeepCyclicCopyOptions, cycles: WeakMap, -): Array { +): T { const newArray = options.keepPrototype - ? // $FlowFixMe: getPrototypeOf an array is OK. - new (Object.getPrototypeOf(array)).constructor(array.length) + ? new (Object.getPrototypeOf(array)).constructor(array.length) : []; const length = array.length; diff --git a/packages/jest-util/src/formatTestResults.js b/packages/jest-util/src/formatTestResults.ts similarity index 68% rename from packages/jest-util/src/formatTestResults.js rename to packages/jest-util/src/formatTestResults.ts index f6b64cc533b9..465573a524af 100644 --- a/packages/jest-util/src/formatTestResults.js +++ b/packages/jest-util/src/formatTestResults.ts @@ -3,28 +3,17 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type { - AggregatedResult, - AssertionResult, - CodeCoverageFormatter, - CodeCoverageReporter, - FormattedAssertionResult, - FormattedTestResult, - FormattedTestResults, - TestResult, -} from 'types/TestResult'; +import {TestResult} from '@jest/types'; const formatResult = ( - testResult: TestResult, - codeCoverageFormatter: CodeCoverageFormatter, - reporter: CodeCoverageReporter, -): FormattedTestResult => { + testResult: TestResult.TestResult, + codeCoverageFormatter: TestResult.CodeCoverageFormatter, + reporter: TestResult.CodeCoverageReporter, +): TestResult.FormattedTestResult => { const now = Date.now(); - const output: FormattedTestResult = { + const output: TestResult.FormattedTestResult = { assertionResults: [], coverage: {}, endTime: now, @@ -56,9 +45,9 @@ const formatResult = ( }; function formatTestAssertion( - assertion: AssertionResult, -): FormattedAssertionResult { - const result: FormattedAssertionResult = { + assertion: TestResult.AssertionResult, +): TestResult.FormattedAssertionResult { + const result: TestResult.FormattedAssertionResult = { ancestorTitles: assertion.ancestorTitles, failureMessages: null, fullName: assertion.fullName, @@ -73,17 +62,15 @@ function formatTestAssertion( } export default function formatTestResults( - results: AggregatedResult, - codeCoverageFormatter?: CodeCoverageFormatter, - reporter?: CodeCoverageReporter, -): FormattedTestResults { + results: TestResult.AggregatedResult, + codeCoverageFormatter?: TestResult.CodeCoverageFormatter | null, + reporter?: TestResult.CodeCoverageReporter, +): TestResult.FormattedTestResults { const formatter = codeCoverageFormatter || (coverage => coverage); const testResults = results.testResults.map(testResult => formatResult(testResult, formatter, reporter), ); - return Object.assign((Object.create(null): any), results, { - testResults, - }); + return {...results, testResults}; } diff --git a/packages/jest-util/src/getCallsite.js b/packages/jest-util/src/getCallsite.ts similarity index 72% rename from packages/jest-util/src/getCallsite.js rename to packages/jest-util/src/getCallsite.ts index 6ce4135c35f8..6a266c9823de 100644 --- a/packages/jest-util/src/getCallsite.js +++ b/packages/jest-util/src/getCallsite.ts @@ -3,27 +3,27 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {SourceMapRegistry} from 'types/SourceMaps'; - import fs from 'graceful-fs'; -import callsites from 'callsites'; +import callsites, {CallSite} from 'callsites'; import {SourceMapConsumer} from 'source-map'; +import {SourceMaps} from '@jest/types'; // Copied from https://github.com/rexxars/sourcemap-decorate-callsites/blob/5b9735a156964973a75dc62fd2c7f0c1975458e8/lib/index.js#L113-L158 -const addSourceMapConsumer = (callsite, consumer) => { +const addSourceMapConsumer = ( + callsite: CallSite, + consumer: SourceMapConsumer, +) => { const getLineNumber = callsite.getLineNumber; const getColumnNumber = callsite.getColumnNumber; - let position = null; + let position: ReturnType | null = null; function getPosition() { if (!position) { position = consumer.originalPositionFor({ - column: getColumnNumber.call(callsite), - line: getLineNumber.call(callsite), + column: getColumnNumber.call(callsite) || -1, + line: getLineNumber.call(callsite) || -1, }); } @@ -46,14 +46,18 @@ const addSourceMapConsumer = (callsite, consumer) => { }); }; -export default (level: number, sourceMaps: ?SourceMapRegistry) => { +export default ( + level: number, + sourceMaps?: SourceMaps.SourceMapRegistry | null, +) => { const levelAfterThisCall = level + 1; const stack = callsites()[levelAfterThisCall]; - const sourceMapFileName = sourceMaps && sourceMaps[stack.getFileName()]; + const sourceMapFileName = sourceMaps && sourceMaps[stack.getFileName() || '']; if (sourceMapFileName) { try { const sourceMap = fs.readFileSync(sourceMapFileName, 'utf8'); + // @ts-ignore: Not allowed to pass string addSourceMapConsumer(stack, new SourceMapConsumer(sourceMap)); } catch (e) { // ignore diff --git a/packages/jest-util/src/getConsoleOutput.js b/packages/jest-util/src/getConsoleOutput.ts similarity index 88% rename from packages/jest-util/src/getConsoleOutput.js rename to packages/jest-util/src/getConsoleOutput.ts index 1f72e74a90bf..1d3b9da964e2 100644 --- a/packages/jest-util/src/getConsoleOutput.js +++ b/packages/jest-util/src/getConsoleOutput.ts @@ -3,17 +3,18 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {ConsoleBuffer} from 'types/Console'; - import path from 'path'; import chalk from 'chalk'; import slash from 'slash'; +import {Console} from '@jest/types'; -export default (root: string, verbose: boolean, buffer: ConsoleBuffer) => { +export default ( + root: string, + verbose: boolean, + buffer: Console.ConsoleBuffer, +) => { const TITLE_INDENT = verbose ? ' ' : ' '; const CONSOLE_INDENT = TITLE_INDENT + ' '; diff --git a/packages/jest-util/src/getFailedSnapshotTests.js b/packages/jest-util/src/getFailedSnapshotTests.ts similarity index 76% rename from packages/jest-util/src/getFailedSnapshotTests.js rename to packages/jest-util/src/getFailedSnapshotTests.ts index 99df31bf4500..192fa524a4b7 100644 --- a/packages/jest-util/src/getFailedSnapshotTests.js +++ b/packages/jest-util/src/getFailedSnapshotTests.ts @@ -3,14 +3,12 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {AggregatedResult} from 'types/TestResult'; +import {Config, TestResult} from '@jest/types'; -function getFailedSnapshotTests(testResults: AggregatedResult) { - const failedTestPaths = []; +function getFailedSnapshotTests(testResults: TestResult.AggregatedResult) { + const failedTestPaths: Array = []; if (testResults.numFailedTests === 0 || !testResults.testResults) { return failedTestPaths; } diff --git a/packages/jest-util/src/index.js b/packages/jest-util/src/index.ts similarity index 97% rename from packages/jest-util/src/index.js rename to packages/jest-util/src/index.ts index 04aed91a67bb..5ea75756a5c5 100644 --- a/packages/jest-util/src/index.js +++ b/packages/jest-util/src/index.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import BufferedConsole from './BufferedConsole'; @@ -26,7 +24,7 @@ import convertDescriptorToString from './convertDescriptorToString'; import * as specialChars from './specialChars'; import replacePathSepForGlob from './replacePathSepForGlob'; -module.exports = { +export = { BufferedConsole, Console: CustomConsole, ErrorWithStack, diff --git a/packages/jest-util/src/installCommonGlobals.js b/packages/jest-util/src/installCommonGlobals.ts similarity index 83% rename from packages/jest-util/src/installCommonGlobals.js rename to packages/jest-util/src/installCommonGlobals.ts index 34e166379adf..17c87ff980db 100644 --- a/packages/jest-util/src/installCommonGlobals.js +++ b/packages/jest-util/src/installCommonGlobals.ts @@ -3,23 +3,22 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {ConfigGlobals} from 'types/Config'; -import type {Global} from 'types/Global'; - import fs from 'fs'; +import {Config} from '@jest/types'; 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) { +export default function( + globalObject: NodeJS.Global, + globals: Config.ConfigGlobals, +): NodeJS.Global & Config.ConfigGlobals { globalObject.process = createProcessObject(); - const symbol = globalObject.Symbol; + const symbol = (globalObject.Symbol as unknown) as SymbolConstructor; // Keep a reference to some globals that Jest needs Object.defineProperties(globalObject, { [symbol.for('jest-native-promise')]: { @@ -56,7 +55,9 @@ export default function(globalObject: Global, globals: ConfigGlobals) { // Forward some APIs. DTRACE.forEach(dtrace => { - globalObject[dtrace] = function(...args) { + // @ts-ignore: no index + globalObject[dtrace] = function(...args: Array) { + // @ts-ignore: no index return global[dtrace].apply(this, args); }; }); diff --git a/packages/jest-util/src/isInteractive.js b/packages/jest-util/src/isInteractive.ts similarity index 56% rename from packages/jest-util/src/isInteractive.js rename to packages/jest-util/src/isInteractive.ts index 5597b9fdae9b..0dca9ee48f88 100644 --- a/packages/jest-util/src/isInteractive.js +++ b/packages/jest-util/src/isInteractive.ts @@ -2,4 +2,4 @@ import isCI from 'is-ci'; -export default process.stdout.isTTY && process.env.TERM !== 'dumb' && !isCI; +export default !!process.stdout.isTTY && process.env.TERM !== 'dumb' && !isCI; diff --git a/packages/jest-util/src/replacePathSepForGlob.js b/packages/jest-util/src/replacePathSepForGlob.ts similarity index 68% rename from packages/jest-util/src/replacePathSepForGlob.js rename to packages/jest-util/src/replacePathSepForGlob.ts index 1c4362b02d81..01b59b3b2950 100644 --- a/packages/jest-util/src/replacePathSepForGlob.js +++ b/packages/jest-util/src/replacePathSepForGlob.ts @@ -3,12 +3,10 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Path, Glob} from 'types/Config'; +import {Config} from '@jest/types'; -export default function replacePathSepForGlob(path: Path): Glob { +export default function replacePathSepForGlob(path: Config.Path): Config.Glob { return path.replace(/\\(?![{}()+?.^$])/g, '/'); } diff --git a/packages/jest-util/src/setGlobal.ts b/packages/jest-util/src/setGlobal.ts new file mode 100644 index 000000000000..e7eb341f05a0 --- /dev/null +++ b/packages/jest-util/src/setGlobal.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +export default ( + globalToMutate: NodeJS.Global | Window, + key: string, + value: unknown, +) => { + // @ts-ignore: no index + globalToMutate[key] = value; +}; diff --git a/packages/jest-util/src/specialChars.js b/packages/jest-util/src/specialChars.ts similarity index 97% rename from packages/jest-util/src/specialChars.js rename to packages/jest-util/src/specialChars.ts index 878ce9130daa..0319f5850112 100644 --- a/packages/jest-util/src/specialChars.js +++ b/packages/jest-util/src/specialChars.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ const isWindows = process.platform === 'win32'; diff --git a/packages/jest-util/tsconfig.json b/packages/jest-util/tsconfig.json new file mode 100644 index 000000000000..931ea0e1de53 --- /dev/null +++ b/packages/jest-util/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [{"path": "../jest-types"}] +} diff --git a/yarn.lock b/yarn.lock index dffaf5b9881c..e28de1a42440 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1533,11 +1533,6 @@ dependencies: "@types/resolve" "*" -"@types/callsites@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@types/callsites/-/callsites-2.0.0.tgz#df0db68ea34053651caee9975294be902fd04751" - integrity sha512-kUvhIE1ZlV4dYEyFnZ2lJLXMEOc7eLN6ejolVBQm0boUCX2t6p0M2nx+20UkEtDMJYtRQffuhcwO1h/U029QAw== - "@types/camelcase@^4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@types/camelcase/-/camelcase-4.1.0.tgz#e054f7986f31658d49936261b5cd4588ef29d1ee" @@ -1694,9 +1689,9 @@ "@types/node" "*" "@types/node@*": - version "10.12.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" - integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== + version "10.12.24" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.24.tgz#b13564af612a22a20b5d95ca40f1bffb3af315cf" + integrity sha512-GWWbvt+z9G5otRBW8rssOFgRY87J9N/qbhqfjMZ+gUuL6zoL+Hm6gP/8qQBG4jjimqdaNLCehcVapZ/Fs2WjCQ== "@types/prompts@^1.2.0": version "1.2.0" @@ -1728,6 +1723,13 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/readable-stream@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-2.3.0.tgz#52e4b8bd8c2c222cadbe5366c833bc75fc975763" + integrity sha512-c0+lRjSeBakp3YJaP4BixI+sWWO5JFsG3wKAlUNl2olBB5sbXY8rgRbFbBwLkuuX3xSxNqpCZzeROJzMlsCdig== + dependencies: + "@types/node" "*" + "@types/resolve@*": version "0.0.8" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" @@ -11033,7 +11035,7 @@ read@1, read@~1.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.3, readable-stream@^3.0.6: +readable-stream@^3.0.3, readable-stream@^3.0.6, readable-stream@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.1.1.tgz#ed6bbc6c5ba58b090039ff18ce670515795aeb06" integrity sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA== From 8bbf39429a13209e46baa47a59b25515f771e59a Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sun, 10 Feb 2019 10:47:18 +0100 Subject: [PATCH 034/107] chore: migrate jest-watcher to TypeScript (#7843) --- CHANGELOG.md | 1 + docs/WatchPlugins.md | 1 + .../jest-cli/src/lib/update_global_config.js | 2 - packages/jest-cli/src/plugins/quit.js | 1 - packages/jest-types/src/Config.ts | 10 ++- packages/jest-watcher/package.json | 2 + packages/jest-watcher/src/BaseWatchPlugin.js | 43 ---------- packages/jest-watcher/src/BaseWatchPlugin.ts | 38 +++++++++ .../src/{JestHooks.js => JestHooks.ts} | 23 +++-- .../{PatternPrompt.js => PatternPrompt.ts} | 16 ++-- .../src/{constants.js => constants.ts} | 2 - .../jest-watcher/src/{index.js => index.ts} | 2 - .../src/lib/{Prompt.js => Prompt.ts} | 32 ++++--- ...p => formatTestNameByPattern.test.ts.snap} | 0 ...est.js => formatTestNameByPattern.test.ts} | 4 - .../{prompt.test.js => prompt.test.ts} | 2 - .../{scroll.test.js => scroll.test.ts} | 0 .../src/lib/{colorize.js => colorize.ts} | 2 - ...yPattern.js => formatTestNameByPattern.ts} | 4 +- ...rnModeHelpers.js => patternModeHelpers.ts} | 8 +- .../src/lib/{scroll.js => scroll.ts} | 6 +- packages/jest-watcher/src/types.js | 26 ------ packages/jest-watcher/src/types.ts | 83 +++++++++++++++++++ packages/jest-watcher/tsconfig.json | 8 ++ .../version-23.x/WatchPlugins.md | 1 + .../version-24.0/WatchPlugins.md | 1 + 26 files changed, 185 insertions(+), 133 deletions(-) delete mode 100644 packages/jest-watcher/src/BaseWatchPlugin.js create mode 100644 packages/jest-watcher/src/BaseWatchPlugin.ts rename packages/jest-watcher/src/{JestHooks.js => JestHooks.ts} (80%) rename packages/jest-watcher/src/{PatternPrompt.js => PatternPrompt.ts} (82%) rename packages/jest-watcher/src/{constants.js => constants.ts} (97%) rename packages/jest-watcher/src/{index.js => index.ts} (97%) rename packages/jest-watcher/src/lib/{Prompt.js => Prompt.ts} (79%) rename packages/jest-watcher/src/lib/__tests__/__snapshots__/{formatTestNameByPattern.test.js.snap => formatTestNameByPattern.test.ts.snap} (100%) rename packages/jest-watcher/src/lib/__tests__/{formatTestNameByPattern.test.js => formatTestNameByPattern.test.ts} (98%) rename packages/jest-watcher/src/lib/__tests__/{prompt.test.js => prompt.test.ts} (99%) rename packages/jest-watcher/src/lib/__tests__/{scroll.test.js => scroll.test.ts} (100%) rename packages/jest-watcher/src/lib/{colorize.js => colorize.ts} (97%) rename packages/jest-watcher/src/lib/{formatTestNameByPattern.js => formatTestNameByPattern.ts} (95%) rename packages/jest-watcher/src/lib/{patternModeHelpers.js => patternModeHelpers.ts} (88%) rename packages/jest-watcher/src/lib/{scroll.js => scroll.ts} (89%) delete mode 100644 packages/jest-watcher/src/types.js create mode 100644 packages/jest-watcher/src/types.ts create mode 100644 packages/jest-watcher/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index ea78999740ca..f0a5fbc41f6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - `[jest-message-util]`: Migrate to TypeScript ([#7834](https://github.com/facebook/jest/pull/7834)) - `[@jest/types]`: New package to handle shared types ([#7834](https://github.com/facebook/jest/pull/7834)) - `[jest-util]`: Migrate to TypeScript ([#7844](https://github.com/facebook/jest/pull/7844)) +- `[jest-watcher]`: Migrate to TypeScript ([#7843](https://github.com/facebook/jest/pull/7843)) ### Performance diff --git a/docs/WatchPlugins.md b/docs/WatchPlugins.md index 05818e95b555..6047382eefc0 100644 --- a/docs/WatchPlugins.md +++ b/docs/WatchPlugins.md @@ -155,6 +155,7 @@ class MyWatchPlugin { For stability and safety reasons, only part of the global configuration keys can be updated with `updateConfigAndRun`. The current white list is as follows: - [`bail`](configuration.html#bail-number-boolean) +- [`changedSince`](cli.html#changedsince) - [`collectCoverage`](configuration.html#collectcoverage-boolean) - [`collectCoverageFrom`](configuration.html#collectcoveragefrom-array) - [`collectCoverageOnlyFrom`](configuration.html#collectcoverageonlyfrom-array) diff --git a/packages/jest-cli/src/lib/update_global_config.js b/packages/jest-cli/src/lib/update_global_config.js index 5b0047f8a6b1..beee59cd77b5 100644 --- a/packages/jest-cli/src/lib/update_global_config.js +++ b/packages/jest-cli/src/lib/update_global_config.js @@ -23,11 +23,9 @@ export type Options = { coverageDirectory?: $PropertyType, coverageReporters?: $PropertyType, mode?: 'watch' | 'watchAll', - noSCM?: $PropertyType, notify?: $PropertyType, notifyMode?: $PropertyType, onlyFailures?: $PropertyType, - passWithNoTests?: $PropertyType, reporters?: $PropertyType, testNamePattern?: $PropertyType, testPathPattern?: $PropertyType, diff --git a/packages/jest-cli/src/plugins/quit.js b/packages/jest-cli/src/plugins/quit.js index 0c7d63d5930e..68a336cf203e 100644 --- a/packages/jest-cli/src/plugins/quit.js +++ b/packages/jest-cli/src/plugins/quit.js @@ -21,7 +21,6 @@ class QuitPlugin extends BaseWatchPlugin { async run() { if (typeof this._stdin.setRawMode === 'function') { - // $FlowFixMe this._stdin.setRawMode(false); } this._stdout.write('\n'); diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index bd74d87c6b8b..85a5db4df75f 100644 --- a/packages/jest-types/src/Config.ts +++ b/packages/jest-types/src/Config.ts @@ -213,6 +213,14 @@ export type InitialOptions = { export type SnapshotUpdateState = 'all' | 'new' | 'none'; +type NotifyMode = + | 'always' + | 'failure' + | 'success' + | 'change' + | 'success-change' + | 'failure-change'; + export type GlobalConfig = { bail: number; changedSince: string; @@ -260,7 +268,7 @@ export type GlobalConfig = { nonFlagArgs: Array; noSCM: boolean | null | undefined; notify: boolean; - notifyMode: string; + notifyMode: NotifyMode; outputFile: Path | null | undefined; onlyChanged: boolean; onlyFailures: boolean; diff --git a/packages/jest-watcher/package.json b/packages/jest-watcher/package.json index 351a6c7ec3da..95b9ceda6fc3 100644 --- a/packages/jest-watcher/package.json +++ b/packages/jest-watcher/package.json @@ -4,6 +4,8 @@ "version": "24.0.0", "main": "build/index.js", "dependencies": { + "@jest/types": "^24.1.0", + "@types/node": "*", "ansi-escapes": "^3.0.0", "chalk": "^2.0.1", "jest-util": "^24.0.0", diff --git a/packages/jest-watcher/src/BaseWatchPlugin.js b/packages/jest-watcher/src/BaseWatchPlugin.js deleted file mode 100644 index ea239925c459..000000000000 --- a/packages/jest-watcher/src/BaseWatchPlugin.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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 type {GlobalConfig} from 'types/Config'; -import type {JestHookSubscriber} from 'types/JestHooks'; -import type {WatchPlugin, UsageData} from './types'; - -class BaseWatchPlugin implements WatchPlugin { - _stdin: stream$Readable | tty$ReadStream; - _stdout: stream$Writable | tty$WriteStream; - constructor({ - stdin, - stdout, - }: { - stdin: stream$Readable | tty$ReadStream, - stdout: stream$Writable | tty$WriteStream, - }) { - this._stdin = stdin; - this._stdout = stdout; - } - - apply(hooks: JestHookSubscriber) {} - - getUsageInfo(globalConfig: GlobalConfig): ?UsageData { - return null; - } - - onKey(value: string) {} - - run( - globalConfig: GlobalConfig, - updateConfigAndRun: Function, - ): Promise { - return Promise.resolve(); - } -} - -export default BaseWatchPlugin; diff --git a/packages/jest-watcher/src/BaseWatchPlugin.ts b/packages/jest-watcher/src/BaseWatchPlugin.ts new file mode 100644 index 000000000000..f9e6d31b0bdd --- /dev/null +++ b/packages/jest-watcher/src/BaseWatchPlugin.ts @@ -0,0 +1,38 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import {WatchPlugin} from './types'; + +class BaseWatchPlugin implements WatchPlugin { + _stdin: NodeJS.ReadableStream; + _stdout: NodeJS.WritableStream; + + constructor({ + stdin, + stdout, + }: { + stdin: NodeJS.ReadableStream; + stdout: NodeJS.WritableStream; + }) { + this._stdin = stdin; + this._stdout = stdout; + } + + apply() {} + + getUsageInfo() { + return null; + } + + onKey() {} + + run() { + return Promise.resolve(); + } +} + +export default BaseWatchPlugin; diff --git a/packages/jest-watcher/src/JestHooks.js b/packages/jest-watcher/src/JestHooks.ts similarity index 80% rename from packages/jest-watcher/src/JestHooks.js rename to packages/jest-watcher/src/JestHooks.ts index 22d5ccfb0e23..6525b8782038 100644 --- a/packages/jest-watcher/src/JestHooks.js +++ b/packages/jest-watcher/src/JestHooks.ts @@ -3,17 +3,15 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type { +import { JestHookSubscriber, JestHookEmitter, FileChange, ShouldRunTestSuite, TestRunComplete, -} from 'types/JestHooks'; +} from './types'; type AvailableHooks = | 'onFileChange' @@ -22,9 +20,9 @@ type AvailableHooks = class JestHooks { _listeners: { - onFileChange: Array, - onTestRunComplete: Array, - shouldRunTestSuite: Array, + onFileChange: Array; + onTestRunComplete: Array; + shouldRunTestSuite: Array; }; constructor() { @@ -61,14 +59,15 @@ class JestHooks { this._listeners.onTestRunComplete.forEach(listener => listener(results), ), - shouldRunTestSuite: async testSuiteInfo => - Promise.all( + shouldRunTestSuite: async testSuiteInfo => { + const result = await Promise.all( this._listeners.shouldRunTestSuite.map(listener => listener(testSuiteInfo), ), - ).then(result => - result.every(shouldRunTestSuite => shouldRunTestSuite), - ), + ); + + return result.every(Boolean); + }, }; } } diff --git a/packages/jest-watcher/src/PatternPrompt.js b/packages/jest-watcher/src/PatternPrompt.ts similarity index 82% rename from packages/jest-watcher/src/PatternPrompt.js rename to packages/jest-watcher/src/PatternPrompt.ts index 314edb87130e..61593b8615d7 100644 --- a/packages/jest-watcher/src/PatternPrompt.js +++ b/packages/jest-watcher/src/PatternPrompt.ts @@ -3,14 +3,8 @@ * * 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 type {ScrollOptions} from 'types/Watch'; - import chalk from 'chalk'; import ansiEscapes from 'ansi-escapes'; import {specialChars} from 'jest-util'; @@ -28,18 +22,20 @@ const usage = (entity: string) => const usageRows = usage('').split('\n').length; export default class PatternPrompt { - _pipe: stream$Writable | tty$WriteStream; + _pipe: NodeJS.WritableStream; _prompt: Prompt; _entityName: string; _currentUsageRows: number; - constructor(pipe: stream$Writable | tty$WriteStream, prompt: Prompt) { + constructor(pipe: NodeJS.WritableStream, prompt: Prompt) { + // TODO: Should come in the constructor + this._entityName = ''; this._pipe = pipe; this._prompt = prompt; this._currentUsageRows = usageRows; } - run(onSuccess: Function, onCancel: Function, options?: {header: string}) { + run(onSuccess: () => void, onCancel: () => void, options?: {header: string}) { this._pipe.write(ansiEscapes.cursorHide); this._pipe.write(CLEAR); @@ -56,7 +52,7 @@ export default class PatternPrompt { this._prompt.enter(this._onChange.bind(this), onSuccess, onCancel); } - _onChange(pattern: string, options: ScrollOptions) { + protected _onChange() { this._pipe.write(ansiEscapes.eraseLine); this._pipe.write(ansiEscapes.cursorLeft); } diff --git a/packages/jest-watcher/src/constants.js b/packages/jest-watcher/src/constants.ts similarity index 97% rename from packages/jest-watcher/src/constants.js rename to packages/jest-watcher/src/constants.ts index 5e9b2e73296b..d1a29c337421 100644 --- a/packages/jest-watcher/src/constants.js +++ b/packages/jest-watcher/src/constants.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ const isWindows = process.platform === 'win32'; diff --git a/packages/jest-watcher/src/index.js b/packages/jest-watcher/src/index.ts similarity index 97% rename from packages/jest-watcher/src/index.js rename to packages/jest-watcher/src/index.ts index 5a50fc7e909f..766fba239f10 100644 --- a/packages/jest-watcher/src/index.js +++ b/packages/jest-watcher/src/index.ts @@ -3,8 +3,6 @@ * * 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 as BaseWatchPlugin} from './BaseWatchPlugin'; diff --git a/packages/jest-watcher/src/lib/Prompt.js b/packages/jest-watcher/src/lib/Prompt.ts similarity index 79% rename from packages/jest-watcher/src/lib/Prompt.js rename to packages/jest-watcher/src/lib/Prompt.ts index 295d4b8db557..8db199284d1e 100644 --- a/packages/jest-watcher/src/lib/Prompt.js +++ b/packages/jest-watcher/src/lib/Prompt.ts @@ -3,36 +3,44 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {ScrollOptions} from 'types/Watch'; - +import {ScrollOptions} from '../types'; import {KEYS} from '../constants'; export default class Prompt { _entering: boolean; _value: string; - _onChange: Function; - _onSuccess: Function; - _onCancel: Function; + _onChange: () => void; + _onSuccess: (value?: string) => void; + _onCancel: (value?: string) => void; _offset: number; _promptLength: number; _selection: string | null; constructor() { - (this: any)._onResize = this._onResize.bind(this); + // Copied from `enter` to satisfy TS + this._entering = true; + this._value = ''; + this._selection = null; + this._offset = -1; + this._promptLength = 0; + + this._onChange = () => {}; + this._onSuccess = () => {}; + this._onCancel = () => {}; + + this._onResize = this._onResize.bind(this); } - _onResize() { - this._onChange(this._value); + private _onResize() { + this._onChange(); } enter( onChange: (pattern: string, options: ScrollOptions) => void, - onSuccess: Function, - onCancel: Function, + onSuccess: () => void, + onCancel: () => void, ) { this._entering = true; this._value = ''; diff --git a/packages/jest-watcher/src/lib/__tests__/__snapshots__/formatTestNameByPattern.test.js.snap b/packages/jest-watcher/src/lib/__tests__/__snapshots__/formatTestNameByPattern.test.ts.snap similarity index 100% rename from packages/jest-watcher/src/lib/__tests__/__snapshots__/formatTestNameByPattern.test.js.snap rename to packages/jest-watcher/src/lib/__tests__/__snapshots__/formatTestNameByPattern.test.ts.snap diff --git a/packages/jest-watcher/src/lib/__tests__/formatTestNameByPattern.test.js b/packages/jest-watcher/src/lib/__tests__/formatTestNameByPattern.test.ts similarity index 98% rename from packages/jest-watcher/src/lib/__tests__/formatTestNameByPattern.test.js rename to packages/jest-watcher/src/lib/__tests__/formatTestNameByPattern.test.ts index afa7611b43ef..9a491f8a601d 100644 --- a/packages/jest-watcher/src/lib/__tests__/formatTestNameByPattern.test.js +++ b/packages/jest-watcher/src/lib/__tests__/formatTestNameByPattern.test.ts @@ -3,12 +3,8 @@ * * 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 formatTestNameByPattern from '../formatTestNameByPattern'; describe('for multiline test name returns', () => { diff --git a/packages/jest-watcher/src/lib/__tests__/prompt.test.js b/packages/jest-watcher/src/lib/__tests__/prompt.test.ts similarity index 99% rename from packages/jest-watcher/src/lib/__tests__/prompt.test.js rename to packages/jest-watcher/src/lib/__tests__/prompt.test.ts index aa3e3ef0d039..1ade9d33331c 100644 --- a/packages/jest-watcher/src/lib/__tests__/prompt.test.js +++ b/packages/jest-watcher/src/lib/__tests__/prompt.test.ts @@ -6,8 +6,6 @@ * */ -'use strict'; - import Prompt from '../Prompt'; import {KEYS} from '../../constants'; diff --git a/packages/jest-watcher/src/lib/__tests__/scroll.test.js b/packages/jest-watcher/src/lib/__tests__/scroll.test.ts similarity index 100% rename from packages/jest-watcher/src/lib/__tests__/scroll.test.js rename to packages/jest-watcher/src/lib/__tests__/scroll.test.ts diff --git a/packages/jest-watcher/src/lib/colorize.js b/packages/jest-watcher/src/lib/colorize.ts similarity index 97% rename from packages/jest-watcher/src/lib/colorize.js rename to packages/jest-watcher/src/lib/colorize.ts index d474f97a86b2..8adf88ae2df1 100644 --- a/packages/jest-watcher/src/lib/colorize.js +++ b/packages/jest-watcher/src/lib/colorize.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import chalk from 'chalk'; diff --git a/packages/jest-watcher/src/lib/formatTestNameByPattern.js b/packages/jest-watcher/src/lib/formatTestNameByPattern.ts similarity index 95% rename from packages/jest-watcher/src/lib/formatTestNameByPattern.js rename to packages/jest-watcher/src/lib/formatTestNameByPattern.ts index c42e15b3dfab..b941c9887758 100644 --- a/packages/jest-watcher/src/lib/formatTestNameByPattern.js +++ b/packages/jest-watcher/src/lib/formatTestNameByPattern.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import chalk from 'chalk'; @@ -31,7 +29,7 @@ export default (testName: string, pattern: string, width: number) => { return chalk.dim(inlineTestName); } - const startPatternIndex = Math.max(match.index, 0); + const startPatternIndex = Math.max(match.index || 0, 0); const endPatternIndex = startPatternIndex + match[0].length; if (inlineTestName.length <= width) { diff --git a/packages/jest-watcher/src/lib/patternModeHelpers.js b/packages/jest-watcher/src/lib/patternModeHelpers.ts similarity index 88% rename from packages/jest-watcher/src/lib/patternModeHelpers.js rename to packages/jest-watcher/src/lib/patternModeHelpers.ts index fc57d5ad92f3..df411ad12d18 100644 --- a/packages/jest-watcher/src/lib/patternModeHelpers.js +++ b/packages/jest-watcher/src/lib/patternModeHelpers.ts @@ -3,19 +3,15 @@ * * 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 chalk from 'chalk'; import ansiEscapes from 'ansi-escapes'; import stringLength from 'string-length'; export const printPatternCaret = ( pattern: string, - pipe: stream$Writable | tty$WriteStream, + pipe: NodeJS.WritableStream, ) => { const inputText = `${chalk.dim(' pattern \u203A')} ${pattern}`; @@ -27,7 +23,7 @@ export const printPatternCaret = ( export const printRestoredPatternCaret = ( pattern: string, currentUsageRows: number, - pipe: stream$Writable | tty$WriteStream, + pipe: NodeJS.WritableStream, ) => { const inputText = `${chalk.dim(' pattern \u203A')} ${pattern}`; diff --git a/packages/jest-watcher/src/lib/scroll.js b/packages/jest-watcher/src/lib/scroll.ts similarity index 89% rename from packages/jest-watcher/src/lib/scroll.js rename to packages/jest-watcher/src/lib/scroll.ts index 3f51c5821685..977d9e85786b 100644 --- a/packages/jest-watcher/src/lib/scroll.js +++ b/packages/jest-watcher/src/lib/scroll.ts @@ -3,13 +3,9 @@ * * 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 type {ScrollOptions} from 'types/Watch'; +import {ScrollOptions} from '../types'; export default function scroll(size: number, {offset, max}: ScrollOptions) { let start = 0; diff --git a/packages/jest-watcher/src/types.js b/packages/jest-watcher/src/types.js deleted file mode 100644 index 35f8bd0ef357..000000000000 --- a/packages/jest-watcher/src/types.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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 type {GlobalConfig} from 'types/Config'; -import type {JestHookSubscriber} from 'types/JestHooks'; - -export type UsageData = { - key: string, - prompt: string, -}; - -export interface WatchPlugin { - +isInternal?: boolean; - +apply?: (hooks: JestHookSubscriber) => void; - +getUsageInfo?: (globalConfig: GlobalConfig) => ?UsageData; - +onKey?: (value: string) => void; - +run?: ( - globalConfig: GlobalConfig, - updateConfigAndRun: Function, - ) => Promise; -} diff --git a/packages/jest-watcher/src/types.ts b/packages/jest-watcher/src/types.ts new file mode 100644 index 000000000000..8886c011cd1f --- /dev/null +++ b/packages/jest-watcher/src/types.ts @@ -0,0 +1,83 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import {Config, TestResult} from '@jest/types'; + +type TestSuiteInfo = { + config: Config.ProjectConfig; + duration?: number; + testPath: string; +}; + +export type JestHookExposedFS = { + projects: Array<{ + config: Config.ProjectConfig; + testPaths: Array; + }>; +}; + +export type FileChange = (fs: JestHookExposedFS) => void; +export type ShouldRunTestSuite = ( + testSuiteInfo: TestSuiteInfo, +) => Promise; +export type TestRunComplete = (results: TestResult.AggregatedResult) => void; + +export type JestHookSubscriber = { + onFileChange: (fn: FileChange) => void; + onTestRunComplete: (fn: TestRunComplete) => void; + shouldRunTestSuite: (fn: ShouldRunTestSuite) => void; +}; + +export type JestHookEmitter = { + onFileChange: (fs: JestHookExposedFS) => void; + onTestRunComplete: (results: TestResult.AggregatedResult) => void; + shouldRunTestSuite: (testSuiteInfo: TestSuiteInfo) => Promise; +}; + +export type UsageData = { + key: string; + prompt: string; +}; + +export type AllowedConfigOptions = Partial< + Pick< + Config.GlobalConfig, + | 'bail' + | 'changedSince' + | 'collectCoverage' + | 'collectCoverageFrom' + | 'collectCoverageOnlyFrom' + | 'coverageDirectory' + | 'coverageReporters' + | 'notify' + | 'notifyMode' + | 'onlyFailures' + | 'reporters' + | 'testNamePattern' + | 'testPathPattern' + | 'updateSnapshot' + | 'verbose' + > & {mode: 'watch' | 'watchAll'} +>; + +export interface WatchPlugin { + isInternal?: boolean; + apply?: (hooks: JestHookSubscriber) => void; + getUsageInfo?: ( + globalConfig: Config.GlobalConfig, + ) => UsageData | undefined | null; + onKey?: (value: string) => void; + run?: ( + globalConfig: Config.GlobalConfig, + updateConfigAndRun: (config?: AllowedConfigOptions) => void, + ) => Promise; +} + +export type ScrollOptions = { + offset: number; + max: number; +}; diff --git a/packages/jest-watcher/tsconfig.json b/packages/jest-watcher/tsconfig.json new file mode 100644 index 000000000000..2bea30d53a40 --- /dev/null +++ b/packages/jest-watcher/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [{"path": "../jest-types"}, {"path": "../jest-util"}] +} diff --git a/website/versioned_docs/version-23.x/WatchPlugins.md b/website/versioned_docs/version-23.x/WatchPlugins.md index a2c9c8934189..6f830989149c 100644 --- a/website/versioned_docs/version-23.x/WatchPlugins.md +++ b/website/versioned_docs/version-23.x/WatchPlugins.md @@ -156,6 +156,7 @@ class MyWatchPlugin { For stability and safety reasons, only part of the global configuration keys can be updated with `updateConfigAndRun`. The current white list is as follows: - [`bail`](configuration.html#bail-boolean) +- [`changedSince`](cli.html#changedsince) - [`collectCoverage`](configuration.html#collectcoverage-boolean) - [`collectCoverageFrom`](configuration.html#collectcoveragefrom-array) - [`collectCoverageOnlyFrom`](configuration.html#collectcoverageonlyfrom-array) diff --git a/website/versioned_docs/version-24.0/WatchPlugins.md b/website/versioned_docs/version-24.0/WatchPlugins.md index e9f2c9b1c807..8b6acd192ed7 100644 --- a/website/versioned_docs/version-24.0/WatchPlugins.md +++ b/website/versioned_docs/version-24.0/WatchPlugins.md @@ -156,6 +156,7 @@ class MyWatchPlugin { For stability and safety reasons, only part of the global configuration keys can be updated with `updateConfigAndRun`. The current white list is as follows: - [`bail`](configuration.html#bail-number-boolean) +- [`changedSince`](cli.html#changedsince) - [`collectCoverage`](configuration.html#collectcoverage-boolean) - [`collectCoverageFrom`](configuration.html#collectcoveragefrom-array) - [`collectCoverageOnlyFrom`](configuration.html#collectcoverageonlyfrom-array) From ecf99525041c760ec123d42945a6214cd9d2f085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Sun, 10 Feb 2019 12:46:09 +0100 Subject: [PATCH 035/107] chore: migrate jest-mock to TypeScript (#7847) --- .eslintrc.js | 1 + CHANGELOG.md | 1 + packages/jest-mock/package.json | 4 + .../{jest_mock.test.js => index.test.ts} | 5 +- packages/jest-mock/src/{index.js => index.ts} | 396 +++++++++++++----- packages/jest-mock/tsconfig.json | 8 + packages/jest-types/src/Mocks.ts | 31 ++ packages/jest-types/src/index.ts | 3 +- packages/jest-util/src/FakeTimers.ts | 10 +- .../src/__tests__/fakeTimers.test.ts | 5 +- packages/jest-util/tsconfig.json | 2 +- 11 files changed, 339 insertions(+), 127 deletions(-) rename packages/jest-mock/src/__tests__/{jest_mock.test.js => index.test.ts} (99%) rename packages/jest-mock/src/{index.js => index.ts} (70%) create mode 100644 packages/jest-mock/tsconfig.json create mode 100644 packages/jest-types/src/Mocks.ts diff --git a/.eslintrc.js b/.eslintrc.js index d7ed999831eb..db8aa1b063a8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -25,6 +25,7 @@ module.exports = { {argsIgnorePattern: '^_'}, ], 'import/order': 'error', + 'no-dupe-class-members': 'off', 'no-unused-vars': 'off', }, }, diff --git a/CHANGELOG.md b/CHANGELOG.md index f0a5fbc41f6e..566573f13318 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - `[@jest/types]`: New package to handle shared types ([#7834](https://github.com/facebook/jest/pull/7834)) - `[jest-util]`: Migrate to TypeScript ([#7844](https://github.com/facebook/jest/pull/7844)) - `[jest-watcher]`: Migrate to TypeScript ([#7843](https://github.com/facebook/jest/pull/7843)) +- `[jest-mock]`: Migrate to TypeScript ([#7847](https://github.com/facebook/jest/pull/7847)) ### Performance diff --git a/packages/jest-mock/package.json b/packages/jest-mock/package.json index 9f10c2f9a30f..3dfe72868f3b 100644 --- a/packages/jest-mock/package.json +++ b/packages/jest-mock/package.json @@ -9,8 +9,12 @@ "engines": { "node": ">= 6" }, + "dependencies": { + "@jest/types": "^24.1.0" + }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "browser": "build-es5/index.js", "gitHead": "634e5a54f46b2a62d1dc81a170562e6f4e55ad60" } diff --git a/packages/jest-mock/src/__tests__/jest_mock.test.js b/packages/jest-mock/src/__tests__/index.test.ts similarity index 99% rename from packages/jest-mock/src/__tests__/jest_mock.test.js rename to packages/jest-mock/src/__tests__/index.test.ts index bd38cb281806..31cee40614c8 100644 --- a/packages/jest-mock/src/__tests__/jest_mock.test.js +++ b/packages/jest-mock/src/__tests__/index.test.ts @@ -6,9 +6,7 @@ * */ -'use strict'; - -const vm = require('vm'); +import vm from 'vm'; describe('moduleMocker', () => { let moduleMocker; @@ -182,6 +180,7 @@ describe('moduleMocker', () => { it('mocks ES2015 non-enumerable static properties and methods', () => { class ClassFoo { static foo() {} + static fooProp: Function; } ClassFoo.fooProp = () => {}; diff --git a/packages/jest-mock/src/index.js b/packages/jest-mock/src/index.ts similarity index 70% rename from packages/jest-mock/src/index.js rename to packages/jest-mock/src/index.ts index 9a95f5d9f2e1..9e6450e093ba 100644 --- a/packages/jest-mock/src/index.js +++ b/packages/jest-mock/src/index.ts @@ -3,23 +3,11 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Global} from 'types/Global'; - -type Mock = any; -export type MockFunctionMetadata = { - ref?: any, - members?: {[key: string]: MockFunctionMetadata}, - mockImpl?: () => any, - name?: string, - refID?: string | number, - type?: string, - value?: any, - length?: number, -}; +import {Mocks} from '@jest/types'; + +type Global = NodeJS.Global; // | Window – add once TS improves typings; /** * Possible types of a MockFunctionResult. @@ -38,33 +26,65 @@ type MockFunctionResult = { /** * Indicates how the call completed. */ - type: MockFunctionResultType, + type: MockFunctionResultType; /** * The value that was either thrown or returned by the function. * Undefined when type === 'incomplete'. */ - value: any, + value: unknown; }; -type MockFunctionState = { - instances: Array, - calls: Array>, +type MockFunctionState = { + calls: Array; + instances: Array; + invocationCallOrder: Array; /** * List of results of calls to the mock function. */ - results: Array, - invocationCallOrder: Array, + results: Array; }; type MockFunctionConfig = { - isReturnValueLastSet: boolean, - defaultReturnValue: any, - mockImpl: any, - mockName: string, - specificReturnValues: Array, - specificMockImpls: Array, + isReturnValueLastSet: boolean; + defaultReturnValue: unknown; + mockImpl: Function | undefined; + mockName: string; + specificReturnValues: Array; + specificMockImpls: Array; }; +interface Mock + extends Function, + MockInstance { + new (...args: Y): T; + (...args: Y): T; +} + +interface SpyInstance extends MockInstance {} + +interface MockInstance { + _isMockFunction: boolean; + _protoImpl: Function; + getMockName(): string; + getMockImplementation(): Function | undefined; + mock: MockFunctionState; + mockClear(): this; + mockReset(): this; + mockRestore(): void; + mockImplementation(fn: (...args: Y) => T): this; + mockImplementation(fn: () => Promise): this; + mockImplementationOnce(fn: (...args: Y) => T): this; + mockImplementationOnce(fn: () => Promise): this; + mockName(name: string): this; + mockReturnThis(): this; + mockReturnValue(value: T): this; + mockReturnValueOnce(value: T): this; + mockResolvedValue(value: T): this; + mockResolvedValueOnce(value: T): this; + mockRejectedValue(value: T): this; + mockRejectedValueOnce(value: T): this; +} + const MOCK_CONSTRUCTOR_NAME = 'mockConstructor'; const FUNCTION_NAME_RESERVED_PATTERN = /[\s!-\/:-@\[-`{-~]/; @@ -124,57 +144,113 @@ const RESERVED_KEYWORDS = new Set([ 'yield', ]); -function matchArity(fn: any, length: number): any { +function matchArity(fn: Function, length: number): Function { let mockConstructor; switch (length) { case 1: - mockConstructor = function(a) { + mockConstructor = function(this: unknown, _a: unknown) { return fn.apply(this, arguments); }; break; case 2: - mockConstructor = function(a, b) { + mockConstructor = function(this: unknown, _a: unknown, _b: unknown) { return fn.apply(this, arguments); }; break; case 3: - mockConstructor = function(a, b, c) { + mockConstructor = function( + this: unknown, + _a: unknown, + _b: unknown, + _c: unknown, + ) { return fn.apply(this, arguments); }; break; case 4: - mockConstructor = function(a, b, c, d) { + mockConstructor = function( + this: unknown, + _a: unknown, + _b: unknown, + _c: unknown, + _d: unknown, + ) { return fn.apply(this, arguments); }; break; case 5: - mockConstructor = function(a, b, c, d, e) { + mockConstructor = function( + this: unknown, + _a: unknown, + _b: unknown, + _c: unknown, + _d: unknown, + _e: unknown, + ) { return fn.apply(this, arguments); }; break; case 6: - mockConstructor = function(a, b, c, d, e, f) { + mockConstructor = function( + this: unknown, + _a: unknown, + _b: unknown, + _c: unknown, + _d: unknown, + _e: unknown, + _f: unknown, + ) { return fn.apply(this, arguments); }; break; case 7: - mockConstructor = function(a, b, c, d, e, f, g) { + mockConstructor = function( + this: unknown, + _a: unknown, + _b: unknown, + _c: unknown, + _d: unknown, + _e: unknown, + _f: unknown, + _g: unknown, + ) { return fn.apply(this, arguments); }; break; case 8: - mockConstructor = function(a, b, c, d, e, f, g, h) { + mockConstructor = function( + this: unknown, + _a: unknown, + _b: unknown, + _c: unknown, + _d: unknown, + _e: unknown, + _f: unknown, + _g: unknown, + _h: unknown, + ) { return fn.apply(this, arguments); }; break; case 9: - mockConstructor = function(a, b, c, d, e, f, g, h, i) { + mockConstructor = function( + this: unknown, + _a: unknown, + _b: unknown, + _c: unknown, + _d: unknown, + _e: unknown, + _f: unknown, + _g: unknown, + _h: unknown, + _i: unknown, + ) { return fn.apply(this, arguments); }; break; default: - mockConstructor = function() { + mockConstructor = function(this: unknown) { return fn.apply(this, arguments); }; break; @@ -183,11 +259,11 @@ function matchArity(fn: any, length: number): any { return mockConstructor; } -function getObjectType(value: any): string { +function getObjectType(value: unknown): string { return Object.prototype.toString.apply(value).slice(8, -1); } -function getType(ref?: any): string | null { +function getType(ref?: unknown): Mocks.MockFunctionMetadataType | null { const typeName = getObjectType(ref); if ( typeName === 'Function' || @@ -253,10 +329,10 @@ function isReadonlyProp(object: any, prop: string): boolean { class ModuleMockerClass { _environmentGlobal: Global; - _mockState: WeakMap; + _mockState: WeakMap, MockFunctionState>; _mockConfigRegistry: WeakMap; _spyState: Set<() => void>; - ModuleMocker: Class; + ModuleMocker: typeof ModuleMockerClass; _invocationCallCounter: number; /** @@ -307,7 +383,7 @@ class ModuleMockerClass { if (!isReadonlyProp(object, prop)) { const propDesc = Object.getOwnPropertyDescriptor(object, prop); - + // @ts-ignore Object.__esModule if ((propDesc !== undefined && !propDesc.get) || object.__esModule) { slots.add(prop); } @@ -320,7 +396,7 @@ class ModuleMockerClass { return Array.from(slots); } - _ensureMockConfig(f: Mock): MockFunctionConfig { + _ensureMockConfig(f: Mock): MockFunctionConfig { let config = this._mockConfigRegistry.get(f); if (!config) { config = this._defaultMockConfig(); @@ -329,7 +405,9 @@ class ModuleMockerClass { return config; } - _ensureMockState(f: Mock): MockFunctionState { + _ensureMockState( + f: Mock, + ): MockFunctionState { let state = this._mockState.get(f); if (!state) { state = this._defaultMockState(); @@ -349,7 +427,7 @@ class ModuleMockerClass { }; } - _defaultMockState(): MockFunctionState { + _defaultMockState(): MockFunctionState { return { calls: [], instances: [], @@ -358,7 +436,34 @@ class ModuleMockerClass { }; } - _makeComponent(metadata: MockFunctionMetadata, restore?: () => void): Mock { + _makeComponent( + metadata: Mocks.MockFunctionMetadata, + restore?: () => void, + ): Object; + _makeComponent( + metadata: Mocks.MockFunctionMetadata, + restore?: () => void, + ): Array; + _makeComponent( + metadata: Mocks.MockFunctionMetadata, + restore?: () => void, + ): RegExp; + _makeComponent( + metadata: Mocks.MockFunctionMetadata< + T, + Y, + 'constant' | 'collection' | 'null' | 'undefined' + >, + restore?: () => void, + ): T; + _makeComponent( + metadata: Mocks.MockFunctionMetadata, + restore?: () => void, + ): Mock; + _makeComponent( + metadata: Mocks.MockFunctionMetadata, + restore?: () => void, + ): Object | Array | RegExp | T | undefined | Mock { if (metadata.type === 'object') { return new this._environmentGlobal.Object(); } else if (metadata.type === 'array') { @@ -373,10 +478,6 @@ class ModuleMockerClass { ) { return metadata.value; } else if (metadata.type === 'function') { - /* eslint-disable prefer-const */ - let f; - /* eslint-enable prefer-const */ - const prototype = (metadata.members && metadata.members.prototype && @@ -384,17 +485,17 @@ class ModuleMockerClass { {}; const prototypeSlots = this._getSlots(prototype); const mocker = this; - const mockConstructor = matchArity(function() { + const mockConstructor = matchArity(function(this: T, ...args: Y) { const mockState = mocker._ensureMockState(f); const mockConfig = mocker._ensureMockConfig(f); mockState.instances.push(this); - mockState.calls.push(Array.prototype.slice.call(arguments)); + mockState.calls.push(args); // 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', + type: ('incomplete' as unknown) as MockFunctionResultType, value: undefined, }; mockState.results.push(mockResult); @@ -422,8 +523,11 @@ class ModuleMockerClass { // it easier to interact with mock instance call and // return values if (prototype[slot].type === 'function') { + // @ts-ignore no index signature const protoImpl = this[slot]; + // @ts-ignore no index signature this[slot] = mocker.generateFromMetadata(prototype[slot]); + // @ts-ignore no index signature this[slot]._protoImpl = protoImpl; } }); @@ -487,7 +591,10 @@ class ModuleMockerClass { return finalReturnValue; }, metadata.length || 0); - f = this._createMockFunction(metadata, mockConstructor); + const f = (this._createMockFunction( + metadata, + mockConstructor, + ) as unknown) as Mock; f._isMockFunction = true; f.getMockImplementation = () => this._ensureMockConfig(f).mockImpl; @@ -495,10 +602,9 @@ class ModuleMockerClass { this._spyState.add(restore); } - this._mockState.set(f, this._defaultMockState()); + this._mockState.set(f, this._defaultMockState()); this._mockConfigRegistry.set(f, this._defaultMockConfig()); - // $FlowFixMe - defineProperty getters not supported Object.defineProperty(f, 'mock', { configurable: false, enumerable: true, @@ -522,20 +628,20 @@ class ModuleMockerClass { return restore ? restore() : undefined; }; - f.mockReturnValueOnce = value => { + f.mockReturnValueOnce = (value: T) => { // next function call will return this value or default return value const mockConfig = this._ensureMockConfig(f); mockConfig.specificReturnValues.push(value); return f; }; - f.mockResolvedValueOnce = value => + f.mockResolvedValueOnce = (value: T) => f.mockImplementationOnce(() => Promise.resolve(value)); - f.mockRejectedValueOnce = value => + f.mockRejectedValueOnce = (value: T) => f.mockImplementationOnce(() => Promise.reject(value)); - f.mockReturnValue = value => { + f.mockReturnValue = (value: T) => { // next function call will return specified return value or this one const mockConfig = this._ensureMockConfig(f); mockConfig.isReturnValueLastSet = true; @@ -543,13 +649,15 @@ class ModuleMockerClass { return f; }; - f.mockResolvedValue = value => + f.mockResolvedValue = (value: T) => f.mockImplementation(() => Promise.resolve(value)); - f.mockRejectedValue = value => + f.mockRejectedValue = (value: T) => f.mockImplementation(() => Promise.reject(value)); - f.mockImplementationOnce = fn => { + f.mockImplementationOnce = ( + fn: ((...args: Y) => T) | (() => Promise), + ): Mock => { // next function call will use this mock implementation return value // or default mock implementation return value const mockConfig = this._ensureMockConfig(f); @@ -558,7 +666,9 @@ class ModuleMockerClass { return f; }; - f.mockImplementation = fn => { + f.mockImplementation = ( + fn: ((...args: Y) => T) | (() => Promise), + ): Mock => { // next function call will use mock implementation return value const mockConfig = this._ensureMockConfig(f); mockConfig.isReturnValueLastSet = false; @@ -568,11 +678,11 @@ class ModuleMockerClass { }; f.mockReturnThis = () => - f.mockImplementation(function() { + f.mockImplementation(function(this: T) { return this; }); - f.mockName = name => { + f.mockName = (name: string) => { if (name) { const mockConfig = this._ensureMockConfig(f); mockConfig.mockName = name; @@ -596,10 +706,10 @@ class ModuleMockerClass { } } - _createMockFunction( - metadata: MockFunctionMetadata, - mockConstructor: () => any, - ): any { + _createMockFunction( + metadata: Mocks.MockFunctionMetadata, + mockConstructor: Function, + ): Function { let name = metadata.name; if (!name) { return mockConstructor; @@ -656,11 +766,22 @@ class ModuleMockerClass { return createConstructor(mockConstructor); } - _generateMock( - metadata: MockFunctionMetadata, - callbacks: Array<() => any>, - refs: Object, - ): Mock { + _generateMock( + metadata: Mocks.MockFunctionMetadata, + callbacks: Array, + refs: { + [key: string]: + | Object + | Array + | RegExp + | T + | undefined + | Mock; + }, + ): Mock { + // metadata not compatible but it's the same type, maybe problem with + // overloading of _makeComponent and not _generateMock? + // @ts-ignore const mock = this._makeComponent(metadata); if (metadata.refID != null) { refs[metadata.refID] = mock; @@ -669,7 +790,11 @@ class ModuleMockerClass { this._getSlots(metadata.members).forEach(slot => { const slotMetadata = (metadata.members && metadata.members[slot]) || {}; if (slotMetadata.ref != null) { - callbacks.push(() => (mock[slot] = refs[slotMetadata.ref])); + callbacks.push( + (function(ref) { + return () => (mock[slot] = refs[ref]); + })(slotMetadata.ref), + ); } else { mock[slot] = this._generateMock(slotMetadata, callbacks, refs); } @@ -691,8 +816,10 @@ class ModuleMockerClass { * @param _metadata Metadata for the mock in the schema returned by the * getMetadata method of this module. */ - generateFromMetadata(_metadata: MockFunctionMetadata): Mock { - const callbacks = []; + generateFromMetadata( + _metadata: Mocks.MockFunctionMetadata, + ): Mock { + const callbacks: Function[] = []; const refs = {}; const mock = this._generateMock(_metadata, callbacks, refs); callbacks.forEach(setter => setter()); @@ -703,8 +830,11 @@ class ModuleMockerClass { * @see README.md * @param component The component for which to retrieve metadata. */ - getMetadata(component: any, _refs?: Map): ?MockFunctionMetadata { - const refs = _refs || new Map(); + getMetadata( + component: T, + _refs?: Map, + ): Mocks.MockFunctionMetadata | null { + const refs = _refs || new Map(); const ref = refs.get(component); if (ref != null) { return {ref}; @@ -715,7 +845,7 @@ class ModuleMockerClass { return null; } - const metadata: MockFunctionMetadata = {type}; + const metadata: Mocks.MockFunctionMetadata = {type}; if ( type === 'constant' || type === 'collection' || @@ -725,8 +855,11 @@ class ModuleMockerClass { metadata.value = component; return metadata; } else if (type === 'function') { + // @ts-ignore this is a function so it has a name metadata.name = component.name; + // @ts-ignore may be a mock if (component._isMockFunction === true) { + // @ts-ignore may be a mock metadata.mockImpl = component.getMockImplementation(); } } @@ -734,28 +867,29 @@ class ModuleMockerClass { metadata.refID = refs.size; refs.set(component, metadata.refID); - let members = null; + let members: { + [key: string]: Mocks.MockFunctionMetadata; + } | null = null; // Leave arrays alone if (type !== 'array') { - if (type !== 'undefined') { - this._getSlots(component).forEach(slot => { - if ( - type === 'function' && - component._isMockFunction === true && - slot.match(/^mock/) - ) { - return; - } - - const slotMetadata = this.getMetadata(component[slot], refs); - if (slotMetadata) { - if (!members) { - members = {}; - } - members[slot] = slotMetadata; + this._getSlots(component).forEach(slot => { + if ( + type === 'function' && + // @ts-ignore may be a mock + component._isMockFunction === true && + slot.match(/^mock/) + ) { + return; + } + // @ts-ignore no index signature + const slotMetadata = this.getMetadata(component[slot], refs); + if (slotMetadata) { + if (!members) { + members = {}; } - }); - } + members[slot] = slotMetadata; + } + }); } if (members) { @@ -769,16 +903,39 @@ class ModuleMockerClass { return !!fn && fn._isMockFunction === true; } - fn(implementation?: any): any { + fn(implementation?: (...args: Y) => T): Mock { const length = implementation ? implementation.length : 0; - const fn = this._makeComponent({length, type: 'function'}); + const fn = this._makeComponent({length, type: 'function'}); if (implementation) { fn.mockImplementation(implementation); } return fn; } - spyOn(object: any, methodName: any, accessType?: string): any { + spyOn( + object: T, + methodName: M, + accessType: 'get', + ): SpyInstance; + + spyOn( + object: T, + methodName: M, + accessType: 'set', + ): SpyInstance; + + spyOn( + object: T, + methodName: M, + ): T[M] extends (...args: any[]) => any + ? SpyInstance, ArgsType> + : never; + + spyOn( + object: T, + methodName: M, + accessType?: 'get' | 'set', + ) { if (accessType) { return this._spyOnProperty(object, methodName, accessType); } @@ -802,11 +959,13 @@ class ModuleMockerClass { ); } + // @ts-ignore overriding original method with a Mock object[methodName] = this._makeComponent({type: 'function'}, () => { object[methodName] = original; }); - object[methodName].mockImplementation(function() { + // @ts-ignore original method is now a Mock + object[methodName].mockImplementation(function(this: unknown) { return original.apply(this, arguments); }); } @@ -814,7 +973,11 @@ class ModuleMockerClass { return object[methodName]; } - _spyOnProperty(obj: any, propertyName: any, accessType: string = 'get'): any { + _spyOnProperty( + obj: T, + propertyName: M, + accessType: 'get' | 'set' = 'get', + ): Mock { if (typeof obj !== 'object' && typeof obj !== 'function') { throw new Error( 'Cannot spyOn on a primitive value; ' + this._typeOf(obj) + ' given', @@ -867,19 +1030,20 @@ class ModuleMockerClass { } descriptor[accessType] = this._makeComponent({type: 'function'}, () => { - // $FlowFixMe - descriptor[accessType] = original; - // $FlowFixMe - Object.defineProperty(obj, propertyName, descriptor); + descriptor![accessType] = original; + Object.defineProperty(obj, propertyName, descriptor!); }); - descriptor[accessType].mockImplementation(function() { + (descriptor[accessType] as Mock).mockImplementation(function( + this: unknown, + ) { + // @ts-ignore return original.apply(this, arguments); }); } Object.defineProperty(obj, propertyName, descriptor); - return descriptor[accessType]; + return descriptor[accessType] as Mock; } clearAllMocks() { @@ -901,5 +1065,7 @@ class ModuleMockerClass { } } -export type ModuleMocker = ModuleMockerClass; -module.exports = new ModuleMockerClass(global); +// TODO: bring this type export back once done with TS migration +// export type ModuleMocker = ModuleMockerClass; + +export = new ModuleMockerClass(global); diff --git a/packages/jest-mock/tsconfig.json b/packages/jest-mock/tsconfig.json new file mode 100644 index 000000000000..3046cb6b9b6a --- /dev/null +++ b/packages/jest-mock/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [{"path": "../jest-types"}] +} diff --git a/packages/jest-types/src/Mocks.ts b/packages/jest-types/src/Mocks.ts new file mode 100644 index 000000000000..36e28a49ca01 --- /dev/null +++ b/packages/jest-types/src/Mocks.ts @@ -0,0 +1,31 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +export type MockFunctionMetadataType = + | 'object' + | 'array' + | 'regexp' + | 'function' + | 'constant' + | 'collection' + | 'null' + | 'undefined'; + +export type MockFunctionMetadata< + T, + Y extends unknown[], + Type = MockFunctionMetadataType +> = { + ref?: number; + members?: {[key: string]: MockFunctionMetadata}; + mockImpl?: (...args: Y) => T; + name?: string; + refID?: number; + type?: Type; + value?: T; + length?: number; +}; diff --git a/packages/jest-types/src/index.ts b/packages/jest-types/src/index.ts index 61dfddbf9407..2c9054b64270 100644 --- a/packages/jest-types/src/index.ts +++ b/packages/jest-types/src/index.ts @@ -9,5 +9,6 @@ import * as Config from './Config'; import * as Console from './Console'; import * as SourceMaps from './SourceMaps'; import * as TestResult from './TestResult'; +import * as Mocks from './Mocks'; -export {Config, Console, SourceMaps, TestResult}; +export {Config, Console, SourceMaps, TestResult, Mocks}; diff --git a/packages/jest-util/src/FakeTimers.ts b/packages/jest-util/src/FakeTimers.ts index d8cfac38d266..a4abbeb78268 100644 --- a/packages/jest-util/src/FakeTimers.ts +++ b/packages/jest-util/src/FakeTimers.ts @@ -5,14 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -// not yet migrated to TS -// import {ModuleMocker} from 'jest-mock'; - -type ModuleMocker = any; - +import mock from 'jest-mock'; import {formatStackTrace, StackTraceConfig} from 'jest-message-util'; import setGlobal from './setGlobal'; +type ModuleMocker = typeof mock; + /** * We don't know the type of arguments for a callback ahead of time which is why * we are disabling the flowtype/no-weak-types rule here. @@ -356,8 +354,10 @@ export default class FakeTimers { _createMocks() { const fn = (impl: Function) => + // @ts-ignore TODO: figure out better typings here this._moduleMocker.fn().mockImplementation(impl); + // TODO: add better typings; these are mocks, but typed as regular timers this._fakeTimerAPIs = { clearImmediate: fn(this._fakeClearImmediate.bind(this)), clearInterval: fn(this._fakeClearTimer.bind(this)), diff --git a/packages/jest-util/src/__tests__/fakeTimers.test.ts b/packages/jest-util/src/__tests__/fakeTimers.test.ts index 3a14fed27b93..7d53b0c91e51 100644 --- a/packages/jest-util/src/__tests__/fakeTimers.test.ts +++ b/packages/jest-util/src/__tests__/fakeTimers.test.ts @@ -6,9 +6,10 @@ */ import vm from 'vm'; -// @ts-ignore: not yet migrated -import mock, {ModuleMocker} from 'jest-mock'; +import mock from 'jest-mock'; import FakeTimers from '../FakeTimers'; +// TODO: import this type directly from jest-mock once TS migration is done +type ModuleMocker = typeof mock; const timerConfig = { idToRef: (id: number) => id, diff --git a/packages/jest-util/tsconfig.json b/packages/jest-util/tsconfig.json index 931ea0e1de53..e6221a0ea3e6 100644 --- a/packages/jest-util/tsconfig.json +++ b/packages/jest-util/tsconfig.json @@ -4,5 +4,5 @@ "rootDir": "src", "outDir": "build" }, - "references": [{"path": "../jest-types"}] + "references": [{"path": "../jest-types"}, {"path": "../jest-mock"}] } From 946d7fb8b6be54406cd0375d230a8ca4453a7dcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Sun, 10 Feb 2019 14:35:22 +0100 Subject: [PATCH 036/107] chore: followup to jest-mock TS migration (#7850) --- CHANGELOG.md | 2 +- packages/jest-mock/src/index.ts | 62 ++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 566573f13318..a19a6ca80c55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ - `[@jest/types]`: New package to handle shared types ([#7834](https://github.com/facebook/jest/pull/7834)) - `[jest-util]`: Migrate to TypeScript ([#7844](https://github.com/facebook/jest/pull/7844)) - `[jest-watcher]`: Migrate to TypeScript ([#7843](https://github.com/facebook/jest/pull/7843)) -- `[jest-mock]`: Migrate to TypeScript ([#7847](https://github.com/facebook/jest/pull/7847)) +- `[jest-mock]`: Migrate to TypeScript ([#7847](https://github.com/facebook/jest/pull/7847), [#7850](https://github.com/facebook/jest/pull/7850)) ### Performance diff --git a/packages/jest-mock/src/index.ts b/packages/jest-mock/src/index.ts index 9e6450e093ba..4d7d3a567664 100644 --- a/packages/jest-mock/src/index.ts +++ b/packages/jest-mock/src/index.ts @@ -53,6 +53,16 @@ type MockFunctionConfig = { specificMockImpls: Array; }; +// see https://github.com/Microsoft/TypeScript/issues/25215 +type NonFunctionPropertyNames = { + [K in keyof T]: T[K] extends (...args: any[]) => any ? never : K +}[keyof T] & + string; +type FunctionPropertyNames = { + [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never +}[keyof T] & + string; + interface Mock extends Function, MockInstance { @@ -328,12 +338,12 @@ function isReadonlyProp(object: any, prop: string): boolean { } class ModuleMockerClass { - _environmentGlobal: Global; - _mockState: WeakMap, MockFunctionState>; - _mockConfigRegistry: WeakMap; - _spyState: Set<() => void>; + private _environmentGlobal: Global; + private _mockState: WeakMap, MockFunctionState>; + private _mockConfigRegistry: WeakMap; + private _spyState: Set<() => void>; + private _invocationCallCounter: number; ModuleMocker: typeof ModuleMockerClass; - _invocationCallCounter: number; /** * @see README.md @@ -349,7 +359,7 @@ class ModuleMockerClass { this._invocationCallCounter = 1; } - _getSlots(object?: Object): Array { + private _getSlots(object?: Object): Array { if (!object) { return []; } @@ -396,7 +406,9 @@ class ModuleMockerClass { return Array.from(slots); } - _ensureMockConfig(f: Mock): MockFunctionConfig { + private _ensureMockConfig( + f: Mock, + ): MockFunctionConfig { let config = this._mockConfigRegistry.get(f); if (!config) { config = this._defaultMockConfig(); @@ -405,7 +417,7 @@ class ModuleMockerClass { return config; } - _ensureMockState( + private _ensureMockState( f: Mock, ): MockFunctionState { let state = this._mockState.get(f); @@ -416,7 +428,7 @@ class ModuleMockerClass { return state; } - _defaultMockConfig(): MockFunctionConfig { + private _defaultMockConfig(): MockFunctionConfig { return { defaultReturnValue: undefined, isReturnValueLastSet: false, @@ -427,7 +439,7 @@ class ModuleMockerClass { }; } - _defaultMockState(): MockFunctionState { + private _defaultMockState(): MockFunctionState { return { calls: [], instances: [], @@ -436,19 +448,19 @@ class ModuleMockerClass { }; } - _makeComponent( + private _makeComponent( metadata: Mocks.MockFunctionMetadata, restore?: () => void, ): Object; - _makeComponent( + private _makeComponent( metadata: Mocks.MockFunctionMetadata, restore?: () => void, ): Array; - _makeComponent( + private _makeComponent( metadata: Mocks.MockFunctionMetadata, restore?: () => void, ): RegExp; - _makeComponent( + private _makeComponent( metadata: Mocks.MockFunctionMetadata< T, Y, @@ -456,11 +468,11 @@ class ModuleMockerClass { >, restore?: () => void, ): T; - _makeComponent( + private _makeComponent( metadata: Mocks.MockFunctionMetadata, restore?: () => void, ): Mock; - _makeComponent( + private _makeComponent( metadata: Mocks.MockFunctionMetadata, restore?: () => void, ): Object | Array | RegExp | T | undefined | Mock { @@ -706,7 +718,7 @@ class ModuleMockerClass { } } - _createMockFunction( + private _createMockFunction( metadata: Mocks.MockFunctionMetadata, mockConstructor: Function, ): Function { @@ -766,7 +778,7 @@ class ModuleMockerClass { return createConstructor(mockConstructor); } - _generateMock( + private _generateMock( metadata: Mocks.MockFunctionMetadata, callbacks: Array, refs: { @@ -899,7 +911,7 @@ class ModuleMockerClass { return metadata; } - isMockFunction(fn: any): boolean { + isMockFunction(fn: any): fn is Mock { return !!fn && fn._isMockFunction === true; } @@ -912,26 +924,26 @@ class ModuleMockerClass { return fn; } - spyOn( + spyOn>( object: T, methodName: M, accessType: 'get', ): SpyInstance; - spyOn( + spyOn>( object: T, methodName: M, accessType: 'set', ): SpyInstance; - spyOn( + spyOn>( object: T, methodName: M, ): T[M] extends (...args: any[]) => any ? SpyInstance, ArgsType> : never; - spyOn( + spyOn>( object: T, methodName: M, accessType?: 'get' | 'set', @@ -973,7 +985,7 @@ class ModuleMockerClass { return object[methodName]; } - _spyOnProperty( + private _spyOnProperty>( obj: T, propertyName: M, accessType: 'get' | 'set' = 'get', @@ -1060,7 +1072,7 @@ class ModuleMockerClass { this._spyState = new Set(); } - _typeOf(value: any): string { + private _typeOf(value: any): string { return value == null ? '' + value : typeof value; } } From 12a297fa27edb558329dc4c3726962cb0513f02d Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sun, 10 Feb 2019 16:57:20 +0100 Subject: [PATCH 037/107] chore: migrate jest-worker to TypeScript (#7853) --- CHANGELOG.md | 1 + .../src/reporters/coverage_reporter.js | 1 - packages/jest-haste-map/src/index.js | 1 - packages/jest-runner/src/index.js | 1 - packages/jest-worker/package.json | 2 + packages/jest-worker/src/{Farm.js => Farm.ts} | 33 ++--- .../src/{WorkerPool.js => WorkerPool.ts} | 6 +- .../{BaseWorkerPool.js => BaseWorkerPool.ts} | 26 ++-- .../jest-worker/src/{index.js => index.ts} | 36 +++-- .../jest-worker/src/{types.js => types.ts} | 135 +++++++++--------- ...ProcessWorker.js => ChildProcessWorker.ts} | 45 +++--- ...eThreadsWorker.js => NodeThreadsWorker.ts} | 42 +++--- .../__tests__/ChildProcessWorker.test.js | 6 +- .../__tests__/NodeThreadsWorker.test.js | 4 +- .../{processChild.js => processChild.ts} | 31 ++-- .../{threadChild.js => threadChild.ts} | 49 +++---- packages/jest-worker/tsconfig.json | 7 + 17 files changed, 197 insertions(+), 229 deletions(-) rename packages/jest-worker/src/{Farm.js => Farm.ts} (80%) rename packages/jest-worker/src/{WorkerPool.js => WorkerPool.ts} (96%) rename packages/jest-worker/src/base/{BaseWorkerPool.js => BaseWorkerPool.ts} (85%) rename packages/jest-worker/src/{index.js => index.ts} (85%) rename packages/jest-worker/src/{types.js => types.ts} (50%) rename packages/jest-worker/src/workers/{ChildProcessWorker.js => ChildProcessWorker.ts} (85%) rename packages/jest-worker/src/workers/{NodeThreadsWorker.js => NodeThreadsWorker.ts} (83%) rename packages/jest-worker/src/workers/{processChild.js => processChild.ts} (87%) rename packages/jest-worker/src/workers/{threadChild.js => threadChild.ts} (79%) create mode 100644 packages/jest-worker/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index a19a6ca80c55..c910de37cdc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - `[jest-util]`: Migrate to TypeScript ([#7844](https://github.com/facebook/jest/pull/7844)) - `[jest-watcher]`: Migrate to TypeScript ([#7843](https://github.com/facebook/jest/pull/7843)) - `[jest-mock]`: Migrate to TypeScript ([#7847](https://github.com/facebook/jest/pull/7847), [#7850](https://github.com/facebook/jest/pull/7850)) +- `[jest-worker]`: Migrate to TypeScript ([#7853](https://github.com/facebook/jest/pull/7853)) ### Performance diff --git a/packages/jest-cli/src/reporters/coverage_reporter.js b/packages/jest-cli/src/reporters/coverage_reporter.js index f2fad46f9d27..d8466110f720 100644 --- a/packages/jest-cli/src/reporters/coverage_reporter.js +++ b/packages/jest-cli/src/reporters/coverage_reporter.js @@ -159,7 +159,6 @@ export default class CoverageReporter extends BaseReporter { if (this._globalConfig.maxWorkers <= 1) { worker = require('./coverage_worker'); } else { - // $FlowFixMe: assignment of a worker with custom properties. worker = new Worker(require.resolve('./coverage_worker'), { exposedMethods: ['worker'], maxRetries: 2, diff --git a/packages/jest-haste-map/src/index.js b/packages/jest-haste-map/src/index.js index c7c6a79ac841..7831169d2ff0 100644 --- a/packages/jest-haste-map/src/index.js +++ b/packages/jest-haste-map/src/index.js @@ -670,7 +670,6 @@ class HasteMap extends EventEmitter { if ((options && options.forceInBand) || this._options.maxWorkers <= 1) { this._worker = {getSha1, worker}; } else { - // $FlowFixMe: assignment of a worker with custom properties. this._worker = (new Worker(require.resolve('./worker'), { exposedMethods: ['getSha1', 'worker'], maxRetries: 3, diff --git a/packages/jest-runner/src/index.js b/packages/jest-runner/src/index.js index cd339cd37e5c..538978903fd8 100644 --- a/packages/jest-runner/src/index.js +++ b/packages/jest-runner/src/index.js @@ -98,7 +98,6 @@ class TestRunner { onResult: OnTestSuccess, onFailure: OnTestFailure, ) { - // $FlowFixMe: class object is augmented with worker when instantiating. const worker: WorkerInterface = new Worker(TEST_WORKER_PATH, { exposedMethods: ['worker'], forkOptions: {stdio: 'pipe'}, diff --git a/packages/jest-worker/package.json b/packages/jest-worker/package.json index b993e76c65da..994afd104cfe 100644 --- a/packages/jest-worker/package.json +++ b/packages/jest-worker/package.json @@ -8,7 +8,9 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { + "@types/node": "*", "merge-stream": "^1.0.1", "supports-color": "^6.1.0" }, diff --git a/packages/jest-worker/src/Farm.js b/packages/jest-worker/src/Farm.ts similarity index 80% rename from packages/jest-worker/src/Farm.js rename to packages/jest-worker/src/Farm.ts index a601eebc42ac..34b0155c8850 100644 --- a/packages/jest-worker/src/Farm.js +++ b/packages/jest-worker/src/Farm.ts @@ -3,35 +3,32 @@ * * 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 type { +import { ChildMessage, + FarmOptions, QueueChildMessage, WorkerInterface, OnStart, OnEnd, + CHILD_MESSAGE_CALL, } from './types'; -import {CHILD_MESSAGE_CALL} from './types'; export default class Farm { - _computeWorkerKey: (string, ...Array) => ?string; - _cacheKeys: {[string]: WorkerInterface, __proto__: null}; + _computeWorkerKey: FarmOptions['computeWorkerKey']; + _cacheKeys: {[key: string]: WorkerInterface}; _callback: Function; _last: Array; _locks: Array; _numOfWorkers: number; _offset: number; - _queue: Array; + _queue: Array; constructor( numOfWorkers: number, callback: Function, - computeWorkerKey?: (string, ...Array) => ?string, + computeWorkerKey?: FarmOptions['computeWorkerKey'], ) { this._callback = callback; this._numOfWorkers = numOfWorkers; @@ -45,16 +42,16 @@ export default class Farm { } } - doWork(method: string, ...args: Array): Promise { + doWork(method: string, ...args: Array): Promise { return new Promise((resolve, reject) => { const computeWorkerKey = this._computeWorkerKey; const request: ChildMessage = [CHILD_MESSAGE_CALL, false, method, args]; - let worker: ?WorkerInterface = null; - let hash: ?string = null; + let worker: WorkerInterface | null = null; + let hash: string | null = null; if (computeWorkerKey) { - hash = computeWorkerKey.apply(this, [method].concat(args)); + hash = computeWorkerKey.call(this, method, ...args); worker = hash == null ? null : this._cacheKeys[hash]; } @@ -64,7 +61,7 @@ export default class Farm { } }; - const onEnd: OnEnd = (error: ?Error, result: ?mixed) => { + const onEnd: OnEnd = (error: Error | null, result: unknown) => { if (error) { reject(error); } else { @@ -81,11 +78,11 @@ export default class Farm { }); } - _getNextJob(workerId: number): ?QueueChildMessage { + _getNextJob(workerId: number): QueueChildMessage | null { let queueHead = this._queue[workerId]; while (queueHead && queueHead.request[1]) { - queueHead = queueHead.next; + queueHead = queueHead.next || null; } this._queue[workerId] = queueHead; @@ -104,7 +101,7 @@ export default class Farm { return this; } - const onEnd = (error: ?Error, result: mixed) => { + const onEnd = (error: Error | null, result: unknown) => { job.onEnd(error, result); this.unlock(workerId); this._process(workerId); diff --git a/packages/jest-worker/src/WorkerPool.js b/packages/jest-worker/src/WorkerPool.ts similarity index 96% rename from packages/jest-worker/src/WorkerPool.js rename to packages/jest-worker/src/WorkerPool.ts index 0c6828288653..dd61ce4dfa8e 100644 --- a/packages/jest-worker/src/WorkerPool.js +++ b/packages/jest-worker/src/WorkerPool.ts @@ -3,15 +3,11 @@ * * 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 BaseWorkerPool from './base/BaseWorkerPool'; -import type { +import { ChildMessage, WorkerOptions, OnStart, diff --git a/packages/jest-worker/src/base/BaseWorkerPool.js b/packages/jest-worker/src/base/BaseWorkerPool.ts similarity index 85% rename from packages/jest-worker/src/base/BaseWorkerPool.js rename to packages/jest-worker/src/base/BaseWorkerPool.ts index 03a3b878517e..db27a42f5492 100644 --- a/packages/jest-worker/src/base/BaseWorkerPool.js +++ b/packages/jest-worker/src/base/BaseWorkerPool.ts @@ -3,26 +3,24 @@ * * 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 mergeStream from 'merge-stream'; import path from 'path'; +import mergeStream from 'merge-stream'; -import {CHILD_MESSAGE_END} from '../types'; - -import type {Readable} from 'stream'; -import type {WorkerPoolOptions, WorkerOptions, WorkerInterface} from '../types'; +import { + CHILD_MESSAGE_END, + WorkerPoolOptions, + WorkerOptions, + WorkerInterface, +} from '../types'; /* istanbul ignore next */ const emptyMethod = () => {}; export default class BaseWorkerPool { - _stderr: Readable; - _stdout: Readable; + _stderr: NodeJS.ReadableStream; + _stdout: NodeJS.ReadableStream; _options: WorkerPoolOptions; _workers: Array; @@ -67,11 +65,11 @@ export default class BaseWorkerPool { this._stderr = stderr; } - getStderr(): Readable { + getStderr(): NodeJS.ReadableStream { return this._stderr; } - getStdout(): Readable { + getStdout(): NodeJS.ReadableStream { return this._stdout; } @@ -83,7 +81,7 @@ export default class BaseWorkerPool { return this._workers[workerId]; } - createWorker(workerOptions: WorkerOptions): WorkerInterface { + createWorker(_workerOptions: WorkerOptions): WorkerInterface { throw Error('Missing method createWorker in WorkerPool'); } diff --git a/packages/jest-worker/src/index.js b/packages/jest-worker/src/index.ts similarity index 85% rename from packages/jest-worker/src/index.js rename to packages/jest-worker/src/index.ts index dcd2c633164f..ffef6bfebf29 100644 --- a/packages/jest-worker/src/index.js +++ b/packages/jest-worker/src/index.ts @@ -3,39 +3,30 @@ * * 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 os from 'os'; import WorkerPool from './WorkerPool'; import Farm from './Farm'; -import type { - WorkerPoolInterface, - WorkerPoolOptions, - FarmOptions, -} from './types'; -import type {Readable} from 'stream'; +import {WorkerPoolInterface, WorkerPoolOptions, FarmOptions} from './types'; function getExposedMethods( workerPath: string, options: FarmOptions, -): $ReadOnlyArray { +): ReadonlyArray { let exposedMethods = options.exposedMethods; // If no methods list is given, try getting it by auto-requiring the module. if (!exposedMethods) { - // $FlowFixMe: This has to be a dynamic require. const module: Function | Object = require(workerPath); exposedMethods = Object.keys(module).filter( + // @ts-ignore: no index name => typeof module[name] === 'function', ); if (typeof module === 'function') { - exposedMethods.push('default'); + exposedMethods = [...exposedMethods, 'default']; } } @@ -75,6 +66,7 @@ export default class JestWorker { constructor(workerPath: string, options?: FarmOptions) { this._options = {...options}; + this._ending = false; const workerPoolOptions: WorkerPoolOptions = { enableWorkerThreads: this._options.enableWorkerThreads || false, @@ -84,9 +76,15 @@ export default class JestWorker { setupArgs: this._options.setupArgs || [], }; - this._workerPool = this._options.WorkerPool - ? new this._options.WorkerPool(workerPath, workerPoolOptions) - : new WorkerPool(workerPath, workerPoolOptions); + if (this._options.WorkerPool) { + // @ts-ignore: constructor target any? + this._workerPool = new this._options.WorkerPool( + workerPath, + workerPoolOptions, + ); + } else { + this._workerPool = new WorkerPool(workerPath, workerPoolOptions); + } this._farm = new Farm( workerPoolOptions.numWorkers, @@ -107,7 +105,7 @@ export default class JestWorker { throw new TypeError('Cannot define a method called ' + name); } - // $FlowFixMe: dynamic extension of the class instance is expected. + // @ts-ignore: dynamic extension of the class instance is expected. this[name] = this._callFunctionWithArgs.bind(this, name); }); } @@ -120,11 +118,11 @@ export default class JestWorker { return this._farm.doWork(method, ...args); } - getStderr(): Readable { + getStderr(): NodeJS.ReadableStream { return this._workerPool.getStderr(); } - getStdout(): Readable { + getStdout(): NodeJS.ReadableStream { return this._workerPool.getStdout(); } diff --git a/packages/jest-worker/src/types.js b/packages/jest-worker/src/types.ts similarity index 50% rename from packages/jest-worker/src/types.js rename to packages/jest-worker/src/types.ts index bb12434eca1a..5c0e7f144cdc 100644 --- a/packages/jest-worker/src/types.js +++ b/packages/jest-worker/src/types.ts @@ -3,15 +3,11 @@ * * 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'; - // Because of the dynamic nature of a worker communication process, all messages // coming from any of the other processes cannot be typed. Thus, many types -// include "any" as a flow type, which is (unfortunately) correct here. +// include "unknown" as a TS type, which is (unfortunately) correct here. export const CHILD_MESSAGE_INITIALIZE: 0 = 0; export const CHILD_MESSAGE_CALL: 1 = 1; @@ -27,98 +23,105 @@ export type PARENT_MESSAGE_ERROR = // Option objects. -import type {Readable} from 'stream'; const EventEmitter = require('events'); export type ForkOptions = { - cwd?: string, - env?: Object, - execPath?: string, - execArgv?: Array, - silent?: boolean, - stdio?: Array, - uid?: number, - gid?: number, + cwd?: string; + env?: NodeJS.ProcessEnv; + execPath?: string; + execArgv?: Array; + silent?: boolean; + stdio?: Array; + uid?: number; + gid?: number; }; export interface WorkerPoolInterface { - getStderr(): Readable; - getStdout(): Readable; + getStderr(): NodeJS.ReadableStream; + getStdout(): NodeJS.ReadableStream; getWorkers(): Array; - createWorker(WorkerOptions): WorkerInterface; - send(number, ChildMessage, Function, Function): void; + createWorker(options: WorkerOptions): WorkerInterface; + send( + workerId: number, + request: ChildMessage, + onStart: OnStart, + onEnd: OnEnd, + ): void; end(): void; } export interface WorkerInterface { - send(ChildMessage, Function, Function): void; + send( + request: ChildMessage, + onProcessStart: OnStart, + onProcessEnd: OnEnd, + ): void; getWorkerId(): number; - getStderr(): Readable; - getStdout(): Readable; - onExit(number): void; - onMessage(any): void; + getStderr(): NodeJS.ReadableStream; + getStdout(): NodeJS.ReadableStream; + onExit(exitCode: number): void; + onMessage(message: ParentMessage): void; } export type FarmOptions = { - computeWorkerKey?: (string, ...Array) => ?string, - exposedMethods?: $ReadOnlyArray, - forkOptions?: ForkOptions, - setupArgs?: Array, - maxRetries?: number, - numWorkers?: number, + computeWorkerKey?: (method: string, ...args: Array) => string | null; + exposedMethods?: ReadonlyArray; + forkOptions?: ForkOptions; + setupArgs?: Array; + maxRetries?: number; + numWorkers?: number; WorkerPool?: ( workerPath: string, options?: WorkerPoolOptions, - ) => WorkerPoolInterface, - enableWorkerThreads?: boolean, + ) => WorkerPoolInterface; + enableWorkerThreads?: boolean; }; -export type WorkerPoolOptions = {| - setupArgs: Array, - forkOptions: ForkOptions, - maxRetries: number, - numWorkers: number, - enableWorkerThreads: boolean, -|}; - -export type WorkerOptions = {| - forkOptions: ForkOptions, - setupArgs: Array, - maxRetries: number, - workerId: number, - workerPath: string, -|}; +export type WorkerPoolOptions = { + setupArgs: Array; + forkOptions: ForkOptions; + maxRetries: number; + numWorkers: number; + enableWorkerThreads: boolean; +}; + +export type WorkerOptions = { + forkOptions: ForkOptions; + setupArgs: Array; + maxRetries: number; + workerId: number; + workerPath: string; +}; // Messages passed from the parent to the children. -export type MessagePort = { - ...typeof EventEmitter, - postMessage(any): void, +export type MessagePort = typeof EventEmitter & { + postMessage(message: unknown): void; }; export type MessageChannel = { - port1: MessagePort, - port2: MessagePort, + port1: MessagePort; + port2: MessagePort; }; export type ChildMessageInitialize = [ typeof CHILD_MESSAGE_INITIALIZE, // type boolean, // processed string, // file - ?Array, // setupArgs - ?MessagePort, // MessagePort + Array | undefined, // setupArgs + MessagePort | undefined // MessagePort ]; export type ChildMessageCall = [ typeof CHILD_MESSAGE_CALL, // type boolean, // processed string, // method - $ReadOnlyArray, // args + Array // args ]; export type ChildMessageEnd = [ typeof CHILD_MESSAGE_END, // type - boolean, // processed + boolean // processed ]; export type ChildMessage = @@ -130,7 +133,7 @@ export type ChildMessage = export type ParentMessageOk = [ typeof PARENT_MESSAGE_OK, // type - any, // result + unknown // result ]; export type ParentMessageError = [ @@ -138,18 +141,18 @@ export type ParentMessageError = [ string, // constructor string, // message string, // stack - any, // extra + unknown // extra ]; export type ParentMessage = ParentMessageOk | ParentMessageError; // Queue types. -export type OnStart = WorkerInterface => void; -export type OnEnd = (?Error, ?any) => void; - -export type QueueChildMessage = {| - request: ChildMessage, - onStart: OnStart, - onEnd: OnEnd, - next?: QueueChildMessage, -|}; +export type OnStart = (worker: WorkerInterface) => void; +export type OnEnd = (err: Error | null, result: unknown) => void; + +export type QueueChildMessage = { + request: ChildMessage; + onStart: OnStart; + onEnd: OnEnd; + next?: QueueChildMessage; +}; diff --git a/packages/jest-worker/src/workers/ChildProcessWorker.js b/packages/jest-worker/src/workers/ChildProcessWorker.ts similarity index 85% rename from packages/jest-worker/src/workers/ChildProcessWorker.js rename to packages/jest-worker/src/workers/ChildProcessWorker.ts index 2ad70be2cd07..ae3945ff0be0 100644 --- a/packages/jest-worker/src/workers/ChildProcessWorker.js +++ b/packages/jest-worker/src/workers/ChildProcessWorker.ts @@ -3,13 +3,10 @@ * * 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 childProcess from 'child_process'; +import childProcess, {ChildProcess} from 'child_process'; +import supportsColor from 'supports-color'; import { CHILD_MESSAGE_INITIALIZE, @@ -17,15 +14,13 @@ import { PARENT_MESSAGE_SETUP_ERROR, PARENT_MESSAGE_OK, WorkerInterface, + ChildMessage, + OnEnd, + OnStart, + WorkerOptions, + ParentMessage, } from '../types'; -import type {ChildProcess} from 'child_process'; -import type {Readable} from 'stream'; - -import type {ChildMessage, OnEnd, OnStart, WorkerOptions} from '../types'; - -import supportsColor from 'supports-color'; - /** * This class wraps the child process and provides a nice interface to * communicate with. It takes care of: @@ -45,10 +40,10 @@ import supportsColor from 'supports-color'; * same call skip it. */ export default class ChildProcessWorker implements WorkerInterface { - _child: ChildProcess; + _child!: ChildProcess; _options: WorkerOptions; - _onProcessEnd: OnEnd; - _retries: number; + _onProcessEnd!: OnEnd; + _retries!: number; constructor(options: WorkerOptions) { this._options = options; @@ -57,13 +52,13 @@ export default class ChildProcessWorker implements WorkerInterface { initialize() { const forceColor = supportsColor.stdout ? {FORCE_COLOR: '1'} : {}; - const child = childProcess.fork(require.resolve('./processChild'), { + const child = childProcess.fork(require.resolve('./processChild'), [], { cwd: process.cwd(), env: { ...process.env, - JEST_WORKER_ID: this._options.workerId, + JEST_WORKER_ID: String(this._options.workerId), ...forceColor, - }, + } as NodeJS.ProcessEnv, // Suppress --debug / --inspect flags while preserving others (like --harmony). execArgv: process.execArgv.filter(v => !/^--(debug|inspect)/.test(v)), silent: true, @@ -93,13 +88,13 @@ export default class ChildProcessWorker implements WorkerInterface { PARENT_MESSAGE_CLIENT_ERROR, error.name, error.message, - error.stack, + error.stack!, {type: 'WorkerError'}, ]); } } - onMessage(response: any /* Should be ParentMessage */) { + onMessage(response: ParentMessage) { let error; switch (response[0]) { @@ -112,16 +107,16 @@ export default class ChildProcessWorker implements WorkerInterface { if (error != null && typeof error === 'object') { const extra = error; + // @ts-ignore: no index const NativeCtor = global[response[1]]; const Ctor = typeof NativeCtor === 'function' ? NativeCtor : Error; error = new Ctor(response[2]); - // $FlowFixMe: adding custom properties to errors. error.type = response[1]; error.stack = response[3]; for (const key in extra) { - // $FlowFixMe: adding custom properties to errors. + // @ts-ignore: adding custom properties to errors. error[key] = extra[key]; } } @@ -132,7 +127,7 @@ export default class ChildProcessWorker implements WorkerInterface { case PARENT_MESSAGE_SETUP_ERROR: error = new Error('Error when calling setup: ' + response[2]); - // $FlowFixMe: adding custom properties to errors. + // @ts-ignore: adding custom properties to errors. error.type = response[1]; error.stack = response[3]; @@ -162,11 +157,11 @@ export default class ChildProcessWorker implements WorkerInterface { return this._options.workerId; } - getStdout(): Readable { + getStdout(): NodeJS.ReadableStream { return this._child.stdout; } - getStderr(): Readable { + getStderr(): NodeJS.ReadableStream { return this._child.stderr; } } diff --git a/packages/jest-worker/src/workers/NodeThreadsWorker.js b/packages/jest-worker/src/workers/NodeThreadsWorker.ts similarity index 83% rename from packages/jest-worker/src/workers/NodeThreadsWorker.js rename to packages/jest-worker/src/workers/NodeThreadsWorker.ts index 3b93ab822b7d..48118eac1c28 100644 --- a/packages/jest-worker/src/workers/NodeThreadsWorker.js +++ b/packages/jest-worker/src/workers/NodeThreadsWorker.ts @@ -3,38 +3,31 @@ * * 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 path from 'path'; +// ESLint doesn't know about this experimental module +// eslint-disable-next-line import/no-unresolved +import {Worker} from 'worker_threads'; import { CHILD_MESSAGE_INITIALIZE, PARENT_MESSAGE_OK, PARENT_MESSAGE_CLIENT_ERROR, PARENT_MESSAGE_SETUP_ERROR, -} from '../types'; - -import path from 'path'; - -import type {Readable} from 'stream'; -import type { ChildMessage, OnEnd, OnStart, WorkerOptions, WorkerInterface, + ParentMessage, } from '../types'; -// $FlowFixMe: Flow doesn't know about experimental features of Node -const {Worker} = require('worker_threads'); - export default class ExperimentalWorker implements WorkerInterface { - _worker: Worker; + _worker!: Worker; _options: WorkerOptions; - _onProcessEnd: OnEnd; - _retries: number; + _onProcessEnd!: OnEnd; + _retries!: number; constructor(options: WorkerOptions) { this._options = options; @@ -48,7 +41,10 @@ export default class ExperimentalWorker implements WorkerInterface { stdout: true, workerData: { cwd: process.cwd(), - env: {...process.env, JEST_WORKER_ID: this._options.workerId}, + env: { + ...process.env, + JEST_WORKER_ID: String(this._options.workerId), + } as NodeJS.ProcessEnv, // Suppress --debug / --inspect flags while preserving others (like --harmony). execArgv: process.execArgv.filter(v => !/^--(debug|inspect)/.test(v)), silent: true, @@ -78,13 +74,13 @@ export default class ExperimentalWorker implements WorkerInterface { PARENT_MESSAGE_CLIENT_ERROR, error.name, error.message, - error.stack, + error.stack!, {type: 'WorkerError'}, ]); } } - onMessage(response: any /* Should be ParentMessage */) { + onMessage(response: ParentMessage) { let error; switch (response[0]) { @@ -97,16 +93,16 @@ export default class ExperimentalWorker implements WorkerInterface { if (error != null && typeof error === 'object') { const extra = error; + // @ts-ignore: no index const NativeCtor = global[response[1]]; const Ctor = typeof NativeCtor === 'function' ? NativeCtor : Error; error = new Ctor(response[2]); - // $FlowFixMe: adding custom properties to errors. error.type = response[1]; error.stack = response[3]; for (const key in extra) { - // $FlowFixMe: adding custom properties to errors. + // @ts-ignore: no index error[key] = extra[key]; } } @@ -116,7 +112,7 @@ export default class ExperimentalWorker implements WorkerInterface { case PARENT_MESSAGE_SETUP_ERROR: error = new Error('Error when calling setup: ' + response[2]); - // $FlowFixMe: adding custom properties to errors. + // @ts-ignore: adding custom properties to errors. error.type = response[1]; error.stack = response[3]; @@ -146,11 +142,11 @@ export default class ExperimentalWorker implements WorkerInterface { return this._options.workerId; } - getStdout(): Readable { + getStdout(): NodeJS.ReadableStream { return this._worker.stdout; } - getStderr(): Readable { + getStderr(): NodeJS.ReadableStream { return this._worker.stderr; } } diff --git a/packages/jest-worker/src/workers/__tests__/ChildProcessWorker.test.js b/packages/jest-worker/src/workers/__tests__/ChildProcessWorker.test.js index d6c5fc8790b7..79944efadb65 100644 --- a/packages/jest-worker/src/workers/__tests__/ChildProcessWorker.test.js +++ b/packages/jest-worker/src/workers/__tests__/ChildProcessWorker.test.js @@ -5,8 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -'use strict'; - /* eslint-disable no-new */ import EventEmitter from 'events'; @@ -63,7 +61,7 @@ it('passes fork options down to child_process.fork, adding the defaults', () => }); expect(childProcess.fork.mock.calls[0][0]).toBe(child); - expect(childProcess.fork.mock.calls[0][1]).toEqual({ + expect(childProcess.fork.mock.calls[0][2]).toEqual({ cwd: '/tmp', // Overridden default option. env: {...process.env, FORCE_COLOR: supportsColor.stdout ? '1' : undefined}, // Default option. execArgv: ['-p'], // Filtered option. @@ -80,7 +78,7 @@ it('passes workerId to the child process and assign it to env.JEST_WORKER_ID', ( workerPath: '/tmp/foo', }); - expect(childProcess.fork.mock.calls[0][1].env.JEST_WORKER_ID).toEqual(2); + expect(childProcess.fork.mock.calls[0][2].env.JEST_WORKER_ID).toEqual('2'); }); it('initializes the child process with the given workerPath', () => { diff --git a/packages/jest-worker/src/workers/__tests__/NodeThreadsWorker.test.js b/packages/jest-worker/src/workers/__tests__/NodeThreadsWorker.test.js index 4406e3795c71..747ba0531c88 100644 --- a/packages/jest-worker/src/workers/__tests__/NodeThreadsWorker.test.js +++ b/packages/jest-worker/src/workers/__tests__/NodeThreadsWorker.test.js @@ -63,7 +63,7 @@ it('passes fork options down to child_process.fork, adding the defaults', () => workerPath: '/tmp/foo/bar/baz.js', }); - expect(childProcess.mock.calls[0][0]).toBe(child); + expect(childProcess.mock.calls[0][0]).toBe(child.replace(/\.ts$/, '.js')); expect(childProcess.mock.calls[0][1]).toEqual({ eval: false, stderr: true, @@ -87,7 +87,7 @@ it('passes workerId to the child process and assign it to env.JEST_WORKER_ID', ( }); expect(childProcess.mock.calls[0][1].workerData.env.JEST_WORKER_ID).toEqual( - 2, + '2', ); }); diff --git a/packages/jest-worker/src/workers/processChild.js b/packages/jest-worker/src/workers/processChild.ts similarity index 87% rename from packages/jest-worker/src/workers/processChild.js rename to packages/jest-worker/src/workers/processChild.ts index 9becbba0fb49..beb395af1a22 100644 --- a/packages/jest-worker/src/workers/processChild.js +++ b/packages/jest-worker/src/workers/processChild.ts @@ -3,29 +3,22 @@ * * 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 { CHILD_MESSAGE_CALL, CHILD_MESSAGE_END, CHILD_MESSAGE_INITIALIZE, PARENT_MESSAGE_CLIENT_ERROR, + PARENT_MESSAGE_ERROR, PARENT_MESSAGE_SETUP_ERROR, PARENT_MESSAGE_OK, -} from '../types'; - -import type { ChildMessageInitialize, ChildMessageCall, - PARENT_MESSAGE_ERROR, } from '../types'; -let file = null; -let setupArgs: Array = []; +let file: string | null = null; +let setupArgs: Array = []; let initialized = false; /** @@ -101,8 +94,7 @@ function reportError(error: Error, type: PARENT_MESSAGE_ERROR) { } function end(): void { - // $FlowFixMe: This has to be a dynamic require. - const main = require(file); + const main = require(file!); if (!main.teardown) { exitProcess(); @@ -117,11 +109,10 @@ function exitProcess(): void { process.exit(0); } -function execMethod(method: string, args: $ReadOnlyArray): void { - // $FlowFixMe: This has to be a dynamic require. - const main = require(file); +function execMethod(method: string, args: Array): void { + const main = require(file!); - let fn; + let fn: (...args: Array) => unknown; if (method === 'default') { fn = main.__esModule ? main['default'] : main; @@ -145,10 +136,10 @@ function execMethod(method: string, args: $ReadOnlyArray): void { } function execFunction( - fn: (...args: $ReadOnlyArray) => mixed, - ctx: mixed, - args: $ReadOnlyArray, - onResult: (result: mixed) => void, + fn: (...args: Array) => any | Promise, + ctx: unknown, + args: Array, + onResult: (result: unknown) => void, onError: (error: Error) => void, ): void { let result; diff --git a/packages/jest-worker/src/workers/threadChild.js b/packages/jest-worker/src/workers/threadChild.ts similarity index 79% rename from packages/jest-worker/src/workers/threadChild.js rename to packages/jest-worker/src/workers/threadChild.ts index e35e4610acc5..4d8272cef8c0 100644 --- a/packages/jest-worker/src/workers/threadChild.js +++ b/packages/jest-worker/src/workers/threadChild.ts @@ -3,36 +3,28 @@ * * 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'; +// ESLint doesn't know about this experimental module +// eslint-disable-next-line import/no-unresolved +import {parentPort, isMainThread} from 'worker_threads'; import { + ChildMessageInitialize, + ChildMessageCall, CHILD_MESSAGE_CALL, CHILD_MESSAGE_END, CHILD_MESSAGE_INITIALIZE, PARENT_MESSAGE_CLIENT_ERROR, + PARENT_MESSAGE_ERROR, PARENT_MESSAGE_SETUP_ERROR, PARENT_MESSAGE_OK, } from '../types'; -import type { - ChildMessageInitialize, - ChildMessageCall, - PARENT_MESSAGE_ERROR, -} from '../types'; - -let file = null; -let setupArgs: Array = []; +let file: string | null = null; +let setupArgs: Array = []; let initialized = false; -/* eslint-disable import/no-unresolved */ -// $FlowFixMe: Flow doesn't support experimental node modules -import {parentPort, isMainThread} from 'worker_threads'; -/* eslint-enable import/no-unresolved */ - /** * This file is a small bootstrapper for workers. It sets up the communication * between the worker and the parent process, interpreting parent messages and @@ -46,7 +38,7 @@ import {parentPort, isMainThread} from 'worker_threads'; * If an invalid message is detected, the child will exit (by throwing) with a * non-zero exit code. */ -parentPort.on('message', (request: any) => { +parentPort!.on('message', (request: any) => { switch (request[0]) { case CHILD_MESSAGE_INITIALIZE: const init: ChildMessageInitialize = request; @@ -75,7 +67,7 @@ function reportSuccess(result: any) { throw new Error('Child can only be used on a forked process'); } - parentPort.postMessage([PARENT_MESSAGE_OK, result]); + parentPort!.postMessage([PARENT_MESSAGE_OK, result]); } function reportClientError(error: Error) { @@ -95,19 +87,17 @@ function reportError(error: Error, type: PARENT_MESSAGE_ERROR) { error = new Error('"null" or "undefined" thrown'); } - parentPort.postMessage([ + parentPort!.postMessage([ type, error.constructor && error.constructor.name, error.message, error.stack, - // $FlowFixMe: this is safe to just inherit from Object. typeof error === 'object' ? {...error} : error, ]); } function end(): void { - // $FlowFixMe: This has to be a dynamic require. - const main = require(file); + const main = require(file!); if (!main.teardown) { exitProcess(); @@ -122,11 +112,10 @@ function exitProcess(): void { process.exit(0); } -function execMethod(method: string, args: $ReadOnlyArray): void { - // $FlowFixMe: This has to be a dynamic require. - const main = require(file); +function execMethod(method: string, args: Array): void { + const main = require(file!); - let fn; + let fn: (...args: Array) => unknown; if (method === 'default') { fn = main.__esModule ? main['default'] : main; @@ -150,10 +139,10 @@ function execMethod(method: string, args: $ReadOnlyArray): void { } function execFunction( - fn: (...args: $ReadOnlyArray) => mixed, - ctx: mixed, - args: $ReadOnlyArray, - onResult: (result: mixed) => void, + fn: (...args: Array) => any, + ctx: unknown, + args: Array, + onResult: (result: unknown) => void, onError: (error: Error) => void, ): void { let result; diff --git a/packages/jest-worker/tsconfig.json b/packages/jest-worker/tsconfig.json new file mode 100644 index 000000000000..7bb06bce6d20 --- /dev/null +++ b/packages/jest-worker/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + } +} From fef7e7068f48df63b35f20e5d24364036e90b35b Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 11 Feb 2019 03:17:37 +0100 Subject: [PATCH 038/107] chore: add missing reference to jest-message-util from jest-util --- packages/jest-util/tsconfig.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/jest-util/tsconfig.json b/packages/jest-util/tsconfig.json index e6221a0ea3e6..c8a0f5ac5aa8 100644 --- a/packages/jest-util/tsconfig.json +++ b/packages/jest-util/tsconfig.json @@ -4,5 +4,9 @@ "rootDir": "src", "outDir": "build" }, - "references": [{"path": "../jest-types"}, {"path": "../jest-mock"}] + "references": [ + {"path": "../jest-types"}, + {"path": "../jest-message-util"}, + {"path": "../jest-mock"} + ] } From 4fbac17078f78b40c915c437de0e01383eeb7114 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 11 Feb 2019 03:21:31 +0100 Subject: [PATCH 039/107] chore: inherit stdio when running tsc for better output --- scripts/build.js | 2 +- scripts/watch.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build.js b/scripts/build.js index 9afc50653ad6..80bf002cc4e0 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -196,7 +196,7 @@ function compileTypes(packages) { fs.existsSync(path.resolve(p, 'tsconfig.json')) ); - execa.sync('tsc', ['-b', ...packageWithTs]); + execa.sync('tsc', ['-b', ...packageWithTs], {stdio: 'inherit'}); } if (files.length) { diff --git a/scripts/watch.js b/scripts/watch.js index d2ab88c9791a..542ec1c1cc87 100644 --- a/scripts/watch.js +++ b/scripts/watch.js @@ -61,7 +61,7 @@ const packageWithTs = packages.filter(p => fs.existsSync(path.resolve(p, 'tsconfig.json')) ); -execa('tsc', ['-b', ...packageWithTs, '--watch']); +execa('tsc', ['-b', ...packageWithTs, '--watch'], {stdio: 'inherit'}); setInterval(() => { const files = Array.from(filesToBuild.keys()); From 68bdc36a4996d92afa35050080510f4b44f9e938 Mon Sep 17 00:00:00 2001 From: Will Smythe Date: Mon, 11 Feb 2019 07:30:36 -0500 Subject: [PATCH 040/107] fix: Windows-specific test timeout issues introduced by Yarn 1.13.0 (#7838) --- .azure-pipelines.yml | 1 - .gitignore | 2 ++ e2e/runJest.js | 71 +++++++++++++++++--------------------------- 3 files changed, 30 insertions(+), 44 deletions(-) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index df022b431dec..e28f5b54627b 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -17,7 +17,6 @@ jobs: git config --global core.autocrlf false git config --global core.symlinks true displayName: 'Preserve LF endings and symbolic links on check out' - - script: npm install -g yarn@1.9.4 - template: .azure-pipelines-steps.yml - job: macOS diff --git a/.gitignore b/.gitignore index 60ba0f22d861..ee3195b8c97a 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,8 @@ /website/translated_docs /website/i18n/* +/reports/* + coverage lerna-debug.log npm-debug.log diff --git a/e2e/runJest.js b/e2e/runJest.js index 9df0726a6f26..ce6a9f8ba943 100644 --- a/e2e/runJest.js +++ b/e2e/runJest.js @@ -10,7 +10,7 @@ import path from 'path'; import fs from 'fs'; -import execa, {sync as spawnSync} from 'execa'; +import execa from 'execa'; import {Writable} from 'readable-stream'; import stripAnsi from 'strip-ansi'; import {normalizeIcons} from './Utils'; @@ -20,7 +20,8 @@ const JEST_PATH = path.resolve(__dirname, '../packages/jest-cli/bin/jest.js'); type RunJestOptions = { nodePath?: string, skipPkgJsonCheck?: boolean, // don't complain if can't find package.json - stripAnsi?: boolean, // remove colors from stdout and stderr + stripAnsi?: boolean, // remove colors from stdout and stderr, + timeout?: number, // kill the Jest process after X milliseconds }; // return the result of the spawned process: @@ -30,6 +31,16 @@ export default function runJest( dir: string, args?: Array, options: RunJestOptions = {}, +) { + return normalizeResult(spawnJest(dir, args, options), options); +} + +// Spawns Jest and returns either a Promise (if spawnAsync is true) or the completed child process +function spawnJest( + dir: string, + args?: Array, + options: RunJestOptions = {}, + spawnAsync: boolean = false, ) { const isRelative = !path.isAbsolute(dir); @@ -51,12 +62,23 @@ export default function runJest( const env = {...process.env, FORCE_COLOR: 0}; if (options.nodePath) env['NODE_PATH'] = options.nodePath; - const result = spawnSync(JEST_PATH, args || [], { + + const spawnArgs = [JEST_PATH, ...(args || [])]; + const spawnOptions = { cwd: dir, env, reject: false, - }); + timeout: options.timeout || 0, + }; + return (spawnAsync ? execa : execa.sync)( + process.execPath, + spawnArgs, + spawnOptions, + ); +} + +function normalizeResult(result, options) { // For compat with cross-spawn result.status = result.code; @@ -101,34 +123,7 @@ export const until = async function( text: string, options: RunJestOptions = {}, ) { - const isRelative = !path.isAbsolute(dir); - - if (isRelative) { - dir = path.resolve(__dirname, dir); - } - - const localPackageJson = path.resolve(dir, 'package.json'); - if (!options.skipPkgJsonCheck && !fs.existsSync(localPackageJson)) { - throw new Error( - ` - Make sure you have a local package.json file at - "${localPackageJson}". - Otherwise Jest will try to traverse the directory tree and find the - the global package.json, which will send Jest into infinite loop. - `, - ); - } - - const env = {...process.env, FORCE_COLOR: 0}; - if (options.nodePath) env['NODE_PATH'] = options.nodePath; - - const jestPromise = execa(JEST_PATH, args || [], { - cwd: dir, - env, - reject: false, - // this should never take more than 5-6 seconds, bailout after 30 - timeout: 30000, - }); + const jestPromise = spawnJest(dir, args, {timeout: 30000, ...options}, true); jestPromise.stderr.pipe( new Writable({ @@ -144,15 +139,5 @@ export const until = async function( }), ); - const result = await jestPromise; - - // For compat with cross-spawn - result.status = result.code; - - result.stdout = normalizeIcons(result.stdout); - if (options.stripAnsi) result.stdout = stripAnsi(result.stdout); - result.stderr = normalizeIcons(result.stderr); - if (options.stripAnsi) result.stderr = stripAnsi(result.stderr); - - return result; + return normalizeResult(await jestPromise, options); }; From 77c064dc69c81f63be270fc8eec5e51e39ce8155 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 12 Feb 2019 01:40:52 +0100 Subject: [PATCH 041/107] chore: migrate jest-haste-map to TypeScript (#7854) --- CHANGELOG.md | 1 + packages/jest-cli/src/TestSequencer.js | 1 - packages/jest-haste-map/package.json | 3 + .../src/{HasteFS.js => HasteFS.ts} | 42 +-- .../src/{ModuleMap.js => ModuleMap.ts} | 62 ++--- .../src/__tests__/index.test.js | 6 +- .../src/{blacklist.js => blacklist.ts} | 2 - .../src/{constants.js => constants.ts} | 9 +- .../src/crawlers/{node.js => node.ts} | 26 +- .../src/crawlers/{watchman.js => watchman.ts} | 45 +-- .../src/{getMockName.js => getMockName.ts} | 2 - .../jest-haste-map/src/{index.js => index.ts} | 256 +++++++++--------- .../jest-haste-map/src/lib/WatchmanWatcher.js | 4 +- .../lib/__tests__/dependencyExtractor.test.js | 2 - .../src/lib/__tests__/fast_path.test.js | 2 - .../__tests__/getPlatformExtension.test.js | 2 - .../lib/__tests__/isRegExpSupported.test.js | 2 - ...ncyExtractor.js => dependencyExtractor.ts} | 13 +- .../src/lib/{fast_path.js => fast_path.ts} | 2 - ...rmExtension.js => getPlatformExtension.ts} | 13 +- ...egExpSupported.js => isRegExpSupported.ts} | 2 - ...ormalizePathSep.js => normalizePathSep.ts} | 6 +- packages/jest-haste-map/src/types.js | 44 --- packages/jest-haste-map/src/types.ts | 104 +++++++ .../src/{worker.js => worker.ts} | 25 +- packages/jest-haste-map/tsconfig.json | 13 + tsconfig.json | 3 +- yarn.lock | 2 +- 28 files changed, 374 insertions(+), 320 deletions(-) rename packages/jest-haste-map/src/{HasteFS.js => HasteFS.ts} (67%) rename packages/jest-haste-map/src/{ModuleMap.js => ModuleMap.ts} (83%) rename packages/jest-haste-map/src/{blacklist.js => blacklist.ts} (99%) rename packages/jest-haste-map/src/{constants.js => constants.ts} (90%) rename packages/jest-haste-map/src/crawlers/{node.js => node.ts} (88%) rename packages/jest-haste-map/src/crawlers/{watchman.js => watchman.ts} (86%) rename packages/jest-haste-map/src/{getMockName.js => getMockName.ts} (97%) rename packages/jest-haste-map/src/{index.js => index.ts} (86%) rename packages/jest-haste-map/src/lib/{dependencyExtractor.js => dependencyExtractor.ts} (86%) rename packages/jest-haste-map/src/lib/{fast_path.js => fast_path.ts} (98%) rename packages/jest-haste-map/src/lib/{getPlatformExtension.js => getPlatformExtension.ts} (79%) rename packages/jest-haste-map/src/lib/{isRegExpSupported.js => isRegExpSupported.ts} (97%) rename packages/jest-haste-map/src/lib/{normalizePathSep.js => normalizePathSep.ts} (84%) delete mode 100644 packages/jest-haste-map/src/types.js create mode 100644 packages/jest-haste-map/src/types.ts rename packages/jest-haste-map/src/{worker.js => worker.ts} (85%) create mode 100644 packages/jest-haste-map/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index c910de37cdc7..7e8004209de7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ - `[jest-watcher]`: Migrate to TypeScript ([#7843](https://github.com/facebook/jest/pull/7843)) - `[jest-mock]`: Migrate to TypeScript ([#7847](https://github.com/facebook/jest/pull/7847), [#7850](https://github.com/facebook/jest/pull/7850)) - `[jest-worker]`: Migrate to TypeScript ([#7853](https://github.com/facebook/jest/pull/7853)) +- `[jest-haste-map]`: Migrate to TypeScript ([#7854](https://github.com/facebook/jest/pull/7854)) ### Performance diff --git a/packages/jest-cli/src/TestSequencer.js b/packages/jest-cli/src/TestSequencer.js index b13c2e261d9e..0fab2560639c 100644 --- a/packages/jest-cli/src/TestSequencer.js +++ b/packages/jest-cli/src/TestSequencer.js @@ -12,7 +12,6 @@ import type {Context} from 'types/Context'; import type {Test} from 'types/TestRunner'; import fs from 'fs'; -// $FlowFixMe: Missing ESM export import {getCacheFilePath} from 'jest-haste-map'; const FAIL = 0; diff --git a/packages/jest-haste-map/package.json b/packages/jest-haste-map/package.json index 1daf9e83bee0..9bb467e58de3 100644 --- a/packages/jest-haste-map/package.json +++ b/packages/jest-haste-map/package.json @@ -8,7 +8,9 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { + "@jest/types": "^24.1.0", "fb-watchman": "^2.0.0", "graceful-fs": "^4.1.15", "invariant": "^2.2.4", @@ -23,6 +25,7 @@ "@types/graceful-fs": "^4.1.2", "@types/invariant": "^2.2.29", "@types/micromatch": "^3.1.0", + "@types/node": "^10.12.24", "@types/sane": "^2.0.0" }, "engines": { diff --git a/packages/jest-haste-map/src/HasteFS.js b/packages/jest-haste-map/src/HasteFS.ts similarity index 67% rename from packages/jest-haste-map/src/HasteFS.js rename to packages/jest-haste-map/src/HasteFS.ts index c703376accde..de3628871f27 100644 --- a/packages/jest-haste-map/src/HasteFS.js +++ b/packages/jest-haste-map/src/HasteFS.ts @@ -3,66 +3,63 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Glob, Path} from 'types/Config'; -import type {FileData} from 'types/HasteMap'; - +import micromatch from 'micromatch'; import {replacePathSepForGlob} from 'jest-util'; +import {Config} from '@jest/types'; +import {FileData} from './types'; import * as fastPath from './lib/fast_path'; -import micromatch from 'micromatch'; import H from './constants'; export default class HasteFS { - _rootDir: Path; - _files: FileData; + private readonly _rootDir: Config.Path; + private readonly _files: FileData; - constructor({rootDir, files}: {rootDir: Path, files: FileData}) { + constructor({rootDir, files}: {rootDir: Config.Path; files: FileData}) { this._rootDir = rootDir; this._files = files; } - getModuleName(file: Path): ?string { + getModuleName(file: Config.Path): string | null { const fileMetadata = this._getFileData(file); return (fileMetadata && fileMetadata[H.ID]) || null; } - getSize(file: Path): ?number { + getSize(file: Config.Path): number | null { const fileMetadata = this._getFileData(file); return (fileMetadata && fileMetadata[H.SIZE]) || null; } - getDependencies(file: Path): ?Array { + getDependencies(file: Config.Path): Array | null { const fileMetadata = this._getFileData(file); return (fileMetadata && fileMetadata[H.DEPENDENCIES]) || null; } - getSha1(file: Path): ?string { + getSha1(file: Config.Path): string | null { const fileMetadata = this._getFileData(file); return (fileMetadata && fileMetadata[H.SHA1]) || null; } - exists(file: Path): boolean { + exists(file: Config.Path): boolean { return this._getFileData(file) != null; } - getAllFiles(): Array { + getAllFiles(): Array { return Array.from(this.getAbsoluteFileIterator()); } - getFileIterator(): Iterator { + getFileIterator(): Iterable { return this._files.keys(); } - *getAbsoluteFileIterator(): Iterator { - for (const file of this._files.keys()) { + *getAbsoluteFileIterator(): Iterable { + for (const file of this.getFileIterator()) { yield fastPath.resolve(this._rootDir, file); } } - matchFiles(pattern: RegExp | string): Array { + matchFiles(pattern: RegExp | string): Array { if (!(pattern instanceof RegExp)) { pattern = new RegExp(pattern); } @@ -75,7 +72,10 @@ export default class HasteFS { return files; } - matchFilesWithGlob(globs: Array, root: ?Path): Set { + matchFilesWithGlob( + globs: Array, + root: Config.Path | null, + ): Set { const files = new Set(); for (const file of this.getAbsoluteFileIterator()) { const filePath = root ? fastPath.relative(root, file) : file; @@ -86,7 +86,7 @@ export default class HasteFS { return files; } - _getFileData(file: Path) { + private _getFileData(file: Config.Path) { const relativePath = fastPath.relative(this._rootDir, file); return this._files.get(relativePath); } diff --git a/packages/jest-haste-map/src/ModuleMap.js b/packages/jest-haste-map/src/ModuleMap.ts similarity index 83% rename from packages/jest-haste-map/src/ModuleMap.js rename to packages/jest-haste-map/src/ModuleMap.ts index 7f2390020fff..07246b1babcb 100644 --- a/packages/jest-haste-map/src/ModuleMap.js +++ b/packages/jest-haste-map/src/ModuleMap.ts @@ -3,12 +3,10 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Path} from 'types/Config'; -import type { +import {Config} from '@jest/types'; +import { DuplicatesSet, HTypeValue, ModuleMetaData, @@ -16,28 +14,26 @@ import type { ModuleMapData, DuplicatesIndex, MockData, -} from 'types/HasteMap'; +} from './types'; import * as fastPath from './lib/fast_path'; import H from './constants'; -const EMPTY_OBJ = {}; +const EMPTY_OBJ = {} as {[key: string]: any}; const EMPTY_MAP = new Map(); -export opaque type SerializableModuleMap = { - // There is no easier way to extract the type of the entries of a Map - duplicates: $Call< - typeof Array.from, - $Call<$PropertyType>, - >, - map: $Call>>, - mocks: $Call>>, - rootDir: string, +type ValueType = T extends Map ? V : never; + +export type SerializableModuleMap = { + duplicates: ReadonlyArray<[string, ValueType]>; + map: ReadonlyArray<[string, ValueType]>; + mocks: ReadonlyArray<[string, ValueType]>; + rootDir: Config.Path; }; export default class ModuleMap { - _raw: RawModuleMap; - static DuplicateHasteCandidatesError: Class; + private readonly _raw: RawModuleMap; + static DuplicateHasteCandidatesError: typeof DuplicateHasteCandidatesError; constructor(raw: RawModuleMap) { this._raw = raw; @@ -45,11 +41,11 @@ export default class ModuleMap { getModule( name: string, - platform: ?string, - supportsNativePlatform: ?boolean, - type: ?HTypeValue, - ): ?Path { - if (!type) { + platform: string | null, + supportsNativePlatform: boolean | null, + type: HTypeValue | null, + ): Config.Path | null { + if (type == null) { type = H.MODULE; } const module = this._getModuleMetadata( @@ -66,13 +62,13 @@ export default class ModuleMap { getPackage( name: string, - platform: ?string, - supportsNativePlatform: ?boolean, - ): ?Path { + platform: string | null, + _supportsNativePlatform: boolean | null, + ): Config.Path | null { return this.getModule(name, platform, null, H.PACKAGE); } - getMockModule(name: string): ?Path { + getMockModule(name: string): Config.Path | undefined { const mockPath = this._raw.mocks.get(name) || this._raw.mocks.get(name + '/index'); return mockPath && fastPath.resolve(this._raw.rootDir, mockPath); @@ -113,11 +109,11 @@ export default class ModuleMap { * extra sure. If metadata exists both in the `duplicates` object and the * `map`, this would be a bug. */ - _getModuleMetadata( + private _getModuleMetadata( name: string, - platform: ?string, + platform: string | null, supportsNativePlatform: boolean, - ): ?ModuleMetaData { + ): ModuleMetaData | null { const map = this._raw.map.get(name) || EMPTY_OBJ; const dupMap = this._raw.duplicates.get(name) || EMPTY_MAP; if (platform != null) { @@ -154,11 +150,11 @@ export default class ModuleMap { return null; } - _assertNoDuplicates( + private _assertNoDuplicates( name: string, platform: string, supportsNativePlatform: boolean, - relativePathSet: ?DuplicatesSet, + relativePathSet: DuplicatesSet | null, ) { if (relativePathSet == null) { return; @@ -180,7 +176,7 @@ export default class ModuleMap { ); } - static create(rootDir: Path) { + static create(rootDir: Config.Path) { return new ModuleMap({ duplicates: new Map(), map: new Map(), @@ -192,7 +188,7 @@ export default class ModuleMap { class DuplicateHasteCandidatesError extends Error { hasteName: string; - platform: ?string; + platform: string | null; supportsNativePlatform: boolean; duplicatesSet: DuplicatesSet; diff --git a/packages/jest-haste-map/src/__tests__/index.test.js b/packages/jest-haste-map/src/__tests__/index.test.js index 7e23749f3d93..c11a752bd8b9 100644 --- a/packages/jest-haste-map/src/__tests__/index.test.js +++ b/packages/jest-haste-map/src/__tests__/index.test.js @@ -6,10 +6,8 @@ * */ -'use strict'; - +import crypto from 'crypto'; import {skipSuiteOnWindows} from '../../../../scripts/ConditionalTest'; -const crypto = require('crypto'); function mockHashContents(contents) { return crypto @@ -75,7 +73,7 @@ jest.mock('sane', () => ({ WatchmanWatcher: mockWatcherConstructor, })); -jest.mock('../lib/WatchmanWatcher.js', () => mockWatcherConstructor); +jest.mock('../lib/WatchmanWatcher', () => mockWatcherConstructor); let mockChangedFiles; let mockFs; diff --git a/packages/jest-haste-map/src/blacklist.js b/packages/jest-haste-map/src/blacklist.ts similarity index 99% rename from packages/jest-haste-map/src/blacklist.js rename to packages/jest-haste-map/src/blacklist.ts index 61e1b550a858..caead002eaeb 100644 --- a/packages/jest-haste-map/src/blacklist.js +++ b/packages/jest-haste-map/src/blacklist.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ // This list is compiled after the MDN list of the most common MIME types (see diff --git a/packages/jest-haste-map/src/constants.js b/packages/jest-haste-map/src/constants.ts similarity index 90% rename from packages/jest-haste-map/src/constants.js rename to packages/jest-haste-map/src/constants.ts index 6bbe7c4fb52e..1717934e47ef 100644 --- a/packages/jest-haste-map/src/constants.js +++ b/packages/jest-haste-map/src/constants.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ /* eslint-disable sort-keys */ @@ -16,7 +14,10 @@ * This constant key map allows to keep the map smaller without having to build * a custom serialization library. */ -export default { + +import {HType} from './types'; + +const constants: HType = { /* file map attributes */ ID: 0, MTIME: 1, @@ -37,3 +38,5 @@ export default { GENERIC_PLATFORM: 'g', NATIVE_PLATFORM: 'native', }; + +export default constants; diff --git a/packages/jest-haste-map/src/crawlers/node.js b/packages/jest-haste-map/src/crawlers/node.ts similarity index 88% rename from packages/jest-haste-map/src/crawlers/node.js rename to packages/jest-haste-map/src/crawlers/node.ts index ffb155ba6321..6813e79ac519 100644 --- a/packages/jest-haste-map/src/crawlers/node.js +++ b/packages/jest-haste-map/src/crawlers/node.ts @@ -3,22 +3,18 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {InternalHasteMap} from 'types/HasteMap'; -import type {IgnoreMatcher, CrawlerOptions} from '../types'; - import fs from 'fs'; import path from 'path'; import {spawn} from 'child_process'; import H from '../constants'; import * as fastPath from '../lib/fast_path'; +import {IgnoreMatcher, InternalHasteMap, CrawlerOptions} from '../types'; + +type Result = Array<[/* id */ string, /* mtime */ number, /* size */ number]>; -type Callback = ( - result: Array<[/* id */ string, /* mtime */ number, /* size */ number]>, -) => void; +type Callback = (result: Result) => void; function find( roots: Array, @@ -26,7 +22,7 @@ function find( ignore: IgnoreMatcher, callback: Callback, ): void { - const result = []; + const result: Result = []; let activeCalls = 0; function search(directory: string): void { @@ -82,7 +78,7 @@ function findNative( ignore: IgnoreMatcher, callback: Callback, ): void { - const args = [].concat(roots); + const args = Array.from(roots); args.push('-type', 'f'); if (extensions.length) { args.push('('); @@ -108,7 +104,7 @@ function findNative( .trim() .split('\n') .filter(x => !ignore(x)); - const result = []; + const result: Result = []; let count = lines.length; if (!count) { callback([]); @@ -127,7 +123,7 @@ function findNative( }); } -module.exports = function nodeCrawl( +export = function nodeCrawl( options: CrawlerOptions, ): Promise { if (options.mapper) { @@ -144,13 +140,11 @@ module.exports = function nodeCrawl( } = options; return new Promise(resolve => { - const callback = list => { + const callback = (list: Result) => { const files = new Map(); list.forEach(fileData => { - const filePath = fileData[0]; + const [filePath, mtime, size] = fileData; const relativeFilePath = fastPath.relative(rootDir, filePath); - const mtime = fileData[1]; - const size = fileData[2]; const existingFile = data.files.get(relativeFilePath); if (existingFile && existingFile[H.MTIME] === mtime) { files.set(relativeFilePath, existingFile); diff --git a/packages/jest-haste-map/src/crawlers/watchman.js b/packages/jest-haste-map/src/crawlers/watchman.ts similarity index 86% rename from packages/jest-haste-map/src/crawlers/watchman.js rename to packages/jest-haste-map/src/crawlers/watchman.ts index b07b9e635172..b7f5fc453f0b 100644 --- a/packages/jest-haste-map/src/crawlers/watchman.js +++ b/packages/jest-haste-map/src/crawlers/watchman.ts @@ -3,18 +3,17 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {InternalHasteMap} from 'types/HasteMap'; -import type {CrawlerOptions} from '../types'; - -import * as fastPath from '../lib/fast_path'; -import normalizePathSep from '../lib/normalizePathSep'; import path from 'path'; import watchman from 'fb-watchman'; +import {Config} from '@jest/types'; +import * as fastPath from '../lib/fast_path'; +import normalizePathSep from '../lib/normalizePathSep'; import H from '../constants'; +import {InternalHasteMap, CrawlerOptions, FileMetaData} from '../types'; + +type WatchmanRoots = Map>; const watchmanURL = 'https://facebook.github.io/watchman/docs/troubleshooting.html'; @@ -26,7 +25,7 @@ function WatchmanError(error: Error): Error { return error; } -module.exports = async function watchmanCrawl( +export = async function watchmanCrawl( options: CrawlerOptions, ): Promise { const fields = ['name', 'exists', 'mtime_ms', 'size']; @@ -34,7 +33,7 @@ module.exports = async function watchmanCrawl( const defaultWatchExpression = [ 'allof', ['type', 'f'], - ['anyof'].concat(extensions.map(extension => ['suffix', extension])), + ['anyof', ...extensions.map(extension => ['suffix', extension])], ]; const clocks = data.clocks; const client = new watchman.Client(); @@ -42,7 +41,8 @@ module.exports = async function watchmanCrawl( let clientError; client.on('error', error => (clientError = WatchmanError(error))); - const cmd = (...args) => + // TODO: type better than `any` + const cmd = (...args: Array): Promise => new Promise((resolve, reject) => client.command(args, (error, result) => error ? reject(WatchmanError(error)) : resolve(result), @@ -57,7 +57,9 @@ module.exports = async function watchmanCrawl( } } - async function getWatchmanRoots(roots) { + async function getWatchmanRoots( + roots: Array, + ): Promise { const watchmanRoots = new Map(); await Promise.all( roots.map(async root => { @@ -85,7 +87,7 @@ module.exports = async function watchmanCrawl( return watchmanRoots; } - async function queryWatchmanForDirs(rootProjectDirMappings) { + async function queryWatchmanForDirs(rootProjectDirMappings: WatchmanRoots) { const files = new Map(); let isFresh = false; await Promise.all( @@ -137,7 +139,7 @@ module.exports = async function watchmanCrawl( } let files = data.files; - let watchmanFiles; + let watchmanFiles: Map; try { const watchmanRoots = await getWatchmanRoots(roots); const watchmanFileResults = await queryWatchmanForDirs(watchmanRoots); @@ -157,7 +159,8 @@ module.exports = async function watchmanCrawl( throw clientError; } - for (const [watchRoot, response] of watchmanFiles) { + // TODO: remove non-null + for (const [watchRoot, response] of watchmanFiles!) { const fsRoot = normalizePathSep(watchRoot); const relativeFsRoot = fastPath.relative(rootDir, fsRoot); clocks.set(relativeFsRoot, response.clock); @@ -181,7 +184,7 @@ module.exports = async function watchmanCrawl( } const existingFileData = data.files.get(relativeFilePath); - let nextData; + let nextData: FileMetaData; if (existingFileData && existingFileData[H.MTIME] === mtime) { nextData = existingFileData; @@ -190,10 +193,16 @@ module.exports = async function watchmanCrawl( sha1hex && existingFileData[H.SHA1] === sha1hex ) { - nextData = [...existingFileData]; - nextData[1] = mtime; + nextData = [ + existingFileData[0], + mtime, + existingFileData[2], + existingFileData[3], + existingFileData[4], + existingFileData[5], + ]; } else { - // See ../constants.js + // See ../constants.ts nextData = ['', mtime, size, 0, [], sha1hex]; } diff --git a/packages/jest-haste-map/src/getMockName.js b/packages/jest-haste-map/src/getMockName.ts similarity index 97% rename from packages/jest-haste-map/src/getMockName.js rename to packages/jest-haste-map/src/getMockName.ts index af35f390c7a9..3040e20a5ee3 100644 --- a/packages/jest-haste-map/src/getMockName.js +++ b/packages/jest-haste-map/src/getMockName.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import path from 'path'; diff --git a/packages/jest-haste-map/src/index.js b/packages/jest-haste-map/src/index.ts similarity index 86% rename from packages/jest-haste-map/src/index.js rename to packages/jest-haste-map/src/index.ts index 7831169d2ff0..f6bd3bad058b 100644 --- a/packages/jest-haste-map/src/index.js +++ b/packages/jest-haste-map/src/index.ts @@ -3,103 +3,100 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import {execSync} from 'child_process'; -import {version as VERSION} from '../package.json'; -import {getSha1, worker} from './worker'; import crypto from 'crypto'; import EventEmitter from 'events'; import fs from 'fs'; +import os from 'os'; +import path from 'path'; +import sane from 'sane'; +import invariant from 'invariant'; +import {Config} from '@jest/types'; +import serializer from 'jest-serializer'; +import Worker from 'jest-worker'; +import {getSha1, worker} from './worker'; import getMockName from './getMockName'; import getPlatformExtension from './lib/getPlatformExtension'; import H from './constants'; import HasteFS from './HasteFS'; -import HasteModuleMap from './ModuleMap'; -import invariant from 'invariant'; -// eslint-disable-next-line import/default +import HasteModuleMap, { + SerializableModuleMap as HasteSerializableModuleMap, +} from './ModuleMap'; import nodeCrawl from './crawlers/node'; 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'; +// @ts-ignore: not converted to TypeScript - it's a fork: https://github.com/facebook/jest/pull/5387 import WatchmanWatcher from './lib/WatchmanWatcher'; import * as fastPath from './lib/fast_path'; -import Worker from 'jest-worker'; - -import type {Console} from 'console'; -import type {Mapper} from './types'; -import type {Path} from 'types/Config'; -import type { +import { + FileMetaData, HasteMap as HasteMapObject, - InternalHasteMap, - ModuleMetaData, - ModuleMapData, HasteRegExp, + InternalHasteMap, + Mapper, MockData, -} from 'types/HasteMap'; -import type {SerializableModuleMap as HasteSerializableModuleMap} from './ModuleMap'; + ModuleMapData, + ModuleMetaData, + WorkerMetadata, +} from './types'; type HType = typeof H; type Options = { - cacheDirectory?: string, - computeDependencies?: boolean, - computeSha1?: boolean, - console?: Console, - dependencyExtractor?: string, - extensions: Array, - forceNodeFilesystemAPI?: boolean, - hasteImplModulePath?: string, - ignorePattern?: ?HasteRegExp, - mapper?: ?Mapper, - maxWorkers: number, - mocksPattern?: string, - name: string, - platforms: Array, - providesModuleNodeModules?: Array, - resetCache?: boolean, - retainAllFiles: boolean, - rootDir: string, - roots: Array, - throwOnModuleCollision?: boolean, - useWatchman?: boolean, - watch?: boolean, + cacheDirectory?: string; + computeDependencies?: boolean; + computeSha1?: boolean; + console?: Console; + dependencyExtractor?: string; + extensions: Array; + forceNodeFilesystemAPI?: boolean; + hasteImplModulePath?: string; + ignorePattern?: HasteRegExp; + mapper?: Mapper; + maxWorkers: number; + mocksPattern?: string; + name: string; + platforms: Array; + providesModuleNodeModules?: Array; + resetCache?: boolean; + retainAllFiles: boolean; + rootDir: string; + roots: Array; + throwOnModuleCollision?: boolean; + useWatchman?: boolean; + watch?: boolean; }; type InternalOptions = { - cacheDirectory: string, - computeDependencies: boolean, - computeSha1: boolean, - dependencyExtractor?: string, - extensions: Array, - forceNodeFilesystemAPI: boolean, - hasteImplModulePath?: string, - ignorePattern: ?HasteRegExp, - mapper?: ?Mapper, - maxWorkers: number, - mocksPattern: ?RegExp, - name: string, - platforms: Array, - resetCache: ?boolean, - retainAllFiles: boolean, - rootDir: string, - roots: Array, - throwOnModuleCollision: boolean, - useWatchman: boolean, - watch: boolean, + cacheDirectory: string; + computeDependencies: boolean; + computeSha1: boolean; + dependencyExtractor?: string; + extensions: Array; + forceNodeFilesystemAPI: boolean; + hasteImplModulePath?: string; + ignorePattern?: HasteRegExp; + mapper?: Mapper; + maxWorkers: number; + mocksPattern: RegExp | null; + name: string; + platforms: Array; + resetCache?: boolean; + retainAllFiles: boolean; + rootDir: string; + roots: Array; + throwOnModuleCollision: boolean; + useWatchman: boolean; + watch: boolean; }; type Watcher = { - close(callback: () => void): void, + close(callback: () => void): void; }; -type WorkerInterface = {worker: typeof worker, getSha1: typeof getSha1}; +type WorkerInterface = {worker: typeof worker; getSha1: typeof getSha1}; export type ModuleMap = HasteModuleMap; export type SerializableModuleMap = HasteSerializableModuleMap; @@ -109,6 +106,10 @@ const CHANGE_INTERVAL = 30; const MAX_WAIT_TIME = 240000; const NODE_MODULES = path.sep + 'node_modules' + path.sep; +// TypeScript doesn't like us importing from outside `rootDir`, but it doesn't +// understand `require`. +const {version: VERSION} = require('../package.json'); + const canUseWatchman = ((): boolean => { try { execSync('watchman --version', {stdio: ['ignore']}); @@ -117,10 +118,10 @@ const canUseWatchman = ((): boolean => { return false; })(); -const escapePathSeparator = string => +const escapePathSeparator = (string: string) => path.sep === '\\' ? string.replace(/(\/|\\)/g, '\\\\') : string; -const getWhiteList = (list: ?Array): ?RegExp => { +const getWhiteList = (list: Array | undefined): RegExp | null => { if (list && list.length) { const newList = list.map(item => escapePathSeparator(item.replace(/(\/)/g, path.sep)), @@ -218,14 +219,14 @@ const getWhiteList = (list: ?Array): ?RegExp => { * */ class HasteMap extends EventEmitter { - _buildPromise: ?Promise; - _cachePath: Path; - _changeInterval: IntervalID; + _buildPromise: Promise | null; + _cachePath: Config.Path; + _changeInterval?: NodeJS.Timeout; _console: Console; _options: InternalOptions; _watchers: Array; - _whitelist: ?RegExp; - _worker: ?WorkerInterface; + _whitelist: RegExp | null; + _worker: WorkerInterface | null; constructor(options: Options) { super(); @@ -271,7 +272,6 @@ class HasteMap extends EventEmitter { let dependencyExtractorHash = ''; if (options.hasteImplModulePath) { - // $FlowFixMe: dynamic require const hasteImpl = require(options.hasteImplModulePath); if (hasteImpl.getCacheKey) { hasteImplHash = String(hasteImpl.getCacheKey()); @@ -279,7 +279,6 @@ class HasteMap extends EventEmitter { } if (options.dependencyExtractor) { - // $FlowFixMe: dynamic require const dependencyExtractor = require(options.dependencyExtractor); if (dependencyExtractor.getCacheKey) { dependencyExtractorHash = String(dependencyExtractor.getCacheKey()); @@ -309,7 +308,7 @@ class HasteMap extends EventEmitter { } static getCacheFilePath( - tmpdir: Path, + tmpdir: Config.Path, name: string, ...extra: Array ): string { @@ -344,7 +343,7 @@ class HasteMap extends EventEmitter { }); const __hasteMapForTest = (process.env.NODE_ENV === 'test' && hasteMap) || null; - return this._watch(hasteMap, hasteFS, moduleMap).then(() => ({ + return this._watch(hasteMap).then(() => ({ __hasteMapForTest, hasteFS, moduleMap, @@ -383,8 +382,8 @@ class HasteMap extends EventEmitter { * 2. crawl the file system. */ _buildFileMap(): Promise<{ - deprecatedFiles: Array<{moduleName: string, path: string}>, - hasteMap: InternalHasteMap, + deprecatedFiles: Array<{moduleName: string; path: string}>; + hasteMap: InternalHasteMap; }> { const read = this._options.resetCache ? this._createEmptyMap : this.read; @@ -392,7 +391,7 @@ class HasteMap extends EventEmitter { .then(() => read.call(this)) .catch(() => this._createEmptyMap()) .then(cachedHasteMap => { - const cachedFiles = []; + const cachedFiles: Array<{moduleName: string; path: string}> = []; for (const [relativeFilePath, fileMetadata] of cachedHasteMap.files) { const moduleName = fileMetadata[H.ID]; cachedFiles.push({moduleName, path: relativeFilePath}); @@ -413,15 +412,15 @@ class HasteMap extends EventEmitter { hasteMap: InternalHasteMap, map: ModuleMapData, mocks: MockData, - filePath: Path, - workerOptions: ?{forceInBand: boolean}, - ): ?Promise { + filePath: Config.Path, + workerOptions?: {forceInBand: boolean}, + ): Promise | null { const rootDir = this._options.rootDir; const setModule = (id: string, module: ModuleMetaData) => { let moduleMap = map.get(id); if (!moduleMap) { - moduleMap = Object.create(null); + moduleMap = Object.create(null) as {}; map.set(id, moduleMap); } const platform = @@ -489,7 +488,7 @@ class HasteMap extends EventEmitter { const computeSha1 = this._options.computeSha1 && !fileMetadata[H.SHA1]; // Callback called when the response from the worker is successful. - const workerReply = metadata => { + const workerReply = (metadata: WorkerMetadata) => { // `1` for truthy values instead of `true` to save cache space. fileMetadata[H.VISITED] = 1; @@ -509,13 +508,13 @@ class HasteMap extends EventEmitter { }; // Callback called when the response from the worker is an error. - const workerError = error => { + const workerError = (error: Error | any) => { if (typeof error !== 'object' || !error.message || !error.stack) { error = new Error(error); error.stack = ''; // Remove stack for stack-less errors. } - // $FlowFixMe: checking error code is OK if error comes from "fs". + // @ts-ignore: checking error code is OK if error comes from "fs". if (!['ENOENT', 'EACCES'].includes(error.code)) { throw error; } @@ -584,7 +583,7 @@ class HasteMap extends EventEmitter { const moduleId = fileMetadata[H.ID]; let modulesByPlatform = map.get(moduleId); if (!modulesByPlatform) { - modulesByPlatform = Object.create(null); + modulesByPlatform = Object.create(null) as {}; map.set(moduleId, modulesByPlatform); } modulesByPlatform[platform] = module; @@ -606,8 +605,8 @@ class HasteMap extends EventEmitter { } _buildHasteMap(data: { - deprecatedFiles: Array<{moduleName: string, path: string}>, - hasteMap: InternalHasteMap, + deprecatedFiles: Array<{moduleName: string; path: string}>; + hasteMap: InternalHasteMap; }): Promise { const {deprecatedFiles, hasteMap} = data; const map = new Map(); @@ -647,8 +646,9 @@ class HasteMap extends EventEmitter { _cleanup() { const worker = this._worker; - // $FlowFixMe + // @ts-ignore if (worker && typeof worker.end === 'function') { + // @ts-ignore worker.end(); } @@ -658,36 +658,37 @@ class HasteMap extends EventEmitter { /** * 4. serialize the new `HasteMap` in a cache file. */ - _persist(hasteMap: InternalHasteMap) { + private _persist(hasteMap: InternalHasteMap) { serializer.writeFileSync(this._cachePath, hasteMap); } /** * Creates workers or parses files and extracts metadata in-process. */ - _getWorker(options: ?{forceInBand: boolean}): WorkerInterface { + private _getWorker(options?: {forceInBand: boolean}): WorkerInterface { if (!this._worker) { if ((options && options.forceInBand) || this._options.maxWorkers <= 1) { this._worker = {getSha1, worker}; } else { - this._worker = (new Worker(require.resolve('./worker'), { + // @ts-ignore: assignment of a worker with custom properties. + this._worker = new Worker(require.resolve('./worker'), { exposedMethods: ['getSha1', 'worker'], maxRetries: 3, numWorkers: this._options.maxWorkers, - }): WorkerInterface); + }) as WorkerInterface; } } return this._worker; } - _crawl(hasteMap: InternalHasteMap): Promise { + private _crawl(hasteMap: InternalHasteMap): Promise { const options = this._options; const ignore = this._ignore.bind(this); const crawl = canUseWatchman && this._options.useWatchman ? watchmanCrawl : nodeCrawl; - const retry = error => { + const retry = (error: Error) => { if (crawl === watchmanCrawl) { this._console.warn( `jest-haste-map: Watchman crawl failed. Retrying once with node ` + @@ -737,11 +738,7 @@ class HasteMap extends EventEmitter { /** * Watch mode */ - _watch( - hasteMap: InternalHasteMap, - hasteFS: HasteFS, - moduleMap: ModuleMap, - ): Promise { + private _watch(hasteMap: InternalHasteMap): Promise { if (!this._options.watch) { return Promise.resolve(); } @@ -751,7 +748,7 @@ class HasteMap extends EventEmitter { this._options.throwOnModuleCollision = false; this._options.retainAllFiles = true; - const Watcher = + const Watcher: sane.Watcher = canUseWatchman && this._options.useWatchman ? WatchmanWatcher : os.platform() === 'darwin' @@ -761,12 +758,17 @@ class HasteMap extends EventEmitter { const ignorePattern = this._options.ignorePattern; const rootDir = this._options.rootDir; - let changeQueue = Promise.resolve(); - let eventsQueue = []; + let changeQueue: Promise = Promise.resolve(); + let eventsQueue: Array<{ + filePath: Config.Path; + stat: fs.Stats | undefined; + type: string; + }> = []; // We only need to copy the entire haste map once on every "frame". let mustCopy = true; - const createWatcher = root => { + const createWatcher = (root: Config.Path): Promise => { + // @ts-ignore: TODO how? "Cannot use 'new' with an expression whose type lacks a call or construct signature." const watcher = new Watcher(root, { dot: false, glob: extensions.map(extension => '**/*.' + extension), @@ -809,9 +811,9 @@ class HasteMap extends EventEmitter { const onChange = ( type: string, - filePath: Path, - root: Path, - stat: ?fs.Stats, + filePath: Config.Path, + root: Config.Path, + stat?: fs.Stats, ) => { filePath = path.join(root, normalizePathSep(filePath)); if ( @@ -831,8 +833,8 @@ class HasteMap extends EventEmitter { event.type === type && event.filePath === filePath && ((!event.stat && !stat) || - (event.stat && - stat && + (!!event.stat && + !!stat && event.stat.mtime.getTime() === stat.mtime.getTime())), ) ) { @@ -850,7 +852,10 @@ class HasteMap extends EventEmitter { }; } - const add = () => eventsQueue.push({filePath, stat, type}); + const add = () => { + eventsQueue.push({filePath, stat, type}); + return null; + }; const relativeFilePath = fastPath.relative(rootDir, filePath); const fileMetadata = hasteMap.files.get(relativeFilePath); @@ -894,10 +899,10 @@ class HasteMap extends EventEmitter { stat, 'since the file exists or changed, it should have stats', ); - const fileMetadata = [ + const fileMetadata: FileMetaData = [ '', - stat.mtime.getTime(), - stat.size, + stat ? stat.mtime.getTime() : -1, + stat ? stat.size : 0, 0, [], null, @@ -924,7 +929,7 @@ class HasteMap extends EventEmitter { } return null; }) - .catch(error => { + .catch((error: Error) => { this._console.error( `jest-haste-map: watch error:\n ${error.stack}\n`, ); @@ -947,7 +952,7 @@ class HasteMap extends EventEmitter { * remaining in the group, then we want to restore that single file as the * correct resolution for its ID, and cleanup the duplicates index. */ - _recoverDuplicates( + private _recoverDuplicates( hasteMap: InternalHasteMap, relativeFilePath: string, moduleName: string, @@ -985,7 +990,7 @@ class HasteMap extends EventEmitter { let dedupMap = hasteMap.map.get(moduleName); if (dedupMap == null) { - dedupMap = Object.create(null); + dedupMap = Object.create(null) as {}; hasteMap.map.set(moduleName, dedupMap); } dedupMap[platform] = uniqueModule; @@ -996,6 +1001,7 @@ class HasteMap extends EventEmitter { } end(): Promise { + // @ts-ignore: TODO TS cannot decide if `setInterval` and `clearInterval` comes from NodeJS or the DOM clearInterval(this._changeInterval); if (!this._watchers.length) { return Promise.resolve(); @@ -1013,7 +1019,7 @@ class HasteMap extends EventEmitter { /** * Helpers */ - _ignore(filePath: Path): boolean { + private _ignore(filePath: Config.Path): boolean { const ignorePattern = this._options.ignorePattern; const ignoreMatched = ignorePattern instanceof RegExp @@ -1026,7 +1032,7 @@ class HasteMap extends EventEmitter { ); } - _isNodeModulesDir(filePath: Path): boolean { + private _isNodeModulesDir(filePath: Config.Path): boolean { if (!filePath.includes(NODE_MODULES)) { return false; } @@ -1048,7 +1054,7 @@ class HasteMap extends EventEmitter { return true; } - _createEmptyMap(): InternalHasteMap { + private _createEmptyMap(): InternalHasteMap { return { clocks: new Map(), duplicates: new Map(), @@ -1059,10 +1065,12 @@ class HasteMap extends EventEmitter { } static H: HType; - static ModuleMap: Class; + static ModuleMap: typeof HasteModuleMap; } -const copy = object => Object.assign(Object.create(null), object); +function copy(object: T): T { + return Object.assign(Object.create(null), object); +} function copyMap(input: Map): Map { return new Map(input); diff --git a/packages/jest-haste-map/src/lib/WatchmanWatcher.js b/packages/jest-haste-map/src/lib/WatchmanWatcher.js index f7436aaa9f0f..af0f49c45abd 100644 --- a/packages/jest-haste-map/src/lib/WatchmanWatcher.js +++ b/packages/jest-haste-map/src/lib/WatchmanWatcher.js @@ -8,9 +8,9 @@ import fs from 'fs'; import path from 'path'; import assert from 'assert'; -import common from 'sane/src/common'; -import watchman from 'fb-watchman'; import {EventEmitter} from 'events'; +import watchman from 'fb-watchman'; +import common from 'sane/src/common'; import RecrawlWarning from 'sane/src/utils/recrawl-warning-dedupe'; const CHANGE_EVENT = common.CHANGE_EVENT; diff --git a/packages/jest-haste-map/src/lib/__tests__/dependencyExtractor.test.js b/packages/jest-haste-map/src/lib/__tests__/dependencyExtractor.test.js index 9809a38140b8..e13ed50c735f 100644 --- a/packages/jest-haste-map/src/lib/__tests__/dependencyExtractor.test.js +++ b/packages/jest-haste-map/src/lib/__tests__/dependencyExtractor.test.js @@ -3,8 +3,6 @@ * * 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'; 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 170a7861a55a..fd76610eca4a 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 @@ -7,8 +7,6 @@ * */ -'use strict'; - import path from 'path'; import {relative, resolve} from '../fast_path'; diff --git a/packages/jest-haste-map/src/lib/__tests__/getPlatformExtension.test.js b/packages/jest-haste-map/src/lib/__tests__/getPlatformExtension.test.js index 16a2b0b3cee6..6541ac0e16a2 100644 --- a/packages/jest-haste-map/src/lib/__tests__/getPlatformExtension.test.js +++ b/packages/jest-haste-map/src/lib/__tests__/getPlatformExtension.test.js @@ -7,8 +7,6 @@ * */ -'use strict'; - import getPlatformExtension from '../getPlatformExtension'; describe('getPlatformExtension', () => { diff --git a/packages/jest-haste-map/src/lib/__tests__/isRegExpSupported.test.js b/packages/jest-haste-map/src/lib/__tests__/isRegExpSupported.test.js index 740f3dd5eb3d..b27295eaa320 100644 --- a/packages/jest-haste-map/src/lib/__tests__/isRegExpSupported.test.js +++ b/packages/jest-haste-map/src/lib/__tests__/isRegExpSupported.test.js @@ -3,8 +3,6 @@ * * 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'; diff --git a/packages/jest-haste-map/src/lib/dependencyExtractor.js b/packages/jest-haste-map/src/lib/dependencyExtractor.ts similarity index 86% rename from packages/jest-haste-map/src/lib/dependencyExtractor.js rename to packages/jest-haste-map/src/lib/dependencyExtractor.ts index b55cff653f1d..1e10b59db7c2 100644 --- a/packages/jest-haste-map/src/lib/dependencyExtractor.js +++ b/packages/jest-haste-map/src/lib/dependencyExtractor.ts @@ -3,8 +3,6 @@ * * 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'; @@ -13,22 +11,23 @@ import isRegExpSupported from './isRegExpSupported'; const NOT_A_DOT = isRegExpSupported('(? `([\`'"])([^'"\`]*?)(?:\\${pos})`; +const CAPTURE_STRING_LITERAL = (pos: number) => + `([\`'"])([^'"\`]*?)(?:\\${pos})`; const WORD_SEPARATOR = '\\b'; const LEFT_PARENTHESIS = '\\('; const RIGHT_PARENTHESIS = '\\)'; const WHITESPACE = '\\s*'; const OPTIONAL_COMMA = '(:?,\\s*)?'; -function createRegExp(parts, flags) { +function createRegExp(parts: Array, flags: string) { return new RegExp(parts.join(''), flags); } -function alternatives(...parts) { +function alternatives(...parts: Array) { return `(?:${parts.join('|')})`; } -function functionCallStart(...names) { +function functionCallStart(...names: Array) { return [ NOT_A_DOT, WORD_SEPARATOR, @@ -78,7 +77,7 @@ const JEST_EXTENSIONS_RE = createRegExp( export function extract(code: string): Set { const dependencies = new Set(); - const addDependency = (match: string, q: string, dep: string) => { + const addDependency = (match: string, _: string, dep: string) => { dependencies.add(dep); return match; }; diff --git a/packages/jest-haste-map/src/lib/fast_path.js b/packages/jest-haste-map/src/lib/fast_path.ts similarity index 98% rename from packages/jest-haste-map/src/lib/fast_path.js rename to packages/jest-haste-map/src/lib/fast_path.ts index 4312eee07d93..f293c8a0f2bb 100644 --- a/packages/jest-haste-map/src/lib/fast_path.js +++ b/packages/jest-haste-map/src/lib/fast_path.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import path from 'path'; diff --git a/packages/jest-haste-map/src/lib/getPlatformExtension.js b/packages/jest-haste-map/src/lib/getPlatformExtension.ts similarity index 79% rename from packages/jest-haste-map/src/lib/getPlatformExtension.js rename to packages/jest-haste-map/src/lib/getPlatformExtension.ts index f5fe9e1d7422..f9dac1dace0d 100644 --- a/packages/jest-haste-map/src/lib/getPlatformExtension.js +++ b/packages/jest-haste-map/src/lib/getPlatformExtension.ts @@ -3,22 +3,15 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -const SUPPORTED_PLATFORM_EXTS = { - android: true, - ios: true, - native: true, - web: true, -}; +const SUPPORTED_PLATFORM_EXTS = new Set(['android', 'ios', 'native', 'web']); // Extract platform extension: index.ios.js -> ios export default function getPlatformExtension( file: string, platforms?: Array, -): ?string { +): string | null { const last = file.lastIndexOf('.'); const secondToLast = file.lastIndexOf('.', last - 1); if (secondToLast === -1) { @@ -30,5 +23,5 @@ export default function getPlatformExtension( if (platforms && platforms.indexOf(platform) !== -1) { return platform; } - return SUPPORTED_PLATFORM_EXTS[platform] ? platform : null; + return SUPPORTED_PLATFORM_EXTS.has(platform) ? platform : null; } diff --git a/packages/jest-haste-map/src/lib/isRegExpSupported.js b/packages/jest-haste-map/src/lib/isRegExpSupported.ts similarity index 97% rename from packages/jest-haste-map/src/lib/isRegExpSupported.js rename to packages/jest-haste-map/src/lib/isRegExpSupported.ts index ffcdc63193cc..9f35631ae46d 100644 --- a/packages/jest-haste-map/src/lib/isRegExpSupported.js +++ b/packages/jest-haste-map/src/lib/isRegExpSupported.ts @@ -3,8 +3,6 @@ * * 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 { diff --git a/packages/jest-haste-map/src/lib/normalizePathSep.js b/packages/jest-haste-map/src/lib/normalizePathSep.ts similarity index 84% rename from packages/jest-haste-map/src/lib/normalizePathSep.js rename to packages/jest-haste-map/src/lib/normalizePathSep.ts index b10f0c4f5de4..7c84e8377c55 100644 --- a/packages/jest-haste-map/src/lib/normalizePathSep.js +++ b/packages/jest-haste-map/src/lib/normalizePathSep.ts @@ -3,13 +3,11 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -const path = require('path'); +import path from 'path'; -let normalizePathSep; +let normalizePathSep: (string: string) => string; if (path.sep === '/') { normalizePathSep = (filePath: string) => filePath; } else { diff --git a/packages/jest-haste-map/src/types.js b/packages/jest-haste-map/src/types.js deleted file mode 100644 index 2577c5329296..000000000000 --- a/packages/jest-haste-map/src/types.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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 type {InternalHasteMap, ModuleMetaData} from 'types/HasteMap'; - -export type IgnoreMatcher = (item: string) => boolean; -export type Mapper = (item: string) => ?Array; - -export type WorkerMessage = { - computeDependencies: boolean, - computeSha1: boolean, - dependencyExtractor?: string, - rootDir: string, - filePath: string, - hasteImplModulePath?: string, -}; - -export type WorkerMetadata = {| - dependencies: ?Array, - id: ?string, - module: ?ModuleMetaData, - sha1: ?string, -|}; - -export type CrawlerOptions = {| - computeSha1: boolean, - data: InternalHasteMap, - extensions: Array, - forceNodeFilesystemAPI: boolean, - ignore: IgnoreMatcher, - mapper?: ?Mapper, - rootDir: string, - roots: Array, -|}; - -export type HasteImpl = { - getHasteName(filePath: string): string | void, -}; diff --git a/packages/jest-haste-map/src/types.ts b/packages/jest-haste-map/src/types.ts new file mode 100644 index 000000000000..c5e85fc8d9b9 --- /dev/null +++ b/packages/jest-haste-map/src/types.ts @@ -0,0 +1,104 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import {Config} from '@jest/types'; +import ModuleMap from './ModuleMap'; +import HasteFS from './HasteFS'; + +export type IgnoreMatcher = (item: string) => boolean; +export type Mapper = (item: string) => Array | null; + +export type WorkerMessage = { + computeDependencies: boolean; + computeSha1: boolean; + dependencyExtractor?: string; + rootDir: string; + filePath: string; + hasteImplModulePath?: string; +}; + +export type WorkerMetadata = { + dependencies: Array | undefined | null; + id: string | undefined | null; + module: ModuleMetaData | undefined | null; + sha1: string | undefined | null; +}; + +export type CrawlerOptions = { + computeSha1: boolean; + data: InternalHasteMap; + extensions: Array; + forceNodeFilesystemAPI: boolean; + ignore: IgnoreMatcher; + mapper?: Mapper | null; + rootDir: string; + roots: Array; +}; + +export type HasteImpl = { + getHasteName(filePath: Config.Path): string | undefined; +}; + +export type FileData = Map; + +export type FileMetaData = [ + /* id */ string, + /* mtime */ number, + /* size */ number, + /* visited */ 0 | 1, + /* dependencies */ Array, + /* sha1 */ string | null | undefined +]; + +export type MockData = Map; +export type ModuleMapData = Map; +export type WatchmanClocks = Map; +export type HasteRegExp = RegExp | ((str: string) => boolean); + +export type DuplicatesSet = Map; +export type DuplicatesIndex = Map>; + +export type InternalHasteMap = { + clocks: WatchmanClocks; + duplicates: DuplicatesIndex; + files: FileData; + map: ModuleMapData; + mocks: MockData; +}; + +export type HasteMap = { + hasteFS: HasteFS; + moduleMap: ModuleMap; + __hasteMapForTest?: InternalHasteMap | null; +}; + +export type RawModuleMap = { + rootDir: Config.Path; + duplicates: DuplicatesIndex; + map: ModuleMapData; + mocks: MockData; +}; + +type ModuleMapItem = {[platform: string]: ModuleMetaData}; +export type ModuleMetaData = [Config.Path, /* type */ number]; + +export type HType = { + ID: 0; + MTIME: 1; + SIZE: 2; + VISITED: 3; + DEPENDENCIES: 4; + SHA1: 5; + PATH: 0; + TYPE: 1; + MODULE: 0; + PACKAGE: 1; + GENERIC_PLATFORM: 'g'; + NATIVE_PLATFORM: 'native'; +}; + +export type HTypeValue = HType[keyof HType]; diff --git a/packages/jest-haste-map/src/worker.js b/packages/jest-haste-map/src/worker.ts similarity index 85% rename from packages/jest-haste-map/src/worker.js rename to packages/jest-haste-map/src/worker.ts index 1b0165f19425..4f3772d1db03 100644 --- a/packages/jest-haste-map/src/worker.js +++ b/packages/jest-haste-map/src/worker.ts @@ -3,23 +3,20 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {HasteImpl, WorkerMessage, WorkerMetadata} from './types'; - import crypto from 'crypto'; import path from 'path'; import fs from 'graceful-fs'; +import {HasteImpl, WorkerMessage, WorkerMetadata} from './types'; import blacklist from './blacklist'; import H from './constants'; import * as dependencyExtractor from './lib/dependencyExtractor'; const PACKAGE_JSON = path.sep + 'package.json'; -let hasteImpl: ?HasteImpl = null; -let hasteImplModulePath: ?string = null; +let hasteImpl: HasteImpl | null = null; +let hasteImplModulePath: string | null = null; function sha1hex(content: string | Buffer): string { return crypto @@ -37,15 +34,14 @@ export async function worker(data: WorkerMessage): Promise { throw new Error('jest-haste-map: hasteImplModulePath changed'); } hasteImplModulePath = data.hasteImplModulePath; - // $FlowFixMe: dynamic require - hasteImpl = (require(hasteImplModulePath): HasteImpl); + hasteImpl = require(hasteImplModulePath); } - let content; - let dependencies; - let id; - let module; - let sha1; + let content: string | undefined; + let dependencies: WorkerMetadata['dependencies']; + let id: WorkerMetadata['id']; + let module: WorkerMetadata['module']; + let sha1: WorkerMetadata['sha1']; const {computeDependencies, computeSha1, rootDir, filePath} = data; @@ -80,8 +76,7 @@ export async function worker(data: WorkerMessage): Promise { const content = getContent(); dependencies = Array.from( data.dependencyExtractor - ? // $FlowFixMe - require(data.dependencyExtractor).extract( + ? require(data.dependencyExtractor).extract( content, filePath, dependencyExtractor.extract, diff --git a/packages/jest-haste-map/tsconfig.json b/packages/jest-haste-map/tsconfig.json new file mode 100644 index 000000000000..8290de687869 --- /dev/null +++ b/packages/jest-haste-map/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [ + {"path": "../jest-worker"}, + {"path": "../jest-serializer"}, + {"path": "../jest-util"}, + {"path": "../jest-types"} + ] +} diff --git a/tsconfig.json b/tsconfig.json index 6b1f7373d1ab..9c25fc411393 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,7 +20,8 @@ /* Module Resolution Options */ "moduleResolution": "node", - "esModuleInterop": true + "esModuleInterop": true, + "resolveJsonModule": true }, "exclude": ["**/__tests__/**/*", "**/build/**/*", "**/build-es5/**/*"] } diff --git a/yarn.lock b/yarn.lock index e28de1a42440..4d6dea0f8c63 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1688,7 +1688,7 @@ dependencies: "@types/node" "*" -"@types/node@*": +"@types/node@*", "@types/node@^10.12.24": version "10.12.24" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.24.tgz#b13564af612a22a20b5d95ca40f1bffb3af315cf" integrity sha512-GWWbvt+z9G5otRBW8rssOFgRY87J9N/qbhqfjMZ+gUuL6zoL+Hm6gP/8qQBG4jjimqdaNLCehcVapZ/Fs2WjCQ== From f3261420a3738e0f403c74feadc4a35cf4e66fa5 Mon Sep 17 00:00:00 2001 From: bokuweb Date: Tue, 12 Feb 2019 17:47:28 +0900 Subject: [PATCH 042/107] Use symbol instead of Symbol (#7869) --- packages/pretty-format/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pretty-format/src/index.ts b/packages/pretty-format/src/index.ts index 9ed723356e42..3a496046fdff 100644 --- a/packages/pretty-format/src/index.ts +++ b/packages/pretty-format/src/index.ts @@ -89,7 +89,7 @@ function printFunction(val: Function, printFunctionName: boolean): string { return '[Function ' + (val.name || 'anonymous') + ']'; } -function printSymbol(val: Symbol): string { +function printSymbol(val: symbol): string { return symbolToString.call(val).replace(SYMBOL_REGEXP, 'Symbol($1)'); } From dadb766e1b03fc02a492a348f46a00363d410330 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Tue, 12 Feb 2019 09:53:17 -0500 Subject: [PATCH 043/107] expect: Improve report when matcher fails, part 7 (#7866) * expect: Improve report when matcher fails, part 7 * Update CHANGELOG.md --- CHANGELOG.md | 2 + .../__snapshots__/matchers.test.js.snap | 396 +++++++++--------- packages/expect/src/matchers.js | 88 ++-- 3 files changed, 256 insertions(+), 230 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e8004209de7..24f72e8ba875 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Features +- `[expect]`: Improve report when matcher fails, part 7 ([#7866](https://github.com/facebook/jest/pull/7866)) + ### Fixes - `[jest-cli]` Refactor `-o` and `--coverage` combined ([#7611](https://github.com/facebook/jest/pull/7611)) diff --git a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap index 73d527e4d0d7..ac268c22851e 100644 --- a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap @@ -645,465 +645,465 @@ Received: undefined" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() equal numbers: [-Infinity, -Infinity] 1`] = ` -"expect(received).not.toBeGreaterThanOrEqual(expected) +"expect(received).not.toBeGreaterThanOrEqual(expected) -Expected: -Infinity -Received: -Infinity" +Expected: not >= -Infinity +Received: -Infinity" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() equal numbers: [-Infinity, -Infinity] 2`] = ` -"expect(received).not.toBeLessThanOrEqual(expected) +"expect(received).not.toBeLessThanOrEqual(expected) -Expected: -Infinity -Received: -Infinity" +Expected: not <= -Infinity +Received: -Infinity" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() equal numbers: [1, 1] 1`] = ` -"expect(received).not.toBeGreaterThanOrEqual(expected) +"expect(received).not.toBeGreaterThanOrEqual(expected) -Expected: 1 -Received: 1" +Expected: not >= 1 +Received: 1" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() equal numbers: [1, 1] 2`] = ` -"expect(received).not.toBeLessThanOrEqual(expected) +"expect(received).not.toBeLessThanOrEqual(expected) -Expected: 1 -Received: 1" +Expected: not <= 1 +Received: 1" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() equal numbers: [1.7976931348623157e+308, 1.7976931348623157e+308] 1`] = ` -"expect(received).not.toBeGreaterThanOrEqual(expected) +"expect(received).not.toBeGreaterThanOrEqual(expected) -Expected: 1.7976931348623157e+308 -Received: 1.7976931348623157e+308" +Expected: not >= 1.7976931348623157e+308 +Received: 1.7976931348623157e+308" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() equal numbers: [1.7976931348623157e+308, 1.7976931348623157e+308] 2`] = ` -"expect(received).not.toBeLessThanOrEqual(expected) +"expect(received).not.toBeLessThanOrEqual(expected) -Expected: 1.7976931348623157e+308 -Received: 1.7976931348623157e+308" +Expected: not <= 1.7976931348623157e+308 +Received: 1.7976931348623157e+308" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() equal numbers: [5e-324, 5e-324] 1`] = ` -"expect(received).not.toBeGreaterThanOrEqual(expected) +"expect(received).not.toBeGreaterThanOrEqual(expected) -Expected: 5e-324 -Received: 5e-324" +Expected: not >= 5e-324 +Received: 5e-324" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() equal numbers: [5e-324, 5e-324] 2`] = ` -"expect(received).not.toBeLessThanOrEqual(expected) +"expect(received).not.toBeLessThanOrEqual(expected) -Expected: 5e-324 -Received: 5e-324" +Expected: not <= 5e-324 +Received: 5e-324" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() equal numbers: [Infinity, Infinity] 1`] = ` -"expect(received).not.toBeGreaterThanOrEqual(expected) +"expect(received).not.toBeGreaterThanOrEqual(expected) -Expected: Infinity -Received: Infinity" +Expected: not >= Infinity +Received: Infinity" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() equal numbers: [Infinity, Infinity] 2`] = ` -"expect(received).not.toBeLessThanOrEqual(expected) +"expect(received).not.toBeLessThanOrEqual(expected) -Expected: Infinity -Received: Infinity" +Expected: not <= Infinity +Received: Infinity" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [-Infinity, Infinity] 1`] = ` -"expect(received).toBeGreaterThan(expected) +"expect(received).toBeGreaterThan(expected) -Expected: Infinity -Received: -Infinity" +Expected: > Infinity +Received: -Infinity" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [-Infinity, Infinity] 2`] = ` -"expect(received).not.toBeLessThan(expected) +"expect(received).not.toBeLessThan(expected) -Expected: Infinity -Received: -Infinity" +Expected: not < Infinity +Received: -Infinity" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [-Infinity, Infinity] 3`] = ` -"expect(received).not.toBeGreaterThan(expected) +"expect(received).not.toBeGreaterThan(expected) -Expected: -Infinity -Received: Infinity" +Expected: not > -Infinity +Received: Infinity" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [-Infinity, Infinity] 4`] = ` -"expect(received).toBeLessThan(expected) +"expect(received).toBeLessThan(expected) -Expected: -Infinity -Received: Infinity" +Expected: < -Infinity +Received: Infinity" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [-Infinity, Infinity] 5`] = ` -"expect(received).toBeGreaterThanOrEqual(expected) +"expect(received).toBeGreaterThanOrEqual(expected) -Expected: Infinity -Received: -Infinity" +Expected: >= Infinity +Received: -Infinity" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [-Infinity, Infinity] 6`] = ` -"expect(received).not.toBeLessThanOrEqual(expected) +"expect(received).not.toBeLessThanOrEqual(expected) -Expected: Infinity -Received: -Infinity" +Expected: not <= Infinity +Received: -Infinity" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [-Infinity, Infinity] 7`] = ` -"expect(received).not.toBeGreaterThanOrEqual(expected) +"expect(received).not.toBeGreaterThanOrEqual(expected) -Expected: -Infinity -Received: Infinity" +Expected: not >= -Infinity +Received: Infinity" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [-Infinity, Infinity] 8`] = ` -"expect(received).toBeLessThanOrEqual(expected) +"expect(received).toBeLessThanOrEqual(expected) -Expected: -Infinity -Received: Infinity" +Expected: <= -Infinity +Received: Infinity" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [0.1, 0.2] 1`] = ` -"expect(received).toBeGreaterThan(expected) +"expect(received).toBeGreaterThan(expected) -Expected: 0.2 -Received: 0.1" +Expected: > 0.2 +Received: 0.1" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [0.1, 0.2] 2`] = ` -"expect(received).not.toBeLessThan(expected) +"expect(received).not.toBeLessThan(expected) -Expected: 0.2 -Received: 0.1" +Expected: not < 0.2 +Received: 0.1" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [0.1, 0.2] 3`] = ` -"expect(received).not.toBeGreaterThan(expected) +"expect(received).not.toBeGreaterThan(expected) -Expected: 0.1 -Received: 0.2" +Expected: not > 0.1 +Received: 0.2" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [0.1, 0.2] 4`] = ` -"expect(received).toBeLessThan(expected) +"expect(received).toBeLessThan(expected) -Expected: 0.1 -Received: 0.2" +Expected: < 0.1 +Received: 0.2" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [0.1, 0.2] 5`] = ` -"expect(received).toBeGreaterThanOrEqual(expected) +"expect(received).toBeGreaterThanOrEqual(expected) -Expected: 0.2 -Received: 0.1" +Expected: >= 0.2 +Received: 0.1" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [0.1, 0.2] 6`] = ` -"expect(received).not.toBeLessThanOrEqual(expected) +"expect(received).not.toBeLessThanOrEqual(expected) -Expected: 0.2 -Received: 0.1" +Expected: not <= 0.2 +Received: 0.1" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [0.1, 0.2] 7`] = ` -"expect(received).not.toBeGreaterThanOrEqual(expected) +"expect(received).not.toBeGreaterThanOrEqual(expected) -Expected: 0.1 -Received: 0.2" +Expected: not >= 0.1 +Received: 0.2" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [0.1, 0.2] 8`] = ` -"expect(received).toBeLessThanOrEqual(expected) +"expect(received).toBeLessThanOrEqual(expected) -Expected: 0.1 -Received: 0.2" +Expected: <= 0.1 +Received: 0.2" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [1, 2] 1`] = ` -"expect(received).toBeGreaterThan(expected) +"expect(received).toBeGreaterThan(expected) -Expected: 2 -Received: 1" +Expected: > 2 +Received: 1" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [1, 2] 2`] = ` -"expect(received).not.toBeLessThan(expected) +"expect(received).not.toBeLessThan(expected) -Expected: 2 -Received: 1" +Expected: not < 2 +Received: 1" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [1, 2] 3`] = ` -"expect(received).not.toBeGreaterThan(expected) +"expect(received).not.toBeGreaterThan(expected) -Expected: 1 -Received: 2" +Expected: not > 1 +Received: 2" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [1, 2] 4`] = ` -"expect(received).toBeLessThan(expected) +"expect(received).toBeLessThan(expected) -Expected: 1 -Received: 2" +Expected: < 1 +Received: 2" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [1, 2] 5`] = ` -"expect(received).toBeGreaterThanOrEqual(expected) +"expect(received).toBeGreaterThanOrEqual(expected) -Expected: 2 -Received: 1" +Expected: >= 2 +Received: 1" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [1, 2] 6`] = ` -"expect(received).not.toBeLessThanOrEqual(expected) +"expect(received).not.toBeLessThanOrEqual(expected) -Expected: 2 -Received: 1" +Expected: not <= 2 +Received: 1" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [1, 2] 7`] = ` -"expect(received).not.toBeGreaterThanOrEqual(expected) +"expect(received).not.toBeGreaterThanOrEqual(expected) -Expected: 1 -Received: 2" +Expected: not >= 1 +Received: 2" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [1, 2] 8`] = ` -"expect(received).toBeLessThanOrEqual(expected) +"expect(received).toBeLessThanOrEqual(expected) -Expected: 1 -Received: 2" +Expected: <= 1 +Received: 2" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [3, 7] 1`] = ` -"expect(received).toBeGreaterThan(expected) +"expect(received).toBeGreaterThan(expected) -Expected: 7 -Received: 3" +Expected: > 7 +Received: 3" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [3, 7] 2`] = ` -"expect(received).not.toBeLessThan(expected) +"expect(received).not.toBeLessThan(expected) -Expected: 7 -Received: 3" +Expected: not < 7 +Received: 3" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [3, 7] 3`] = ` -"expect(received).not.toBeGreaterThan(expected) +"expect(received).not.toBeGreaterThan(expected) -Expected: 3 -Received: 7" +Expected: not > 3 +Received: 7" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [3, 7] 4`] = ` -"expect(received).toBeLessThan(expected) +"expect(received).toBeLessThan(expected) -Expected: 3 -Received: 7" +Expected: < 3 +Received: 7" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [3, 7] 5`] = ` -"expect(received).toBeGreaterThanOrEqual(expected) +"expect(received).toBeGreaterThanOrEqual(expected) -Expected: 7 -Received: 3" +Expected: >= 7 +Received: 3" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [3, 7] 6`] = ` -"expect(received).not.toBeLessThanOrEqual(expected) +"expect(received).not.toBeLessThanOrEqual(expected) -Expected: 7 -Received: 3" +Expected: not <= 7 +Received: 3" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [3, 7] 7`] = ` -"expect(received).not.toBeGreaterThanOrEqual(expected) +"expect(received).not.toBeGreaterThanOrEqual(expected) -Expected: 3 -Received: 7" +Expected: not >= 3 +Received: 7" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [3, 7] 8`] = ` -"expect(received).toBeLessThanOrEqual(expected) +"expect(received).toBeLessThanOrEqual(expected) -Expected: 3 -Received: 7" +Expected: <= 3 +Received: 7" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [5e-324, 1.7976931348623157e+308] 1`] = ` -"expect(received).toBeGreaterThan(expected) +"expect(received).toBeGreaterThan(expected) -Expected: 1.7976931348623157e+308 -Received: 5e-324" +Expected: > 1.7976931348623157e+308 +Received: 5e-324" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [5e-324, 1.7976931348623157e+308] 2`] = ` -"expect(received).not.toBeLessThan(expected) +"expect(received).not.toBeLessThan(expected) -Expected: 1.7976931348623157e+308 -Received: 5e-324" +Expected: not < 1.7976931348623157e+308 +Received: 5e-324" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [5e-324, 1.7976931348623157e+308] 3`] = ` -"expect(received).not.toBeGreaterThan(expected) +"expect(received).not.toBeGreaterThan(expected) -Expected: 5e-324 -Received: 1.7976931348623157e+308" +Expected: not > 5e-324 +Received: 1.7976931348623157e+308" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [5e-324, 1.7976931348623157e+308] 4`] = ` -"expect(received).toBeLessThan(expected) +"expect(received).toBeLessThan(expected) -Expected: 5e-324 -Received: 1.7976931348623157e+308" +Expected: < 5e-324 +Received: 1.7976931348623157e+308" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [5e-324, 1.7976931348623157e+308] 5`] = ` -"expect(received).toBeGreaterThanOrEqual(expected) +"expect(received).toBeGreaterThanOrEqual(expected) -Expected: 1.7976931348623157e+308 -Received: 5e-324" +Expected: >= 1.7976931348623157e+308 +Received: 5e-324" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [5e-324, 1.7976931348623157e+308] 6`] = ` -"expect(received).not.toBeLessThanOrEqual(expected) +"expect(received).not.toBeLessThanOrEqual(expected) -Expected: 1.7976931348623157e+308 -Received: 5e-324" +Expected: not <= 1.7976931348623157e+308 +Received: 5e-324" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [5e-324, 1.7976931348623157e+308] 7`] = ` -"expect(received).not.toBeGreaterThanOrEqual(expected) +"expect(received).not.toBeGreaterThanOrEqual(expected) -Expected: 5e-324 -Received: 1.7976931348623157e+308" +Expected: not >= 5e-324 +Received: 1.7976931348623157e+308" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [5e-324, 1.7976931348623157e+308] 8`] = ` -"expect(received).toBeLessThanOrEqual(expected) +"expect(received).toBeLessThanOrEqual(expected) -Expected: 5e-324 -Received: 1.7976931348623157e+308" +Expected: <= 5e-324 +Received: 1.7976931348623157e+308" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [9, 18] 1`] = ` -"expect(received).toBeGreaterThan(expected) +"expect(received).toBeGreaterThan(expected) -Expected: 18 -Received: 9" +Expected: > 18 +Received: 9" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [9, 18] 2`] = ` -"expect(received).not.toBeLessThan(expected) +"expect(received).not.toBeLessThan(expected) -Expected: 18 -Received: 9" +Expected: not < 18 +Received: 9" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [9, 18] 3`] = ` -"expect(received).not.toBeGreaterThan(expected) +"expect(received).not.toBeGreaterThan(expected) -Expected: 9 -Received: 18" +Expected: not > 9 +Received: 18" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [9, 18] 4`] = ` -"expect(received).toBeLessThan(expected) +"expect(received).toBeLessThan(expected) -Expected: 9 -Received: 18" +Expected: < 9 +Received: 18" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [9, 18] 5`] = ` -"expect(received).toBeGreaterThanOrEqual(expected) +"expect(received).toBeGreaterThanOrEqual(expected) -Expected: 18 -Received: 9" +Expected: >= 18 +Received: 9" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [9, 18] 6`] = ` -"expect(received).not.toBeLessThanOrEqual(expected) +"expect(received).not.toBeLessThanOrEqual(expected) -Expected: 18 -Received: 9" +Expected: not <= 18 +Received: 9" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [9, 18] 7`] = ` -"expect(received).not.toBeGreaterThanOrEqual(expected) +"expect(received).not.toBeGreaterThanOrEqual(expected) -Expected: 9 -Received: 18" +Expected: not >= 9 +Received: 18" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [9, 18] 8`] = ` -"expect(received).toBeLessThanOrEqual(expected) +"expect(received).toBeLessThanOrEqual(expected) -Expected: 9 -Received: 18" +Expected: <= 9 +Received: 18" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [17, 34] 1`] = ` -"expect(received).toBeGreaterThan(expected) +"expect(received).toBeGreaterThan(expected) -Expected: 34 -Received: 17" +Expected: > 34 +Received: 17" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [17, 34] 2`] = ` -"expect(received).not.toBeLessThan(expected) +"expect(received).not.toBeLessThan(expected) -Expected: 34 -Received: 17" +Expected: not < 34 +Received: 17" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [17, 34] 3`] = ` -"expect(received).not.toBeGreaterThan(expected) +"expect(received).not.toBeGreaterThan(expected) -Expected: 17 -Received: 34" +Expected: not > 17 +Received: 34" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [17, 34] 4`] = ` -"expect(received).toBeLessThan(expected) +"expect(received).toBeLessThan(expected) -Expected: 17 -Received: 34" +Expected: < 17 +Received: 34" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [17, 34] 5`] = ` -"expect(received).toBeGreaterThanOrEqual(expected) +"expect(received).toBeGreaterThanOrEqual(expected) -Expected: 34 -Received: 17" +Expected: >= 34 +Received: 17" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [17, 34] 6`] = ` -"expect(received).not.toBeLessThanOrEqual(expected) +"expect(received).not.toBeLessThanOrEqual(expected) -Expected: 34 -Received: 17" +Expected: not <= 34 +Received: 17" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [17, 34] 7`] = ` -"expect(received).not.toBeGreaterThanOrEqual(expected) +"expect(received).not.toBeGreaterThanOrEqual(expected) -Expected: 17 -Received: 34" +Expected: not >= 17 +Received: 34" `; exports[`.toBeGreaterThan(), .toBeLessThan(), .toBeGreaterThanOrEqual(), .toBeLessThanOrEqual() throws: [17, 34] 8`] = ` -"expect(received).toBeLessThanOrEqual(expected) +"expect(received).toBeLessThanOrEqual(expected) -Expected: 17 -Received: 34" +Expected: <= 17 +Received: 34" `; exports[`.toBeInstanceOf() failing "a" and [Function String] 1`] = ` diff --git a/packages/expect/src/matchers.js b/packages/expect/src/matchers.js index 298f1d1cb681..844d6fc1b986 100644 --- a/packages/expect/src/matchers.js +++ b/packages/expect/src/matchers.js @@ -144,29 +144,41 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeGreaterThan(actual: number, expected: number) { - ensureNumbers(actual, expected, '.toBeGreaterThan'); - const pass = actual > expected; + toBeGreaterThan(received: number, expected: number) { + const isNot = this.isNot; + const options = { + isNot, + promise: this.promise, + }; + ensureNumbers(received, expected, '.toBeGreaterThan'); + + const pass = received > expected; + const message = () => - matcherHint('.toBeGreaterThan', undefined, undefined, { - isNot: this.isNot, - }) + + matcherHint('toBeGreaterThan', undefined, undefined, options) + '\n\n' + - `Expected: ${printExpected(expected)}\n` + - `Received: ${printReceived(actual)}`; + `Expected:${isNot ? ' not' : ''} > ${printExpected(expected)}\n` + + `Received:${isNot ? ' ' : ''} ${printReceived(received)}`; + return {message, pass}; }, - toBeGreaterThanOrEqual(actual: number, expected: number) { - ensureNumbers(actual, expected, '.toBeGreaterThanOrEqual'); - const pass = actual >= expected; + toBeGreaterThanOrEqual(received: number, expected: number) { + const isNot = this.isNot; + const options = { + isNot, + promise: this.promise, + }; + ensureNumbers(received, expected, '.toBeGreaterThanOrEqual'); + + const pass = received >= expected; + const message = () => - matcherHint('.toBeGreaterThanOrEqual', undefined, undefined, { - isNot: this.isNot, - }) + + matcherHint('toBeGreaterThanOrEqual', undefined, undefined, options) + '\n\n' + - `Expected: ${printExpected(expected)}\n` + - `Received: ${printReceived(actual)}`; + `Expected:${isNot ? ' not' : ''} >= ${printExpected(expected)}\n` + + `Received:${isNot ? ' ' : ''} ${printReceived(received)}`; + return {message, pass}; }, @@ -214,29 +226,41 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeLessThan(actual: number, expected: number) { - ensureNumbers(actual, expected, '.toBeLessThan'); - const pass = actual < expected; + toBeLessThan(received: number, expected: number) { + const isNot = this.isNot; + const options = { + isNot, + promise: this.promise, + }; + ensureNumbers(received, expected, '.toBeLessThan'); + + const pass = received < expected; + const message = () => - matcherHint('.toBeLessThan', undefined, undefined, { - isNot: this.isNot, - }) + + matcherHint('toBeLessThan', undefined, undefined, options) + '\n\n' + - `Expected: ${printExpected(expected)}\n` + - `Received: ${printReceived(actual)}`; + `Expected:${isNot ? ' not' : ''} < ${printExpected(expected)}\n` + + `Received:${isNot ? ' ' : ''} ${printReceived(received)}`; + return {message, pass}; }, - toBeLessThanOrEqual(actual: number, expected: number) { - ensureNumbers(actual, expected, '.toBeLessThanOrEqual'); - const pass = actual <= expected; + toBeLessThanOrEqual(received: number, expected: number) { + const isNot = this.isNot; + const options = { + isNot, + promise: this.promise, + }; + ensureNumbers(received, expected, '.toBeLessThanOrEqual'); + + const pass = received <= expected; + const message = () => - matcherHint('.toBeLessThanOrEqual', undefined, undefined, { - isNot: this.isNot, - }) + + matcherHint('toBeLessThanOrEqual', undefined, undefined, options) + '\n\n' + - `Expected: ${printExpected(expected)}\n` + - `Received: ${printReceived(actual)}`; + `Expected:${isNot ? ' not' : ''} <= ${printExpected(expected)}\n` + + `Received:${isNot ? ' ' : ''} ${printReceived(received)}`; + return {message, pass}; }, From 99af560411f2104196b1a6e5fcf4d1bfe4648e29 Mon Sep 17 00:00:00 2001 From: Tim Seckinger Date: Tue, 12 Feb 2019 21:52:29 +0100 Subject: [PATCH 044/107] contributing guide section about changelog entries (#7877) --- CONTRIBUTING.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c22883451815..c09425c85741 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -72,6 +72,14 @@ _Before_ submitting a pull request, please make sure the following is done… 1. If you haven't already, complete the CLA. +#### Changelog entries + +All changes that add a feature to or fix a bug in any of Jest's packages require a changelog entry containing the names of the packages affected, a description of the change, and the number of and link to the pull request. Try to match the structure of the existing entries. + +For significant changes to the documentation or website and things like cleanup, refactoring, and dependency updates, the "Chore & Maintenance" section of the changelog can be used. + +You can add or edit the changelog entry in the GitHub web interface once you have opened the pull request and know the number and link to it. + #### Testing Code that is written needs to be tested to ensure that it achieves the desired behaviour. Tests either fall into a unit test or an integration test. From 916a7452e2616a4dc533ea45187c0ffaf2781966 Mon Sep 17 00:00:00 2001 From: George Buckingham Date: Tue, 12 Feb 2019 23:14:49 +0100 Subject: [PATCH 045/107] Fix image URLs (#7872) --- CHANGELOG.md | 1 + docs/SnapshotTesting.md | 11 +++++------ .../versioned_docs/version-24.0/SnapshotTesting.md | 11 +++++------ 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24f72e8ba875..872870ca9577 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - `[jest-mock]`: Migrate to TypeScript ([#7847](https://github.com/facebook/jest/pull/7847), [#7850](https://github.com/facebook/jest/pull/7850)) - `[jest-worker]`: Migrate to TypeScript ([#7853](https://github.com/facebook/jest/pull/7853)) - `[jest-haste-map]`: Migrate to TypeScript ([#7854](https://github.com/facebook/jest/pull/7854)) +- `[docs]`: Fix image paths in SnapshotTesting.md for current and version 24 ([#7872](https://github.com/facebook/jest/pull/7872)) ### Performance diff --git a/docs/SnapshotTesting.md b/docs/SnapshotTesting.md index c31926c68dd3..8f0d3b0c6be4 100644 --- a/docs/SnapshotTesting.md +++ b/docs/SnapshotTesting.md @@ -41,8 +41,7 @@ exports[`renders correctly 1`] = ` The snapshot artifact should be committed alongside code changes, and reviewed as part of your code review process. Jest uses [pretty-format](https://github.com/facebook/jest/tree/master/packages/pretty-format) to make snapshots human-readable during code review. On subsequent test runs Jest will simply compare the rendered output with the previous snapshot. If they match, the test will pass. If they don't match, either the test runner found a bug in your code (in this case, it's `` component) that should be fixed, or the implementation has changed and the snapshot needs to be updated. -> Note: The snapshot is directly scoped to the data you render – in our example it's `` component with page prop passed to it. This implies that even if any other file has missing props (Say, `App.js`) in the `` component, it will still pass the test as the test doesn't know the usage of `` component and it's scoped only to the `Link.react.js`. -> Also, Rendering the same component with different props in other snapshot tests will not affect the first one, as the tests don't know about each other. +> Note: The snapshot is directly scoped to the data you render – in our example it's `` component with page prop passed to it. This implies that even if any other file has missing props (Say, `App.js`) in the `` component, it will still pass the test as the test doesn't know the usage of `` component and it's scoped only to the `Link.react.js`. Also, Rendering the same component with different props in other snapshot tests will not affect the first one, as the tests don't know about each other. More information on how snapshot testing works and why we built it can be found on the [release blog post](https://jestjs.io/blog/2016/07/27/jest-14.html). We recommend reading [this blog post](http://benmccormick.org/2016/09/19/testing-with-jest-snapshots-first-impressions/) to get a good sense of when you should use snapshot testing. We also recommend watching this [egghead video](https://egghead.io/lessons/javascript-use-jest-s-snapshot-testing-feature?pl=testing-javascript-with-jest-a36c4074) on Snapshot Testing with Jest. @@ -64,7 +63,7 @@ it('renders correctly', () => { In that case, Jest will print this output: -![](/website/static/img/content/failedSnapshotTest.png) +![](/img/content/failedSnapshotTest.png) Since we just updated our component to point to a different address, it's reasonable to expect changes in the snapshot for this component. Our snapshot test case is failing because the snapshot for our updated component no longer matches the snapshot artifact for this test case. @@ -84,17 +83,17 @@ You can try out this functionality by cloning the [snapshot example](https://git Failed snapshots can also be updated interactively in watch mode: -![](/website/static/img/content/interactiveSnapshot.png) +![](/img/content/interactiveSnapshot.png) Once you enter Interactive Snapshot Mode, Jest will step you through the failed snapshots one test at a time and give you the opportunity to review the failed output. From here you can choose to update that snapshot or skip to the next: -![](/website/static/img/content/interactiveSnapshotUpdate.gif) +![](/img/content/interactiveSnapshotUpdate.gif) Once you're finished, Jest will give you a summary before returning back to watch mode: -![](/website/static/img/content/interactiveSnapshotDone.png) +![](/img/content/interactiveSnapshotDone.png) ### Inline Snapshots diff --git a/website/versioned_docs/version-24.0/SnapshotTesting.md b/website/versioned_docs/version-24.0/SnapshotTesting.md index df7c7ed847e3..072efda901b8 100644 --- a/website/versioned_docs/version-24.0/SnapshotTesting.md +++ b/website/versioned_docs/version-24.0/SnapshotTesting.md @@ -42,8 +42,7 @@ exports[`renders correctly 1`] = ` The snapshot artifact should be committed alongside code changes, and reviewed as part of your code review process. Jest uses [pretty-format](https://github.com/facebook/jest/tree/master/packages/pretty-format) to make snapshots human-readable during code review. On subsequent test runs Jest will simply compare the rendered output with the previous snapshot. If they match, the test will pass. If they don't match, either the test runner found a bug in your code (in this case, it's `` component) that should be fixed, or the implementation has changed and the snapshot needs to be updated. -> Note: The snapshot is directly scoped to the data you render – in our example it's `` component with page prop passed to it. This implies that even if any other file has missing props (Say, `App.js`) in the `` component, it will still pass the test as the test doesn't know the usage of `` component and it's scoped only to the `Link.react.js`. -> Also, Rendering the same component with different props in other snapshot tests will not affect the first one, as the tests don't know about each other. +> Note: The snapshot is directly scoped to the data you render – in our example it's `` component with page prop passed to it. This implies that even if any other file has missing props (Say, `App.js`) in the `` component, it will still pass the test as the test doesn't know the usage of `` component and it's scoped only to the `Link.react.js`. Also, Rendering the same component with different props in other snapshot tests will not affect the first one, as the tests don't know about each other. More information on how snapshot testing works and why we built it can be found on the [release blog post](https://jestjs.io/blog/2016/07/27/jest-14.html). We recommend reading [this blog post](http://benmccormick.org/2016/09/19/testing-with-jest-snapshots-first-impressions/) to get a good sense of when you should use snapshot testing. We also recommend watching this [egghead video](https://egghead.io/lessons/javascript-use-jest-s-snapshot-testing-feature?pl=testing-javascript-with-jest-a36c4074) on Snapshot Testing with Jest. @@ -65,7 +64,7 @@ it('renders correctly', () => { In that case, Jest will print this output: -![](/website/static/img/content/failedSnapshotTest.png) +![](/img/content/failedSnapshotTest.png) Since we just updated our component to point to a different address, it's reasonable to expect changes in the snapshot for this component. Our snapshot test case is failing because the snapshot for our updated component no longer matches the snapshot artifact for this test case. @@ -85,17 +84,17 @@ You can try out this functionality by cloning the [snapshot example](https://git Failed snapshots can also be updated interactively in watch mode: -![](/website/static/img/content/interactiveSnapshot.png) +![](/img/content/interactiveSnapshot.png) Once you enter Interactive Snapshot Mode, Jest will step you through the failed snapshots one test at a time and give you the opportunity to review the failed output. From here you can choose to update that snapshot or skip to the next: -![](/website/static/img/content/interactiveSnapshotUpdate.gif) +![](/img/content/interactiveSnapshotUpdate.gif) Once you're finished, Jest will give you a summary before returning back to watch mode: -![](/website/static/img/content/interactiveSnapshotDone.png) +![](/img/content/interactiveSnapshotDone.png) ### Inline Snapshots From 5aa17d28479316c99bbf4110782af7aa689a2c36 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 13 Feb 2019 09:50:43 +0100 Subject: [PATCH 046/107] chore: fix jest-mock spyOn argument inferring (#7878) --- packages/jest-mock/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-mock/src/index.ts b/packages/jest-mock/src/index.ts index 4d7d3a567664..4015f09a1a16 100644 --- a/packages/jest-mock/src/index.ts +++ b/packages/jest-mock/src/index.ts @@ -940,7 +940,7 @@ class ModuleMockerClass { object: T, methodName: M, ): T[M] extends (...args: any[]) => any - ? SpyInstance, ArgsType> + ? SpyInstance, Parameters> : never; spyOn>( From 31b4c6a1244139af657a016b45796e200b0f4498 Mon Sep 17 00:00:00 2001 From: Don Schrimsher Date: Wed, 13 Feb 2019 03:52:34 -0500 Subject: [PATCH 047/107] Fix custom async matcher stack traces (#7652) --- CHANGELOG.md | 1 + .../customMatcherStackTrace.test.js.snap | 33 ++++++-- .../expectAsyncMatcher.test.js.snap | 80 +++++++++++-------- e2e/__tests__/customMatcherStackTrace.test.js | 12 ++- .../__tests__/asynchronous.test.js | 18 +++++ .../{customMatcher.test.js => sync.test.js} | 0 .../__tests__/failure.test.js | 29 +++---- .../__tests__/success.test.js | 29 +++---- packages/expect/src/index.js | 14 +++- 9 files changed, 133 insertions(+), 83 deletions(-) create mode 100644 e2e/custom-matcher-stack-trace/__tests__/asynchronous.test.js rename e2e/custom-matcher-stack-trace/__tests__/{customMatcher.test.js => sync.test.js} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 872870ca9577..ca91b9d179c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Fixes - `[jest-cli]` Refactor `-o` and `--coverage` combined ([#7611](https://github.com/facebook/jest/pull/7611)) +- `[expect]` Fix custom async matcher stack trace ([#7652](https://github.com/facebook/jest/pull/7652)) ### Chore & Maintenance diff --git a/e2e/__tests__/__snapshots__/customMatcherStackTrace.test.js.snap b/e2e/__tests__/__snapshots__/customMatcherStackTrace.test.js.snap index 8faa1ffd5ebd..15df98763079 100644 --- a/e2e/__tests__/__snapshots__/customMatcherStackTrace.test.js.snap +++ b/e2e/__tests__/__snapshots__/customMatcherStackTrace.test.js.snap @@ -1,7 +1,26 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`custom async matchers 1`] = ` +FAIL __tests__/asynchronous.test.js + ✕ showing the stack trace for an async matcher + + ● showing the stack trace for an async matcher + + We expect the stack trace and code fence for this matcher to be shown in the console. + + 9 | + 10 | test('showing the stack trace for an async matcher', async () => { + > 11 | await expect(true).toThrowCustomAsyncMatcherError(); + | ^ + 12 | }); + 13 | + 14 | async function toThrowCustomAsyncMatcherError() { + + at Object.toThrowCustomAsyncMatcherError (__tests__/asynchronous.test.js:11:22) +`; + exports[`works with custom matchers 1`] = ` -FAIL __tests__/customMatcher.test.js +FAIL __tests__/sync.test.js Custom matcher ✓ passes ✓ fails @@ -19,10 +38,10 @@ FAIL __tests__/customMatcher.test.js 47 | 48 | // This expecation fails due to an error we throw (intentionally) - at Error (__tests__/customMatcher.test.js:45:13) - at baz (__tests__/customMatcher.test.js:43:23) - at bar (__tests__/customMatcher.test.js:42:23) - at foo (__tests__/customMatcher.test.js:52:7) - at Object.callback (__tests__/customMatcher.test.js:11:18) - at Object.toCustomMatch (__tests__/customMatcher.test.js:53:8) + at Error (__tests__/sync.test.js:45:13) + at baz (__tests__/sync.test.js:43:23) + at bar (__tests__/sync.test.js:42:23) + at foo (__tests__/sync.test.js:52:7) + at Object.callback (__tests__/sync.test.js:11:18) + at Object.toCustomMatch (__tests__/sync.test.js:53:8) `; diff --git a/e2e/__tests__/__snapshots__/expectAsyncMatcher.test.js.snap b/e2e/__tests__/__snapshots__/expectAsyncMatcher.test.js.snap index 6ef5f68f3fca..b7d07fa98db9 100644 --- a/e2e/__tests__/__snapshots__/expectAsyncMatcher.test.js.snap +++ b/e2e/__tests__/__snapshots__/expectAsyncMatcher.test.js.snap @@ -10,22 +10,40 @@ FAIL __tests__/failure.test.js ● fail with expected non promise values Expected value to have length: + 2 + Received: + 1 + received.length: + 1 + + 11 | + 12 | it('fail with expected non promise values', () => + > 13 | expect([1]).toHaveLengthAsync(Promise.resolve(2))); + | ^ + 14 | + 15 | it('fail with expected non promise values and not', () => + 16 | expect([1, 2]).not.toHaveLengthAsync(Promise.resolve(2))); - 2 - Received: - 1 - received.length: - 1 + at Object.toHaveLengthAsync (__tests__/failure.test.js:13:15) ● fail with expected non promise values and not Expected value to not have length: + 2 + Received: + 1,2 + received.length: + 2 + + 14 | + 15 | it('fail with expected non promise values and not', () => + > 16 | expect([1, 2]).not.toHaveLengthAsync(Promise.resolve(2))); + | ^ + 17 | + 18 | it('fail with expected promise values', () => + 19 | expect(Promise.resolve([1])).resolves.toHaveLengthAsync(Promise.resolve(2))); - 2 - Received: - 1,2 - received.length: - 2 + at Object.toHaveLengthAsync (__tests__/failure.test.js:16:22) ● fail with expected promise values @@ -36,19 +54,15 @@ FAIL __tests__/failure.test.js received.length: 1 - 22 | - 23 | it('fail with expected promise values', async () => { - > 24 | await (expect(Promise.resolve([1])): any).resolves.toHaveLengthAsync( - | ^ - 25 | Promise.resolve(2) - 26 | ); - 27 | }); + 17 | + 18 | it('fail with expected promise values', () => + > 19 | expect(Promise.resolve([1])).resolves.toHaveLengthAsync(Promise.resolve(2))); + | ^ + 20 | + 21 | it('fail with expected promise values and not', () => + 22 | expect(Promise.resolve([1, 2])).resolves.not.toHaveLengthAsync( - at Object.toHaveLengthAsync (__tests__/failure.test.js:24:54) - at asyncGeneratorStep (__tests__/failure.test.js:11:103) - at _next (__tests__/failure.test.js:13:194) - at __tests__/failure.test.js:13:364 - at Object. (__tests__/failure.test.js:13:97) + at Object.toHaveLengthAsync (__tests__/failure.test.js:19:41) ● fail with expected promise values and not @@ -59,17 +73,13 @@ FAIL __tests__/failure.test.js received.length: 2 - 28 | - 29 | it('fail with expected promise values and not', async () => { - > 30 | await (expect(Promise.resolve([1, 2])).resolves.not: any).toHaveLengthAsync( - | ^ - 31 | Promise.resolve(2) - 32 | ); - 33 | }); - - at Object.toHaveLengthAsync (__tests__/failure.test.js:30:61) - at asyncGeneratorStep (__tests__/failure.test.js:11:103) - at _next (__tests__/failure.test.js:13:194) - at __tests__/failure.test.js:13:364 - at Object. (__tests__/failure.test.js:13:97) + 20 | + 21 | it('fail with expected promise values and not', () => + > 22 | expect(Promise.resolve([1, 2])).resolves.not.toHaveLengthAsync( + | ^ + 23 | Promise.resolve(2) + 24 | )); + 25 | + + at Object.toHaveLengthAsync (__tests__/failure.test.js:22:48) `; diff --git a/e2e/__tests__/customMatcherStackTrace.test.js b/e2e/__tests__/customMatcherStackTrace.test.js index c9b13df573e0..be338b2b7ee7 100644 --- a/e2e/__tests__/customMatcherStackTrace.test.js +++ b/e2e/__tests__/customMatcherStackTrace.test.js @@ -12,7 +12,7 @@ import {extractSummary} from '../Utils'; import {wrap} from 'jest-snapshot-serializer-raw'; test('works with custom matchers', () => { - const {stderr} = runJest('custom-matcher-stack-trace'); + const {stderr} = runJest('custom-matcher-stack-trace', ['sync.test.js']); let {rest} = extractSummary(stderr); @@ -23,3 +23,13 @@ test('works with custom matchers', () => { expect(wrap(rest)).toMatchSnapshot(); }); + +test('custom async matchers', () => { + const {stderr} = runJest('custom-matcher-stack-trace', [ + 'asynchronous.test.js', + ]); + + const {rest} = extractSummary(stderr); + + expect(wrap(rest)).toMatchSnapshot(); +}); diff --git a/e2e/custom-matcher-stack-trace/__tests__/asynchronous.test.js b/e2e/custom-matcher-stack-trace/__tests__/asynchronous.test.js new file mode 100644 index 000000000000..33061bffa53e --- /dev/null +++ b/e2e/custom-matcher-stack-trace/__tests__/asynchronous.test.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +expect.extend({toThrowCustomAsyncMatcherError}); + +test('showing the stack trace for an async matcher', async () => { + await expect(true).toThrowCustomAsyncMatcherError(); +}); + +async function toThrowCustomAsyncMatcherError() { + const message = () => + 'We expect the stack trace and code fence for this matcher to be shown in the console.'; + return {message, pass: false}; +} diff --git a/e2e/custom-matcher-stack-trace/__tests__/customMatcher.test.js b/e2e/custom-matcher-stack-trace/__tests__/sync.test.js similarity index 100% rename from e2e/custom-matcher-stack-trace/__tests__/customMatcher.test.js rename to e2e/custom-matcher-stack-trace/__tests__/sync.test.js diff --git a/e2e/expect-async-matcher/__tests__/failure.test.js b/e2e/expect-async-matcher/__tests__/failure.test.js index e10b1947c556..206948d90a9b 100644 --- a/e2e/expect-async-matcher/__tests__/failure.test.js +++ b/e2e/expect-async-matcher/__tests__/failure.test.js @@ -4,30 +4,21 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -'use strict'; import {toHaveLengthAsync} from '../matchers'; -expect.extend({ - toHaveLengthAsync, -}); +expect.extend({toHaveLengthAsync}); -it('fail with expected non promise values', async () => { - await (expect([1]): any).toHaveLengthAsync(Promise.resolve(2)); -}); +it('fail with expected non promise values', () => + expect([1]).toHaveLengthAsync(Promise.resolve(2))); -it('fail with expected non promise values and not', async () => { - await (expect([1, 2]): any).not.toHaveLengthAsync(Promise.resolve(2)); -}); +it('fail with expected non promise values and not', () => + expect([1, 2]).not.toHaveLengthAsync(Promise.resolve(2))); -it('fail with expected promise values', async () => { - await (expect(Promise.resolve([1])): any).resolves.toHaveLengthAsync( - Promise.resolve(2) - ); -}); +it('fail with expected promise values', () => + expect(Promise.resolve([1])).resolves.toHaveLengthAsync(Promise.resolve(2))); -it('fail with expected promise values and not', async () => { - await (expect(Promise.resolve([1, 2])).resolves.not: any).toHaveLengthAsync( +it('fail with expected promise values and not', () => + expect(Promise.resolve([1, 2])).resolves.not.toHaveLengthAsync( Promise.resolve(2) - ); -}); + )); diff --git a/e2e/expect-async-matcher/__tests__/success.test.js b/e2e/expect-async-matcher/__tests__/success.test.js index ac3a8a738b94..8f597b9788ea 100644 --- a/e2e/expect-async-matcher/__tests__/success.test.js +++ b/e2e/expect-async-matcher/__tests__/success.test.js @@ -4,30 +4,21 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -'use strict'; import {toHaveLengthAsync} from '../matchers'; -expect.extend({ - toHaveLengthAsync, -}); +expect.extend({toHaveLengthAsync}); -it('works with expected non promise values', async () => { - await (expect([1]): any).toHaveLengthAsync(Promise.resolve(1)); -}); +it('works with expected non promise values', () => + expect([1]).toHaveLengthAsync(Promise.resolve(1))); -it('works with expected non promise values and not', async () => { - await (expect([1, 2]): any).not.toHaveLengthAsync(Promise.resolve(1)); -}); +it('works with expected non promise values and not', () => + expect([1, 2]).not.toHaveLengthAsync(Promise.resolve(1))); -it('works with expected promise values', async () => { - await (expect(Promise.resolve([1])).resolves: any).toHaveLengthAsync( - Promise.resolve(1) - ); -}); +it('works with expected promise values', () => + expect(Promise.resolve([1])).resolves.toHaveLengthAsync(Promise.resolve(1))); -it('works with expected promise values and not', async () => { - await (expect(Promise.resolve([1, 2])).resolves.not: any).toHaveLengthAsync( +it('works with expected promise values and not', () => + expect(Promise.resolve([1, 2])).resolves.not.toHaveLengthAsync( Promise.resolve(1) - ); -}); + )); diff --git a/packages/expect/src/index.js b/packages/expect/src/index.js index 1c41fc8e683f..1f50866a4e67 100644 --- a/packages/expect/src/index.js +++ b/packages/expect/src/index.js @@ -251,7 +251,10 @@ const makeThrowingMatcher = ( utils, }; - const processResult = (result: SyncExpectationResult) => { + const processResult = ( + result: SyncExpectationResult, + asyncError?: JestAssertionError, + ) => { _validateResult(result); getState().assertionCalls++; @@ -264,6 +267,9 @@ const makeThrowingMatcher = ( if (err) { error = err; error.message = message; + } else if (asyncError) { + error = asyncError; + error.message = message; } else { error = new JestAssertionError(message); @@ -307,9 +313,13 @@ const makeThrowingMatcher = ( if (isPromise((potentialResult: any))) { const asyncResult = ((potentialResult: any): AsyncExpectationResult); + const asyncError = new JestAssertionError(); + if (Error.captureStackTrace) { + Error.captureStackTrace(asyncError, throwingMatcher); + } return asyncResult - .then(aResult => processResult(aResult)) + .then(aResult => processResult(aResult, asyncError)) .catch(error => handlError(error)); } else { const syncResult = ((potentialResult: any): SyncExpectationResult); From 7dda866edd6533a62b2367ef8463be3aed9fead5 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 13 Feb 2019 10:13:23 +0100 Subject: [PATCH 048/107] chore: migrate babel-jest to TypeScript (#7862) --- CHANGELOG.md | 1 + .../__snapshots__/transform.test.js.snap | 2 +- packages/babel-jest/package.json | 2 + .../src/__tests__/{index.js => index.ts} | 12 +++- .../babel-jest/src/{index.js => index.ts} | 68 ++++++++++++------- packages/babel-jest/tsconfig.json | 9 +++ packages/jest-types/package.json | 5 +- packages/jest-types/src/Transform.ts | 59 ++++++++++++++++ packages/jest-types/src/index.ts | 3 +- 9 files changed, 130 insertions(+), 31 deletions(-) rename packages/babel-jest/src/__tests__/{index.js => index.ts} (70%) rename packages/babel-jest/src/{index.js => index.ts} (68%) create mode 100644 packages/babel-jest/tsconfig.json create mode 100644 packages/jest-types/src/Transform.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ca91b9d179c4..8e24ec6d84c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ - `[jest-worker]`: Migrate to TypeScript ([#7853](https://github.com/facebook/jest/pull/7853)) - `[jest-haste-map]`: Migrate to TypeScript ([#7854](https://github.com/facebook/jest/pull/7854)) - `[docs]`: Fix image paths in SnapshotTesting.md for current and version 24 ([#7872](https://github.com/facebook/jest/pull/7872)) +- `[babel-jest]`: Migrate to TypeScript ([#7862](https://github.com/facebook/jest/pull/7862)) ### Performance diff --git a/e2e/__tests__/__snapshots__/transform.test.js.snap b/e2e/__tests__/__snapshots__/transform.test.js.snap index 791e7ec6ae94..00c81c270f78 100644 --- a/e2e/__tests__/__snapshots__/transform.test.js.snap +++ b/e2e/__tests__/__snapshots__/transform.test.js.snap @@ -6,7 +6,7 @@ FAIL __tests__/ignoredFile.test.js babel-jest: Babel ignores __tests__/ignoredFile.test.js - make sure to include the file in Jest's transformIgnorePatterns as well. - at loadBabelConfig (../../../packages/babel-jest/build/index.js:134:13) + at loadBabelConfig (../../../packages/babel-jest/build/index.js:133:13) `; exports[`babel-jest instruments only specific files and collects coverage 1`] = ` diff --git a/packages/babel-jest/package.json b/packages/babel-jest/package.json index 48c4552b1380..a827589d2319 100644 --- a/packages/babel-jest/package.json +++ b/packages/babel-jest/package.json @@ -9,7 +9,9 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { + "@jest/types": "^24.1.0", "babel-plugin-istanbul": "^5.1.0", "babel-preset-jest": "^24.1.0", "chalk": "^2.4.2", diff --git a/packages/babel-jest/src/__tests__/index.js b/packages/babel-jest/src/__tests__/index.ts similarity index 70% rename from packages/babel-jest/src/__tests__/index.js rename to packages/babel-jest/src/__tests__/index.ts index 955e231c5a06..01fbd54b92c9 100644 --- a/packages/babel-jest/src/__tests__/index.js +++ b/packages/babel-jest/src/__tests__/index.ts @@ -4,7 +4,9 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -const babelJest = require('../index'); + +import {Config, Transform} from '@jest/types'; +import babelJest from '../index'; //Mock data for all the tests const sourceString = ` @@ -24,12 +26,18 @@ const mockConfig = { }; test(`Returns source string with inline maps when no transformOptions is passed`, () => { - const result = babelJest.process(sourceString, 'dummy_path.js', mockConfig); + const result = babelJest.process( + sourceString, + 'dummy_path.js', + (mockConfig as unknown) as Config.ProjectConfig, + ) as Transform.TransformedSource; expect(typeof result).toBe('object'); expect(result.code).toBeDefined(); expect(result.map).toBeDefined(); expect(result.code).toMatch('//# sourceMappingURL'); expect(result.code).toMatch('customMultiply'); + // @ts-ignore: it's fine if we get wrong types, the tests will fail then expect(result.map.sources).toEqual(['dummy_path.js']); + // @ts-ignore: it's fine if we get wrong types, the tests will fail then expect(JSON.stringify(result.map.sourcesContent)).toMatch('customMultiply'); }); diff --git a/packages/babel-jest/src/index.js b/packages/babel-jest/src/index.ts similarity index 68% rename from packages/babel-jest/src/index.js rename to packages/babel-jest/src/index.ts index b659d715588f..ea8a5bde75cd 100644 --- a/packages/babel-jest/src/index.js +++ b/packages/babel-jest/src/index.ts @@ -3,22 +3,18 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Path, ProjectConfig} from 'types/Config'; -import type { - CacheKeyOptions, - Transformer, - TransformOptions, - TransformedSource, -} from 'types/Transform'; - import crypto from 'crypto'; import fs from 'fs'; import path from 'path'; -import {transformSync as babelTransform, loadPartialConfig} from '@babel/core'; +import {Config, Transform} from '@jest/types'; +import { + transformSync as babelTransform, + loadPartialConfig, + TransformOptions, + PartialConfig, +} from '@babel/core'; import chalk from 'chalk'; import slash from 'slash'; @@ -26,9 +22,12 @@ const THIS_FILE = fs.readFileSync(__filename); const jestPresetPath = require.resolve('babel-preset-jest'); const babelIstanbulPlugin = require.resolve('babel-plugin-istanbul'); -const createTransformer = (options: any): Transformer => { +const createTransformer = ( + options: TransformOptions = {}, +): Transform.Transformer => { options = { ...options, + // @ts-ignore: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/32955 caller: { name: 'babel-jest', supportsStaticESM: false, @@ -39,10 +38,10 @@ const createTransformer = (options: any): Transformer => { sourceMaps: 'both', }; - delete options.cacheDirectory; - delete options.filename; - - function loadBabelConfig(cwd, filename) { + function loadBabelConfig( + cwd: Config.Path, + filename: Config.Path, + ): PartialConfig { // `cwd` first to allow incoming options to override it const babelConfig = loadPartialConfig({cwd, ...options, filename}); @@ -63,9 +62,13 @@ const createTransformer = (options: any): Transformer => { canInstrument: true, getCacheKey( fileData: string, - filename: Path, + filename: Config.Path, configString: string, - {config, instrument, rootDir}: {config: ProjectConfig} & CacheKeyOptions, + { + config, + instrument, + rootDir, + }: {config: Config.ProjectConfig} & Transform.CacheKeyOptions, ): string { const babelOptions = loadBabelConfig(config.cwd, filename); const configPath = [ @@ -96,16 +99,16 @@ const createTransformer = (options: any): Transformer => { }, process( src: string, - filename: Path, - config: ProjectConfig, - transformOptions?: TransformOptions, - ): string | TransformedSource { + filename: Config.Path, + config: Config.ProjectConfig, + transformOptions?: Transform.TransformOptions, + ): string | Transform.TransformedSource { const babelOptions = {...loadBabelConfig(config.cwd, filename).options}; if (transformOptions && transformOptions.instrument) { babelOptions.auxiliaryCommentBefore = ' istanbul ignore next '; // Copied from jest-runtime transform.js - babelOptions.plugins = babelOptions.plugins.concat([ + babelOptions.plugins = (babelOptions.plugins || []).concat([ [ babelIstanbulPlugin, { @@ -119,10 +122,23 @@ const createTransformer = (options: any): Transformer => { const transformResult = babelTransform(src, babelOptions); - return transformResult || src; + if (transformResult) { + const {code, map} = transformResult; + if (typeof code === 'string') { + return {code, map}; + } + } + + return src; }, }; }; -module.exports = createTransformer(); -(module.exports: any).createTransformer = createTransformer; +const transformer = createTransformer(); + +// FIXME: This is not part of the exported TS types. When fixed, remember to +// move @types/babel__core to `dependencies` instead of `devDependencies` +// (one fix is to use ESM, maybe for Jest 25?) +transformer.createTransformer = createTransformer; + +export = transformer; diff --git a/packages/babel-jest/tsconfig.json b/packages/babel-jest/tsconfig.json new file mode 100644 index 000000000000..042289e959d6 --- /dev/null +++ b/packages/babel-jest/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + // TODO: include `babel-preset-jest` even though we don't care about its types + "references": [{"path": "../jest-types"}] +} diff --git a/packages/jest-types/package.json b/packages/jest-types/package.json index 86018bd9b822..572e2b791882 100644 --- a/packages/jest-types/package.json +++ b/packages/jest-types/package.json @@ -11,5 +11,8 @@ }, "license": "MIT", "main": "build/index.js", - "types": "build/index.d.ts" + "types": "build/index.d.ts", + "dependencies": { + "source-map": "^0.6.1" + } } diff --git a/packages/jest-types/src/Transform.ts b/packages/jest-types/src/Transform.ts new file mode 100644 index 000000000000..473434637f90 --- /dev/null +++ b/packages/jest-types/src/Transform.ts @@ -0,0 +1,59 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import {Script} from 'vm'; +import {RawSourceMap} from 'source-map'; +import {Path, ProjectConfig} from './Config'; + +// https://stackoverflow.com/a/48216010/1850276 +type Omit = Pick>; + +// This is fixed in a newer version, but that depends on Node 8 which is a +// breaking change (engine warning when installing) +interface FixedRawSourceMap extends Omit { + version: number; +} + +export type TransformedSource = { + code: string; + map?: FixedRawSourceMap | string | null; +}; + +export type TransformResult = { + script: Script; + mapCoverage: boolean; + sourceMapPath?: string; +}; + +export type TransformOptions = { + instrument: boolean; +}; + +export type CacheKeyOptions = { + config: ProjectConfig; + instrument: boolean; + rootDir: string; +}; + +export type Transformer = { + canInstrument?: boolean; + createTransformer?: (options: any) => Transformer; + + getCacheKey: ( + fileData: string, + filePath: Path, + configStr: string, + options: CacheKeyOptions, + ) => string; + + process: ( + sourceText: string, + sourcePath: Path, + config: ProjectConfig, + options?: TransformOptions, + ) => string | TransformedSource; +}; diff --git a/packages/jest-types/src/index.ts b/packages/jest-types/src/index.ts index 2c9054b64270..769fb97b881b 100644 --- a/packages/jest-types/src/index.ts +++ b/packages/jest-types/src/index.ts @@ -10,5 +10,6 @@ import * as Console from './Console'; import * as SourceMaps from './SourceMaps'; import * as TestResult from './TestResult'; import * as Mocks from './Mocks'; +import * as Transform from './Transform'; -export {Config, Console, SourceMaps, TestResult, Mocks}; +export {Config, Console, SourceMaps, TestResult, Mocks, Transform}; From b65ccaaafa67d3bd6efd05fa344b78499e06acc4 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 13 Feb 2019 10:16:08 +0100 Subject: [PATCH 049/107] chore: fix test for node 6 --- .../customMatcherStackTrace.test.js.snap | 16 ++++++++-------- .../__tests__/asynchronous.test.js | 9 ++++----- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/e2e/__tests__/__snapshots__/customMatcherStackTrace.test.js.snap b/e2e/__tests__/__snapshots__/customMatcherStackTrace.test.js.snap index 15df98763079..9baa065aa805 100644 --- a/e2e/__tests__/__snapshots__/customMatcherStackTrace.test.js.snap +++ b/e2e/__tests__/__snapshots__/customMatcherStackTrace.test.js.snap @@ -9,14 +9,14 @@ FAIL __tests__/asynchronous.test.js We expect the stack trace and code fence for this matcher to be shown in the console. 9 | - 10 | test('showing the stack trace for an async matcher', async () => { - > 11 | await expect(true).toThrowCustomAsyncMatcherError(); - | ^ - 12 | }); - 13 | - 14 | async function toThrowCustomAsyncMatcherError() { - - at Object.toThrowCustomAsyncMatcherError (__tests__/asynchronous.test.js:11:22) + 10 | test('showing the stack trace for an async matcher', () => + > 11 | expect(true).toThrowCustomAsyncMatcherError()); + | ^ + 12 | + 13 | function toThrowCustomAsyncMatcherError() { + 14 | const message = () => + + at Object.toThrowCustomAsyncMatcherError (__tests__/asynchronous.test.js:11:16) `; exports[`works with custom matchers 1`] = ` diff --git a/e2e/custom-matcher-stack-trace/__tests__/asynchronous.test.js b/e2e/custom-matcher-stack-trace/__tests__/asynchronous.test.js index 33061bffa53e..d989da108644 100644 --- a/e2e/custom-matcher-stack-trace/__tests__/asynchronous.test.js +++ b/e2e/custom-matcher-stack-trace/__tests__/asynchronous.test.js @@ -7,12 +7,11 @@ expect.extend({toThrowCustomAsyncMatcherError}); -test('showing the stack trace for an async matcher', async () => { - await expect(true).toThrowCustomAsyncMatcherError(); -}); +test('showing the stack trace for an async matcher', () => + expect(true).toThrowCustomAsyncMatcherError()); -async function toThrowCustomAsyncMatcherError() { +function toThrowCustomAsyncMatcherError() { const message = () => 'We expect the stack trace and code fence for this matcher to be shown in the console.'; - return {message, pass: false}; + return Promise.resolve({message, pass: false}); } From 2bc4d443bf0225b4e26a60bf7172c4d716cb7105 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 13 Feb 2019 12:21:11 +0100 Subject: [PATCH 050/107] chore: run typescript build as part of typecheck, not normal build (#7855) --- CHANGELOG.md | 2 +- package.json | 7 +++++-- scripts/build.js | 30 +----------------------------- scripts/buildTs.js | 38 ++++++++++++++++++++++++++++++++++++++ scripts/buildUtils.js | 39 +++++++++++++++++++++++++++++++++++++++ scripts/getPackages.js | 19 ------------------- scripts/watch.js | 7 ------- 7 files changed, 84 insertions(+), 58 deletions(-) create mode 100644 scripts/buildTs.js create mode 100644 scripts/buildUtils.js delete mode 100644 scripts/getPackages.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e24ec6d84c6..a760de897316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ ### Chore & Maintenance -- `[*]`: Setup building, linting and testing of TypeScript ([#7808](https://github.com/facebook/jest/pull/7808)) +- `[*]`: Setup building, linting and testing of TypeScript ([#7808](https://github.com/facebook/jest/pull/7808), [#7855](https://github.com/facebook/jest/pull/7855)) - `[pretty-format]`: Migrate to TypeScript ([#7809](https://github.com/facebook/jest/pull/7809)) - `[diff-sequences]`: Migrate to Typescript ([#7820](https://github.com/facebook/jest/pull/7820)) - `[jest-get-type]`: Migrate to TypeScript ([#7818](https://github.com/facebook/jest/pull/7818)) diff --git a/package.json b/package.json index 2b98f9bfca09..08349c89df70 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,9 @@ }, "scripts": { "build-clean": "rm -rf ./packages/*/build ./packages/*/build-es5", + "prebuild": "yarn build:ts", "build": "node ./scripts/build.js", + "build:ts": "node scripts/buildTs.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 '{}' +", @@ -84,7 +86,7 @@ "lint:md": "yarn --silent lint:md:ci --fix", "lint:md:ci": "prettylint '**/*.{md,yml,yaml}' --ignore-path .gitignore", "postinstall": "opencollective postinstall && yarn build", - "publish": "yarn build-clean && yarn build && lerna publish --silent", + "publish": "yarn build-clean && yarn typecheck && yarn build && lerna publish --silent", "test-ci-es5-build-in-browser": "karma start --single-run", "test-ci": "yarn jest-coverage -i --config jest.config.ci.js && yarn test-leak && node scripts/mapCoverage.js && codecov", "test-ci-partial": "yarn jest -i --config jest.config.ci.js", @@ -92,7 +94,8 @@ "test-leak": "yarn jest -i --detectLeaks jest-mock jest-diff jest-repl", "test": "yarn typecheck && yarn lint && yarn jest", "typecheck": "flow check --include-warnings", - "watch": "yarn build && node ./scripts/watch.js" + "watch": "yarn build && node ./scripts/watch.js", + "watch:ts": "yarn build:ts --watch" }, "workspaces": { "packages": [ diff --git a/scripts/build.js b/scripts/build.js index 80bf002cc4e0..2930ee8804e2 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -24,17 +24,14 @@ const fs = require('fs'); const path = require('path'); const glob = require('glob'); const mkdirp = require('mkdirp'); -const execa = require('execa'); const babel = require('@babel/core'); const chalk = require('chalk'); const micromatch = require('micromatch'); const prettier = require('prettier'); -const stringLength = require('string-length'); -const getPackages = require('./getPackages'); +const {getPackages, adjustToTerminalWidth, OK} = require('./buildUtils'); const browserBuild = require('./browserBuild'); -const OK = chalk.reset.inverse.bold.green(' DONE '); const SRC_DIR = 'src'; const BUILD_DIR = 'build'; const BUILD_ES5_DIR = 'build-es5'; @@ -51,20 +48,6 @@ const prettierConfig = prettier.resolveConfig.sync(__filename); prettierConfig.trailingComma = 'none'; prettierConfig.parser = 'babel'; -const adjustToTerminalWidth = str => { - const columns = process.stdout.columns || 80; - const WIDTH = columns - stringLength(OK) + 1; - const strs = str.match(new RegExp(`(.{1,${WIDTH}})`, 'g')); - let lastString = strs[strs.length - 1]; - if (lastString.length < WIDTH) { - lastString += Array(WIDTH - lastString.length).join(chalk.dim('.')); - } - return strs - .slice(0, -1) - .concat(lastString) - .join('\n'); -}; - function getPackageName(file) { return path.relative(PACKAGES_DIR, file).split(path.sep)[0]; } @@ -191,21 +174,10 @@ function buildFile(file, silent) { const files = process.argv.slice(2); -function compileTypes(packages) { - const packageWithTs = packages.filter(p => - fs.existsSync(path.resolve(p, 'tsconfig.json')) - ); - - execa.sync('tsc', ['-b', ...packageWithTs], {stdio: 'inherit'}); -} - if (files.length) { files.forEach(buildFile); } else { const packages = getPackages(); - process.stdout.write(chalk.inverse(' Typechecking \n')); - compileTypes(packages); - process.stdout.write(`${OK}\n\n`); process.stdout.write(chalk.inverse(' Building packages \n')); packages.forEach(buildNodePackage); process.stdout.write('\n'); diff --git a/scripts/buildTs.js b/scripts/buildTs.js new file mode 100644 index 000000000000..ec5479be5c4b --- /dev/null +++ b/scripts/buildTs.js @@ -0,0 +1,38 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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 path = require('path'); + +const chalk = require('chalk'); +const execa = require('execa'); +const {getPackages, adjustToTerminalWidth, OK} = require('./buildUtils'); + +const packages = getPackages(); + +const packagesWithTs = packages.filter(p => + fs.existsSync(path.resolve(p, 'tsconfig.json')) +); + +const args = ['-b', ...packagesWithTs, ...process.argv.slice(2)]; + +console.log(chalk.inverse('Building TypeScript definition files')); +process.stdout.write(adjustToTerminalWidth('Building\n')); + +try { + execa.sync('tsc', args, {stdio: 'inherit'}); + process.stdout.write(`${OK}\n`); +} catch (e) { + process.stdout.write('\n'); + console.error( + chalk.inverse.red('Unable to build TypeScript definition files') + ); + console.error(e.stack); + process.exitCode = 1; +} diff --git a/scripts/buildUtils.js b/scripts/buildUtils.js new file mode 100644 index 000000000000..4b04789115fb --- /dev/null +++ b/scripts/buildUtils.js @@ -0,0 +1,39 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +const fs = require('fs'); +const path = require('path'); +const chalk = require('chalk'); +const stringLength = require('string-length'); + +const PACKAGES_DIR = path.resolve(__dirname, '../packages'); + +const OK = chalk.reset.inverse.bold.green(' DONE '); + +// Get absolute paths of all directories under packages/* +module.exports.getPackages = function getPackages() { + return fs + .readdirSync(PACKAGES_DIR) + .map(file => path.resolve(PACKAGES_DIR, file)) + .filter(f => fs.lstatSync(path.resolve(f)).isDirectory()); +}; + +module.exports.adjustToTerminalWidth = function adjustToTerminalWidth(str) { + const columns = process.stdout.columns || 80; + const WIDTH = columns - stringLength(OK) + 1; + const strs = str.match(new RegExp(`(.{1,${WIDTH}})`, 'g')); + let lastString = strs[strs.length - 1]; + if (lastString.length < WIDTH) { + lastString += Array(WIDTH - lastString.length).join(chalk.dim('.')); + } + return strs + .slice(0, -1) + .concat(lastString) + .join('\n'); +}; + +module.exports.OK = OK; diff --git a/scripts/getPackages.js b/scripts/getPackages.js deleted file mode 100644 index 8c2b88dacfbf..000000000000 --- a/scripts/getPackages.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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. - */ - -const fs = require('fs'); -const path = require('path'); - -const PACKAGES_DIR = path.resolve(__dirname, '../packages'); - -// Get absolute paths of all directories under packages/* -module.exports = function getPackages() { - return fs - .readdirSync(PACKAGES_DIR) - .map(file => path.resolve(PACKAGES_DIR, file)) - .filter(f => fs.lstatSync(path.resolve(f)).isDirectory()); -}; diff --git a/scripts/watch.js b/scripts/watch.js index 542ec1c1cc87..1d010a79faea 100644 --- a/scripts/watch.js +++ b/scripts/watch.js @@ -13,7 +13,6 @@ const fs = require('fs'); const {execSync} = require('child_process'); const path = require('path'); const chalk = require('chalk'); -const execa = require('execa'); const getPackages = require('./getPackages'); const BUILD_CMD = `node ${path.resolve(__dirname, './build.js')}`; @@ -57,12 +56,6 @@ packages.forEach(p => { } }); -const packageWithTs = packages.filter(p => - fs.existsSync(path.resolve(p, 'tsconfig.json')) -); - -execa('tsc', ['-b', ...packageWithTs, '--watch'], {stdio: 'inherit'}); - setInterval(() => { const files = Array.from(filesToBuild.keys()); if (files.length) { From a986f0787b78fa5df5c9da42968c56371f7e628e Mon Sep 17 00:00:00 2001 From: Jun Wu Date: Wed, 13 Feb 2019 03:23:50 -0800 Subject: [PATCH 051/107] Improve hg revset used by jest-changed-files (#7880) --- CHANGELOG.md | 1 + packages/jest-changed-files/src/hg.ts | 13 +------------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a760de897316..075bb36680ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - `[jest-cli]` Refactor `-o` and `--coverage` combined ([#7611](https://github.com/facebook/jest/pull/7611)) - `[expect]` Fix custom async matcher stack trace ([#7652](https://github.com/facebook/jest/pull/7652)) +- `[jest-changed-files]` Improve default file selection for Mercurial repos ([#7880](https://github.com/facebook/jest/pull/7880)) ### Chore & Maintenance diff --git a/packages/jest-changed-files/src/hg.ts b/packages/jest-changed-files/src/hg.ts index 0b3b3ecf7963..96cb347a5882 100644 --- a/packages/jest-changed-files/src/hg.ts +++ b/packages/jest-changed-files/src/hg.ts @@ -13,17 +13,6 @@ import {Path, Options, SCMAdapter} from './types'; const env = {...process.env, HGPLAIN: '1'}; -const ANCESTORS = [ - // Parent commit to this one. - '.^', - - // The first commit of my branch, only if we are not on the default branch. - 'min(branch(.)) and not min(branch(default))', - - // Latest public commit. - 'max(public())', -]; - const adapter: SCMAdapter = { findChangedFiles: async ( cwd: string, @@ -33,7 +22,7 @@ const adapter: SCMAdapter = { const args = ['status', '-amnu']; if (options && options.withAncestor) { - args.push('--rev', `ancestor(${ANCESTORS.join(', ')})`); + args.push('--rev', `min(!public() & ::.)^`); } else if (options && options.changedSince) { args.push('--rev', `ancestor(., ${options.changedSince})`); } else if (options && options.lastCommit === true) { From feea117d2b30fdaab2272ccba08b7d672ceb0d99 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 13 Feb 2019 14:16:59 +0100 Subject: [PATCH 052/107] chore: use more `private` (#7882) --- packages/jest-haste-map/src/index.ts | 24 ++++---- packages/jest-mock/src/index.ts | 2 +- packages/jest-util/src/FakeTimers.ts | 56 ++++++++++--------- packages/jest-watcher/src/BaseWatchPlugin.ts | 4 +- packages/jest-watcher/src/JestHooks.ts | 2 +- packages/jest-watcher/src/PatternPrompt.ts | 8 +-- packages/jest-watcher/src/lib/Prompt.ts | 16 +++--- packages/jest-worker/src/Farm.ts | 24 ++++---- .../jest-worker/src/base/BaseWorkerPool.ts | 8 +-- packages/jest-worker/src/index.ts | 18 ++++-- .../src/workers/ChildProcessWorker.ts | 8 +-- .../src/workers/NodeThreadsWorker.ts | 8 +-- 12 files changed, 94 insertions(+), 84 deletions(-) diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index f6bd3bad058b..0bf7125abaf8 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -219,14 +219,14 @@ const getWhiteList = (list: Array | undefined): RegExp | null => { * */ class HasteMap extends EventEmitter { - _buildPromise: Promise | null; - _cachePath: Config.Path; - _changeInterval?: NodeJS.Timeout; - _console: Console; - _options: InternalOptions; - _watchers: Array; - _whitelist: RegExp | null; - _worker: WorkerInterface | null; + private _buildPromise: Promise | null; + private _cachePath: Config.Path; + private _changeInterval?: NodeJS.Timeout; + private _console: Console; + private _options: InternalOptions; + private _watchers: Array; + private _whitelist: RegExp | null; + private _worker: WorkerInterface | null; constructor(options: Options) { super(); @@ -381,7 +381,7 @@ class HasteMap extends EventEmitter { /** * 2. crawl the file system. */ - _buildFileMap(): Promise<{ + private _buildFileMap(): Promise<{ deprecatedFiles: Array<{moduleName: string; path: string}>; hasteMap: InternalHasteMap; }> { @@ -408,7 +408,7 @@ class HasteMap extends EventEmitter { /** * 3. parse and extract metadata from changed files. */ - _processFile( + private _processFile( hasteMap: InternalHasteMap, map: ModuleMapData, mocks: MockData, @@ -604,7 +604,7 @@ class HasteMap extends EventEmitter { .then(workerReply, workerError); } - _buildHasteMap(data: { + private _buildHasteMap(data: { deprecatedFiles: Array<{moduleName: string; path: string}>; hasteMap: InternalHasteMap; }): Promise { @@ -643,7 +643,7 @@ class HasteMap extends EventEmitter { }); } - _cleanup() { + private _cleanup() { const worker = this._worker; // @ts-ignore diff --git a/packages/jest-mock/src/index.ts b/packages/jest-mock/src/index.ts index 4015f09a1a16..6a620ed27efe 100644 --- a/packages/jest-mock/src/index.ts +++ b/packages/jest-mock/src/index.ts @@ -73,7 +73,7 @@ interface Mock interface SpyInstance extends MockInstance {} interface MockInstance { - _isMockFunction: boolean; + _isMockFunction: true; _protoImpl: Function; getMockName(): string; getMockImplementation(): Function | undefined; diff --git a/packages/jest-util/src/FakeTimers.ts b/packages/jest-util/src/FakeTimers.ts index a4abbeb78268..aea4916981b5 100644 --- a/packages/jest-util/src/FakeTimers.ts +++ b/packages/jest-util/src/FakeTimers.ts @@ -51,21 +51,21 @@ type TimerConfig = { const MS_IN_A_YEAR = 31536000000; export default class FakeTimers { - _cancelledImmediates!: {[key: string]: boolean}; - _cancelledTicks!: {[key: string]: boolean}; - _config: StackTraceConfig; - _disposed?: boolean; - _fakeTimerAPIs!: TimerAPI; - _global: NodeJS.Global; - _immediates!: Array; - _maxLoops: number; - _moduleMocker: ModuleMocker; - _now!: number; - _ticks!: Array; - _timerAPIs: TimerAPI; - _timers!: {[key: string]: Timer}; - _uuidCounter: number; - _timerConfig: TimerConfig; + private _cancelledImmediates!: {[key: string]: boolean}; + private _cancelledTicks!: {[key: string]: boolean}; + private _config: StackTraceConfig; + private _disposed?: boolean; + private _fakeTimerAPIs!: TimerAPI; + private _global: NodeJS.Global; + private _immediates!: Array; + private _maxLoops: number; + private _moduleMocker: ModuleMocker; + private _now!: number; + private _ticks!: Array; + private _timerAPIs: TimerAPI; + private _timers!: {[key: string]: Timer}; + private _uuidCounter: number; + private _timerConfig: TimerConfig; constructor({ global, @@ -176,7 +176,7 @@ export default class FakeTimers { } } - _runImmediate(immediate: Tick) { + private _runImmediate(immediate: Tick) { if (!this._cancelledImmediates.hasOwnProperty(immediate.uuid)) { // Callback may throw, so update the map prior calling. this._cancelledImmediates[immediate.uuid] = true; @@ -334,7 +334,7 @@ export default class FakeTimers { return Object.keys(this._timers).length; } - _checkFakeTimers() { + private _checkFakeTimers() { if (this._global.setTimeout !== this._fakeTimerAPIs.setTimeout) { this._global.console.warn( `A function to advance timers was called but the timers API is not ` + @@ -352,7 +352,7 @@ export default class FakeTimers { } } - _createMocks() { + private _createMocks() { const fn = (impl: Function) => // @ts-ignore TODO: figure out better typings here this._moduleMocker.fn().mockImplementation(impl); @@ -369,7 +369,7 @@ export default class FakeTimers { }; } - _fakeClearTimer(timerRef: TimerRef) { + private _fakeClearTimer(timerRef: TimerRef) { const uuid = this._timerConfig.refToId(timerRef); if (uuid && this._timers.hasOwnProperty(uuid)) { @@ -377,11 +377,11 @@ export default class FakeTimers { } } - _fakeClearImmediate(uuid: TimerID) { + private _fakeClearImmediate(uuid: TimerID) { this._cancelledImmediates[uuid] = true; } - _fakeNextTick(callback: Callback, ...args: Array) { + private _fakeNextTick(callback: Callback, ...args: Array) { if (this._disposed) { return; } @@ -403,7 +403,7 @@ export default class FakeTimers { }); } - _fakeSetImmediate(callback: Callback, ...args: Array) { + private _fakeSetImmediate(callback: Callback, ...args: Array) { if (this._disposed) { return null; } @@ -427,7 +427,7 @@ export default class FakeTimers { return uuid; } - _fakeSetInterval( + private _fakeSetInterval( callback: Callback, intervalDelay?: number, ...args: Array @@ -452,7 +452,11 @@ export default class FakeTimers { return this._timerConfig.idToRef(uuid); } - _fakeSetTimeout(callback: Callback, delay?: number, ...args: Array) { + private _fakeSetTimeout( + callback: Callback, + delay?: number, + ...args: Array + ) { if (this._disposed) { return null; } @@ -472,7 +476,7 @@ export default class FakeTimers { return this._timerConfig.idToRef(uuid); } - _getNextTimerHandle() { + private _getNextTimerHandle() { let nextTimerHandle = null; let uuid; let soonestTime = MS_IN_A_YEAR; @@ -488,7 +492,7 @@ export default class FakeTimers { return nextTimerHandle; } - _runTimerHandle(timerHandle: TimerID) { + private _runTimerHandle(timerHandle: TimerID) { const timer = this._timers[timerHandle]; if (!timer) { diff --git a/packages/jest-watcher/src/BaseWatchPlugin.ts b/packages/jest-watcher/src/BaseWatchPlugin.ts index f9e6d31b0bdd..3570559bcab7 100644 --- a/packages/jest-watcher/src/BaseWatchPlugin.ts +++ b/packages/jest-watcher/src/BaseWatchPlugin.ts @@ -8,8 +8,8 @@ import {WatchPlugin} from './types'; class BaseWatchPlugin implements WatchPlugin { - _stdin: NodeJS.ReadableStream; - _stdout: NodeJS.WritableStream; + protected _stdin: NodeJS.ReadableStream; + protected _stdout: NodeJS.WritableStream; constructor({ stdin, diff --git a/packages/jest-watcher/src/JestHooks.ts b/packages/jest-watcher/src/JestHooks.ts index 6525b8782038..5f8cad6b775c 100644 --- a/packages/jest-watcher/src/JestHooks.ts +++ b/packages/jest-watcher/src/JestHooks.ts @@ -19,7 +19,7 @@ type AvailableHooks = | 'shouldRunTestSuite'; class JestHooks { - _listeners: { + private _listeners: { onFileChange: Array; onTestRunComplete: Array; shouldRunTestSuite: Array; diff --git a/packages/jest-watcher/src/PatternPrompt.ts b/packages/jest-watcher/src/PatternPrompt.ts index 61593b8615d7..8663932f2b69 100644 --- a/packages/jest-watcher/src/PatternPrompt.ts +++ b/packages/jest-watcher/src/PatternPrompt.ts @@ -22,10 +22,10 @@ const usage = (entity: string) => const usageRows = usage('').split('\n').length; export default class PatternPrompt { - _pipe: NodeJS.WritableStream; - _prompt: Prompt; - _entityName: string; - _currentUsageRows: number; + protected _pipe: NodeJS.WritableStream; + protected _prompt: Prompt; + protected _entityName: string; + protected _currentUsageRows: number; constructor(pipe: NodeJS.WritableStream, prompt: Prompt) { // TODO: Should come in the constructor diff --git a/packages/jest-watcher/src/lib/Prompt.ts b/packages/jest-watcher/src/lib/Prompt.ts index 8db199284d1e..c3e26c233e87 100644 --- a/packages/jest-watcher/src/lib/Prompt.ts +++ b/packages/jest-watcher/src/lib/Prompt.ts @@ -9,14 +9,14 @@ import {ScrollOptions} from '../types'; import {KEYS} from '../constants'; export default class Prompt { - _entering: boolean; - _value: string; - _onChange: () => void; - _onSuccess: (value?: string) => void; - _onCancel: (value?: string) => void; - _offset: number; - _promptLength: number; - _selection: string | null; + private _entering: boolean; + private _value: string; + private _onChange: () => void; + private _onSuccess: (value?: string) => void; + private _onCancel: (value?: string) => void; + private _offset: number; + private _promptLength: number; + private _selection: string | null; constructor() { // Copied from `enter` to satisfy TS diff --git a/packages/jest-worker/src/Farm.ts b/packages/jest-worker/src/Farm.ts index 34b0155c8850..1006f05e94bf 100644 --- a/packages/jest-worker/src/Farm.ts +++ b/packages/jest-worker/src/Farm.ts @@ -16,14 +16,14 @@ import { } from './types'; export default class Farm { - _computeWorkerKey: FarmOptions['computeWorkerKey']; - _cacheKeys: {[key: string]: WorkerInterface}; - _callback: Function; - _last: Array; - _locks: Array; - _numOfWorkers: number; - _offset: number; - _queue: Array; + private _computeWorkerKey: FarmOptions['computeWorkerKey']; + private _cacheKeys: {[key: string]: WorkerInterface}; + private _callback: Function; + private _last: Array; + private _locks: Array; + private _numOfWorkers: number; + private _offset: number; + private _queue: Array; constructor( numOfWorkers: number, @@ -78,7 +78,7 @@ export default class Farm { }); } - _getNextJob(workerId: number): QueueChildMessage | null { + private _getNextJob(workerId: number): QueueChildMessage | null { let queueHead = this._queue[workerId]; while (queueHead && queueHead.request[1]) { @@ -90,7 +90,7 @@ export default class Farm { return queueHead; } - _process(workerId: number): Farm { + private _process(workerId: number): Farm { if (this.isLocked(workerId)) { return this; } @@ -116,7 +116,7 @@ export default class Farm { return this; } - _enqueue(task: QueueChildMessage, workerId: number): Farm { + private _enqueue(task: QueueChildMessage, workerId: number): Farm { if (task.request[1]) { return this; } @@ -133,7 +133,7 @@ export default class Farm { return this; } - _push(task: QueueChildMessage): Farm { + private _push(task: QueueChildMessage): Farm { for (let i = 0; i < this._numOfWorkers; i++) { const workerIdx = (this._offset + i) % this._numOfWorkers; this._enqueue(task, workerIdx); diff --git a/packages/jest-worker/src/base/BaseWorkerPool.ts b/packages/jest-worker/src/base/BaseWorkerPool.ts index db27a42f5492..8ce0878e105a 100644 --- a/packages/jest-worker/src/base/BaseWorkerPool.ts +++ b/packages/jest-worker/src/base/BaseWorkerPool.ts @@ -19,10 +19,10 @@ import { const emptyMethod = () => {}; export default class BaseWorkerPool { - _stderr: NodeJS.ReadableStream; - _stdout: NodeJS.ReadableStream; - _options: WorkerPoolOptions; - _workers: Array; + private readonly _stderr: NodeJS.ReadableStream; + private readonly _stdout: NodeJS.ReadableStream; + protected readonly _options: WorkerPoolOptions; + private readonly _workers: Array; constructor(workerPath: string, options: WorkerPoolOptions) { this._options = options; diff --git a/packages/jest-worker/src/index.ts b/packages/jest-worker/src/index.ts index ffef6bfebf29..812d41db2b32 100644 --- a/packages/jest-worker/src/index.ts +++ b/packages/jest-worker/src/index.ts @@ -59,10 +59,10 @@ function getExposedMethods( * caching results. */ export default class JestWorker { - _ending: boolean; - _farm: Farm; - _options: FarmOptions; - _workerPool: WorkerPoolInterface; + private _ending: boolean; + private _farm: Farm; + private _options: FarmOptions; + private _workerPool: WorkerPoolInterface; constructor(workerPath: string, options?: FarmOptions) { this._options = {...options}; @@ -95,7 +95,10 @@ export default class JestWorker { this._bindExposedWorkerMethods(workerPath, this._options); } - _bindExposedWorkerMethods(workerPath: string, options: FarmOptions): void { + private _bindExposedWorkerMethods( + workerPath: string, + options: FarmOptions, + ): void { getExposedMethods(workerPath, options).forEach(name => { if (name.startsWith('_')) { return; @@ -110,7 +113,10 @@ export default class JestWorker { }); } - _callFunctionWithArgs(method: string, ...args: Array): Promise { + private _callFunctionWithArgs( + method: string, + ...args: Array + ): Promise { if (this._ending) { throw new Error('Farm is ended, no more calls can be done to it'); } diff --git a/packages/jest-worker/src/workers/ChildProcessWorker.ts b/packages/jest-worker/src/workers/ChildProcessWorker.ts index ae3945ff0be0..d72833a5b2c3 100644 --- a/packages/jest-worker/src/workers/ChildProcessWorker.ts +++ b/packages/jest-worker/src/workers/ChildProcessWorker.ts @@ -40,10 +40,10 @@ import { * same call skip it. */ export default class ChildProcessWorker implements WorkerInterface { - _child!: ChildProcess; - _options: WorkerOptions; - _onProcessEnd!: OnEnd; - _retries!: number; + private _child!: ChildProcess; + private _options: WorkerOptions; + private _onProcessEnd!: OnEnd; + private _retries!: number; constructor(options: WorkerOptions) { this._options = options; diff --git a/packages/jest-worker/src/workers/NodeThreadsWorker.ts b/packages/jest-worker/src/workers/NodeThreadsWorker.ts index 48118eac1c28..6a96c6e72f10 100644 --- a/packages/jest-worker/src/workers/NodeThreadsWorker.ts +++ b/packages/jest-worker/src/workers/NodeThreadsWorker.ts @@ -24,10 +24,10 @@ import { } from '../types'; export default class ExperimentalWorker implements WorkerInterface { - _worker!: Worker; - _options: WorkerOptions; - _onProcessEnd!: OnEnd; - _retries!: number; + private _worker!: Worker; + private _options: WorkerOptions; + private _onProcessEnd!: OnEnd; + private _retries!: number; constructor(options: WorkerOptions) { this._options = options; From b137ffb490d786453a601dc887a8e0203bbeeb0a Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Wed, 13 Feb 2019 09:23:26 -0500 Subject: [PATCH 053/107] expect: Improve report when matcher fails, part 8 (#7876) --- CHANGELOG.md | 1 + .../__snapshots__/matchers.test.js.snap | 175 +++++++++++------- packages/expect/src/matchers.js | 54 ++++-- 3 files changed, 148 insertions(+), 82 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 075bb36680ac..5c70ac4f9656 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features - `[expect]`: Improve report when matcher fails, part 7 ([#7866](https://github.com/facebook/jest/pull/7866)) +- `[expect]`: Improve report when matcher fails, part 8 ([#7876](https://github.com/facebook/jest/pull/7876)) ### Fixes diff --git a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap index ac268c22851e..422a60a6b004 100644 --- a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap @@ -389,139 +389,178 @@ Received: true" `; exports[`.toBeCloseTo() {pass: false} expect(-Infinity)toBeCloseTo( -1.23) 1`] = ` -"expect(received).toBeCloseTo(expected) +"expect(received).toBeCloseTo(expected) -Precision: 2-digit -Expected: -1.23 -Received: -Infinity" +Expected: -1.23 +Received: -Infinity + +Expected precision: 2 +Expected difference: < 0.005 +Received difference: Infinity" `; exports[`.toBeCloseTo() {pass: false} expect(Infinity)toBeCloseTo( -Infinity) 1`] = ` -"expect(received).toBeCloseTo(expected) +"expect(received).toBeCloseTo(expected) + +Expected: -Infinity +Received: Infinity -Precision: 2-digit -Expected: -Infinity -Received: Infinity" +Expected precision: 2 +Expected difference: < 0.005 +Received difference: Infinity" `; exports[`.toBeCloseTo() {pass: false} expect(Infinity)toBeCloseTo( 1.23) 1`] = ` -"expect(received).toBeCloseTo(expected) +"expect(received).toBeCloseTo(expected) + +Expected: 1.23 +Received: Infinity -Precision: 2-digit -Expected: 1.23 -Received: Infinity" +Expected precision: 2 +Expected difference: < 0.005 +Received difference: Infinity" `; exports[`.toBeCloseTo() {pass: true} expect(-Infinity)toBeCloseTo( -Infinity) 1`] = ` -"expect(received).not.toBeCloseTo(expected) +"expect(received).not.toBeCloseTo(expected) -Precision: 2-digit -Expected: -Infinity -Received: -Infinity" +Expected: not -Infinity +" `; exports[`.toBeCloseTo() {pass: true} expect(0)toBeCloseTo( 0) 1`] = ` -"expect(received).not.toBeCloseTo(expected) +"expect(received).not.toBeCloseTo(expected) -Precision: 2-digit -Expected: 0 -Received: 0" +Expected: not 0 +" `; exports[`.toBeCloseTo() {pass: true} expect(0)toBeCloseTo( 0.001) 1`] = ` -"expect(received).not.toBeCloseTo(expected) +"expect(received).not.toBeCloseTo(expected) -Precision: 2-digit -Expected: 0.001 -Received: 0" +Expected: not 0.001 +Received: 0 + +Expected precision: 2 +Expected difference: not < 0.005 +Received difference: 0.001" `; exports[`.toBeCloseTo() {pass: true} expect(1.23)toBeCloseTo( 1.225) 1`] = ` -"expect(received).not.toBeCloseTo(expected) +"expect(received).not.toBeCloseTo(expected) + +Expected: not 1.225 +Received: 1.23 -Precision: 2-digit -Expected: 1.225 -Received: 1.23" +Expected precision: 2 +Expected difference: not < 0.005 +Received difference: 0.004999999999999893" `; exports[`.toBeCloseTo() {pass: true} expect(1.23)toBeCloseTo( 1.226) 1`] = ` -"expect(received).not.toBeCloseTo(expected) +"expect(received).not.toBeCloseTo(expected) -Precision: 2-digit -Expected: 1.226 -Received: 1.23" +Expected: not 1.226 +Received: 1.23 + +Expected precision: 2 +Expected difference: not < 0.005 +Received difference: 0.0040000000000000036" `; exports[`.toBeCloseTo() {pass: true} expect(1.23)toBeCloseTo( 1.229) 1`] = ` -"expect(received).not.toBeCloseTo(expected) +"expect(received).not.toBeCloseTo(expected) + +Expected: not 1.229 +Received: 1.23 -Precision: 2-digit -Expected: 1.229 -Received: 1.23" +Expected precision: 2 +Expected difference: not < 0.005 +Received difference: 0.0009999999999998899" `; exports[`.toBeCloseTo() {pass: true} expect(1.23)toBeCloseTo( 1.234) 1`] = ` -"expect(received).not.toBeCloseTo(expected) +"expect(received).not.toBeCloseTo(expected) -Precision: 2-digit -Expected: 1.234 -Received: 1.23" +Expected: not 1.234 +Received: 1.23 + +Expected precision: 2 +Expected difference: not < 0.005 +Received difference: 0.0040000000000000036" `; exports[`.toBeCloseTo() {pass: true} expect(Infinity)toBeCloseTo( Infinity) 1`] = ` -"expect(received).not.toBeCloseTo(expected) +"expect(received).not.toBeCloseTo(expected) -Precision: 2-digit -Expected: Infinity -Received: Infinity" +Expected: not Infinity +" `; exports[`.toBeCloseTo() accepts an optional precision argument: [0, 0.000004, 5] 1`] = ` -"expect(received).not.toBeCloseTo(expected, precision) +"expect(received).not.toBeCloseTo(expected, precision) -Precision: 5-digit -Expected: 0.000004 -Received: 0" +Expected: not 0.000004 +Received: 0 + +Expected precision: 5 +Expected difference: not < 0.000005 +Received difference: 0.000004" `; exports[`.toBeCloseTo() accepts an optional precision argument: [0, 0.0001, 3] 1`] = ` -"expect(received).not.toBeCloseTo(expected, precision) +"expect(received).not.toBeCloseTo(expected, precision) + +Expected: not 0.0001 +Received: 0 -Precision: 3-digit -Expected: 0.0001 -Received: 0" +Expected precision: 3 +Expected difference: not < 0.0005 +Received difference: 0.0001" `; exports[`.toBeCloseTo() accepts an optional precision argument: [0, 0.1, 0] 1`] = ` -"expect(received).not.toBeCloseTo(expected, precision) +"expect(received).not.toBeCloseTo(expected, precision) -Precision: 0-digit -Expected: 0.1 -Received: 0" +Expected: not 0.1 +Received: 0 + +Expected precision: 0 +Expected difference: not < 0.5 +Received difference: 0.1" `; exports[`.toBeCloseTo() throws: [0, 0.01] 1`] = ` -"expect(received).toBeCloseTo(expected) +"expect(received).toBeCloseTo(expected) + +Expected: 0.01 +Received: 0 -Precision: 2-digit -Expected: 0.01 -Received: 0" +Expected precision: 2 +Expected difference: < 0.005 +Received difference: 0.01" `; exports[`.toBeCloseTo() throws: [1, 1.23] 1`] = ` -"expect(received).toBeCloseTo(expected) +"expect(received).toBeCloseTo(expected) -Precision: 2-digit -Expected: 1.23 -Received: 1" +Expected: 1.23 +Received: 1 + +Expected precision: 2 +Expected difference: < 0.005 +Received difference: 0.22999999999999998" `; exports[`.toBeCloseTo() throws: [1.23, 1.2249999] 1`] = ` -"expect(received).toBeCloseTo(expected) +"expect(received).toBeCloseTo(expected) + +Expected: 1.2249999 +Received: 1.23 -Precision: 2-digit -Expected: 1.2249999 -Received: 1.23" +Expected precision: 2 +Expected difference: < 0.005 +Received difference: 0.005000099999999952" `; exports[`.toBeDefined(), .toBeUndefined() '"a"' is defined 1`] = ` diff --git a/packages/expect/src/matchers.js b/packages/expect/src/matchers.js index 844d6fc1b986..bc093152ca15 100644 --- a/packages/expect/src/matchers.js +++ b/packages/expect/src/matchers.js @@ -87,25 +87,51 @@ const matchers: MatchersObject = { return {actual: received, expected, message, name: 'toBe', pass}; }, - toBeCloseTo(actual: number, expected: number, precision?: number = 2) { + toBeCloseTo(received: number, expected: number, precision?: number = 2) { const secondArgument = arguments.length === 3 ? 'precision' : null; - ensureNumbers(actual, expected, '.toBeCloseTo'); + const isNot = this.isNot; + const options = { + isNot, + promise: this.promise, + secondArgument, + }; + ensureNumbers(received, expected, '.toBeCloseTo'); let pass = false; + let expectedDiff = 0; + let receivedDiff = 0; - if (actual == Infinity && expected == Infinity) pass = true; - else if (actual == -Infinity && expected == -Infinity) pass = true; - else pass = Math.abs(expected - actual) < Math.pow(10, -precision) / 2; + if (received === Infinity && expected === Infinity) { + pass = true; // Infinity - Infinity is NaN + } else if (received === -Infinity && expected === -Infinity) { + pass = true; // -Infinity - -Infinity is NaN + } else { + expectedDiff = Math.pow(10, -precision) / 2; + receivedDiff = Math.abs(expected - received); + pass = receivedDiff < expectedDiff; + } - const message = () => - matcherHint('.toBeCloseTo', undefined, undefined, { - isNot: this.isNot, - secondArgument, - }) + - '\n\n' + - `Precision: ${printExpected(precision)}-digit\n` + - `Expected: ${printExpected(expected)}\n` + - `Received: ${printReceived(actual)}`; + const message = pass + ? () => + matcherHint('toBeCloseTo', undefined, undefined, options) + + '\n\n' + + `Expected: not ${printExpected(expected)}\n` + + (receivedDiff === 0 + ? '' + : `Received: ${printReceived(received)}\n` + + '\n' + + `Expected precision: ${printExpected(precision)}\n` + + `Expected difference: not < ${printExpected(expectedDiff)}\n` + + `Received difference: ${printReceived(receivedDiff)}`) + : () => + matcherHint('toBeCloseTo', undefined, undefined, options) + + '\n\n' + + `Expected: ${printExpected(expected)}\n` + + `Received: ${printReceived(received)}\n` + + '\n' + + `Expected precision: ${printExpected(precision)}\n` + + `Expected difference: < ${printExpected(expectedDiff)}\n` + + `Received difference: ${printReceived(receivedDiff)}`; return {message, pass}; }, From 3e2d22880bf6366b4f53d31ae153a8201db85d4d Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Wed, 13 Feb 2019 19:15:10 +0100 Subject: [PATCH 054/107] chore: migrate jest-resolve to TypeScript (#7871) --- CHANGELOG.md | 1 + .../moduleNameMapper.test.js.snap | 2 +- .../resolveNoFileExtensions.test.js.snap | 2 +- package.json | 2 +- packages/jest-cli/package.json | 2 +- packages/jest-config/package.json | 2 +- packages/jest-haste-map/src/ModuleMap.ts | 10 +- packages/jest-resolve/package.json | 7 +- ...Module.test.js => isBuiltinModule.test.ts} | 1 - .../{resolve.test.js => resolve.test.ts} | 34 +++-- ...{defaultResolver.js => defaultResolver.ts} | 49 +++--- .../jest-resolve/src/{index.js => index.ts} | 140 +++++++++--------- ...{isBuiltinModule.js => isBuiltinModule.ts} | 15 +- ...odeModulesPaths.js => nodeModulesPaths.ts} | 22 ++- packages/jest-resolve/src/types.ts | 26 ++++ packages/jest-resolve/tsconfig.json | 8 + packages/jest-runtime/package.json | 2 +- yarn.lock | 8 +- 18 files changed, 180 insertions(+), 153 deletions(-) rename packages/jest-resolve/src/__tests__/{isBuiltinModule.test.js => isBuiltinModule.test.ts} (98%) rename packages/jest-resolve/src/__tests__/{resolve.test.js => resolve.test.ts} (92%) rename packages/jest-resolve/src/{defaultResolver.js => defaultResolver.ts} (80%) rename packages/jest-resolve/src/{index.js => index.ts} (83%) rename packages/jest-resolve/src/{isBuiltinModule.js => isBuiltinModule.ts} (67%) rename packages/jest-resolve/src/{nodeModulesPaths.js => nodeModulesPaths.ts} (84%) create mode 100644 packages/jest-resolve/src/types.ts create mode 100644 packages/jest-resolve/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c70ac4f9656..d486997c2912 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ - `[jest-haste-map]`: Migrate to TypeScript ([#7854](https://github.com/facebook/jest/pull/7854)) - `[docs]`: Fix image paths in SnapshotTesting.md for current and version 24 ([#7872](https://github.com/facebook/jest/pull/7872)) - `[babel-jest]`: Migrate to TypeScript ([#7862](https://github.com/facebook/jest/pull/7862)) +- `[jest-resolve]`: Migrate to TypeScript ([#7871](https://github.com/facebook/jest/pull/7871)) ### Performance diff --git a/e2e/__tests__/__snapshots__/moduleNameMapper.test.js.snap b/e2e/__tests__/__snapshots__/moduleNameMapper.test.js.snap index 312abe793d4a..687ca938186f 100644 --- a/e2e/__tests__/__snapshots__/moduleNameMapper.test.js.snap +++ b/e2e/__tests__/__snapshots__/moduleNameMapper.test.js.snap @@ -30,6 +30,6 @@ FAIL __tests__/index.js 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/index.js:436:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/index.js:434:17) at Object.require (index.js:10:1) `; diff --git a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.js.snap b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.js.snap index 407b4c3bac23..13dc33fad1c2 100644 --- a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.js.snap +++ b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.js.snap @@ -33,6 +33,6 @@ FAIL __tests__/test.js | ^ 4 | - at Resolver.resolveModule (../../packages/jest-resolve/build/index.js:203:17) + at Resolver.resolveModule (../../packages/jest-resolve/build/index.js:201:17) at Object.require (index.js:3:18) `; diff --git a/package.json b/package.json index 08349c89df70..9767523ea0a2 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "progress": "^2.0.0", "promise": "^8.0.2", "readable-stream": "^3.0.3", - "realpath-native": "^1.0.0", + "realpath-native": "^1.1.0", "resolve": "^1.4.0", "rimraf": "^2.6.2", "slash": "^2.0.0", diff --git a/packages/jest-cli/package.json b/packages/jest-cli/package.json index 90c8e788397b..bc0caf236cad 100644 --- a/packages/jest-cli/package.json +++ b/packages/jest-cli/package.json @@ -35,7 +35,7 @@ "p-each-series": "^1.0.0", "pirates": "^4.0.0", "prompts": "^2.0.1", - "realpath-native": "^1.0.0", + "realpath-native": "^1.1.0", "rimraf": "^2.5.4", "slash": "^2.0.0", "string-length": "^2.0.0", diff --git a/packages/jest-config/package.json b/packages/jest-config/package.json index c1dc8cde0f46..fb97cfa3117a 100644 --- a/packages/jest-config/package.json +++ b/packages/jest-config/package.json @@ -23,7 +23,7 @@ "jest-validate": "^24.0.0", "micromatch": "^3.1.10", "pretty-format": "^24.0.0", - "realpath-native": "^1.0.2" + "realpath-native": "^1.1.0" }, "devDependencies": { "@types/babel__core": "^7.0.4", diff --git a/packages/jest-haste-map/src/ModuleMap.ts b/packages/jest-haste-map/src/ModuleMap.ts index 07246b1babcb..8b86aa2845e5 100644 --- a/packages/jest-haste-map/src/ModuleMap.ts +++ b/packages/jest-haste-map/src/ModuleMap.ts @@ -41,9 +41,9 @@ export default class ModuleMap { getModule( name: string, - platform: string | null, - supportsNativePlatform: boolean | null, - type: HTypeValue | null, + platform?: string | null, + supportsNativePlatform?: boolean | null, + type?: HTypeValue | null, ): Config.Path | null { if (type == null) { type = H.MODULE; @@ -62,7 +62,7 @@ export default class ModuleMap { getPackage( name: string, - platform: string | null, + platform: string | null | undefined, _supportsNativePlatform: boolean | null, ): Config.Path | null { return this.getModule(name, platform, null, H.PACKAGE); @@ -111,7 +111,7 @@ export default class ModuleMap { */ private _getModuleMetadata( name: string, - platform: string | null, + platform: string | null | undefined, supportsNativePlatform: boolean, ): ModuleMetaData | null { const map = this._raw.map.get(name) || EMPTY_OBJ; diff --git a/packages/jest-resolve/package.json b/packages/jest-resolve/package.json index d44ad9c6b183..07bcc5de9bb7 100644 --- a/packages/jest-resolve/package.json +++ b/packages/jest-resolve/package.json @@ -8,15 +8,20 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { + "@jest/types": "^24.1.0", "browser-resolve": "^1.11.3", "chalk": "^2.0.1", - "realpath-native": "^1.0.0" + "realpath-native": "^1.1.0" }, "devDependencies": { "@types/browser-resolve": "^0.0.5", "jest-haste-map": "^24.0.0" }, + "peerDependencies": { + "jest-haste-map": "^24.0.0" + }, "engines": { "node": ">= 6" }, diff --git a/packages/jest-resolve/src/__tests__/isBuiltinModule.test.js b/packages/jest-resolve/src/__tests__/isBuiltinModule.test.ts similarity index 98% rename from packages/jest-resolve/src/__tests__/isBuiltinModule.test.js rename to packages/jest-resolve/src/__tests__/isBuiltinModule.test.ts index ba68e4fa1b9e..0bbbb84d732b 100644 --- a/packages/jest-resolve/src/__tests__/isBuiltinModule.test.js +++ b/packages/jest-resolve/src/__tests__/isBuiltinModule.test.ts @@ -1,5 +1,4 @@ // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. -// @flow import isBuiltinModule from '../isBuiltinModule'; diff --git a/packages/jest-resolve/src/__tests__/resolve.test.js b/packages/jest-resolve/src/__tests__/resolve.test.ts similarity index 92% rename from packages/jest-resolve/src/__tests__/resolve.test.js rename to packages/jest-resolve/src/__tests__/resolve.test.ts index d074456ca2ad..014407bc47c7 100644 --- a/packages/jest-resolve/src/__tests__/resolve.test.js +++ b/packages/jest-resolve/src/__tests__/resolve.test.ts @@ -6,16 +6,18 @@ * */ -'use strict'; - import fs from 'fs'; import path from 'path'; -import {ModuleMap} from 'jest-haste-map'; -// eslint-disable-next-line import/default +import HasteMap from 'jest-haste-map'; import Resolver from '../'; +// @ts-ignore: js file import userResolver from '../__mocks__/userResolver'; import nodeModulesPaths from '../nodeModulesPaths'; import defaultResolver from '../defaultResolver'; +import {ResolverConfig} from '../types'; + +// @ts-ignore: types are wrong. not sure how... +const {ModuleMap} = HasteMap; jest.mock('../__mocks__/userResolver'); @@ -28,21 +30,21 @@ describe('isCoreModule', () => { const moduleMap = ModuleMap.create('/'); const resolver = new Resolver(moduleMap, { hasCoreModules: false, - }); + } as ResolverConfig); const isCore = resolver.isCoreModule('assert'); expect(isCore).toEqual(false); }); it('returns true if `hasCoreModules` is true and `moduleName` is a core module.', () => { const moduleMap = ModuleMap.create('/'); - const resolver = new Resolver(moduleMap, {}); + const resolver = new Resolver(moduleMap, {} as ResolverConfig); const isCore = resolver.isCoreModule('assert'); expect(isCore).toEqual(true); }); it('returns false if `hasCoreModules` is true and `moduleName` is not a core module.', () => { const moduleMap = ModuleMap.create('/'); - const resolver = new Resolver(moduleMap, {}); + const resolver = new Resolver(moduleMap, {} as ResolverConfig); const isCore = resolver.isCoreModule('not-a-core-module'); expect(isCore).toEqual(false); }); @@ -84,7 +86,7 @@ describe('findNodeModule', () => { }); describe('resolveModule', () => { - let moduleMap; + let moduleMap: typeof ModuleMap; beforeEach(() => { moduleMap = ModuleMap.create('/'); }); @@ -92,7 +94,7 @@ describe('resolveModule', () => { it('is possible to resolve node modules', () => { const resolver = new Resolver(moduleMap, { extensions: ['.js'], - }); + } as ResolverConfig); const src = require.resolve('../'); const resolved = resolver.resolveModule( src, @@ -104,7 +106,7 @@ describe('resolveModule', () => { it('is possible to resolve node modules with custom extensions', () => { const resolver = new Resolver(moduleMap, { extensions: ['.js', '.jsx'], - }); + } as ResolverConfig); const src = require.resolve('../'); const resolvedJsx = resolver.resolveModule( src, @@ -119,7 +121,7 @@ describe('resolveModule', () => { const resolver = new Resolver(moduleMap, { extensions: ['.js', '.jsx'], platforms: ['native'], - }); + } as ResolverConfig); const src = require.resolve('../'); const resolvedJsx = resolver.resolveModule( src, @@ -133,7 +135,7 @@ describe('resolveModule', () => { it('is possible to resolve node modules by resolving their realpath', () => { const resolver = new Resolver(moduleMap, { extensions: ['.js'], - }); + } as ResolverConfig); const src = path.join( path.resolve(__dirname, '../../src/__mocks__/bar/node_modules/'), 'foo/index.js', @@ -147,7 +149,7 @@ describe('resolveModule', () => { it('is possible to specify custom resolve paths', () => { const resolver = new Resolver(moduleMap, { extensions: ['.js'], - }); + } as ResolverConfig); const src = require.resolve('../'); const resolved = resolver.resolveModule(src, 'mockJsDependency', { paths: [ @@ -173,7 +175,7 @@ describe('getMockModule', () => { }, ], resolver: require.resolve('../__mocks__/userResolver'), - }); + } as ResolverConfig); const src = require.resolve('../'); resolver.getMockModule(src, 'dependentModule'); @@ -196,7 +198,7 @@ describe('nodeModulesPaths', () => { describe('Resolver.getModulePaths() -> nodeModulesPaths()', () => { const _path = path; - let moduleMap; + let moduleMap: typeof ModuleMap; beforeEach(() => { jest.resetModules(); @@ -208,7 +210,7 @@ describe('Resolver.getModulePaths() -> nodeModulesPaths()', () => { // This test suite won't work otherwise, since we cannot make assumptions // about the test environment when it comes to absolute paths. jest.doMock('realpath-native', () => ({ - sync: dirInput => dirInput, + sync: (dirInput: string) => dirInput, })); }); diff --git a/packages/jest-resolve/src/defaultResolver.js b/packages/jest-resolve/src/defaultResolver.ts similarity index 80% rename from packages/jest-resolve/src/defaultResolver.js rename to packages/jest-resolve/src/defaultResolver.ts index 3549bf4d1933..6cda0840d98e 100644 --- a/packages/jest-resolve/src/defaultResolver.js +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -3,33 +3,29 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Path} from 'types/Config'; -import type {ErrorWithCode} from 'types/Errors'; - -import browserResolve from 'browser-resolve'; import fs from 'fs'; import path from 'path'; +import browserResolve from 'browser-resolve'; +import {Config} from '@jest/types'; import isBuiltinModule from './isBuiltinModule'; import nodeModulesPaths from './nodeModulesPaths'; -type ResolverOptions = {| - basedir: Path, - browser?: boolean, - defaultResolver: typeof defaultResolver, - extensions?: Array, - moduleDirectory?: Array, - paths?: ?Array, - rootDir: ?Path, -|}; +type ResolverOptions = { + basedir: Config.Path; + browser?: boolean; + defaultResolver: typeof defaultResolver; + extensions?: Array; + moduleDirectory?: Array; + paths?: Array; + rootDir?: Config.Path; +}; export default function defaultResolver( - path: Path, + path: Config.Path, options: ResolverOptions, -): Path { +): Config.Path { const resolve = options.browser ? browserResolve.sync : resolveSync; return resolve(path, { @@ -44,7 +40,10 @@ export default function defaultResolver( const REGEX_RELATIVE_IMPORT = /^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[\\\/])/; -function resolveSync(target: Path, options: ResolverOptions): Path { +function resolveSync( + target: Config.Path, + options: ResolverOptions, +): Config.Path { const basedir = options.basedir; const extensions = options.extensions || ['.js']; const paths = options.paths || []; @@ -75,7 +74,7 @@ function resolveSync(target: Path, options: ResolverOptions): Path { return target; } - const err: ErrorWithCode = new Error( + const err: Error & {code?: string} = new Error( "Cannot find module '" + target + "' from '" + basedir + "'", ); err.code = 'MODULE_NOT_FOUND'; @@ -84,7 +83,7 @@ function resolveSync(target: Path, options: ResolverOptions): Path { /* * contextual helper functions */ - function tryResolve(name: Path): ?Path { + function tryResolve(name: Config.Path): Config.Path | undefined { const dir = path.dirname(name); let result; if (isDirectory(dir)) { @@ -98,7 +97,7 @@ function resolveSync(target: Path, options: ResolverOptions): Path { return result; } - function resolveAsFile(name: Path): ?Path { + function resolveAsFile(name: Config.Path): Config.Path | undefined { if (isFile(name)) { return name; } @@ -113,7 +112,7 @@ function resolveSync(target: Path, options: ResolverOptions): Path { return undefined; } - function resolveAsDirectory(name: Path): ?Path { + function resolveAsDirectory(name: Config.Path): Config.Path | undefined { if (!isDirectory(name)) { return undefined; } @@ -140,7 +139,7 @@ function resolveSync(target: Path, options: ResolverOptions): Path { /* * helper functions */ -function isFile(file: Path): boolean { +function isFile(file: Config.Path): boolean { let result; try { @@ -156,7 +155,7 @@ function isFile(file: Path): boolean { return result; } -function isDirectory(dir: Path): boolean { +function isDirectory(dir: Config.Path): boolean { let result; try { @@ -172,6 +171,6 @@ function isDirectory(dir: Path): boolean { return result; } -function isCurrentDirectory(testPath: Path): boolean { +function isCurrentDirectory(testPath: Config.Path): boolean { return path.resolve('.') === path.resolve(testPath); } diff --git a/packages/jest-resolve/src/index.js b/packages/jest-resolve/src/index.ts similarity index 83% rename from packages/jest-resolve/src/index.js rename to packages/jest-resolve/src/index.ts index b75fb2b6afd0..cfc053f08001 100644 --- a/packages/jest-resolve/src/index.js +++ b/packages/jest-resolve/src/index.ts @@ -3,51 +3,34 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Path} from 'types/Config'; -import type {ModuleMap} from 'types/HasteMap'; -import type {ResolveModuleConfig} from 'types/Resolve'; -import type {ErrorWithCode} from 'types/Errors'; - import path from 'path'; +import {Config} from '@jest/types'; +import {ModuleMap} from 'jest-haste-map'; import {sync as realpath} from 'realpath-native'; +import chalk from 'chalk'; import nodeModulesPaths from './nodeModulesPaths'; import isBuiltinModule from './isBuiltinModule'; import defaultResolver from './defaultResolver'; -import chalk from 'chalk'; +import {ResolverConfig} from './types'; -type ResolverConfig = {| - browser?: boolean, - defaultPlatform: ?string, - extensions: Array, - hasCoreModules: boolean, - moduleDirectories: Array, - moduleNameMapper: ?Array, - modulePaths: Array, - platforms?: Array, - resolver: ?Path, - rootDir: ?Path, -|}; - -type FindNodeModuleConfig = {| - basedir: Path, - browser?: boolean, - extensions?: Array, - moduleDirectory?: Array, - paths?: Array, - resolver?: ?Path, - rootDir?: ?Path, -|}; - -type ModuleNameMapperConfig = {| - regex: RegExp, - moduleName: string, -|}; +type ResolveModuleConfig = { + skipNodeResolution?: boolean; + paths?: Config.Path[]; +}; -type BooleanObject = {[key: string]: boolean, __proto__: null}; +type FindNodeModuleConfig = { + basedir: Config.Path; + browser?: boolean; + extensions?: Array; + moduleDirectory?: Array; + paths?: Array; + resolver?: Config.Path; + rootDir?: Config.Path; +}; + +type BooleanObject = {[key: string]: boolean}; const NATIVE_PLATFORM = 'native'; @@ -62,12 +45,12 @@ const nodePaths = process.env.NODE_PATH : null; class Resolver { - _options: ResolverConfig; - _moduleMap: ModuleMap; - _moduleIDCache: {[key: string]: string, __proto__: null}; - _moduleNameCache: {[name: string]: Path, __proto__: null}; - _modulePathCache: {[path: Path]: Array, __proto__: null}; - _supportsNativePlatform: boolean; + private readonly _options: ResolverConfig; + private readonly _moduleMap: ModuleMap; + private readonly _moduleIDCache: {[key: string]: string}; + private readonly _moduleNameCache: {[name: string]: Config.Path}; + private readonly _modulePathCache: {[path: string]: Array}; + private readonly _supportsNativePlatform: boolean; constructor(moduleMap: ModuleMap, options: ResolverConfig) { this._options = { @@ -92,7 +75,10 @@ class Resolver { this._modulePathCache = Object.create(null); } - static findNodeModule(path: Path, options: FindNodeModuleConfig): ?Path { + static findNodeModule( + path: Config.Path, + options: FindNodeModuleConfig, + ): Config.Path | null { const resolver = options.resolver ? /* $FlowFixMe */ require(options.resolver) @@ -114,10 +100,10 @@ class Resolver { } resolveModuleFromDirIfExists( - dirname: Path, + dirname: Config.Path, moduleName: string, options?: ResolveModuleConfig, - ): ?Path { + ): Config.Path | null { const paths = (options && options.paths) || this._options.modulePaths; const moduleDirectory = this._options.moduleDirectories; const key = dirname + path.delimiter + moduleName; @@ -157,7 +143,7 @@ class Resolver { const skipResolution = options && options.skipNodeResolution && !moduleName.includes(path.sep); - const resolveNodeModule = name => + const resolveNodeModule = (name: Config.Path) => Resolver.findNodeModule(name, { basedir: dirname, browser: this._options.browser, @@ -179,7 +165,7 @@ class Resolver { // 4. Resolve "haste packages" which are `package.json` files outside of // `node_modules` folders anywhere in the file system. const parts = moduleName.split('/'); - const hastePackage = this.getPackage(parts.shift()); + const hastePackage = this.getPackage(parts.shift()!); if (hastePackage) { try { const module = path.join.apply( @@ -197,10 +183,10 @@ class Resolver { } resolveModule( - from: Path, + from: Config.Path, moduleName: string, options?: ResolveModuleConfig, - ): Path { + ): Config.Path { const dirname = path.dirname(from); const module = this.resolveModuleFromDirIfExists( dirname, @@ -213,10 +199,10 @@ class Resolver { // produces an error based on the dirname but we have the actual current // module name available. const relativePath = path.relative(dirname, from); - const err = new Error( + const err: Error & {code?: string} = new Error( `Cannot find module '${moduleName}' from '${relativePath || '.'}'`, ); - (err: ErrorWithCode).code = 'MODULE_NOT_FOUND'; + err.code = 'MODULE_NOT_FOUND'; throw err; } @@ -224,7 +210,7 @@ class Resolver { return this._options.hasCoreModules && isBuiltinModule(moduleName); } - getModule(name: string): ?Path { + getModule(name: string): Config.Path | null { return this._moduleMap.getModule( name, this._options.defaultPlatform, @@ -232,14 +218,14 @@ class Resolver { ); } - getModulePath(from: Path, moduleName: string) { + getModulePath(from: Config.Path, moduleName: string) { if (moduleName[0] !== '.' || path.isAbsolute(moduleName)) { return moduleName; } return path.normalize(path.dirname(from) + '/' + moduleName); } - getPackage(name: string): ?Path { + getPackage(name: string): Config.Path | null { return this._moduleMap.getPackage( name, this._options.defaultPlatform, @@ -247,7 +233,7 @@ class Resolver { ); } - getMockModule(from: Path, name: string): ?Path { + getMockModule(from: Config.Path, name: string): Config.Path | null { const mock = this._moduleMap.getMockModule(name); if (mock) { return mock; @@ -260,7 +246,7 @@ class Resolver { return null; } - getModulePaths(from: Path): Array { + getModulePaths(from: Config.Path): Array { if (!this._modulePathCache[from]) { const moduleDirectory = this._options.moduleDirectories; const paths = nodeModulesPaths(from, {moduleDirectory}); @@ -275,8 +261,8 @@ class Resolver { getModuleID( virtualMocks: BooleanObject, - from: Path, - _moduleName?: ?string, + from: Config.Path, + _moduleName?: string, ): string { const moduleName = _moduleName || ''; @@ -299,15 +285,15 @@ class Resolver { return (this._moduleIDCache[key] = id); } - _getModuleType(moduleName: string): 'node' | 'user' { + private _getModuleType(moduleName: string): 'node' | 'user' { return this.isCoreModule(moduleName) ? 'node' : 'user'; } - _getAbsolutePath( + private _getAbsolutePath( virtualMocks: BooleanObject, - from: Path, + from: Config.Path, moduleName: string, - ): ?string { + ): Config.Path | null { if (this.isCoreModule(moduleName)) { return moduleName; } @@ -316,17 +302,20 @@ class Resolver { : this._getVirtualMockPath(virtualMocks, from, moduleName); } - _getMockPath(from: Path, moduleName: string): ?string { + private _getMockPath( + from: Config.Path, + moduleName: string, + ): Config.Path | null { return !this.isCoreModule(moduleName) ? this.getMockModule(from, moduleName) : null; } - _getVirtualMockPath( + private _getVirtualMockPath( virtualMocks: BooleanObject, - from: Path, + from: Config.Path, moduleName: string, - ): Path { + ): Config.Path { const virtualMockPath = this.getModulePath(from, moduleName); return virtualMocks[virtualMockPath] ? virtualMockPath @@ -335,13 +324,16 @@ class Resolver { : from; } - _isModuleResolved(from: Path, moduleName: string): boolean { + private _isModuleResolved(from: Config.Path, moduleName: string): boolean { return !!( this.getModule(moduleName) || this.getMockModule(from, moduleName) ); } - resolveStubModuleName(from: Path, moduleName: string): ?Path { + resolveStubModuleName( + from: Config.Path, + moduleName: string, + ): Config.Path | null { const dirname = path.dirname(from); const paths = this._options.modulePaths; const extensions = this._options.extensions.slice(); @@ -404,11 +396,11 @@ class Resolver { } const createNoMappedModuleFoundError = ( - moduleName, - updatedName, - mappedModuleName, - regex, - resolver, + moduleName: string, + updatedName: string, + mappedModuleName: string, + regex: RegExp, + resolver: Function | string, ) => { const error = new Error( chalk.red(`${chalk.bold('Configuration error')}: @@ -430,4 +422,4 @@ Please check your configuration for these entries: return error; }; -module.exports = Resolver; +export = Resolver; diff --git a/packages/jest-resolve/src/isBuiltinModule.js b/packages/jest-resolve/src/isBuiltinModule.ts similarity index 67% rename from packages/jest-resolve/src/isBuiltinModule.js rename to packages/jest-resolve/src/isBuiltinModule.ts index bd674f65864e..c7e64c914434 100644 --- a/packages/jest-resolve/src/isBuiltinModule.js +++ b/packages/jest-resolve/src/isBuiltinModule.ts @@ -3,23 +3,20 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -// $FlowFixMe: Flow doesn't know about the `module` module -import {builtinModules} from 'module'; +import module from 'module'; -// https://github.com/facebook/flow/pull/5160 -declare var process: { - binding(type: string): {}, +// @ts-ignore: "private" api +declare const process: { + binding(type: string): {}; }; const EXPERIMENTAL_MODULES = ['worker_threads']; const BUILTIN_MODULES = new Set( - builtinModules - ? builtinModules.concat(EXPERIMENTAL_MODULES) + module.builtinModules + ? module.builtinModules.concat(EXPERIMENTAL_MODULES) : Object.keys(process.binding('natives')) .filter((module: string) => !/^internal\//.test(module)) .concat(EXPERIMENTAL_MODULES), diff --git a/packages/jest-resolve/src/nodeModulesPaths.js b/packages/jest-resolve/src/nodeModulesPaths.ts similarity index 84% rename from packages/jest-resolve/src/nodeModulesPaths.js rename to packages/jest-resolve/src/nodeModulesPaths.ts index 3e714f9d2b3d..4526660808d6 100644 --- a/packages/jest-resolve/src/nodeModulesPaths.js +++ b/packages/jest-resolve/src/nodeModulesPaths.ts @@ -5,26 +5,24 @@ * LICENSE file in the root directory of this source tree. * * Adapted from: https://github.com/substack/node-resolve - * - * @flow */ -import type {Path} from 'types/Config'; import path from 'path'; +import {Config} from '@jest/types'; import {sync as realpath} from 'realpath-native'; -type NodeModulesPathsOptions = {| - moduleDirectory?: Array, - paths?: ?Array, -|}; +type NodeModulesPathsOptions = { + moduleDirectory?: Array; + paths?: Array; +}; export default function nodeModulesPaths( - basedir: Path, + basedir: Config.Path, options: NodeModulesPathsOptions, -): Path[] { +): Config.Path[] { const modules = options && options.moduleDirectory - ? [].concat(options.moduleDirectory) + ? Array.from(options.moduleDirectory) : ['node_modules']; // ensure that `basedir` is an absolute path at this point, @@ -48,7 +46,7 @@ export default function nodeModulesPaths( physicalBasedir = basedirAbs; } - const paths = [physicalBasedir]; + const paths: Array = [physicalBasedir]; let parsed = path.parse(physicalBasedir); while (parsed.dir !== paths[paths.length - 1]) { paths.push(parsed.dir); @@ -67,7 +65,7 @@ export default function nodeModulesPaths( : path.join(prefix, aPath, moduleDir), ), ), - [], + [] as Array, ) .filter(dir => dir !== ''); diff --git a/packages/jest-resolve/src/types.ts b/packages/jest-resolve/src/types.ts new file mode 100644 index 000000000000..b765f0333107 --- /dev/null +++ b/packages/jest-resolve/src/types.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import {Config} from '@jest/types'; + +export type ResolverConfig = { + browser?: boolean; + defaultPlatform?: string; + extensions: Array; + hasCoreModules: boolean; + moduleDirectories: Array; + moduleNameMapper?: Array; + modulePaths: Array; + platforms?: Array; + resolver: Config.Path; + rootDir: Config.Path; +}; + +type ModuleNameMapperConfig = { + regex: RegExp; + moduleName: string; +}; diff --git a/packages/jest-resolve/tsconfig.json b/packages/jest-resolve/tsconfig.json new file mode 100644 index 000000000000..befa0d307f51 --- /dev/null +++ b/packages/jest-resolve/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [{"path": "../jest-types"}, {"path": "../jest-haste-map"}] +} diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index 2bfa2989ad6d..8524b6900536 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -26,7 +26,7 @@ "jest-util": "^24.0.0", "jest-validate": "^24.0.0", "micromatch": "^3.1.10", - "realpath-native": "^1.0.0", + "realpath-native": "^1.1.0", "slash": "^2.0.0", "strip-bom": "^3.0.0", "write-file-atomic": "2.4.1", diff --git a/yarn.lock b/yarn.lock index 4d6dea0f8c63..111752599f80 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11063,10 +11063,10 @@ readdirp@^2.0.0: micromatch "^3.1.10" readable-stream "^2.0.2" -realpath-native@^1.0.0, realpath-native@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.0.2.tgz#cd51ce089b513b45cf9b1516c82989b51ccc6560" - integrity sha512-+S3zTvVt9yTntFrBpm7TQmQ3tzpCrnA1a/y+3cUHAc9ZR6aIjG0WNLR+Rj79QpJktY+VeW/TQtFlQ1bzsehI8g== +realpath-native@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" + integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== dependencies: util.promisify "^1.0.0" From f3d1bb054b73b5615bdcbf30b0b44fbb7b4c601c Mon Sep 17 00:00:00 2001 From: Bruno Sampaio Date: Thu, 14 Feb 2019 13:21:35 +0100 Subject: [PATCH 055/107] Support React.memo in pretty-format (#7891) --- CHANGELOG.md | 1 + packages/pretty-format/src/__tests__/react.test.tsx | 10 ++++++++++ packages/pretty-format/src/plugins/ReactElement.ts | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d486997c2912..a82d5ef3f82d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - `[expect]`: Improve report when matcher fails, part 7 ([#7866](https://github.com/facebook/jest/pull/7866)) - `[expect]`: Improve report when matcher fails, part 8 ([#7876](https://github.com/facebook/jest/pull/7876)) +- `[pretty-format]` Support `React.memo` ([#7891](https://github.com/facebook/jest/pull/7891)) ### Fixes diff --git a/packages/pretty-format/src/__tests__/react.test.tsx b/packages/pretty-format/src/__tests__/react.test.tsx index 44d33d4b083e..0f3c3a4f173a 100644 --- a/packages/pretty-format/src/__tests__/react.test.tsx +++ b/packages/pretty-format/src/__tests__/react.test.tsx @@ -709,6 +709,16 @@ test('supports forwardRef with a child', () => { ).toEqual('\n mouse\n'); }); +test('supports memo with a child', () => { + function Dog(props: any) { + return React.createElement('div', props, props.children); + } + + expect( + formatElement(React.createElement(React.memo(Dog), null, 'cat')), + ).toEqual('\n cat\n'); +}); + test('supports context Provider with a child', () => { const {Provider} = React.createContext('test'); diff --git a/packages/pretty-format/src/plugins/ReactElement.ts b/packages/pretty-format/src/plugins/ReactElement.ts index aa05d4ebf72e..36fc15f3deee 100644 --- a/packages/pretty-format/src/plugins/ReactElement.ts +++ b/packages/pretty-format/src/plugins/ReactElement.ts @@ -19,6 +19,7 @@ const fragmentSymbol = Symbol.for('react.fragment'); const forwardRefSymbol = Symbol.for('react.forward_ref'); const providerSymbol = Symbol.for('react.provider'); const contextSymbol = Symbol.for('react.context'); +const memoSymbol = Symbol.for('react.memo'); // Given element.props.children, or subtree during recursive traversal, // return flattened array of children. @@ -60,6 +61,12 @@ const getType = (element: any) => { ? 'ForwardRef(' + functionName + ')' : 'ForwardRef'; } + + if (type.$$typeof === memoSymbol) { + const functionName = type.type.displayName || type.type.name || ''; + + return functionName !== '' ? 'Memo(' + functionName + ')' : 'Memo'; + } } return 'UNDEFINED'; }; From 0dc842a85e84229ce740f815042a11b7119cff2f Mon Sep 17 00:00:00 2001 From: Deniz Susman Date: Thu, 14 Feb 2019 17:00:13 +0300 Subject: [PATCH 056/107] Typo fix in code comment (#7897) --- packages/jest-jasmine2/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-jasmine2/src/index.js b/packages/jest-jasmine2/src/index.js index 17a5302af33f..78ae577ce84b 100644 --- a/packages/jest-jasmine2/src/index.js +++ b/packages/jest-jasmine2/src/index.js @@ -124,7 +124,7 @@ async function jasmine2( if (globalConfig.errorOnDeprecated) { installErrorOnPrivate(environment.global); } else { - // $FlowFixMe Flow seems to be confused about accessors and tries to enfoce having a `value` property. + // $FlowFixMe Flow seems to be confused about accessors and tries to enforce having a `value` property. Object.defineProperty(jasmine, 'DEFAULT_TIMEOUT_INTERVAL', { configurable: true, enumerable: true, From da14b66e1585488a63fb339fc0893a767c9ead00 Mon Sep 17 00:00:00 2001 From: ehmicky Date: Thu, 14 Feb 2019 15:01:02 +0100 Subject: [PATCH 057/107] fix: validating async functions (#7894) (#7896) ## Summary `jest-validate` distinguishes between sync and async functions, but it should not. Fixes #7894. ## Test plan ```js const { validate } = require('jest-validate') assert(validate({ name: async () => {} }, { exampleConfig: { name: () => {} } }).isValid) assert(validate({ name: () => {} }, { exampleConfig: { name: async () => {} } }).isValid) assert(validate({ name: async () => {} }, { exampleConfig: { name: async () => {} } }).isValid) assert(validate({ name: () => {} }, { exampleConfig: { name: () => {} } }).isValid) ``` --- CHANGELOG.md | 1 + .../jest-validate/src/__tests__/validate.test.js | 14 ++++++++++++++ packages/jest-validate/src/condition.js | 1 + 3 files changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a82d5ef3f82d..909f7ed9490e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - `[jest-cli]` Refactor `-o` and `--coverage` combined ([#7611](https://github.com/facebook/jest/pull/7611)) - `[expect]` Fix custom async matcher stack trace ([#7652](https://github.com/facebook/jest/pull/7652)) - `[jest-changed-files]` Improve default file selection for Mercurial repos ([#7880](https://github.com/facebook/jest/pull/7880)) +- `[jest-validate]` Fix validating async functions ([#7894](https://github.com/facebook/jest/issues/7894)) ### Chore & Maintenance diff --git a/packages/jest-validate/src/__tests__/validate.test.js b/packages/jest-validate/src/__tests__/validate.test.js index f9160817e346..936ab1ae9b7f 100644 --- a/packages/jest-validate/src/__tests__/validate.test.js +++ b/packages/jest-validate/src/__tests__/validate.test.js @@ -89,6 +89,20 @@ test('recursively omits null and undefined config values', () => { }); }); +test.each([ + [function() {}, function() {}], + [async function() {}, function() {}], + [function() {}, async function() {}], + [async function() {}, async function() {}], +])( + 'treat async and non-async functions as equivalent', + (value, exampleValue) => { + expect( + validate({name: value}, {exampleConfig: {name: exampleValue}}), + ).toEqual({hasDeprecationWarnings: false, isValid: true}); + }, +); + test('respects blacklist', () => { const warn = console.warn; console.warn = jest.fn(); diff --git a/packages/jest-validate/src/condition.js b/packages/jest-validate/src/condition.js index 100129124709..39fd9107c07c 100644 --- a/packages/jest-validate/src/condition.js +++ b/packages/jest-validate/src/condition.js @@ -15,6 +15,7 @@ function validationConditionSingle(option: any, validOption: any): boolean { return ( option === null || option === undefined || + (typeof option === 'function' && typeof validOption === 'function') || toString.call(option) === toString.call(validOption) ); } From 38698cbd65e6db2be0b5651abdb8429057a8b81f Mon Sep 17 00:00:00 2001 From: doniyor2109 Date: Thu, 14 Feb 2019 22:32:47 +0500 Subject: [PATCH 058/107] Inherit "only" mode unless there is tests with "only" mode already (#7888) --- CHANGELOG.md | 1 + e2e/__tests__/focusedTests.test.js | 17 ++++++++++++++++ e2e/focused-tests/__tests__/tests.js | 25 ++++++++++++++++++++++++ e2e/focused-tests/package.json | 1 + packages/jest-circus/src/eventHandler.js | 22 +++++++++++++++++++++ packages/jest-circus/src/utils.js | 8 +------- 6 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 e2e/__tests__/focusedTests.test.js create mode 100644 e2e/focused-tests/__tests__/tests.js create mode 100644 e2e/focused-tests/package.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 909f7ed9490e..3816ad097265 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - `[expect]` Fix custom async matcher stack trace ([#7652](https://github.com/facebook/jest/pull/7652)) - `[jest-changed-files]` Improve default file selection for Mercurial repos ([#7880](https://github.com/facebook/jest/pull/7880)) - `[jest-validate]` Fix validating async functions ([#7894](https://github.com/facebook/jest/issues/7894)) +- `[jest-circus]` Fix bug with test.only ([#7888](https://github.com/facebook/jest/pull/7888)) ### Chore & Maintenance diff --git a/e2e/__tests__/focusedTests.test.js b/e2e/__tests__/focusedTests.test.js new file mode 100644 index 000000000000..e0fe7493b78b --- /dev/null +++ b/e2e/__tests__/focusedTests.test.js @@ -0,0 +1,17 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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 {json} from '../runJest'; + +it('runs only "it.only" tests', () => { + const { + json: {numPassedTests, numPendingTests}, + } = json('focused-tests'); + expect(numPassedTests).toBe(1); + expect(numPendingTests).toBe(2); +}); diff --git a/e2e/focused-tests/__tests__/tests.js b/e2e/focused-tests/__tests__/tests.js new file mode 100644 index 000000000000..2b2bde1d831d --- /dev/null +++ b/e2e/focused-tests/__tests__/tests.js @@ -0,0 +1,25 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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'; + +/* eslint-disable jest/no-focused-tests */ + +describe('describe', () => { + it('it', () => { + expect(1).toBe(1); + }); +}); + +describe.only('describe only', () => { + it.only('it only', () => { + expect(1).toBe(1); + }); + + it('it', () => { + expect(1).toBe(1); + }); +}); diff --git a/e2e/focused-tests/package.json b/e2e/focused-tests/package.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/e2e/focused-tests/package.json @@ -0,0 +1 @@ +{} diff --git a/packages/jest-circus/src/eventHandler.js b/packages/jest-circus/src/eventHandler.js index f1b64054f693..663249c689a8 100644 --- a/packages/jest-circus/src/eventHandler.js +++ b/packages/jest-circus/src/eventHandler.js @@ -55,6 +55,28 @@ const eventHandler: EventHandler = (event, state): void => { }); } + // inherit mode from its parent describe but + // do not inherit "only" mode when there is already tests with "only" mode + const shouldInheritMode = !( + currentDescribeBlock.mode === 'only' && + currentDescribeBlock.tests.find(test => test.mode === 'only') + ); + + if (shouldInheritMode) { + currentDescribeBlock.tests.forEach(test => { + if (!test.mode) { + test.mode = currentDescribeBlock.mode; + } + }); + } + + if ( + !state.hasFocusedTests && + currentDescribeBlock.tests.some(test => test.mode === 'only') + ) { + state.hasFocusedTests = true; + } + if (currentDescribeBlock.parent) { state.currentDescribeBlock = currentDescribeBlock.parent; } diff --git a/packages/jest-circus/src/utils.js b/packages/jest-circus/src/utils.js index 80a58fc2d9cd..0c56d8619b5b 100644 --- a/packages/jest-circus/src/utils.js +++ b/packages/jest-circus/src/utils.js @@ -65,12 +65,6 @@ export const makeTest = ( timeout: ?number, asyncError: Exception, ): TestEntry => { - let _mode = mode; - if (!mode) { - // if not set explicitly, inherit from its parent describe - _mode = parent.mode; - } - const errors: Array<[?Exception, Exception]> = []; return { @@ -79,7 +73,7 @@ export const makeTest = ( errors, fn, invocations: 0, - mode: _mode, + mode, name: convertDescriptorToString(name), parent, startedAt: null, From 05b1c6b15ef11619a922753357e112c83fe0d011 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 14 Feb 2019 19:53:23 +0100 Subject: [PATCH 059/107] chore: fix import in build watch script --- scripts/watch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/watch.js b/scripts/watch.js index 1d010a79faea..b17e0379d5e3 100644 --- a/scripts/watch.js +++ b/scripts/watch.js @@ -13,7 +13,7 @@ const fs = require('fs'); const {execSync} = require('child_process'); const path = require('path'); const chalk = require('chalk'); -const getPackages = require('./getPackages'); +const {getPackages} = require('./buildUtils'); const BUILD_CMD = `node ${path.resolve(__dirname, './build.js')}`; From 1080a59c26f86fd440054fdb579b8660d0da69b9 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 14 Feb 2019 20:16:46 +0100 Subject: [PATCH 060/107] chore: upgrade @types/jest (#7901) --- package.json | 2 +- .../pretty-format/src/__tests__/setPrettyPrint.ts | 1 - yarn.lock | 15 +++++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 9767523ea0a2..109352dede9e 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "@types/babel__core": "^7.0.0", "@types/babel__generator": "^7.0.0", "@types/babel__template": "^7.0.0", - "@types/jest": "^24.0.0", + "@types/jest": "^24.0.4", "@typescript-eslint/eslint-plugin": "^1.3.0", "@typescript-eslint/parser": "^1.3.0", "ansi-regex": "^4.0.0", diff --git a/packages/pretty-format/src/__tests__/setPrettyPrint.ts b/packages/pretty-format/src/__tests__/setPrettyPrint.ts index 98fe6e9fb894..ed3c2ee4126f 100644 --- a/packages/pretty-format/src/__tests__/setPrettyPrint.ts +++ b/packages/pretty-format/src/__tests__/setPrettyPrint.ts @@ -31,7 +31,6 @@ const setPrettyPrint = (plugins: Plugins) => { `Received:\n` + ` ${this.utils.printReceived(prettyFormatted)}` : () => { - // @ts-ignore: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/32891 const diffString = this.utils.diff(expected, prettyFormatted, { expand: this.expand, }); diff --git a/yarn.lock b/yarn.lock index 111752599f80..42f57554fa8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1630,10 +1630,17 @@ "@types/istanbul-lib-coverage" "*" source-map "^0.6.1" -"@types/jest@^24.0.0": - version "24.0.0" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.0.tgz#848492026c327b3548d92be0352a545c36a21e8a" - integrity sha512-kOafJnUTnMd7/OfEO/x3I47EHswNjn+dbz9qk3mtonr1RvKT+1FGVxnxAx08I9K8Tl7j9hpoJRE7OCf+t10fng== +"@types/jest-diff@*": + version "20.0.1" + resolved "https://registry.yarnpkg.com/@types/jest-diff/-/jest-diff-20.0.1.tgz#35cc15b9c4f30a18ef21852e255fdb02f6d59b89" + integrity sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA== + +"@types/jest@^24.0.0", "@types/jest@^24.0.4": + version "24.0.4" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.4.tgz#9e91eeebd464536a2f57043fb50da29a91d1271d" + integrity sha512-7PbnoOkfxpvmPehi8hf/JapEc3nwQi2hOs3cB5SsiBiPcniT0ejrbjjEIY4bPsXlEYFuS/RiN/jpA6hrcbOokA== + dependencies: + "@types/jest-diff" "*" "@types/jsdom@^11.12.0": version "11.12.0" From 3c48eef677bf638b677408a023016f355800e527 Mon Sep 17 00:00:00 2001 From: Tony Cassara Date: Thu, 14 Feb 2019 12:43:31 -0800 Subject: [PATCH 061/107] Fixes Prototype Pollution vulnerability in Handlebars dependency (#7904) --- CHANGELOG.md | 1 + packages/jest-cli/package.json | 2 +- .../script_transformer.test.js.snap | 36 +++---- yarn.lock | 95 ++++++++++--------- 4 files changed, 70 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3816ad097265..7418adb3c9d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Fixes +- `[jest-cli]` Fix prototype pollution vulnerability in dependency ([#7904](https://github.com/facebook/jest/pull/7904)) - `[jest-cli]` Refactor `-o` and `--coverage` combined ([#7611](https://github.com/facebook/jest/pull/7611)) - `[expect]` Fix custom async matcher stack trace ([#7652](https://github.com/facebook/jest/pull/7652)) - `[jest-changed-files]` Improve default file selection for Mercurial repos ([#7880](https://github.com/facebook/jest/pull/7880)) diff --git a/packages/jest-cli/package.json b/packages/jest-cli/package.json index bc0caf236cad..98364d921fc4 100644 --- a/packages/jest-cli/package.json +++ b/packages/jest-cli/package.json @@ -11,7 +11,7 @@ "graceful-fs": "^4.1.15", "import-local": "^2.0.0", "is-ci": "^2.0.0", - "istanbul-api": "^2.0.8", + "istanbul-api": "^2.1.1", "istanbul-lib-coverage": "^2.0.2", "istanbul-lib-instrument": "^3.0.1", "istanbul-lib-source-maps": "^3.0.1", diff --git a/packages/jest-runtime/src/__tests__/__snapshots__/script_transformer.test.js.snap b/packages/jest-runtime/src/__tests__/__snapshots__/script_transformer.test.js.snap index de782a1c3c6a..d2d473e38b9f 100644 --- a/packages/jest-runtime/src/__tests__/__snapshots__/script_transformer.test.js.snap +++ b/packages/jest-runtime/src/__tests__/__snapshots__/script_transformer.test.js.snap @@ -25,12 +25,14 @@ Object { exports[`ScriptTransformer transforms a file properly 1`] = ` "({\\"Object.\\":function(module,exports,require,__dirname,__filename,global,jest){/* istanbul ignore next */ var cov_25u22311x4 = function () { - var path = \\"/fruits/banana.js\\", - hash = \\"ef12c7f3c0d46c0ac007781d50b4e524293578c6\\", - Function = function () {}.constructor, - global = new Function('return this')(), - gcv = \\"__coverage__\\", - coverageData = { + var path = \\"/fruits/banana.js\\"; + var hash = \\"ef12c7f3c0d46c0ac007781d50b4e524293578c6\\"; + + var Function = function () {}.constructor; + + var global = new Function(\\"return this\\")(); + var gcv = \\"__coverage__\\"; + var coverageData = { path: \\"/fruits/banana.js\\", statementMap: { \\"0\\": { @@ -52,8 +54,8 @@ var cov_25u22311x4 = function () { f: {}, b: {}, _coverageSchema: \\"43e27e138ebf9cfc5966b082cf9a028302ed4184\\" - }, - coverage = global[gcv] || (global[gcv] = {}); + }; + var coverage = global[gcv] || (global[gcv] = {}); if (coverage[path] && coverage[path].hash === hash) { return coverage[path]; @@ -71,12 +73,14 @@ module.exports = \\"banana\\"; exports[`ScriptTransformer transforms a file properly 2`] = ` "({\\"Object.\\":function(module,exports,require,__dirname,__filename,global,jest){/* istanbul ignore next */ var cov_23yvu8etmu = function () { - var path = \\"/fruits/kiwi.js\\", - hash = \\"90cf6273dfbcd93e4510a6c5503c2125aab1f1b8\\", - Function = function () {}.constructor, - global = new Function('return this')(), - gcv = \\"__coverage__\\", - coverageData = { + var path = \\"/fruits/kiwi.js\\"; + var hash = \\"90cf6273dfbcd93e4510a6c5503c2125aab1f1b8\\"; + + var Function = function () {}.constructor; + + var global = new Function(\\"return this\\")(); + var gcv = \\"__coverage__\\"; + var coverageData = { path: \\"/fruits/kiwi.js\\", statementMap: { \\"0\\": { @@ -136,8 +140,8 @@ var cov_23yvu8etmu = function () { }, b: {}, _coverageSchema: \\"43e27e138ebf9cfc5966b082cf9a028302ed4184\\" - }, - coverage = global[gcv] || (global[gcv] = {}); + }; + var coverage = global[gcv] || (global[gcv] = {}); if (coverage[path] && coverage[path].hash === hash) { return coverage[path]; diff --git a/yarn.lock b/yarn.lock index 42f57554fa8c..71ed496d78f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4240,7 +4240,7 @@ debug@^3.1.0: dependencies: ms "^2.1.1" -debug@^4.0.1, debug@^4.1.0: +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -6410,10 +6410,10 @@ gzip-size@3.0.0: dependencies: duplexer "^0.1.1" -handlebars@^4.0.11, handlebars@^4.0.2: - version "4.0.12" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.12.tgz#2c15c8a96d46da5e266700518ba8cb8d919d5bc5" - integrity sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA== +handlebars@^4.0.2, handlebars@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.0.tgz#0d6a6f34ff1f63cecec8423aa4169827bf787c3a" + integrity sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w== dependencies: async "^2.5.0" optimist "^0.6.1" @@ -7415,75 +7415,76 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul-api@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-2.0.8.tgz#5621503c5595e5adbbacd5ce257090417c7f55da" - integrity sha512-ITCccemErW+BhZotmyQ/ktlYTAp9r7oWfz1oxxMpgKQVTUw0NAYRbKLbOSNaInipecIKul7U7O5BfCQBBRZa3w== +istanbul-api@^2.0.8, istanbul-api@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-2.1.1.tgz#194b773f6d9cbc99a9258446848b0f988951c4d0" + integrity sha512-kVmYrehiwyeBAk/wE71tW6emzLiHGjYIiDrc8sfyty4F8M02/lrgXSm+R1kXysmF20zArvmZXjlE/mg24TVPJw== dependencies: async "^2.6.1" compare-versions "^3.2.1" fileset "^2.0.3" - istanbul-lib-coverage "^2.0.2" - istanbul-lib-hook "^2.0.2" - istanbul-lib-instrument "^3.0.1" - istanbul-lib-report "^2.0.3" - istanbul-lib-source-maps "^3.0.1" - istanbul-reports "^2.0.3" + istanbul-lib-coverage "^2.0.3" + istanbul-lib-hook "^2.0.3" + istanbul-lib-instrument "^3.1.0" + istanbul-lib-report "^2.0.4" + istanbul-lib-source-maps "^3.0.2" + istanbul-reports "^2.1.1" js-yaml "^3.12.0" make-dir "^1.3.0" + minimatch "^3.0.4" once "^1.4.0" -istanbul-lib-coverage@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.2.tgz#d5db9a7a4bb8fdbd62ec746226385987b73a8f43" - integrity sha512-4CsY730KHy12ya/YNKubrMlb7EZZVsEPhXntyRY/Cbs7HN5HdznLbI4UbvIGHgocxHx3VkGe7l6IN1lipetuGg== +istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#0b891e5ad42312c2b9488554f603795f9a2211ba" + integrity sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw== -istanbul-lib-hook@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-2.0.2.tgz#9ddd28aeac10f3bb6a4d02325e72b35044d17d3a" - integrity sha512-m0MwviQ0Av6qBNDkvKdLBxxuK6ffXo8761gE2bfT+/b+dhg8LUyQhp1nFh795LO12DpiSocuCPIRwILCsN1//Q== +istanbul-lib-hook@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz#e0e581e461c611be5d0e5ef31c5f0109759916fb" + integrity sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA== dependencies: append-transform "^1.0.0" -istanbul-lib-instrument@^3.0.0, istanbul-lib-instrument@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.0.1.tgz#dd631e117dd9891e8bf1de7bb400cb8e491363af" - integrity sha512-/LTPhh1YKXjJlb5uggsiZjJHuViIljcIsB1zqmZegIw2yQ4l8LRksRGebJrZUFVEE28ZtKzmmT50W5tpAucfJg== +istanbul-lib-instrument@^3.0.0, istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz#a2b5484a7d445f1f311e93190813fa56dfb62971" + integrity sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA== dependencies: "@babel/generator" "^7.0.0" "@babel/parser" "^7.0.0" "@babel/template" "^7.0.0" "@babel/traverse" "^7.0.0" "@babel/types" "^7.0.0" - istanbul-lib-coverage "^2.0.2" + istanbul-lib-coverage "^2.0.3" semver "^5.5.0" -istanbul-lib-report@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.3.tgz#8e22534766e9cc8e20ae96283331b4405da9dce9" - integrity sha512-25gX27Mbd3MjM41hwGl5lWcQEqaPaMP79YDFS20xuTUujItNmHgTBS3WRZvzyzLE0IAKaL+JpLrryou2WlZNMw== +istanbul-lib-report@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz#bfd324ee0c04f59119cb4f07dab157d09f24d7e4" + integrity sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA== dependencies: - istanbul-lib-coverage "^2.0.2" + istanbul-lib-coverage "^2.0.3" make-dir "^1.3.0" - supports-color "^5.4.0" + supports-color "^6.0.0" -istanbul-lib-source-maps@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.1.tgz#002936e1106c4fa49714a946e6c63c1098b52e11" - integrity sha512-DBsZMpCwCPewRCmyd0FETHtzarQK/kKejQkDPBqKPwLYQmhs2p6a7yytfVDqib7PgXGSJZNTc1b6B3jl9G8FqA== +istanbul-lib-source-maps@^3.0.1, istanbul-lib-source-maps@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz#f1e817229a9146e8424a28e5d69ba220fda34156" + integrity sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ== dependencies: - debug "^3.1.0" - istanbul-lib-coverage "^2.0.2" + debug "^4.1.1" + istanbul-lib-coverage "^2.0.3" make-dir "^1.3.0" rimraf "^2.6.2" source-map "^0.6.1" -istanbul-reports@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.0.3.tgz#332eda684c9ee891f199dfba305c3e776f55fc16" - integrity sha512-qpQ5ZWBkOatTxmTelS+HV5ybPSq7EeXmwXrPbGv7ebP+9DJOtveUcv6hCncZE4IxSAEkdmLEh3xo31SCttbApQ== +istanbul-reports@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.1.1.tgz#72ef16b4ecb9a4a7bd0e2001e00f95d1eec8afa9" + integrity sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw== dependencies: - handlebars "^4.0.11" + handlebars "^4.1.0" isurl@^1.0.0-alpha5: version "1.0.0" @@ -12318,14 +12319,14 @@ supports-color@^3.2.3: dependencies: has-flag "^1.0.0" -supports-color@^5.3.0, supports-color@^5.4.0: +supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" -supports-color@^6.1.0: +supports-color@^6.0.0, supports-color@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== From 51d5a8ab24a818a4b0e02d53a331a84efb4dd621 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Fri, 15 Feb 2019 11:03:47 +0100 Subject: [PATCH 062/107] chore: split out reporters into separate package (#7902) --- CHANGELOG.md | 1 + packages/jest-cli/package.json | 13 +---- packages/jest-cli/src/SearchSource.js | 3 +- .../jest-cli/src/SnapshotInteractiveMode.js | 3 +- packages/jest-cli/src/TestScheduler.js | 12 +++-- .../src/__tests__/TestScheduler.test.js | 6 +-- packages/jest-cli/src/__tests__/watch.test.js | 52 +++++++------------ packages/jest-cli/src/cli/index.js | 5 +- packages/jest-cli/src/watch.js | 5 +- packages/jest-reporters/.npmignore | 3 ++ packages/jest-reporters/package.json | 46 ++++++++++++++++ .../src}/Status.js | 0 .../generateEmptyCoverage.test.js.snap | 0 .../get_snapshot_status.test.js.snap | 0 .../get_snapshot_summary.test.js.snap | 0 .../notify_reporter.test.js.snap | 0 .../summary_reporter.test.js.snap | 0 .../__snapshots__/utils.test.js.snap | 0 .../src}/__tests__/coverage_reporter.test.js | 7 ++- .../src}/__tests__/coverage_worker.test.js | 4 +- .../src}/__tests__/default_reporter.test.js | 0 .../__tests__/generateEmptyCoverage.test.js | 0 .../__tests__/get_snapshot_status.test.js | 0 .../__tests__/get_snapshot_summary.test.js | 0 .../src/__tests__/notify_reporter.test.js | 18 ++----- .../src}/__tests__/summary_reporter.test.js | 0 .../src}/__tests__/utils.test.js | 0 .../src}/__tests__/verbose_reporter.test.js | 0 .../src}/base_reporter.js | 4 +- .../src}/coverage_reporter.js | 0 .../src}/coverage_worker.js | 5 +- .../src}/default_reporter.js | 0 .../src/generateEmptyCoverage.js | 0 .../src}/get_result_header.js | 0 .../src}/get_snapshot_status.js | 4 +- .../src}/get_snapshot_summary.js | 3 +- packages/jest-reporters/src/index.js | 15 ++++++ .../src}/notify_reporter.js | 2 +- .../src}/summary_reporter.js | 2 +- .../reporters => jest-reporters/src}/utils.js | 4 +- .../src}/verbose_reporter.js | 0 packages/jest-util/src/index.ts | 6 +++ packages/jest-util/src/pluralize.ts | 10 ++++ .../src/preRunMessage.ts} | 10 ++-- .../src/testPathPatternToRegExp.ts} | 7 +-- types/TestScheduler.js | 16 ++++++ 46 files changed, 163 insertions(+), 103 deletions(-) create mode 100644 packages/jest-reporters/.npmignore create mode 100644 packages/jest-reporters/package.json rename packages/{jest-cli/src/reporters => jest-reporters/src}/Status.js (100%) rename packages/{jest-cli => jest-reporters}/src/__tests__/__snapshots__/generateEmptyCoverage.test.js.snap (100%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/__tests__/__snapshots__/get_snapshot_status.test.js.snap (100%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/__tests__/__snapshots__/get_snapshot_summary.test.js.snap (100%) rename packages/{jest-cli => jest-reporters}/src/__tests__/__snapshots__/notify_reporter.test.js.snap (100%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/__tests__/__snapshots__/summary_reporter.test.js.snap (100%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/__tests__/__snapshots__/utils.test.js.snap (100%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/__tests__/coverage_reporter.test.js (99%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/__tests__/coverage_worker.test.js (91%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/__tests__/default_reporter.test.js (100%) rename packages/{jest-cli => jest-reporters}/src/__tests__/generateEmptyCoverage.test.js (100%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/__tests__/get_snapshot_status.test.js (100%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/__tests__/get_snapshot_summary.test.js (100%) rename packages/{jest-cli => jest-reporters}/src/__tests__/notify_reporter.test.js (88%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/__tests__/summary_reporter.test.js (100%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/__tests__/utils.test.js (100%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/__tests__/verbose_reporter.test.js (100%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/base_reporter.js (92%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/coverage_reporter.js (100%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/coverage_worker.js (88%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/default_reporter.js (100%) rename packages/{jest-cli => jest-reporters}/src/generateEmptyCoverage.js (100%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/get_result_header.js (100%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/get_snapshot_status.js (95%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/get_snapshot_summary.js (97%) create mode 100644 packages/jest-reporters/src/index.js rename packages/{jest-cli/src/reporters => jest-reporters/src}/notify_reporter.js (98%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/summary_reporter.js (99%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/utils.js (98%) rename packages/{jest-cli/src/reporters => jest-reporters/src}/verbose_reporter.js (100%) create mode 100644 packages/jest-util/src/pluralize.ts rename packages/{jest-cli/src/preRunMessage.js => jest-util/src/preRunMessage.ts} (65%) rename packages/{jest-cli/src/testPathPatternToRegexp.js => jest-util/src/testPathPatternToRegExp.ts} (74%) create mode 100644 types/TestScheduler.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 7418adb3c9d2..9bfdfa66e605 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ - `[docs]`: Fix image paths in SnapshotTesting.md for current and version 24 ([#7872](https://github.com/facebook/jest/pull/7872)) - `[babel-jest]`: Migrate to TypeScript ([#7862](https://github.com/facebook/jest/pull/7862)) - `[jest-resolve]`: Migrate to TypeScript ([#7871](https://github.com/facebook/jest/pull/7871)) +- `[@jest/reporter]`: New package extracted from `jest-cli` ([#7902](https://github.com/facebook/jest/pull/7902)) ### Performance diff --git a/packages/jest-cli/package.json b/packages/jest-cli/package.json index 98364d921fc4..c168519633fc 100644 --- a/packages/jest-cli/package.json +++ b/packages/jest-cli/package.json @@ -4,6 +4,7 @@ "version": "24.1.0", "main": "build/jest.js", "dependencies": { + "@jest/reporters": "^24.1.0", "ansi-escapes": "^3.0.0", "chalk": "^2.0.1", "exit": "^0.1.2", @@ -11,10 +12,6 @@ "graceful-fs": "^4.1.15", "import-local": "^2.0.0", "is-ci": "^2.0.0", - "istanbul-api": "^2.1.1", - "istanbul-lib-coverage": "^2.0.2", - "istanbul-lib-instrument": "^3.0.1", - "istanbul-lib-source-maps": "^3.0.1", "jest-changed-files": "^24.0.0", "jest-config": "^24.1.0", "jest-environment-jsdom": "^24.0.0", @@ -29,15 +26,12 @@ "jest-util": "^24.0.0", "jest-validate": "^24.0.0", "jest-watcher": "^24.0.0", - "jest-worker": "^24.0.0", "micromatch": "^3.1.10", - "node-notifier": "^5.2.1", "p-each-series": "^1.0.0", "pirates": "^4.0.0", "prompts": "^2.0.1", "realpath-native": "^1.1.0", "rimraf": "^2.5.4", - "slash": "^2.0.0", "string-length": "^2.0.0", "strip-ansi": "^5.0.0", "which": "^1.2.12", @@ -49,14 +43,11 @@ "@types/glob": "^7.1.1", "@types/graceful-fs": "^4.1.2", "@types/is-ci": "^1.1.0", - "@types/istanbul-lib-coverage": "^1.1.0", - "@types/istanbul-lib-instrument": "^1.7.2", - "@types/istanbul-lib-source-maps": "^1.2.1", "@types/micromatch": "^3.1.0", - "@types/node-notifier": "^0.0.28", "@types/prompts": "^1.2.0", "@types/rimraf": "^2.0.2", "@types/string-length": "^2.0.0", + "@types/strip-ansi": "^3.0.0", "@types/which": "^1.3.1", "@types/yargs": "^12.0.2" }, diff --git a/packages/jest-cli/src/SearchSource.js b/packages/jest-cli/src/SearchSource.js index 52d83bdb3f5a..bba5158d00e6 100644 --- a/packages/jest-cli/src/SearchSource.js +++ b/packages/jest-cli/src/SearchSource.js @@ -15,11 +15,10 @@ import type {ChangedFilesInfo} from 'types/ChangedFiles'; import path from 'path'; import micromatch from 'micromatch'; import DependencyResolver from 'jest-resolve-dependencies'; -import testPathPatternToRegExp from './testPathPatternToRegexp'; import {escapePathForRegex} from 'jest-regex-util'; import {replaceRootDirInPath} from 'jest-config'; import {buildSnapshotResolver} from 'jest-snapshot'; -import {replacePathSepForGlob} from 'jest-util'; +import {replacePathSepForGlob, testPathPatternToRegExp} from 'jest-util'; type SearchResult = {| noSCM?: boolean, diff --git a/packages/jest-cli/src/SnapshotInteractiveMode.js b/packages/jest-cli/src/SnapshotInteractiveMode.js index d21910269b29..07c2a5a8f478 100644 --- a/packages/jest-cli/src/SnapshotInteractiveMode.js +++ b/packages/jest-cli/src/SnapshotInteractiveMode.js @@ -14,8 +14,7 @@ import chalk from 'chalk'; import ansiEscapes from 'ansi-escapes'; import {KEYS} from 'jest-watcher'; -import {pluralize} from './reporters/utils'; -import {specialChars} from 'jest-util'; +import {pluralize, specialChars} from 'jest-util'; const {ARROW, CLEAR} = specialChars; diff --git a/packages/jest-cli/src/TestScheduler.js b/packages/jest-cli/src/TestScheduler.js index 28511803998a..3427f60aa576 100644 --- a/packages/jest-cli/src/TestScheduler.js +++ b/packages/jest-cli/src/TestScheduler.js @@ -19,16 +19,18 @@ import { buildFailureTestResult, makeEmptyAggregatedTestResult, } from './testResultHelpers'; -import CoverageReporter from './reporters/coverage_reporter'; -import DefaultReporter from './reporters/default_reporter'; +import { + CoverageReporter, + DefaultReporter, + NotifyReporter, + SummaryReporter, + VerboseReporter, +} from '@jest/reporters'; import exit from 'exit'; -import NotifyReporter from './reporters/notify_reporter'; import ReporterDispatcher from './ReporterDispatcher'; import snapshot from 'jest-snapshot'; -import SummaryReporter from './reporters/summary_reporter'; import TestRunner from 'jest-runner'; import TestWatcher from './TestWatcher'; -import VerboseReporter from './reporters/verbose_reporter'; import {shouldRunInBand} from './testSchedulerHelper'; // The default jest-runner is required because it is the default test runner diff --git a/packages/jest-cli/src/__tests__/TestScheduler.test.js b/packages/jest-cli/src/__tests__/TestScheduler.test.js index 37e0ec836290..d99f93de8cb2 100644 --- a/packages/jest-cli/src/__tests__/TestScheduler.test.js +++ b/packages/jest-cli/src/__tests__/TestScheduler.test.js @@ -6,13 +6,11 @@ * */ -'use strict'; - +import {SummaryReporter} from '@jest/reporters'; import TestScheduler from '../TestScheduler'; -import SummaryReporter from '../reporters/summary_reporter'; import * as testSchedulerHelper from '../testSchedulerHelper'; -jest.mock('../reporters/default_reporter'); +jest.mock('@jest/reporters'); const mockSerialRunner = { isSerial: true, runTests: jest.fn(), diff --git a/packages/jest-cli/src/__tests__/watch.test.js b/packages/jest-cli/src/__tests__/watch.test.js index 8fd2b2926157..3d480a29fc23 100644 --- a/packages/jest-cli/src/__tests__/watch.test.js +++ b/packages/jest-cli/src/__tests__/watch.test.js @@ -91,13 +91,13 @@ const regularUpdateGlobalConfig = require('../lib/update_global_config') const updateGlobalConfig = jest.fn(regularUpdateGlobalConfig); jest.doMock('../lib/update_global_config', () => updateGlobalConfig); -const watch = require('../watch').default; - const nextTick = () => new Promise(res => process.nextTick(res)); afterEach(runJestMock.mockReset); describe('Watch mode flows', () => { + let watch; + let isInteractive; let pipe; let hasteMapInstances; let globalConfig; @@ -105,6 +105,9 @@ describe('Watch mode flows', () => { let stdin; beforeEach(() => { + isInteractive = true; + jest.doMock('jest-util/build/isInteractive', () => isInteractive); + watch = require('../watch').default; const config = {roots: [], testPathIgnorePatterns: [], testRegex: []}; pipe = {write: jest.fn()}; globalConfig = {watch: true}; @@ -114,6 +117,10 @@ describe('Watch mode flows', () => { results = {snapshot: {}}; }); + afterEach(() => { + jest.resetModules(); + }); + it('Correctly passing test path pattern', () => { globalConfig.testPathPattern = 'test-*'; @@ -143,12 +150,7 @@ describe('Watch mode flows', () => { }); it('Runs Jest once by default and shows usage', () => { - jest.unmock('jest-util'); - const util = require('jest-util'); - util.isInteractive = true; - - const ci_watch = require('../watch').default; - ci_watch(globalConfig, contexts, pipe, hasteMapInstances, stdin); + watch(globalConfig, contexts, pipe, hasteMapInstances, stdin); expect(runJestMock.mock.calls[0][0]).toMatchObject({ contexts, globalConfig, @@ -160,12 +162,11 @@ describe('Watch mode flows', () => { }); it('Runs Jest in a non-interactive environment not showing usage', () => { - jest.unmock('jest-util'); - const util = require('jest-util'); - util.isInteractive = false; + jest.resetModules(); + isInteractive = false; - const ci_watch = require('../watch').default; - ci_watch(globalConfig, contexts, pipe, hasteMapInstances, stdin); + watch = require('../watch').default; + watch(globalConfig, contexts, pipe, hasteMapInstances, stdin); expect(runJestMock.mock.calls[0][0]).toMatchObject({ contexts, globalConfig, @@ -193,12 +194,7 @@ describe('Watch mode flows', () => { }); it('shows prompts for WatchPlugins in alphabetical order', async () => { - jest.unmock('jest-util'); - const util = require('jest-util'); - util.isInteractive = true; - - const ci_watch = require('../watch').default; - ci_watch( + watch( { ...globalConfig, rootDir: __dirname, @@ -223,13 +219,9 @@ describe('Watch mode flows', () => { }); it('shows update snapshot prompt (without interactive)', async () => { - jest.unmock('jest-util'); - const util = require('jest-util'); - util.isInteractive = true; results = {snapshot: {failure: true}}; - const ci_watch = require('../watch').default; - ci_watch( + watch( { ...globalConfig, rootDir: __dirname, @@ -251,9 +243,6 @@ describe('Watch mode flows', () => { }); it('shows update snapshot prompt (with interactive)', async () => { - jest.unmock('jest-util'); - const util = require('jest-util'); - util.isInteractive = true; results = { numFailedTests: 1, snapshot: { @@ -275,8 +264,7 @@ describe('Watch mode flows', () => { ], }; - const ci_watch = require('../watch').default; - ci_watch( + watch( { ...globalConfig, rootDir: __dirname, @@ -938,11 +926,7 @@ describe('Watch mode flows', () => { }); it('shows the correct usage for the f key in "only failed tests" mode', () => { - jest.unmock('jest-util'); - const util = require('jest-util'); - util.isInteractive = true; - const ci_watch = require('../watch').default; - ci_watch(globalConfig, contexts, pipe, hasteMapInstances, stdin); + watch(globalConfig, contexts, pipe, hasteMapInstances, stdin); stdin.emit('f'); stdin.emit('w'); diff --git a/packages/jest-cli/src/cli/index.js b/packages/jest-cli/src/cli/index.js index a5d766fdd490..69a3ba889d78 100644 --- a/packages/jest-cli/src/cli/index.js +++ b/packages/jest-cli/src/cli/index.js @@ -12,7 +12,7 @@ import type {Argv} from 'types/Argv'; import type {GlobalConfig, Path} from 'types/Config'; import path from 'path'; -import {Console, clearLine, createDirectory} from 'jest-util'; +import {Console, clearLine, createDirectory, preRunMessage} from 'jest-util'; import {validateCLIOptions} from 'jest-validate'; import {readConfigs, deprecationEntries} from 'jest-config'; import * as args from './args'; @@ -22,7 +22,6 @@ import exit from 'exit'; import getChangedFilesPromise from '../getChangedFilesPromise'; import {formatHandleErrors} from '../collectHandles'; import handleDeprecationWarnings from '../lib/handle_deprecation_warnings'; -import {print as preRunMessagePrint} from '../preRunMessage'; import runJest from '../runJest'; import Runtime from 'jest-runtime'; import TestWatcher from '../TestWatcher'; @@ -35,6 +34,8 @@ import init from '../lib/init'; import logDebugMessages from '../lib/log_debug_messages'; import getVersion from '../version'; +const {print: preRunMessagePrint} = preRunMessage; + export async function run(maybeArgv?: Argv, project?: Path) { try { // $FlowFixMe:`allow reduced return diff --git a/packages/jest-cli/src/watch.js b/packages/jest-cli/src/watch.js index 3a060b1474e8..f630f8b085a4 100644 --- a/packages/jest-cli/src/watch.js +++ b/packages/jest-cli/src/watch.js @@ -19,8 +19,7 @@ import exit from 'exit'; import HasteMap from 'jest-haste-map'; import {formatExecError} from 'jest-message-util'; import isValidPath from './lib/is_valid_path'; -import {isInteractive, specialChars} from 'jest-util'; -import {print as preRunMessagePrint} from './preRunMessage'; +import {isInteractive, preRunMessage, specialChars} from 'jest-util'; import createContext from './lib/create_context'; import runJest from './runJest'; import updateGlobalConfig from './lib/update_global_config'; @@ -40,6 +39,8 @@ import { import {ValidationError} from 'jest-validate'; import activeFilters from './lib/active_filters_message'; +const {print: preRunMessagePrint} = preRunMessage; + let hasExitListener = false; const INTERNAL_PLUGINS = [ diff --git a/packages/jest-reporters/.npmignore b/packages/jest-reporters/.npmignore new file mode 100644 index 000000000000..85e48fe7b0a4 --- /dev/null +++ b/packages/jest-reporters/.npmignore @@ -0,0 +1,3 @@ +**/__mocks__/** +**/__tests__/** +src diff --git a/packages/jest-reporters/package.json b/packages/jest-reporters/package.json new file mode 100644 index 000000000000..ac961627707c --- /dev/null +++ b/packages/jest-reporters/package.json @@ -0,0 +1,46 @@ +{ + "name": "@jest/reporters", + "description": "Jest's reporters", + "version": "24.1.0", + "main": "build/index.js", + "dependencies": { + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "jest-runtime": "^24.1.0", + "istanbul-api": "^2.1.1", + "istanbul-lib-coverage": "^2.0.2", + "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-source-maps": "^3.0.1", + "jest-util": "^24.0.0", + "jest-worker": "^24.0.0", + "node-notifier": "^5.2.1", + "slash": "^2.0.0", + "string-length": "^2.0.0" + }, + "devDependencies": { + "@types/exit": "^0.1.30", + "@types/glob": "^7.1.1", + "@types/istanbul-lib-coverage": "^1.1.0", + "@types/istanbul-lib-instrument": "^1.7.2", + "@types/istanbul-lib-source-maps": "^1.2.1", + "@types/node-notifier": "^0.0.28", + "@types/slash": "^2.0.0", + "@types/string-length": "^2.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">= 6" + }, + "repository": { + "type": "git", + "url": "https://github.com/facebook/jest", + "directory": "packages/jest-reporters" + }, + "bugs": { + "url": "https://github.com/facebook/jest/issues" + }, + "homepage": "https://jestjs.io/", + "license": "MIT", + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" +} diff --git a/packages/jest-cli/src/reporters/Status.js b/packages/jest-reporters/src/Status.js similarity index 100% rename from packages/jest-cli/src/reporters/Status.js rename to packages/jest-reporters/src/Status.js diff --git a/packages/jest-cli/src/__tests__/__snapshots__/generateEmptyCoverage.test.js.snap b/packages/jest-reporters/src/__tests__/__snapshots__/generateEmptyCoverage.test.js.snap similarity index 100% rename from packages/jest-cli/src/__tests__/__snapshots__/generateEmptyCoverage.test.js.snap rename to packages/jest-reporters/src/__tests__/__snapshots__/generateEmptyCoverage.test.js.snap diff --git a/packages/jest-cli/src/reporters/__tests__/__snapshots__/get_snapshot_status.test.js.snap b/packages/jest-reporters/src/__tests__/__snapshots__/get_snapshot_status.test.js.snap similarity index 100% rename from packages/jest-cli/src/reporters/__tests__/__snapshots__/get_snapshot_status.test.js.snap rename to packages/jest-reporters/src/__tests__/__snapshots__/get_snapshot_status.test.js.snap diff --git a/packages/jest-cli/src/reporters/__tests__/__snapshots__/get_snapshot_summary.test.js.snap b/packages/jest-reporters/src/__tests__/__snapshots__/get_snapshot_summary.test.js.snap similarity index 100% rename from packages/jest-cli/src/reporters/__tests__/__snapshots__/get_snapshot_summary.test.js.snap rename to packages/jest-reporters/src/__tests__/__snapshots__/get_snapshot_summary.test.js.snap diff --git a/packages/jest-cli/src/__tests__/__snapshots__/notify_reporter.test.js.snap b/packages/jest-reporters/src/__tests__/__snapshots__/notify_reporter.test.js.snap similarity index 100% rename from packages/jest-cli/src/__tests__/__snapshots__/notify_reporter.test.js.snap rename to packages/jest-reporters/src/__tests__/__snapshots__/notify_reporter.test.js.snap diff --git a/packages/jest-cli/src/reporters/__tests__/__snapshots__/summary_reporter.test.js.snap b/packages/jest-reporters/src/__tests__/__snapshots__/summary_reporter.test.js.snap similarity index 100% rename from packages/jest-cli/src/reporters/__tests__/__snapshots__/summary_reporter.test.js.snap rename to packages/jest-reporters/src/__tests__/__snapshots__/summary_reporter.test.js.snap diff --git a/packages/jest-cli/src/reporters/__tests__/__snapshots__/utils.test.js.snap b/packages/jest-reporters/src/__tests__/__snapshots__/utils.test.js.snap similarity index 100% rename from packages/jest-cli/src/reporters/__tests__/__snapshots__/utils.test.js.snap rename to packages/jest-reporters/src/__tests__/__snapshots__/utils.test.js.snap diff --git a/packages/jest-cli/src/reporters/__tests__/coverage_reporter.test.js b/packages/jest-reporters/src/__tests__/coverage_reporter.test.js similarity index 99% rename from packages/jest-cli/src/reporters/__tests__/coverage_reporter.test.js rename to packages/jest-reporters/src/__tests__/coverage_reporter.test.js index a93b1cc71749..1cebec0afdfd 100644 --- a/packages/jest-cli/src/reporters/__tests__/coverage_reporter.test.js +++ b/packages/jest-reporters/src/__tests__/coverage_reporter.test.js @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -'use strict'; jest.mock('istanbul-lib-source-maps').mock('istanbul-api'); @@ -322,7 +321,7 @@ describe('onRunComplete', () => { }); }); - test(`getLastError() returns 'undefined' when file and directory path + test(`getLastError() returns 'undefined' when file and directory path threshold groups overlap`, () => { const covThreshold = {}; [ @@ -356,8 +355,8 @@ describe('onRunComplete', () => { }); }); - test(`that if globs or paths are specified alongside global, coverage - data for matching paths will be subtracted from overall coverage + test(`that if globs or paths are specified alongside global, coverage + data for matching paths will be subtracted from overall coverage and thresholds will be applied independently`, () => { const testReporter = new CoverageReporter( { diff --git a/packages/jest-cli/src/reporters/__tests__/coverage_worker.test.js b/packages/jest-reporters/src/__tests__/coverage_worker.test.js similarity index 91% rename from packages/jest-cli/src/reporters/__tests__/coverage_worker.test.js rename to packages/jest-reporters/src/__tests__/coverage_worker.test.js index fe9d1f1bb5db..b3351ba29006 100644 --- a/packages/jest-cli/src/reporters/__tests__/coverage_worker.test.js +++ b/packages/jest-reporters/src/__tests__/coverage_worker.test.js @@ -7,7 +7,7 @@ 'use strict'; -jest.mock('fs').mock('../../generateEmptyCoverage'); +jest.mock('fs').mock('../generateEmptyCoverage'); const globalConfig = {collectCoverage: true}; const config = {}; @@ -21,7 +21,7 @@ beforeEach(() => { jest.resetModules(); fs = require('fs'); - generateEmptyCoverage = require('../../generateEmptyCoverage').default; + generateEmptyCoverage = require('../generateEmptyCoverage').default; worker = require('../coverage_worker').worker; }); diff --git a/packages/jest-cli/src/reporters/__tests__/default_reporter.test.js b/packages/jest-reporters/src/__tests__/default_reporter.test.js similarity index 100% rename from packages/jest-cli/src/reporters/__tests__/default_reporter.test.js rename to packages/jest-reporters/src/__tests__/default_reporter.test.js diff --git a/packages/jest-cli/src/__tests__/generateEmptyCoverage.test.js b/packages/jest-reporters/src/__tests__/generateEmptyCoverage.test.js similarity index 100% rename from packages/jest-cli/src/__tests__/generateEmptyCoverage.test.js rename to packages/jest-reporters/src/__tests__/generateEmptyCoverage.test.js diff --git a/packages/jest-cli/src/reporters/__tests__/get_snapshot_status.test.js b/packages/jest-reporters/src/__tests__/get_snapshot_status.test.js similarity index 100% rename from packages/jest-cli/src/reporters/__tests__/get_snapshot_status.test.js rename to packages/jest-reporters/src/__tests__/get_snapshot_status.test.js diff --git a/packages/jest-cli/src/reporters/__tests__/get_snapshot_summary.test.js b/packages/jest-reporters/src/__tests__/get_snapshot_summary.test.js similarity index 100% rename from packages/jest-cli/src/reporters/__tests__/get_snapshot_summary.test.js rename to packages/jest-reporters/src/__tests__/get_snapshot_summary.test.js diff --git a/packages/jest-cli/src/__tests__/notify_reporter.test.js b/packages/jest-reporters/src/__tests__/notify_reporter.test.js similarity index 88% rename from packages/jest-cli/src/__tests__/notify_reporter.test.js rename to packages/jest-reporters/src/__tests__/notify_reporter.test.js index 424b6f212d1a..274a2ca5d21c 100644 --- a/packages/jest-cli/src/__tests__/notify_reporter.test.js +++ b/packages/jest-reporters/src/__tests__/notify_reporter.test.js @@ -6,14 +6,11 @@ * */ -'use strict'; - -import TestScheduler from '../TestScheduler'; -import NotifyReporter from '../reporters/notify_reporter'; -import type {TestSchedulerContext} from '../TestScheduler'; +import type {TestSchedulerContext} from 'types/TestScheduler'; +import NotifyReporter from '../notify_reporter'; import type {AggregatedResult} from '../../../../types/TestResult'; -jest.mock('../reporters/default_reporter'); +jest.mock('../default_reporter'); jest.mock('node-notifier', () => ({ notify: jest.fn(), })); @@ -68,15 +65,6 @@ const notifyEvents = [ aggregatedResultsFailure, ]; -test('.addReporter() .removeReporter()', () => { - const scheduler = new TestScheduler({}, {}, {...initialContext}); - const reporter = new NotifyReporter(); - scheduler.addReporter(reporter); - expect(scheduler._dispatcher._reporters).toContain(reporter); - scheduler.removeReporter(NotifyReporter); - expect(scheduler._dispatcher._reporters).not.toContain(reporter); -}); - const testModes = ({notifyMode, arl, rootDir, moduleName}) => { const notify = require('node-notifier'); diff --git a/packages/jest-cli/src/reporters/__tests__/summary_reporter.test.js b/packages/jest-reporters/src/__tests__/summary_reporter.test.js similarity index 100% rename from packages/jest-cli/src/reporters/__tests__/summary_reporter.test.js rename to packages/jest-reporters/src/__tests__/summary_reporter.test.js diff --git a/packages/jest-cli/src/reporters/__tests__/utils.test.js b/packages/jest-reporters/src/__tests__/utils.test.js similarity index 100% rename from packages/jest-cli/src/reporters/__tests__/utils.test.js rename to packages/jest-reporters/src/__tests__/utils.test.js diff --git a/packages/jest-cli/src/reporters/__tests__/verbose_reporter.test.js b/packages/jest-reporters/src/__tests__/verbose_reporter.test.js similarity index 100% rename from packages/jest-cli/src/reporters/__tests__/verbose_reporter.test.js rename to packages/jest-reporters/src/__tests__/verbose_reporter.test.js diff --git a/packages/jest-cli/src/reporters/base_reporter.js b/packages/jest-reporters/src/base_reporter.js similarity index 92% rename from packages/jest-cli/src/reporters/base_reporter.js rename to packages/jest-reporters/src/base_reporter.js index 15e4bb613ebe..9cd86e89fb6a 100644 --- a/packages/jest-cli/src/reporters/base_reporter.js +++ b/packages/jest-reporters/src/base_reporter.js @@ -12,7 +12,9 @@ import type {Context} from 'types/Context'; import type {Test} from 'types/TestRunner'; import type {ReporterOnStartOptions} from 'types/Reporters'; -import {remove as preRunMessageRemove} from '../preRunMessage'; +import {preRunMessage} from 'jest-util'; + +const {remove: preRunMessageRemove} = preRunMessage; export default class BaseReporter { _error: ?Error; diff --git a/packages/jest-cli/src/reporters/coverage_reporter.js b/packages/jest-reporters/src/coverage_reporter.js similarity index 100% rename from packages/jest-cli/src/reporters/coverage_reporter.js rename to packages/jest-reporters/src/coverage_reporter.js diff --git a/packages/jest-cli/src/reporters/coverage_worker.js b/packages/jest-reporters/src/coverage_worker.js similarity index 88% rename from packages/jest-cli/src/reporters/coverage_worker.js rename to packages/jest-reporters/src/coverage_worker.js index 6a7feded80e1..13c41e4dceea 100644 --- a/packages/jest-cli/src/reporters/coverage_worker.js +++ b/packages/jest-reporters/src/coverage_worker.js @@ -12,8 +12,9 @@ import type {CoverageReporterOptions} from './coverage_reporter'; import exit from 'exit'; import fs from 'fs'; -import generateEmptyCoverage from '../generateEmptyCoverage'; -import type {CoverageWorkerResult} from '../generateEmptyCoverage'; +import generateEmptyCoverage, { + type CoverageWorkerResult, +} from './generateEmptyCoverage'; export type CoverageWorkerData = {| globalConfig: GlobalConfig, diff --git a/packages/jest-cli/src/reporters/default_reporter.js b/packages/jest-reporters/src/default_reporter.js similarity index 100% rename from packages/jest-cli/src/reporters/default_reporter.js rename to packages/jest-reporters/src/default_reporter.js diff --git a/packages/jest-cli/src/generateEmptyCoverage.js b/packages/jest-reporters/src/generateEmptyCoverage.js similarity index 100% rename from packages/jest-cli/src/generateEmptyCoverage.js rename to packages/jest-reporters/src/generateEmptyCoverage.js diff --git a/packages/jest-cli/src/reporters/get_result_header.js b/packages/jest-reporters/src/get_result_header.js similarity index 100% rename from packages/jest-cli/src/reporters/get_result_header.js rename to packages/jest-reporters/src/get_result_header.js diff --git a/packages/jest-cli/src/reporters/get_snapshot_status.js b/packages/jest-reporters/src/get_snapshot_status.js similarity index 95% rename from packages/jest-cli/src/reporters/get_snapshot_status.js rename to packages/jest-reporters/src/get_snapshot_status.js index b4822af53578..8406fa3fa3a2 100644 --- a/packages/jest-cli/src/reporters/get_snapshot_status.js +++ b/packages/jest-reporters/src/get_snapshot_status.js @@ -9,9 +9,9 @@ import type {TestResult} from 'types/TestResult'; -const chalk = require('chalk'); +import chalk from 'chalk'; -const {pluralize} = require('./utils'); +import {pluralize} from 'jest-util'; const ARROW = ' \u203A '; const DOT = ' \u2022 '; diff --git a/packages/jest-cli/src/reporters/get_snapshot_summary.js b/packages/jest-reporters/src/get_snapshot_summary.js similarity index 97% rename from packages/jest-cli/src/reporters/get_snapshot_summary.js rename to packages/jest-reporters/src/get_snapshot_summary.js index b5a3a63cb891..60eea8aa0cab 100644 --- a/packages/jest-cli/src/reporters/get_snapshot_summary.js +++ b/packages/jest-reporters/src/get_snapshot_summary.js @@ -11,7 +11,8 @@ import type {SnapshotSummary} from 'types/TestResult'; import type {GlobalConfig} from 'types/Config'; import chalk from 'chalk'; -import {formatTestPath, pluralize} from './utils'; +import {pluralize} from 'jest-util'; +import {formatTestPath} from './utils'; const ARROW = ' \u203A '; const DOWN_ARROW = ' \u21B3 '; diff --git a/packages/jest-reporters/src/index.js b/packages/jest-reporters/src/index.js new file mode 100644 index 000000000000..c714b0cce89c --- /dev/null +++ b/packages/jest-reporters/src/index.js @@ -0,0 +1,15 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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 as BaseReporter} from './base_reporter'; +export {default as CoverageReporter} from './coverage_reporter'; +export {default as DefaultReporter} from './default_reporter'; +export {default as NotifyReporter} from './notify_reporter'; +export {default as SummaryReporter} from './summary_reporter'; +export {default as VerboseReporter} from './verbose_reporter'; diff --git a/packages/jest-cli/src/reporters/notify_reporter.js b/packages/jest-reporters/src/notify_reporter.js similarity index 98% rename from packages/jest-cli/src/reporters/notify_reporter.js rename to packages/jest-reporters/src/notify_reporter.js index 3ab911c12844..9a6c9a27f3cb 100644 --- a/packages/jest-cli/src/reporters/notify_reporter.js +++ b/packages/jest-reporters/src/notify_reporter.js @@ -9,6 +9,7 @@ import type {GlobalConfig} from 'types/Config'; import type {AggregatedResult} from 'types/TestResult'; +import type {TestSchedulerContext} from 'types/TestScheduler'; import type {Context} from 'types/Context'; import exit from 'exit'; @@ -16,7 +17,6 @@ import path from 'path'; import util from 'util'; import notifier from 'node-notifier'; import BaseReporter from './base_reporter'; -import type {TestSchedulerContext} from '../TestScheduler'; const isDarwin = process.platform === 'darwin'; diff --git a/packages/jest-cli/src/reporters/summary_reporter.js b/packages/jest-reporters/src/summary_reporter.js similarity index 99% rename from packages/jest-cli/src/reporters/summary_reporter.js rename to packages/jest-reporters/src/summary_reporter.js index d51a1a6eef6a..863ab6e0db41 100644 --- a/packages/jest-cli/src/reporters/summary_reporter.js +++ b/packages/jest-reporters/src/summary_reporter.js @@ -13,11 +13,11 @@ import type {Context} from 'types/Context'; import type {ReporterOnStartOptions} from 'types/Reporters'; import chalk from 'chalk'; +import {testPathPatternToRegExp} from 'jest-util'; import BaseReporter from './base_reporter'; import {getSummary} from './utils'; import getResultHeader from './get_result_header'; import getSnapshotSummary from './get_snapshot_summary'; -import testPathPatternToRegExp from '../testPathPatternToRegexp'; const TEST_SUMMARY_THRESHOLD = 20; diff --git a/packages/jest-cli/src/reporters/utils.js b/packages/jest-reporters/src/utils.js similarity index 98% rename from packages/jest-cli/src/reporters/utils.js rename to packages/jest-reporters/src/utils.js index 997ca3a0a3a7..f2f17e52f0a2 100644 --- a/packages/jest-cli/src/reporters/utils.js +++ b/packages/jest-reporters/src/utils.js @@ -13,6 +13,7 @@ import type {AggregatedResult} from 'types/TestResult'; import path from 'path'; import chalk from 'chalk'; import slash from 'slash'; +import {pluralize} from 'jest-util'; type SummaryOptions = {| estimatedTime?: number, @@ -92,9 +93,6 @@ export const relativePath = ( return {basename, dirname}; }; -export const pluralize = (word: string, count: number) => - `${count} ${word}${count === 1 ? '' : 's'}`; - export const getSummary = ( aggregatedResults: AggregatedResult, options?: SummaryOptions, diff --git a/packages/jest-cli/src/reporters/verbose_reporter.js b/packages/jest-reporters/src/verbose_reporter.js similarity index 100% rename from packages/jest-cli/src/reporters/verbose_reporter.js rename to packages/jest-reporters/src/verbose_reporter.js diff --git a/packages/jest-util/src/index.ts b/packages/jest-util/src/index.ts index 5ea75756a5c5..66c38c2b63e0 100644 --- a/packages/jest-util/src/index.ts +++ b/packages/jest-util/src/index.ts @@ -23,6 +23,9 @@ import deepCyclicCopy from './deepCyclicCopy'; import convertDescriptorToString from './convertDescriptorToString'; import * as specialChars from './specialChars'; import replacePathSepForGlob from './replacePathSepForGlob'; +import testPathPatternToRegExp from './testPathPatternToRegExp'; +import * as preRunMessage from './preRunMessage'; +import pluralize from './pluralize'; export = { BufferedConsole, @@ -40,7 +43,10 @@ export = { getFailedSnapshotTests, installCommonGlobals, isInteractive, + pluralize, + preRunMessage, replacePathSepForGlob, setGlobal, specialChars, + testPathPatternToRegExp, }; diff --git a/packages/jest-util/src/pluralize.ts b/packages/jest-util/src/pluralize.ts new file mode 100644 index 000000000000..95f29391cc5a --- /dev/null +++ b/packages/jest-util/src/pluralize.ts @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +export default function pluralize(word: string, count: number) { + return `${count} ${word}${count === 1 ? '' : 's'}`; +} diff --git a/packages/jest-cli/src/preRunMessage.js b/packages/jest-util/src/preRunMessage.ts similarity index 65% rename from packages/jest-cli/src/preRunMessage.js rename to packages/jest-util/src/preRunMessage.ts index 3089222cb6b2..d6c8979fa1e6 100644 --- a/packages/jest-cli/src/preRunMessage.js +++ b/packages/jest-util/src/preRunMessage.ts @@ -3,21 +3,19 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import {clearLine, isInteractive} from 'jest-util'; - import chalk from 'chalk'; +import clearLine from './clearLine'; +import isInteractive from './isInteractive'; -export const print = (stream: stream$Writable | tty$WriteStream) => { +export const print = (stream: NodeJS.WritableStream) => { if (isInteractive) { stream.write(chalk.bold.dim('Determining test suites to run...')); } }; -export const remove = (stream: stream$Writable | tty$WriteStream) => { +export const remove = (stream: NodeJS.WritableStream) => { if (isInteractive) { clearLine(stream); } diff --git a/packages/jest-cli/src/testPathPatternToRegexp.js b/packages/jest-util/src/testPathPatternToRegExp.ts similarity index 74% rename from packages/jest-cli/src/testPathPatternToRegexp.js rename to packages/jest-util/src/testPathPatternToRegExp.ts index 8bc775f23b78..c64f8d2af7da 100644 --- a/packages/jest-cli/src/testPathPatternToRegexp.js +++ b/packages/jest-util/src/testPathPatternToRegExp.ts @@ -3,11 +3,12 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ +import {Config} from '@jest/types'; + // Because we serialize/deserialize globalConfig when we spawn workers, // we can't pass regular expression. Using this shared function on both sides // will ensure that we produce consistent regexp for testPathPattern. -export default (testPathPattern: string) => new RegExp(testPathPattern, 'i'); +export default (testPathPattern: Config.GlobalConfig['testPathPattern']) => + new RegExp(testPathPattern, 'i'); diff --git a/types/TestScheduler.js b/types/TestScheduler.js new file mode 100644 index 000000000000..eab290edb364 --- /dev/null +++ b/types/TestScheduler.js @@ -0,0 +1,16 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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 type {Path} from './Config'; + +export type TestSchedulerContext = {| + firstRun: boolean, + previousSuccess: boolean, + changedFiles?: Set, +|}; From 8cc3bfd1ecd8f078fe857d3ea10898445bb82e79 Mon Sep 17 00:00:00 2001 From: Piotr Kuczynski Date: Fri, 15 Feb 2019 13:23:50 +0100 Subject: [PATCH 063/107] Fix typo in config init (#7905) --- .../jest-cli/src/lib/__tests__/__snapshots__/init.test.js.snap | 2 +- packages/jest-config/src/Descriptions.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 4d5fcf712ef1..9c645a4a956a 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 @@ -69,7 +69,7 @@ module.exports = { // Make calling deprecated APIs throw helpful error messages // errorOnDeprecated: false, - // Force coverage collection from ignored files usin a array of glob patterns + // Force coverage collection from ignored files using an array of glob patterns // forceCoverageMatch: [], // A path to a module which exports an async function that is triggered once before all test suites diff --git a/packages/jest-config/src/Descriptions.js b/packages/jest-config/src/Descriptions.js index 605c1e36e937..44c4e4d6c116 100644 --- a/packages/jest-config/src/Descriptions.js +++ b/packages/jest-config/src/Descriptions.js @@ -30,7 +30,7 @@ export default ({ errorOnDeprecated: 'Make calling deprecated APIs throw helpful error messages', forceCoverageMatch: - 'Force coverage collection from ignored files usin a array of glob patterns', + 'Force coverage collection from ignored files using an array of glob patterns', globalSetup: 'A path to a module which exports an async function that is triggered once before all test suites', globalTeardown: From b6834d45955f6c2bb7815134f30cd77cae56a48d Mon Sep 17 00:00:00 2001 From: doniyor2109 Date: Sat, 16 Feb 2019 16:59:52 +0500 Subject: [PATCH 064/107] jest snapshot - TS migration (#7899) --- CHANGELOG.md | 1 + packages/jest-message-util/src/index.ts | 7 +- packages/jest-message-util/src/types.ts | 12 ++ packages/jest-snapshot/package.json | 8 ++ .../jest-snapshot/src/{State.js => State.ts} | 67 +++++----- ...t.js.snap => mock_serializer.test.ts.snap} | 0 ...js.snap => snapshot_resolver.test.ts.snap} | 0 ...shots.test.js => inline_snapshots.test.ts} | 84 +++++------- .../{matcher.test.js => matcher.test.ts} | 10 +- ...alizer.test.js => mock_serializer.test.ts} | 2 - .../{plugins.test.js => plugins.test.ts} | 4 +- ...lver.test.js => snapshot_resolver.test.ts} | 16 ++- ..._matcher.test.js => throw_matcher.test.ts} | 20 +-- .../{utils.test.js => utils.test.ts} | 36 +++--- .../jest-snapshot/src/{index.js => index.ts} | 51 ++++---- ...nline_snapshots.js => inline_snapshots.ts} | 57 ++++---- ...{mock_serializer.js => mock_serializer.ts} | 25 ++-- .../src/{plugins.js => plugins.ts} | 10 +- ...pshot_resolver.js => snapshot_resolver.ts} | 47 ++++--- packages/jest-snapshot/src/types.ts | 8 ++ .../jest-snapshot/src/{utils.js => utils.ts} | 46 +++---- packages/jest-snapshot/tsconfig.json | 16 +++ packages/jest-types/src/Matchers.ts | 40 ++++++ packages/jest-types/src/PrettyFormat.ts | 117 +++++++++++++++++ packages/jest-types/src/index.ts | 13 +- packages/pretty-format/package.json | 1 + packages/pretty-format/src/types.ts | 122 ++---------------- packages/pretty-format/tsconfig.json | 3 +- yarn.lock | 10 ++ 29 files changed, 492 insertions(+), 341 deletions(-) create mode 100644 packages/jest-message-util/src/types.ts rename packages/jest-snapshot/src/{State.js => State.ts} (85%) rename packages/jest-snapshot/src/__tests__/__snapshots__/{mock_serializer.test.js.snap => mock_serializer.test.ts.snap} (100%) rename packages/jest-snapshot/src/__tests__/__snapshots__/{snapshot_resolver.test.js.snap => snapshot_resolver.test.ts.snap} (100%) rename packages/jest-snapshot/src/__tests__/{inline_snapshots.test.js => inline_snapshots.test.ts} (73%) rename packages/jest-snapshot/src/__tests__/{matcher.test.js => matcher.test.ts} (76%) rename packages/jest-snapshot/src/__tests__/{mock_serializer.test.js => mock_serializer.test.ts} (99%) rename packages/jest-snapshot/src/__tests__/{plugins.test.js => plugins.test.ts} (96%) rename packages/jest-snapshot/src/__tests__/{snapshot_resolver.test.js => snapshot_resolver.test.ts} (91%) rename packages/jest-snapshot/src/__tests__/{throw_matcher.test.js => throw_matcher.test.ts} (82%) rename packages/jest-snapshot/src/__tests__/{utils.test.js => utils.test.ts} (89%) rename packages/jest-snapshot/src/{index.js => index.ts} (91%) rename packages/jest-snapshot/src/{inline_snapshots.js => inline_snapshots.ts} (80%) rename packages/jest-snapshot/src/{mock_serializer.js => mock_serializer.ts} (75%) rename packages/jest-snapshot/src/{plugins.js => plugins.ts} (83%) rename packages/jest-snapshot/src/{snapshot_resolver.js => snapshot_resolver.ts} (68%) create mode 100644 packages/jest-snapshot/src/types.ts rename packages/jest-snapshot/src/{utils.js => utils.ts} (85%) create mode 100644 packages/jest-snapshot/tsconfig.json create mode 100644 packages/jest-types/src/Matchers.ts create mode 100644 packages/jest-types/src/PrettyFormat.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bfdfa66e605..88cc0354d2c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ - `[babel-jest]`: Migrate to TypeScript ([#7862](https://github.com/facebook/jest/pull/7862)) - `[jest-resolve]`: Migrate to TypeScript ([#7871](https://github.com/facebook/jest/pull/7871)) - `[@jest/reporter]`: New package extracted from `jest-cli` ([#7902](https://github.com/facebook/jest/pull/7902)) +- `[jest-snapshot]`: Migrate to TypeScript ([#7899](https://github.com/facebook/jest/pull/7899)) ### Performance diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index 4e1b969f5d23..f1c6ad699215 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -13,6 +13,9 @@ import micromatch from 'micromatch'; import slash from 'slash'; import {codeFrameColumns} from '@babel/code-frame'; import StackUtils from 'stack-utils'; +import {Frame} from './types'; + +export {Frame} from './types'; type Path = Config.Path; type AssertionResult = TestResult.AssertionResult; @@ -227,7 +230,7 @@ export const getStackTraceLines = ( options: StackTraceOptions = {noStackTrace: false}, ) => removeInternalStackEntries(stack.split(/\n/), options); -export const getTopFrame = (lines: string[]) => { +export const getTopFrame = (lines: string[]): Frame | null => { for (const line of lines) { if (line.includes(PATH_NODE_MODULES) || line.includes(PATH_JEST_PACKAGES)) { continue; @@ -236,7 +239,7 @@ export const getTopFrame = (lines: string[]) => { const parsedFrame = stackUtils.parseLine(line.trim()); if (parsedFrame && parsedFrame.file) { - return parsedFrame; + return parsedFrame as Frame; } } diff --git a/packages/jest-message-util/src/types.ts b/packages/jest-message-util/src/types.ts new file mode 100644 index 000000000000..d8c9530b9b35 --- /dev/null +++ b/packages/jest-message-util/src/types.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import {StackData} from 'stack-utils'; + +export interface Frame extends StackData { + file: string; +} diff --git a/packages/jest-snapshot/package.json b/packages/jest-snapshot/package.json index 43c27d8bbab0..2f42a9bbc86b 100644 --- a/packages/jest-snapshot/package.json +++ b/packages/jest-snapshot/package.json @@ -8,8 +8,10 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { "@babel/types": "^7.0.0", + "@jest/types": "^24.1.0", "chalk": "^2.0.1", "jest-diff": "^24.0.0", "jest-matcher-utils": "^24.0.0", @@ -20,9 +22,15 @@ "pretty-format": "^24.0.0", "semver": "^5.5.0" }, + "peerDependencies": { + "jest-haste-map": "^24.0.0" + }, "devDependencies": { "@types/mkdirp": "^0.5.2", + "@types/natural-compare": "^1.4.0", + "@types/prettier": "^1.16.1", "@types/semver": "^5.5.0", + "jest-haste-map": "^24.0.0", "prettier": "^1.13.4" }, "engines": { diff --git a/packages/jest-snapshot/src/State.js b/packages/jest-snapshot/src/State.ts similarity index 85% rename from packages/jest-snapshot/src/State.js rename to packages/jest-snapshot/src/State.ts index ed380afbc1fb..c6fd06953b73 100644 --- a/packages/jest-snapshot/src/State.js +++ b/packages/jest-snapshot/src/State.ts @@ -3,13 +3,11 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Path, SnapshotUpdateState} from 'types/Config'; - import fs from 'fs'; +import {Config} from '@jest/types'; + import {getTopFrame, getStackTraceLines} from 'jest-message-util'; import { saveSnapshotFile, @@ -19,41 +17,44 @@ import { testNameToKey, unescape, } from './utils'; -import {saveInlineSnapshots, type InlineSnapshot} from './inline_snapshots'; +import {saveInlineSnapshots, InlineSnapshot} from './inline_snapshots'; +import {SnapshotData} from './types'; -export type SnapshotStateOptions = {| - updateSnapshot: SnapshotUpdateState, - getPrettier: () => null | any, - getBabelTraverse: () => Function, - expand?: boolean, -|}; +export type SnapshotStateOptions = { + updateSnapshot: Config.SnapshotUpdateState; + getPrettier: () => null | any; + getBabelTraverse: () => Function; + expand?: boolean; +}; -export type SnapshotMatchOptions = {| - testName: string, - received: any, - key?: string, - inlineSnapshot?: string, - error?: Error, -|}; +export type SnapshotMatchOptions = { + testName: string; + received: any; + key?: string; + inlineSnapshot?: string; + error?: Error; +}; export default class SnapshotState { - _counters: Map; - _dirty: boolean; - _index: number; - _updateSnapshot: SnapshotUpdateState; - _snapshotData: {[key: string]: string}; - _snapshotPath: Path; - _inlineSnapshots: Array; - _uncheckedKeys: Set; - _getBabelTraverse: () => Function; - _getPrettier: () => null | any; + private _counters: Map; + private _dirty: boolean; + // @ts-ignore + private _index: number; + private _updateSnapshot: Config.SnapshotUpdateState; + private _snapshotData: SnapshotData; + private _snapshotPath: Config.Path; + private _inlineSnapshots: Array; + private _uncheckedKeys: Set; + private _getBabelTraverse: () => Function; + private _getPrettier: () => null | any; + added: number; expand: boolean; matched: number; unmatched: number; updated: number; - constructor(snapshotPath: Path, options: SnapshotStateOptions) { + constructor(snapshotPath: Config.Path, options: SnapshotStateOptions) { this._snapshotPath = snapshotPath; const {data, dirty} = getSnapshotData( this._snapshotPath, @@ -83,15 +84,15 @@ export default class SnapshotState { }); } - _addSnapshot( + private _addSnapshot( key: string, receivedSerialized: string, - options: {isInline: boolean, error?: Error}, + options: {isInline: boolean; error?: Error}, ) { this._dirty = true; if (options.isInline) { const error = options.error || new Error(); - const lines = getStackTraceLines(error.stack); + const lines = getStackTraceLines(error.stack || ''); const frame = getTopFrame(lines); if (!frame) { throw new Error( @@ -251,7 +252,7 @@ export default class SnapshotState { } } - fail(testName: string, received: any, key?: string) { + fail(testName: string, _received: any, key?: string) { this._counters.set(testName, (this._counters.get(testName) || 0) + 1); const count = Number(this._counters.get(testName)); diff --git a/packages/jest-snapshot/src/__tests__/__snapshots__/mock_serializer.test.js.snap b/packages/jest-snapshot/src/__tests__/__snapshots__/mock_serializer.test.ts.snap similarity index 100% rename from packages/jest-snapshot/src/__tests__/__snapshots__/mock_serializer.test.js.snap rename to packages/jest-snapshot/src/__tests__/__snapshots__/mock_serializer.test.ts.snap diff --git a/packages/jest-snapshot/src/__tests__/__snapshots__/snapshot_resolver.test.js.snap b/packages/jest-snapshot/src/__tests__/__snapshots__/snapshot_resolver.test.ts.snap similarity index 100% rename from packages/jest-snapshot/src/__tests__/__snapshots__/snapshot_resolver.test.js.snap rename to packages/jest-snapshot/src/__tests__/__snapshots__/snapshot_resolver.test.ts.snap diff --git a/packages/jest-snapshot/src/__tests__/inline_snapshots.test.js b/packages/jest-snapshot/src/__tests__/inline_snapshots.test.ts similarity index 73% rename from packages/jest-snapshot/src/__tests__/inline_snapshots.test.js rename to packages/jest-snapshot/src/__tests__/inline_snapshots.test.ts index 63c744e73d0c..a7b7ad418eeb 100644 --- a/packages/jest-snapshot/src/__tests__/inline_snapshots.test.js +++ b/packages/jest-snapshot/src/__tests__/inline_snapshots.test.ts @@ -3,19 +3,18 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ jest.mock('fs'); jest.mock('prettier'); -const fs = require('fs'); -const path = require('path'); -const prettier = require('prettier'); -const babelTraverse = require('@babel/traverse').default; +import fs from 'fs'; +import path from 'path'; +import prettier from 'prettier'; +import babelTraverse from '@babel/traverse'; +import {Frame} from 'jest-message-util'; -const {saveInlineSnapshots} = require('../inline_snapshots'); +import {saveInlineSnapshots} from '../inline_snapshots'; const writeFileSync = fs.writeFileSync; const readFileSync = fs.readFileSync; @@ -23,45 +22,34 @@ 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 => ({ + (fs.statSync as jest.Mock).mockImplementation(filePath => ({ isDirectory: () => !filePath.endsWith('.js'), })); - // $FlowFixMe mock fs.readdirSync = jest.fn(() => []); - prettier.resolveConfig.sync.mockReset(); + (prettier.resolveConfig.sync as jest.Mock).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( + (fs.readFileSync as jest.Mock).mockImplementation( () => `expect(1).toMatchInlineSnapshot();\n`, - ): any); + ); saveInlineSnapshots( [ { - frame: {column: 11, file: filename, line: 1}, + frame: {column: 11, file: filename, line: 1} as Frame, snapshot: `1`, }, ], @@ -79,17 +67,16 @@ 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( + (fs.readFileSync as jest.Mock).mockImplementation( () => 'expect(1).toMatchInlineSnapshot(`2`);\n', - ): any); + ); - prettier.resolveConfig.sync.mockReturnValue({parser}); + (prettier.resolveConfig.sync as jest.Mock).mockReturnValue({parser}); saveInlineSnapshots( [ { - frame: {column: 11, file: filename, line: 1}, + frame: {column: 11, file: filename, line: 1} as Frame, snapshot: `1`, }, ], @@ -97,7 +84,9 @@ test.each([['babylon'], ['flow'], ['typescript']])( babelTraverse, ); - expect(prettier.resolveConfig.sync.mock.results[0].value).toEqual({parser}); + expect( + (prettier.resolveConfig.sync as jest.Mock).mock.results[0].value, + ).toEqual({parser}); expect(fs.writeFileSync).toHaveBeenCalledWith( filename, @@ -108,15 +97,14 @@ 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( + (fs.readFileSync as jest.Mock).mockImplementation( () => 'expect(1).toMatchInlineSnapshot({}, `2`);\n', - ): any); + ); saveInlineSnapshots( [ { - frame: {column: 11, file: filename, line: 1}, + frame: {column: 11, file: filename, line: 1} as Frame, snapshot: `1`, }, ], @@ -132,16 +120,15 @@ 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( + (fs.readFileSync as jest.Mock).mockImplementation( () => 'expect(1).toMatchInlineSnapshot();\n', - ): any); + ); const save = () => saveInlineSnapshots( [ { - frame: {column: 2 /* incorrect */, file: filename, line: 1}, + frame: {column: 2 /* incorrect */, file: filename, line: 1} as Frame, snapshot: `1`, }, ], @@ -154,12 +141,11 @@ 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( + (fs.readFileSync as jest.Mock).mockImplementation( () => 'expect(1).toMatchInlineSnapshot();\n', - ): any); + ); - const frame = {column: 11, file: filename, line: 1}; + const frame = {column: 11, file: filename, line: 1} as Frame; const save = () => saveInlineSnapshots( [{frame, snapshot: `1`}, {frame, snapshot: `2`}], @@ -174,12 +160,11 @@ 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( + (fs.readFileSync as jest.Mock).mockImplementation( () => 'expect("`").toMatchInlineSnapshot();\n', - ): any); + ); - const frame = {column: 13, file: filename, line: 1}; + const frame = {column: 13, file: filename, line: 1} as Frame; saveInlineSnapshots([{frame, snapshot: '`'}], prettier, babelTraverse); expect(fs.writeFileSync).toHaveBeenCalledWith( @@ -190,11 +175,10 @@ 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( + (fs.readFileSync as jest.Mock).mockImplementation( () => `expect({a: 'a'}).toMatchInlineSnapshot();\n`, - ): any); - prettier.resolveConfig.sync.mockReturnValue({ + ); + (prettier.resolveConfig.sync as jest.Mock).mockReturnValue({ bracketSpacing: false, singleQuote: true, }); @@ -202,7 +186,7 @@ test('saveInlineSnapshots() works with non-literals in expect call', () => { saveInlineSnapshots( [ { - frame: {column: 18, file: filename, line: 1}, + frame: {column: 18, file: filename, line: 1} as Frame, snapshot: `{a: 'a'}`, }, ], diff --git a/packages/jest-snapshot/src/__tests__/matcher.test.js b/packages/jest-snapshot/src/__tests__/matcher.test.ts similarity index 76% rename from packages/jest-snapshot/src/__tests__/matcher.test.js rename to packages/jest-snapshot/src/__tests__/matcher.test.ts index e487a545dade..5bbfe1226577 100644 --- a/packages/jest-snapshot/src/__tests__/matcher.test.js +++ b/packages/jest-snapshot/src/__tests__/matcher.test.ts @@ -3,17 +3,19 @@ * * 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 {toMatchSnapshot} = require('../'); +import jestSnapshot from '../'; + +const {toMatchSnapshot} = jestSnapshot; it(`matcher returns matcher name, expected and actual values`, () => { const actual = 'a'; const expected = 'b'; const matcher = toMatchSnapshot.bind({ - snapshotState: {match: (testName, received) => ({actual, expected})}, + snapshotState: { + match: (_testName: string, _received: any) => ({actual, expected}), + }, }); const matcherResult = matcher({a: 1}); diff --git a/packages/jest-snapshot/src/__tests__/mock_serializer.test.js b/packages/jest-snapshot/src/__tests__/mock_serializer.test.ts similarity index 99% rename from packages/jest-snapshot/src/__tests__/mock_serializer.test.js rename to packages/jest-snapshot/src/__tests__/mock_serializer.test.ts index 702898f65a50..b859a9671a30 100644 --- a/packages/jest-snapshot/src/__tests__/mock_serializer.test.js +++ b/packages/jest-snapshot/src/__tests__/mock_serializer.test.ts @@ -3,9 +3,7 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * */ -'use strict'; import prettyFormat from 'pretty-format'; diff --git a/packages/jest-snapshot/src/__tests__/plugins.test.js b/packages/jest-snapshot/src/__tests__/plugins.test.ts similarity index 96% rename from packages/jest-snapshot/src/__tests__/plugins.test.js rename to packages/jest-snapshot/src/__tests__/plugins.test.ts index 8f0a9629dc99..feb47a2ea591 100644 --- a/packages/jest-snapshot/src/__tests__/plugins.test.js +++ b/packages/jest-snapshot/src/__tests__/plugins.test.ts @@ -3,13 +3,11 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * */ -'use strict'; beforeEach(() => jest.resetModules()); -const testPath = names => { +const testPath = (names: string[]) => { const {addSerializer, getSerializers} = require('../plugins'); const prev = getSerializers(); const added = names.map(name => diff --git a/packages/jest-snapshot/src/__tests__/snapshot_resolver.test.js b/packages/jest-snapshot/src/__tests__/snapshot_resolver.test.ts similarity index 91% rename from packages/jest-snapshot/src/__tests__/snapshot_resolver.test.js rename to packages/jest-snapshot/src/__tests__/snapshot_resolver.test.ts index fd53291ef0ca..5afee9699182 100644 --- a/packages/jest-snapshot/src/__tests__/snapshot_resolver.test.js +++ b/packages/jest-snapshot/src/__tests__/snapshot_resolver.test.ts @@ -1,14 +1,16 @@ // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. -const path = require('path'); -const {buildSnapshotResolver} = require('../snapshot_resolver'); +import path from 'path'; +import {Config} from '@jest/types'; + +import {buildSnapshotResolver, SnapshotResolver} from '../snapshot_resolver'; describe('defaults', () => { - let snapshotResolver; + let snapshotResolver: SnapshotResolver; const projectConfig = { rootDir: 'default', // snapshotResolver: null, - }; + } as Config.ProjectConfig; beforeEach(() => { snapshotResolver = buildSnapshotResolver(projectConfig); @@ -32,7 +34,7 @@ describe('defaults', () => { }); describe('custom resolver in project config', () => { - let snapshotResolver; + let snapshotResolver: SnapshotResolver; const customSnapshotResolverFile = path.join( __dirname, 'fixtures', @@ -41,7 +43,7 @@ describe('custom resolver in project config', () => { const projectConfig = { rootDir: 'custom1', snapshotResolver: customSnapshotResolverFile, - }; + } as Config.ProjectConfig; beforeEach(() => { snapshotResolver = buildSnapshotResolver(projectConfig); @@ -78,7 +80,7 @@ describe('malformed custom resolver in project config', () => { return { rootDir: 'missing-resolveSnapshotPath', snapshotResolver: customSnapshotResolverFile, - }; + } as Config.ProjectConfig; }; it('missing resolveSnapshotPath throws ', () => { diff --git a/packages/jest-snapshot/src/__tests__/throw_matcher.test.js b/packages/jest-snapshot/src/__tests__/throw_matcher.test.ts similarity index 82% rename from packages/jest-snapshot/src/__tests__/throw_matcher.test.js rename to packages/jest-snapshot/src/__tests__/throw_matcher.test.ts index d08d4e9877f9..bbe8b9d477db 100644 --- a/packages/jest-snapshot/src/__tests__/throw_matcher.test.js +++ b/packages/jest-snapshot/src/__tests__/throw_matcher.test.ts @@ -3,13 +3,13 @@ * * 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 {toThrowErrorMatchingSnapshot} = require('../'); +import jestSnapshot from '../'; + +const {toThrowErrorMatchingSnapshot} = jestSnapshot; -let matchFn; +let matchFn: jest.Mock; beforeEach(() => { matchFn = jest.fn(() => ({ @@ -23,9 +23,13 @@ it('throw matcher can take func', () => { snapshotState: {match: matchFn}, }); - throwMatcher(() => { - throw new Error('coconut'); - }); + throwMatcher( + () => { + throw new Error('coconut'); + }, + undefined, + false, + ); expect(matchFn).toHaveBeenCalledWith( expect.objectContaining({received: 'coconut', testName: ''}), @@ -33,7 +37,7 @@ it('throw matcher can take func', () => { }); describe('throw matcher from promise', () => { - let throwMatcher; + let throwMatcher: typeof toThrowErrorMatchingSnapshot; beforeEach(() => { throwMatcher = toThrowErrorMatchingSnapshot.bind({ diff --git a/packages/jest-snapshot/src/__tests__/utils.test.js b/packages/jest-snapshot/src/__tests__/utils.test.ts similarity index 89% rename from packages/jest-snapshot/src/__tests__/utils.test.js rename to packages/jest-snapshot/src/__tests__/utils.test.ts index d4d34a2d2131..981070691777 100644 --- a/packages/jest-snapshot/src/__tests__/utils.test.js +++ b/packages/jest-snapshot/src/__tests__/utils.test.ts @@ -7,21 +7,21 @@ jest.mock('fs'); -const fs = require('fs'); -const path = require('path'); -const chalk = require('chalk'); +import fs from 'fs'; +import path from 'path'; +import chalk from 'chalk'; -const { +import { + deepMerge, getSnapshotData, keyToTestName, saveSnapshotFile, serialize, testNameToKey, - deepMerge, SNAPSHOT_GUIDE_LINK, SNAPSHOT_VERSION, SNAPSHOT_VERSION_WARNING, -} = require('../utils'); +} from '../utils'; const writeFileSync = fs.writeFileSync; const readFileSync = fs.readFileSync; @@ -80,7 +80,9 @@ test('saveSnapshotFile() works with \r', () => { test('getSnapshotData() throws when no snapshot version', () => { const filename = path.join(__dirname, 'old-snapshot.snap'); - fs.readFileSync = jest.fn(() => 'exports[`myKey`] = `
\n
`;\n'); + (fs.readFileSync as jest.Mock).mockImplementation( + () => 'exports[`myKey`] = `
\n
`;\n', + ); const update = 'none'; expect(() => getSnapshotData(filename, update)).toThrowError( @@ -95,7 +97,7 @@ test('getSnapshotData() throws when no snapshot version', () => { test('getSnapshotData() throws for older snapshot version', () => { const filename = path.join(__dirname, 'old-snapshot.snap'); - fs.readFileSync = jest.fn( + (fs.readFileSync as jest.Mock).mockImplementation( () => `// Jest Snapshot v0.99, ${SNAPSHOT_GUIDE_LINK}\n\n` + 'exports[`myKey`] = `
\n
`;\n', @@ -118,7 +120,7 @@ test('getSnapshotData() throws for older snapshot version', () => { test('getSnapshotData() throws for newer snapshot version', () => { const filename = path.join(__dirname, 'old-snapshot.snap'); - fs.readFileSync = jest.fn( + (fs.readFileSync as jest.Mock).mockImplementation( () => `// Jest Snapshot v2, ${SNAPSHOT_GUIDE_LINK}\n\n` + 'exports[`myKey`] = `
\n
`;\n', @@ -141,7 +143,9 @@ test('getSnapshotData() throws for newer snapshot version', () => { test('getSnapshotData() does not throw for when updating', () => { const filename = path.join(__dirname, 'old-snapshot.snap'); - fs.readFileSync = jest.fn(() => 'exports[`myKey`] = `
\n
`;\n'); + (fs.readFileSync as jest.Mock).mockImplementation( + () => 'exports[`myKey`] = `
\n
`;\n', + ); const update = 'all'; expect(() => getSnapshotData(filename, update)).not.toThrow(); @@ -149,7 +153,9 @@ test('getSnapshotData() does not throw for when updating', () => { test('getSnapshotData() marks invalid snapshot dirty when updating', () => { const filename = path.join(__dirname, 'old-snapshot.snap'); - fs.readFileSync = jest.fn(() => 'exports[`myKey`] = `
\n
`;\n'); + (fs.readFileSync as jest.Mock).mockImplementation( + () => 'exports[`myKey`] = `
\n
`;\n', + ); const update = 'all'; expect(getSnapshotData(filename, update)).toMatchObject({dirty: true}); @@ -157,7 +163,7 @@ test('getSnapshotData() marks invalid snapshot dirty when updating', () => { test('getSnapshotData() marks valid snapshot not dirty when updating', () => { const filename = path.join(__dirname, 'old-snapshot.snap'); - fs.readFileSync = jest.fn( + (fs.readFileSync as jest.Mock).mockImplementation( () => `// Jest Snapshot v${SNAPSHOT_VERSION}, ${SNAPSHOT_GUIDE_LINK}\n\n` + 'exports[`myKey`] = `
\n
`;\n', @@ -171,14 +177,14 @@ test('escaping', () => { const filename = path.join(__dirname, 'escaping.snap'); const data = '"\'\\'; saveSnapshotFile({key: data}, filename); - const writtenData = fs.writeFileSync.mock.calls[0][1]; + const writtenData = (fs.writeFileSync as jest.Mock).mock.calls[0][1]; expect(writtenData).toBe( `// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` + 'exports[`key`] = `"\'\\\\`;\n', ); - // eslint-disable-next-line no-unused-vars - const exports = {}; + // @ts-ignore + const exports = {}; // eslint-disable-line // eslint-disable-next-line no-eval const readData = eval('var exports = {}; ' + writtenData + ' exports'); expect(readData).toEqual({key: data}); diff --git a/packages/jest-snapshot/src/index.js b/packages/jest-snapshot/src/index.ts similarity index 91% rename from packages/jest-snapshot/src/index.js rename to packages/jest-snapshot/src/index.ts index 7c0cb0687303..bc22a7e4b9d9 100644 --- a/packages/jest-snapshot/src/index.js +++ b/packages/jest-snapshot/src/index.ts @@ -3,33 +3,34 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {HasteFS} from 'types/HasteMap'; -import type {MatcherState} from 'types/Matchers'; -import type {Path, SnapshotUpdateState} from 'types/Config'; -import type {SnapshotResolver} from 'types/SnapshotResolver'; - import fs from 'fs'; +import {Config, Matchers} from '@jest/types'; +import {FS as HasteFS} from 'jest-haste-map'; + import diff from 'jest-diff'; import {EXPECTED_COLOR, matcherHint, RECEIVED_COLOR} from 'jest-matcher-utils'; import { buildSnapshotResolver, isSnapshotPath, + SnapshotResolver, EXTENSION, } from './snapshot_resolver'; import SnapshotState from './State'; import {addSerializer, getSerializers} from './plugins'; import * as utils from './utils'; -const fileExists = (filePath: Path, hasteFS: HasteFS): boolean => +type Context = Matchers.MatcherState & { + snapshotState: SnapshotState; +}; + +const fileExists = (filePath: Config.Path, hasteFS: HasteFS): boolean => hasteFS.exists(filePath) || fs.existsSync(filePath); const cleanup = ( hasteFS: HasteFS, - update: SnapshotUpdateState, + update: Config.SnapshotUpdateState, snapshotResolver: SnapshotResolver, ) => { const pattern = '\\.' + EXTENSION + '$'; @@ -51,9 +52,10 @@ const cleanup = ( }; const toMatchSnapshot = function( + this: Context, received: any, propertyMatchers?: any, - testName?: string, + testName?: Config.Path, ) { if (arguments.length === 3 && !propertyMatchers) { throw new Error( @@ -70,6 +72,7 @@ const toMatchSnapshot = function( }; const toMatchInlineSnapshot = function( + this: Context, received: any, propertyMatchersOrInlineSnapshot?: any, inlineSnapshot?: string, @@ -95,11 +98,11 @@ const _toMatchSnapshot = ({ testName, inlineSnapshot, }: { - context: MatcherState & {dontThrow?: () => any}, - received: any, - propertyMatchers?: any, - testName?: string, - inlineSnapshot?: string, + context: Context; + received: any; + propertyMatchers?: any; + testName?: string; + inlineSnapshot?: string; }) => { context.dontThrow && context.dontThrow(); testName = typeof propertyMatchers === 'string' ? propertyMatchers : testName; @@ -168,7 +171,7 @@ const _toMatchSnapshot = ({ const {pass} = result; let {actual, expected} = result; - let report; + let report: () => string; if (pass) { return {message: () => '', pass: true}; } else if (!expected) { @@ -211,8 +214,9 @@ const _toMatchSnapshot = ({ }; const toThrowErrorMatchingSnapshot = function( + this: Context, received: any, - testName?: string, + testName: string | undefined, fromPromise: boolean, ) { return _toThrowErrorMatchingSnapshot({ @@ -224,6 +228,7 @@ const toThrowErrorMatchingSnapshot = function( }; const toThrowErrorMatchingInlineSnapshot = function( + this: Context, received: any, inlineSnapshot?: string, fromPromise?: boolean, @@ -243,11 +248,11 @@ const _toThrowErrorMatchingSnapshot = ({ fromPromise, inlineSnapshot, }: { - context: MatcherState & {dontThrow?: () => any}, - received: any, - testName?: string, - fromPromise?: boolean, - inlineSnapshot?: string, + context: Context; + received: any; + testName?: string; + fromPromise?: boolean; + inlineSnapshot?: string; }) => { context.dontThrow && context.dontThrow(); const {isNot} = context; @@ -291,7 +296,7 @@ const _toThrowErrorMatchingSnapshot = ({ }); }; -module.exports = { +export = { EXTENSION, SnapshotState, addSerializer, diff --git a/packages/jest-snapshot/src/inline_snapshots.js b/packages/jest-snapshot/src/inline_snapshots.ts similarity index 80% rename from packages/jest-snapshot/src/inline_snapshots.js rename to packages/jest-snapshot/src/inline_snapshots.ts index cd171877a4a7..9468c1c4ec43 100644 --- a/packages/jest-snapshot/src/inline_snapshots.js +++ b/packages/jest-snapshot/src/inline_snapshots.ts @@ -3,22 +3,26 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import fs from 'fs'; -import semver from 'semver'; import path from 'path'; -import {templateElement, templateLiteral, file} from '@babel/types'; - -import type {Path} from 'types/Config'; +import semver from 'semver'; +import { + templateElement, + templateLiteral, + file, + CallExpression, +} from '@babel/types'; +import {Frame} from 'jest-message-util'; + +import {Config} from '@jest/types'; import {escapeBacktickString} from './utils'; -export type InlineSnapshot = {| - snapshot: string, - frame: {line: number, column: number, file: string}, -|}; +export type InlineSnapshot = { + snapshot: string; + frame: Frame; +}; export const saveInlineSnapshots = ( snapshots: InlineSnapshot[], @@ -54,7 +58,7 @@ export const saveInlineSnapshots = ( const saveSnapshotsForFile = ( snapshots: Array, - sourceFilePath: Path, + sourceFilePath: Config.Path, prettier: any, babelTraverse: Function, ) => { @@ -86,16 +90,21 @@ const saveSnapshotsForFile = ( } }; -const groupSnapshotsBy = (createKey: InlineSnapshot => string) => ( - snapshots: Array, -) => - snapshots.reduce((object, inlineSnapshot) => { - const key = createKey(inlineSnapshot); - return {...object, [key]: (object[key] || []).concat(inlineSnapshot)}; - }, {}); - -const groupSnapshotsByFrame = groupSnapshotsBy( - ({frame: {line, column}}) => `${line}:${column - 1}`, +const groupSnapshotsBy = ( + createKey: (inlineSnapshot: InlineSnapshot) => string, +) => (snapshots: Array) => + snapshots.reduce<{[key: string]: InlineSnapshot[]}>( + (object, inlineSnapshot) => { + const key = createKey(inlineSnapshot); + return {...object, [key]: (object[key] || []).concat(inlineSnapshot)}; + }, + {}, + ); + +const groupSnapshotsByFrame = groupSnapshotsBy(({frame: {line, column}}) => + typeof line === 'number' && typeof column === 'number' + ? `${line}:${column - 1}` + : '', ); const groupSnapshotsByFile = groupSnapshotsBy(({frame: {file}}) => file); @@ -105,7 +114,7 @@ const createParser = ( babelTraverse: Function, ) => ( text: string, - parsers: {[key: string]: (string) => any}, + parsers: {[key: string]: (text: string) => any}, options: any, ) => { // Workaround for https://github.com/prettier/prettier/issues/3150 @@ -122,7 +131,7 @@ const createParser = ( } babelTraverse(ast, { - CallExpression({node: {arguments: args, callee}}) { + CallExpression({node: {arguments: args, callee}}: {node: CallExpression}) { if ( callee.type !== 'MemberExpression' || callee.property.type !== 'Identifier' @@ -167,7 +176,7 @@ const createParser = ( return ast; }; -const simpleDetectParser = (filePath: Path) => { +const simpleDetectParser = (filePath: Config.Path) => { const extname = path.extname(filePath); if (/tsx?$/.test(extname)) { return 'typescript'; diff --git a/packages/jest-snapshot/src/mock_serializer.js b/packages/jest-snapshot/src/mock_serializer.ts similarity index 75% rename from packages/jest-snapshot/src/mock_serializer.js rename to packages/jest-snapshot/src/mock_serializer.ts index 410c6d9f2a9b..fbfaf7a1448c 100644 --- a/packages/jest-snapshot/src/mock_serializer.js +++ b/packages/jest-snapshot/src/mock_serializer.ts @@ -3,19 +3,17 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Config, NewPlugin, Printer, Refs} from 'types/PrettyFormat'; +import {PrettyFormat} from '@jest/types'; -export const serialize = ( - val: any, - config: Config, - indentation: string, - depth: number, - refs: Refs, - printer: Printer, +export const serialize: PrettyFormat.NewPlugin['serialize'] = ( + val, + config, + indentation, + depth, + refs, + printer, ): string => { // Serialize a non-default name, even if config.printFunctionName is false. const name = val.getMockName(); @@ -44,6 +42,9 @@ export const serialize = ( return '[MockFunction' + nameString + ']' + callsString; }; -export const test = (val: any) => val && !!val._isMockFunction; +export const test: PrettyFormat.NewPlugin['test'] = val => + val && !!val._isMockFunction; + +const plugin: PrettyFormat.NewPlugin = {serialize, test}; -export default ({serialize, test}: NewPlugin); +export default plugin; diff --git a/packages/jest-snapshot/src/plugins.js b/packages/jest-snapshot/src/plugins.ts similarity index 83% rename from packages/jest-snapshot/src/plugins.js rename to packages/jest-snapshot/src/plugins.ts index e445ce59c2f0..ebf50c0eb651 100644 --- a/packages/jest-snapshot/src/plugins.js +++ b/packages/jest-snapshot/src/plugins.ts @@ -3,13 +3,11 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Plugin} from 'types/PrettyFormat'; - import prettyFormat from 'pretty-format'; +import {PrettyFormat} from '@jest/types'; + import jestMockSerializer from './mock_serializer'; const { @@ -21,7 +19,7 @@ const { AsymmetricMatcher, } = prettyFormat.plugins; -let PLUGINS: Array = [ +let PLUGINS: PrettyFormat.Plugins = [ ReactTestComponent, ReactElement, DOMElement, @@ -32,7 +30,7 @@ let PLUGINS: Array = [ ]; // Prepend to list so the last added is the first tested. -export const addSerializer = (plugin: Plugin) => { +export const addSerializer = (plugin: PrettyFormat.Plugin) => { PLUGINS = [plugin].concat(PLUGINS); }; diff --git a/packages/jest-snapshot/src/snapshot_resolver.js b/packages/jest-snapshot/src/snapshot_resolver.ts similarity index 68% rename from packages/jest-snapshot/src/snapshot_resolver.js rename to packages/jest-snapshot/src/snapshot_resolver.ts index c8c8829aa19f..824fb5307dfa 100644 --- a/packages/jest-snapshot/src/snapshot_resolver.js +++ b/packages/jest-snapshot/src/snapshot_resolver.ts @@ -1,9 +1,19 @@ -// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ -import type {ProjectConfig, Path} from 'types/Config'; -import type {SnapshotResolver} from 'types/SnapshotResolver'; -import chalk from 'chalk'; import path from 'path'; +import {Config} from '@jest/types'; +import chalk from 'chalk'; + +export type SnapshotResolver = { + testPathForConsistencyCheck: string; + resolveSnapshotPath(testPath: Config.Path, extension?: string): Config.Path; + resolveTestPath(snapshotPath: Config.Path, extension?: string): Config.Path; +}; export const EXTENSION = 'snap'; export const DOT_EXTENSION = '.' + EXTENSION; @@ -11,32 +21,34 @@ export const DOT_EXTENSION = '.' + EXTENSION; export const isSnapshotPath = (path: string): boolean => path.endsWith(DOT_EXTENSION); -const cache: Map = new Map(); +const cache: Map = new Map(); export const buildSnapshotResolver = ( - config: ProjectConfig, + config: Config.ProjectConfig, ): SnapshotResolver => { const key = config.rootDir; if (!cache.has(key)) { cache.set(key, createSnapshotResolver(config.snapshotResolver)); } - return cache.get(key); + return cache.get(key)!; }; -function createSnapshotResolver(snapshotResolverPath: ?Path): SnapshotResolver { +function createSnapshotResolver( + snapshotResolverPath?: Config.Path | null, +): SnapshotResolver { return typeof snapshotResolverPath === 'string' ? createCustomSnapshotResolver(snapshotResolverPath) : createDefaultSnapshotResolver(); } -function createDefaultSnapshotResolver() { +function createDefaultSnapshotResolver(): SnapshotResolver { return { - resolveSnapshotPath: (testPath: Path) => + resolveSnapshotPath: (testPath: Config.Path) => path.join( path.join(path.dirname(testPath), '__snapshots__'), path.basename(testPath) + DOT_EXTENSION, ), - resolveTestPath: (snapshotPath: Path) => + resolveTestPath: (snapshotPath: Config.Path) => path.resolve( path.dirname(snapshotPath), '..', @@ -52,24 +64,25 @@ function createDefaultSnapshotResolver() { } function createCustomSnapshotResolver( - snapshotResolverPath: Path, + snapshotResolverPath: Config.Path, ): SnapshotResolver { - const custom = (require(snapshotResolverPath): SnapshotResolver); + const custom: SnapshotResolver = require(snapshotResolverPath); - [ + const keys: [keyof SnapshotResolver, string][] = [ ['resolveSnapshotPath', 'function'], ['resolveTestPath', 'function'], ['testPathForConsistencyCheck', 'string'], - ].forEach(([propName, requiredType]) => { + ]; + keys.forEach(([propName, requiredType]) => { if (typeof custom[propName] !== requiredType) { throw new TypeError(mustImplement(propName, requiredType)); } }); const customResolver = { - resolveSnapshotPath: testPath => + resolveSnapshotPath: (testPath: Config.Path) => custom.resolveSnapshotPath(testPath, DOT_EXTENSION), - resolveTestPath: snapshotPath => + resolveTestPath: (snapshotPath: Config.Path) => custom.resolveTestPath(snapshotPath, DOT_EXTENSION), testPathForConsistencyCheck: custom.testPathForConsistencyCheck, }; diff --git a/packages/jest-snapshot/src/types.ts b/packages/jest-snapshot/src/types.ts new file mode 100644 index 000000000000..439a6d013ed1 --- /dev/null +++ b/packages/jest-snapshot/src/types.ts @@ -0,0 +1,8 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +export type SnapshotData = {[key: string]: string}; diff --git a/packages/jest-snapshot/src/utils.js b/packages/jest-snapshot/src/utils.ts similarity index 85% rename from packages/jest-snapshot/src/utils.js rename to packages/jest-snapshot/src/utils.ts index 61b812ebdf68..992fa72926d0 100644 --- a/packages/jest-snapshot/src/utils.js +++ b/packages/jest-snapshot/src/utils.ts @@ -3,19 +3,17 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Path, SnapshotUpdateState} from 'types/Config'; - -import {getSerializers} from './plugins'; -import chalk from 'chalk'; import fs from 'fs'; +import path from 'path'; import mkdirp from 'mkdirp'; import naturalCompare from 'natural-compare'; -import path from 'path'; +import chalk from 'chalk'; +import {Config} from '@jest/types'; import prettyFormat from 'pretty-format'; +import {getSerializers} from './plugins'; +import {SnapshotData} from './types'; export const SNAPSHOT_VERSION = '1'; const SNAPSHOT_VERSION_REGEXP = /^\/\/ Jest Snapshot v(.+),/; @@ -76,14 +74,14 @@ const validateSnapshotVersion = (snapshotContents: string) => { return null; }; -function isObject(item) { +function isObject(item: unknown): boolean { return item && typeof item === 'object' && !Array.isArray(item); } -export const testNameToKey = (testName: string, count: number) => +export const testNameToKey = (testName: Config.Path, count: number): string => testName + ' ' + count; -export const keyToTestName = (key: string) => { +export const keyToTestName = (key: string): string => { if (!/ \d+$/.test(key)) { throw new Error('Snapshot keys must end with a number.'); } @@ -92,9 +90,12 @@ export const keyToTestName = (key: string) => { }; export const getSnapshotData = ( - snapshotPath: Path, - update: SnapshotUpdateState, -) => { + snapshotPath: Config.Path, + update: Config.SnapshotUpdateState, +): { + data: SnapshotData; + dirty: boolean; +} => { const data = Object.create(null); let snapshotContents = ''; let dirty = false; @@ -104,7 +105,6 @@ export const getSnapshotData = ( snapshotContents = fs.readFileSync(snapshotPath, 'utf8'); // eslint-disable-next-line no-new-func const populate = new Function('exports', snapshotContents); - // $FlowFixMe populate(data); } catch (e) {} } @@ -120,15 +120,15 @@ export const getSnapshotData = ( dirty = true; } - return ({data, dirty}: {data: any, dirty: boolean}); + return {data, dirty}; }; // Extra line breaks at the beginning and at the end of the snapshot are useful // to make the content of the snapshot easier to read -const addExtraLineBreaks = string => +const addExtraLineBreaks = (string: string): string => string.includes('\n') ? `\n${string}\n` : string; -export const serialize = (data: any): string => +export const serialize = (data: string): string => addExtraLineBreaks( normalizeNewlines( prettyFormat(data, { @@ -140,25 +140,25 @@ export const serialize = (data: any): string => ); // unescape double quotes -export const unescape = (data: any): string => data.replace(/\\(")/g, '$1'); +export const unescape = (data: string): string => data.replace(/\\(")/g, '$1'); -export const escapeBacktickString = (str: string) => +export const escapeBacktickString = (str: string): string => str.replace(/`|\\|\${/g, '\\$&'); -const printBacktickString = (str: string) => +const printBacktickString = (str: string): string => '`' + escapeBacktickString(str) + '`'; -export const ensureDirectoryExists = (filePath: Path) => { +export const ensureDirectoryExists = (filePath: Config.Path) => { try { mkdirp.sync(path.join(path.dirname(filePath)), '777'); } catch (e) {} }; -const normalizeNewlines = string => string.replace(/\r\n|\r/g, '\n'); +const normalizeNewlines = (string: string) => string.replace(/\r\n|\r/g, '\n'); export const saveSnapshotFile = ( snapshotData: {[key: string]: string}, - snapshotPath: Path, + snapshotPath: Config.Path, ) => { const snapshots = Object.keys(snapshotData) .sort(naturalCompare) diff --git a/packages/jest-snapshot/tsconfig.json b/packages/jest-snapshot/tsconfig.json new file mode 100644 index 000000000000..2ed3fb0d58dc --- /dev/null +++ b/packages/jest-snapshot/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [ + {"path": "../jest-diff"}, + {"path": "../jest-haste-map"}, + {"path": "../jest-matcher-utils"}, + {"path": "../jest-message-util"}, + {"path": "../jest-resolve"}, + {"path": "../jest-types"}, + {"path": "../pretty-format"} + ] +} diff --git a/packages/jest-types/src/Matchers.ts b/packages/jest-types/src/Matchers.ts new file mode 100644 index 000000000000..e4e419e0ae23 --- /dev/null +++ b/packages/jest-types/src/Matchers.ts @@ -0,0 +1,40 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +// TODO: Move this to `expect` when it's migrated + +import {Path} from './Config'; + +type Tester = (a: any, b: any) => boolean | undefined; + +export type MatcherState = { + assertionCalls: number; + currentTestName?: string; + dontThrow?: () => void; + error?: Error; + equals: ( + a: unknown, + b: unknown, + customTesters?: Array, + strictCheck?: boolean, + ) => boolean; + expand?: boolean; + expectedAssertionsNumber?: number; + isExpectingAssertions?: boolean; + isNot: boolean; + promise: string; + suppressedErrors: Array; + testPath?: Path; + // This is output from `jest-matcher-utils` plus iterableEquality, subsetEquality + // Type it correctly when moving it to `expect` + utils: { + printExpected: (value: unknown) => string; + printReceived: (value: unknown) => string; + iterableEquality: Tester; + subsetEquality: Tester; + }; +}; diff --git a/packages/jest-types/src/PrettyFormat.ts b/packages/jest-types/src/PrettyFormat.ts new file mode 100644 index 000000000000..89d06d5bd418 --- /dev/null +++ b/packages/jest-types/src/PrettyFormat.ts @@ -0,0 +1,117 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +export type Colors = { + comment: {close: string; open: string}; + content: {close: string; open: string}; + prop: {close: string; open: string}; + tag: {close: string; open: string}; + value: {close: string; open: string}; +}; +type Indent = (arg0: string) => string; +export type Refs = Array; +type Print = (arg0: any) => string; + +export type Theme = { + comment: string; + content: string; + prop: string; + tag: string; + value: string; +}; + +type ThemeReceived = { + comment?: string; + content?: string; + prop?: string; + tag?: string; + value?: string; +}; + +export type Options = { + callToJSON: boolean; + escapeRegex: boolean; + escapeString: boolean; + highlight: boolean; + indent: number; + maxDepth: number; + min: boolean; + plugins: Plugins; + printFunctionName: boolean; + theme: Theme; +}; + +export type OptionsReceived = { + callToJSON?: boolean; + escapeRegex?: boolean; + escapeString?: boolean; + highlight?: boolean; + indent?: number; + maxDepth?: number; + min?: boolean; + plugins?: Plugins; + printFunctionName?: boolean; + theme?: ThemeReceived; +}; + +export type Config = { + callToJSON: boolean; + colors: Colors; + escapeRegex: boolean; + escapeString: boolean; + indent: string; + maxDepth: number; + min: boolean; + plugins: Plugins; + printFunctionName: boolean; + spacingInner: string; + spacingOuter: string; +}; + +export type Printer = ( + val: any, + config: Config, + indentation: string, + depth: number, + refs: Refs, + hasCalledToJSON?: boolean, +) => string; + +type Test = (arg0: any) => boolean; + +export type NewPlugin = { + serialize: ( + val: any, + config: Config, + indentation: string, + depth: number, + refs: Refs, + printer: Printer, + ) => string; + test: Test; +}; + +type PluginOptions = { + edgeSpacing: string; + min: boolean; + spacing: string; +}; + +type OldPlugin = { + print: ( + val: any, + print: Print, + indent: Indent, + options: PluginOptions, + colors: Colors, + ) => string; + test: Test; +}; + +export type Plugin = NewPlugin | OldPlugin; + +export type Plugins = Array; diff --git a/packages/jest-types/src/index.ts b/packages/jest-types/src/index.ts index 769fb97b881b..5f72f2e75feb 100644 --- a/packages/jest-types/src/index.ts +++ b/packages/jest-types/src/index.ts @@ -11,5 +11,16 @@ import * as SourceMaps from './SourceMaps'; import * as TestResult from './TestResult'; import * as Mocks from './Mocks'; import * as Transform from './Transform'; +import * as PrettyFormat from './PrettyFormat'; +import * as Matchers from './Matchers'; -export {Config, Console, SourceMaps, TestResult, Mocks, Transform}; +export { + Config, + Console, + SourceMaps, + TestResult, + Mocks, + Transform, + PrettyFormat, + Matchers, +}; diff --git a/packages/pretty-format/package.json b/packages/pretty-format/package.json index ae89d57ddda0..ef976df12eef 100644 --- a/packages/pretty-format/package.json +++ b/packages/pretty-format/package.json @@ -13,6 +13,7 @@ "browser": "build-es5/index.js", "author": "James Kyle ", "dependencies": { + "@jest/types": "^24.1.0", "ansi-regex": "^4.0.0", "ansi-styles": "^3.2.0" }, diff --git a/packages/pretty-format/src/types.ts b/packages/pretty-format/src/types.ts index 89d06d5bd418..71f14783ed5f 100644 --- a/packages/pretty-format/src/types.ts +++ b/packages/pretty-format/src/types.ts @@ -5,113 +5,15 @@ * LICENSE file in the root directory of this source tree. */ -export type Colors = { - comment: {close: string; open: string}; - content: {close: string; open: string}; - prop: {close: string; open: string}; - tag: {close: string; open: string}; - value: {close: string; open: string}; -}; -type Indent = (arg0: string) => string; -export type Refs = Array; -type Print = (arg0: any) => string; - -export type Theme = { - comment: string; - content: string; - prop: string; - tag: string; - value: string; -}; - -type ThemeReceived = { - comment?: string; - content?: string; - prop?: string; - tag?: string; - value?: string; -}; - -export type Options = { - callToJSON: boolean; - escapeRegex: boolean; - escapeString: boolean; - highlight: boolean; - indent: number; - maxDepth: number; - min: boolean; - plugins: Plugins; - printFunctionName: boolean; - theme: Theme; -}; - -export type OptionsReceived = { - callToJSON?: boolean; - escapeRegex?: boolean; - escapeString?: boolean; - highlight?: boolean; - indent?: number; - maxDepth?: number; - min?: boolean; - plugins?: Plugins; - printFunctionName?: boolean; - theme?: ThemeReceived; -}; - -export type Config = { - callToJSON: boolean; - colors: Colors; - escapeRegex: boolean; - escapeString: boolean; - indent: string; - maxDepth: number; - min: boolean; - plugins: Plugins; - printFunctionName: boolean; - spacingInner: string; - spacingOuter: string; -}; - -export type Printer = ( - val: any, - config: Config, - indentation: string, - depth: number, - refs: Refs, - hasCalledToJSON?: boolean, -) => string; - -type Test = (arg0: any) => boolean; - -export type NewPlugin = { - serialize: ( - val: any, - config: Config, - indentation: string, - depth: number, - refs: Refs, - printer: Printer, - ) => string; - test: Test; -}; - -type PluginOptions = { - edgeSpacing: string; - min: boolean; - spacing: string; -}; - -type OldPlugin = { - print: ( - val: any, - print: Print, - indent: Indent, - options: PluginOptions, - colors: Colors, - ) => string; - test: Test; -}; - -export type Plugin = NewPlugin | OldPlugin; - -export type Plugins = Array; +import {PrettyFormat} from '@jest/types'; + +export type Colors = PrettyFormat.Colors; +export type Config = PrettyFormat.Config; +export type Options = PrettyFormat.Options; +export type OptionsReceived = PrettyFormat.OptionsReceived; +export type NewPlugin = PrettyFormat.NewPlugin; +export type Plugin = PrettyFormat.Plugin; +export type Plugins = PrettyFormat.Plugins; +export type Refs = PrettyFormat.Refs; +export type Theme = PrettyFormat.Theme; +export type Printer = PrettyFormat.Printer; diff --git a/packages/pretty-format/tsconfig.json b/packages/pretty-format/tsconfig.json index 7bb06bce6d20..3046cb6b9b6a 100644 --- a/packages/pretty-format/tsconfig.json +++ b/packages/pretty-format/tsconfig.json @@ -3,5 +3,6 @@ "compilerOptions": { "rootDir": "src", "outDir": "build" - } + }, + "references": [{"path": "../jest-types"}] } diff --git a/yarn.lock b/yarn.lock index 71ed496d78f9..d14756f8cfac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1688,6 +1688,11 @@ dependencies: "@types/node" "*" +"@types/natural-compare@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@types/natural-compare/-/natural-compare-1.4.0.tgz#b3c54f7edc339758d573c5dcac7808c58cf8c31e" + integrity sha512-bNtBj6AF1F90jp54KRPOrYfilGNfPr2kpaUN7rMJjauAtfGBXzT/T/REZN6jb4qUs9FTxU37kir3Nrn5WsTUDw== + "@types/node-notifier@^0.0.28": version "0.0.28" resolved "https://registry.yarnpkg.com/@types/node-notifier/-/node-notifier-0.0.28.tgz#86ba3d3aa8d918352cc3191d88de328b20dc93c1" @@ -1700,6 +1705,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.24.tgz#b13564af612a22a20b5d95ca40f1bffb3af315cf" integrity sha512-GWWbvt+z9G5otRBW8rssOFgRY87J9N/qbhqfjMZ+gUuL6zoL+Hm6gP/8qQBG4jjimqdaNLCehcVapZ/Fs2WjCQ== +"@types/prettier@^1.16.1": + version "1.16.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.16.1.tgz#328d1c9b54402e44119398bcb6a31b7bbd606d59" + integrity sha512-db6pZL5QY3JrlCHBhYQzYDci0xnoDuxfseUuguLRr3JNk+bnCfpkK6p8quiUDyO8A0vbpBKkk59Fw125etrNeA== + "@types/prompts@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@types/prompts/-/prompts-1.2.0.tgz#891e73f735ad5e82e8adae3a99424128e105fb62" From 0d697f8a0301e8fb2018a97c6c365acf00e70c97 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sat, 16 Feb 2019 17:21:18 +0100 Subject: [PATCH 065/107] feat: create new @jest/transform package (#7915) --- CHANGELOG.md | 1 + packages/jest-cli/package.json | 1 + packages/jest-cli/src/runGlobalHook.js | 4 +- packages/jest-reporters/package.json | 2 +- .../__tests__/generateEmptyCoverage.test.js | 13 ++++--- .../src/generateEmptyCoverage.js | 6 +-- packages/jest-runtime/package.json | 15 ++------ .../src/__tests__/instrumentation.test.js | 2 +- packages/jest-runtime/src/helpers.js | 32 ---------------- packages/jest-runtime/src/index.js | 3 +- packages/jest-transform/.npmignore | 3 ++ packages/jest-transform/package.json | 37 +++++++++++++++++++ .../src/ScriptTransformer.js | 17 ++------- .../script_transformer.test.js.snap | 0 .../src/__tests__/script_transformer.test.js | 9 ++--- .../src/__tests__/should_instrument.test.js | 9 +---- .../src/enhanceUnexpectedTokenMessage.js | 36 ++++++++++++++++++ packages/jest-transform/src/index.js | 11 ++++++ .../src/shouldInstrument.js | 2 +- packages/jest-transform/src/types.js | 21 +++++++++++ yarn.lock | 2 +- 21 files changed, 140 insertions(+), 86 deletions(-) create mode 100644 packages/jest-transform/.npmignore create mode 100644 packages/jest-transform/package.json rename packages/{jest-runtime => jest-transform}/src/ScriptTransformer.js (97%) rename packages/{jest-runtime => jest-transform}/src/__tests__/__snapshots__/script_transformer.test.js.snap (100%) rename packages/{jest-runtime => jest-transform}/src/__tests__/script_transformer.test.js (99%) rename packages/{jest-runtime => jest-transform}/src/__tests__/should_instrument.test.js (98%) create mode 100644 packages/jest-transform/src/enhanceUnexpectedTokenMessage.js create mode 100644 packages/jest-transform/src/index.js rename packages/{jest-runtime => jest-transform}/src/shouldInstrument.js (98%) create mode 100644 packages/jest-transform/src/types.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 88cc0354d2c1..c0abed449e6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ - `[jest-resolve]`: Migrate to TypeScript ([#7871](https://github.com/facebook/jest/pull/7871)) - `[@jest/reporter]`: New package extracted from `jest-cli` ([#7902](https://github.com/facebook/jest/pull/7902)) - `[jest-snapshot]`: Migrate to TypeScript ([#7899](https://github.com/facebook/jest/pull/7899)) +- `[@jest/transform]`: New package extracted from `jest-runtime` ([#7915](https://github.com/facebook/jest/pull/7915)) ### Performance diff --git a/packages/jest-cli/package.json b/packages/jest-cli/package.json index c168519633fc..4267412020a5 100644 --- a/packages/jest-cli/package.json +++ b/packages/jest-cli/package.json @@ -5,6 +5,7 @@ "main": "build/jest.js", "dependencies": { "@jest/reporters": "^24.1.0", + "@jest/transform": "^24.1.0", "ansi-escapes": "^3.0.0", "chalk": "^2.0.1", "exit": "^0.1.2", diff --git a/packages/jest-cli/src/runGlobalHook.js b/packages/jest-cli/src/runGlobalHook.js index 855d21df57e3..6c89f943f4f0 100644 --- a/packages/jest-cli/src/runGlobalHook.js +++ b/packages/jest-cli/src/runGlobalHook.js @@ -13,7 +13,7 @@ import type {Test} from 'types/TestRunner'; import {extname} from 'path'; import pEachSeries from 'p-each-series'; import {addHook} from 'pirates'; -import Runtime from 'jest-runtime'; +import {ScriptTransformer} from '@jest/transform'; // copied from https://github.com/babel/babel/blob/56044c7851d583d498f919e9546caddf8f80a72f/packages/babel-helpers/src/helpers.js#L558-L562 function _interopRequireDefault(obj) { @@ -52,7 +52,7 @@ export default ({ : // Fallback to first config allTests[0].context.config; - const transformer = new Runtime.ScriptTransformer(projectConfig); + const transformer = new ScriptTransformer(projectConfig); // Load the transformer to avoid a cycle where we need to load a // transformer in order to transform it in the require hooks diff --git a/packages/jest-reporters/package.json b/packages/jest-reporters/package.json index ac961627707c..fceb1f9e0de0 100644 --- a/packages/jest-reporters/package.json +++ b/packages/jest-reporters/package.json @@ -4,10 +4,10 @@ "version": "24.1.0", "main": "build/index.js", "dependencies": { + "@jest/transform": "^24.1.0", "chalk": "^2.0.1", "exit": "^0.1.2", "glob": "^7.1.2", - "jest-runtime": "^24.1.0", "istanbul-api": "^2.1.1", "istanbul-lib-coverage": "^2.0.2", "istanbul-lib-instrument": "^3.0.1", diff --git a/packages/jest-reporters/src/__tests__/generateEmptyCoverage.test.js b/packages/jest-reporters/src/__tests__/generateEmptyCoverage.test.js index 00e97f93a024..de5b901249fd 100644 --- a/packages/jest-reporters/src/__tests__/generateEmptyCoverage.test.js +++ b/packages/jest-reporters/src/__tests__/generateEmptyCoverage.test.js @@ -6,18 +6,19 @@ * * @flow */ -'use strict'; import istanbulCoverage from 'istanbul-lib-coverage'; import libSourceMaps from 'istanbul-lib-source-maps'; import generateEmptyCoverage from '../generateEmptyCoverage'; -import Runtime from 'jest-runtime'; -const path = require('path'); -const os = require('os'); -const {makeGlobalConfig, makeProjectConfig} = require('../../../../TestUtils'); +import path from 'path'; +import os from 'os'; +import {makeGlobalConfig, makeProjectConfig} from '../../../../TestUtils'; -jest.spyOn(Runtime, 'shouldInstrument').mockImplementation(() => true); +jest.mock('@jest/transform', () => ({ + ...jest.requireActual('@jest/transform'), + shouldInstrument: () => true, +})); const src = ` throw new Error('this should not be thrown'); diff --git a/packages/jest-reporters/src/generateEmptyCoverage.js b/packages/jest-reporters/src/generateEmptyCoverage.js index d43f2de65dab..493d251646d6 100644 --- a/packages/jest-reporters/src/generateEmptyCoverage.js +++ b/packages/jest-reporters/src/generateEmptyCoverage.js @@ -11,7 +11,7 @@ import type {GlobalConfig, ProjectConfig, Path} from 'types/Config'; import {readInitialCoverage} from 'istanbul-lib-instrument'; import {classes} from 'istanbul-lib-coverage'; -import Runtime from 'jest-runtime'; +import {shouldInstrument, ScriptTransformer} from '@jest/transform'; export type CoverageWorkerResult = {| coverage: any, @@ -33,9 +33,9 @@ export default function( collectCoverageFrom: globalConfig.collectCoverageFrom, collectCoverageOnlyFrom: globalConfig.collectCoverageOnlyFrom, }; - if (Runtime.shouldInstrument(filename, coverageOptions, config)) { + if (shouldInstrument(filename, coverageOptions, config)) { // Transform file with instrumentation to make sure initial coverage data is well mapped to original code. - const {code, mapCoverage, sourceMapPath} = new Runtime.ScriptTransformer( + const {code, mapCoverage, sourceMapPath} = new ScriptTransformer( config, ).transformSource(filename, source, true); const extracted = readInitialCoverage(code); diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index 8524b6900536..a4890ce50f79 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -9,12 +9,9 @@ "license": "MIT", "main": "build/index.js", "dependencies": { - "@babel/core": "^7.1.0", - "babel-plugin-istanbul": "^5.1.0", + "@jest/transform": "^24.1.0", "chalk": "^2.0.1", - "convert-source-map": "^1.4.0", "exit": "^0.1.2", - "fast-json-stable-stringify": "^2.0.0", "glob": "^7.1.3", "graceful-fs": "^4.1.15", "jest-config": "^24.1.0", @@ -25,24 +22,18 @@ "jest-snapshot": "^24.1.0", "jest-util": "^24.0.0", "jest-validate": "^24.0.0", - "micromatch": "^3.1.10", "realpath-native": "^1.1.0", "slash": "^2.0.0", "strip-bom": "^3.0.0", - "write-file-atomic": "2.4.1", "yargs": "^12.0.2" }, "devDependencies": { - "@types/babel__core": "^7.0.4", - "@types/convert-source-map": "^1.5.1", "@types/exit": "^0.1.30", "@types/glob": "^7.1.1", "@types/graceful-fs": "^4.1.2", - "@types/micromatch": "^3.1.0", - "@types/strip-bom": "3.0.0", - "@types/write-file-atomic": "^2.1.1", + "@types/slash": "^2.0.0", + "@types/strip-bom": "^3.0.0", "@types/yargs": "^12.0.2", - "jest-environment-jsdom": "^24.0.0", "jest-environment-node": "^24.0.0" }, "bin": { diff --git a/packages/jest-runtime/src/__tests__/instrumentation.test.js b/packages/jest-runtime/src/__tests__/instrumentation.test.js index 8a6ce4be03d8..8f6188d86ca3 100644 --- a/packages/jest-runtime/src/__tests__/instrumentation.test.js +++ b/packages/jest-runtime/src/__tests__/instrumentation.test.js @@ -11,7 +11,7 @@ import vm from 'vm'; import path from 'path'; import os from 'os'; -import ScriptTransformer from '../ScriptTransformer'; +import {ScriptTransformer} from '@jest/transform'; jest.mock('vm'); diff --git a/packages/jest-runtime/src/helpers.js b/packages/jest-runtime/src/helpers.js index c80f82755d4c..19265d836044 100644 --- a/packages/jest-runtime/src/helpers.js +++ b/packages/jest-runtime/src/helpers.js @@ -5,41 +5,9 @@ import type {Path} from 'types/Config'; import path from 'path'; -import chalk from 'chalk'; import slash from 'slash'; import glob from 'glob'; -const DOT = ' \u2022 '; - -export const enhanceUnexpectedTokenMessage = (e: Error) => { - e.stack = - `${chalk.bold.red('Jest encountered an unexpected token')} - -This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript. - -By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules". - -Here's what you can do: -${DOT}To have some of your "node_modules" files transformed, you can specify a custom ${chalk.bold( - '"transformIgnorePatterns"', - )} in your config. -${DOT}If you need a custom transformation specify a ${chalk.bold( - '"transform"', - )} option in your config. -${DOT}If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the ${chalk.bold( - '"moduleNameMapper"', - )} config option. - -You'll find more details and examples of these config options in the docs: -${chalk.cyan('https://jestjs.io/docs/en/configuration.html')} - -${chalk.bold.red('Details:')} - -` + e.stack; - - return e; -}; - export const findSiblingsWithFileExtension = ( moduleFileExtensions: Array, from: Path, diff --git a/packages/jest-runtime/src/index.js b/packages/jest-runtime/src/index.js index 2f7290c73711..e6f393eef733 100644 --- a/packages/jest-runtime/src/index.js +++ b/packages/jest-runtime/src/index.js @@ -24,10 +24,9 @@ import Resolver from 'jest-resolve'; import {createDirectory, deepCyclicCopy} from 'jest-util'; import {escapePathForRegex} from 'jest-regex-util'; import Snapshot from 'jest-snapshot'; +import {ScriptTransformer, shouldInstrument} from '@jest/transform'; import fs from 'graceful-fs'; import stripBOM from 'strip-bom'; -import ScriptTransformer from './ScriptTransformer'; -import shouldInstrument from './shouldInstrument'; import {run as cliRun} from './cli'; import {options as cliOptions} from './cli/args'; import {findSiblingsWithFileExtension} from './helpers'; diff --git a/packages/jest-transform/.npmignore b/packages/jest-transform/.npmignore new file mode 100644 index 000000000000..85e48fe7b0a4 --- /dev/null +++ b/packages/jest-transform/.npmignore @@ -0,0 +1,3 @@ +**/__mocks__/** +**/__tests__/** +src diff --git a/packages/jest-transform/package.json b/packages/jest-transform/package.json new file mode 100644 index 000000000000..345321986283 --- /dev/null +++ b/packages/jest-transform/package.json @@ -0,0 +1,37 @@ +{ + "name": "@jest/transform", + "version": "24.1.0", + "repository": { + "type": "git", + "url": "https://github.com/facebook/jest.git", + "directory": "packages/jest-transform" + }, + "license": "MIT", + "main": "build/index.js", + "dependencies": { + "@babel/core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.0.0", + "jest-regex-util": "^24.0.0", + "jest-util": "^24.0.0", + "micromatch": "^3.1.10", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "write-file-atomic": "2.4.1" + }, + "devDependencies": { + "@types/babel__core": "^7.0.4", + "@types/convert-source-map": "^1.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/micromatch": "^3.1.0", + "@types/write-file-atomic": "^2.1.1" + }, + "engines": { + "node": ">= 6" + }, + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" +} diff --git a/packages/jest-runtime/src/ScriptTransformer.js b/packages/jest-transform/src/ScriptTransformer.js similarity index 97% rename from packages/jest-runtime/src/ScriptTransformer.js rename to packages/jest-transform/src/ScriptTransformer.js index 72ba88f9fabd..44b5959fb39e 100644 --- a/packages/jest-runtime/src/ScriptTransformer.js +++ b/packages/jest-transform/src/ScriptTransformer.js @@ -7,13 +7,14 @@ * @flow */ -import type {Glob, Path, ProjectConfig} from 'types/Config'; +import type {Path, ProjectConfig} from 'types/Config'; import type { Transformer, TransformedSource, TransformResult, } from 'types/Transform'; import type {ErrorWithCode} from 'types/Errors'; +import type {Options} from './types'; import crypto from 'crypto'; import path from 'path'; @@ -30,17 +31,7 @@ import {version as VERSION} from '../package.json'; import shouldInstrument from './shouldInstrument'; import writeFileAtomic from 'write-file-atomic'; import {sync as realpath} from 'realpath-native'; -import {enhanceUnexpectedTokenMessage} from './helpers'; - -export type Options = {| - changedFiles: ?Set, - collectCoverage: boolean, - collectCoverageFrom: Array, - collectCoverageOnlyFrom: ?{[key: string]: boolean, __proto__: null}, - extraGlobals?: Array, - isCoreModule?: boolean, - isInternalModule?: boolean, -|}; +import enhanceUnexpectedTokenMessage from './enhanceUnexpectedTokenMessage'; type ProjectCache = {| configString: string, @@ -175,7 +166,7 @@ export default class ScriptTransformer { auxiliaryCommentBefore: ' istanbul ignore next ', babelrc: false, caller: { - name: 'jest-runtime', + name: '@jest/transform', supportsStaticESM: false, }, configFile: false, diff --git a/packages/jest-runtime/src/__tests__/__snapshots__/script_transformer.test.js.snap b/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap similarity index 100% rename from packages/jest-runtime/src/__tests__/__snapshots__/script_transformer.test.js.snap rename to packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap diff --git a/packages/jest-runtime/src/__tests__/script_transformer.test.js b/packages/jest-transform/src/__tests__/script_transformer.test.js similarity index 99% rename from packages/jest-runtime/src/__tests__/script_transformer.test.js rename to packages/jest-transform/src/__tests__/script_transformer.test.js index 7058e0f22f31..bc61eaa04b1b 100644 --- a/packages/jest-runtime/src/__tests__/script_transformer.test.js +++ b/packages/jest-transform/src/__tests__/script_transformer.test.js @@ -32,11 +32,10 @@ jest .mock('jest-haste-map', () => ({ getCacheFilePath: (cacheDir, baseDir, version) => cacheDir + baseDir, })) - .mock('jest-util', () => { - const util = jest.requireActual('jest-util'); - util.createDirectory = jest.fn(); - return util; - }) + .mock('jest-util', () => ({ + ...jest.requireActual('jest-util'), + createDirectory: jest.fn(), + })) .mock('vm') .mock('path', () => jest.requireActual('path').posix); diff --git a/packages/jest-runtime/src/__tests__/should_instrument.test.js b/packages/jest-transform/src/__tests__/should_instrument.test.js similarity index 98% rename from packages/jest-runtime/src/__tests__/should_instrument.test.js rename to packages/jest-transform/src/__tests__/should_instrument.test.js index dc3ba29e8ce3..c05dacc5677e 100644 --- a/packages/jest-runtime/src/__tests__/should_instrument.test.js +++ b/packages/jest-transform/src/__tests__/should_instrument.test.js @@ -6,20 +6,15 @@ * */ -import {normalize} from 'jest-config'; import shouldInstrument from '../shouldInstrument'; +import {makeGlobalConfig} from '../../../../TestUtils'; describe('shouldInstrument', () => { const defaultFilename = 'source_file.test.js'; const defaultOptions = { collectCoverage: true, }; - const defaultConfig = normalize( - { - rootDir: '/', - }, - {}, - ).options; + const defaultConfig = makeGlobalConfig({rootDir: '/'}); describe('should return true', () => { const testShouldInstrument = ( diff --git a/packages/jest-transform/src/enhanceUnexpectedTokenMessage.js b/packages/jest-transform/src/enhanceUnexpectedTokenMessage.js new file mode 100644 index 000000000000..af941fa7cf53 --- /dev/null +++ b/packages/jest-transform/src/enhanceUnexpectedTokenMessage.js @@ -0,0 +1,36 @@ +// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +// @flow + +import chalk from 'chalk'; + +const DOT = ' \u2022 '; + +export default function enhanceUnexpectedTokenMessage(e: Error) { + e.stack = + `${chalk.bold.red('Jest encountered an unexpected token')} + +This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript. + +By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules". + +Here's what you can do: +${DOT}To have some of your "node_modules" files transformed, you can specify a custom ${chalk.bold( + '"transformIgnorePatterns"', + )} in your config. +${DOT}If you need a custom transformation specify a ${chalk.bold( + '"transform"', + )} option in your config. +${DOT}If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the ${chalk.bold( + '"moduleNameMapper"', + )} config option. + +You'll find more details and examples of these config options in the docs: +${chalk.cyan('https://jestjs.io/docs/en/configuration.html')} + +${chalk.bold.red('Details:')} + +` + e.stack; + + return e; +} diff --git a/packages/jest-transform/src/index.js b/packages/jest-transform/src/index.js new file mode 100644 index 000000000000..37cbb258b92a --- /dev/null +++ b/packages/jest-transform/src/index.js @@ -0,0 +1,11 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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 as ScriptTransformer} from './ScriptTransformer'; +export {default as shouldInstrument} from './shouldInstrument'; diff --git a/packages/jest-runtime/src/shouldInstrument.js b/packages/jest-transform/src/shouldInstrument.js similarity index 98% rename from packages/jest-runtime/src/shouldInstrument.js rename to packages/jest-transform/src/shouldInstrument.js index ecfe24fb14f1..c0b869d4318b 100644 --- a/packages/jest-runtime/src/shouldInstrument.js +++ b/packages/jest-transform/src/shouldInstrument.js @@ -8,7 +8,7 @@ */ import type {Path, ProjectConfig} from 'types/Config'; -import type {Options} from './ScriptTransformer'; +import type {Options} from './types'; import path from 'path'; import {escapePathForRegex} from 'jest-regex-util'; diff --git a/packages/jest-transform/src/types.js b/packages/jest-transform/src/types.js new file mode 100644 index 000000000000..00f577bdad80 --- /dev/null +++ b/packages/jest-transform/src/types.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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 type {Glob, Path} from 'types/Config'; + +// TODO: Pick from `GlobalConfig` +export type Options = {| + changedFiles: ?Set, + collectCoverage: boolean, + collectCoverageFrom: Array, + collectCoverageOnlyFrom: ?{[key: string]: boolean, __proto__: null}, + extraGlobals?: Array, + isCoreModule?: boolean, + isInternalModule?: boolean, +|}; diff --git a/yarn.lock b/yarn.lock index d14756f8cfac..467177c5e637 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1801,7 +1801,7 @@ resolved "https://registry.yarnpkg.com/@types/strip-ansi/-/strip-ansi-3.0.0.tgz#9b63d453a6b54aa849182207711a08be8eea48ae" integrity sha1-m2PUU6a1SqhJGCIHcRoIvo7qSK4= -"@types/strip-bom@3.0.0": +"@types/strip-bom@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= From 31497cea4812b21a0e73d090b5cf079040756215 Mon Sep 17 00:00:00 2001 From: Rangga Adhitya Prawira Date: Sat, 16 Feb 2019 23:51:54 +0700 Subject: [PATCH 066/107] chore: Migrate babel-plugin-jest-hoist to Typescript (#7898) --- CHANGELOG.md | 1 + packages/babel-jest/tsconfig.json | 6 +- packages/babel-plugin-jest-hoist/package.json | 7 + packages/babel-plugin-jest-hoist/src/index.js | 168 ---------------- packages/babel-plugin-jest-hoist/src/index.ts | 186 ++++++++++++++++++ .../babel-plugin-jest-hoist/tsconfig.json | 7 + yarn.lock | 20 +- 7 files changed, 215 insertions(+), 180 deletions(-) delete mode 100644 packages/babel-plugin-jest-hoist/src/index.js create mode 100644 packages/babel-plugin-jest-hoist/src/index.ts create mode 100644 packages/babel-plugin-jest-hoist/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index c0abed449e6e..b490b297a6f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ - `[@jest/reporter]`: New package extracted from `jest-cli` ([#7902](https://github.com/facebook/jest/pull/7902)) - `[jest-snapshot]`: Migrate to TypeScript ([#7899](https://github.com/facebook/jest/pull/7899)) - `[@jest/transform]`: New package extracted from `jest-runtime` ([#7915](https://github.com/facebook/jest/pull/7915)) +- `[babel-plugin-jest-hoist]`: Migrate to TypeScript ([#7898](https://github.com/facebook/jest/pull/7898)) ### Performance diff --git a/packages/babel-jest/tsconfig.json b/packages/babel-jest/tsconfig.json index 042289e959d6..75c28b2f54a9 100644 --- a/packages/babel-jest/tsconfig.json +++ b/packages/babel-jest/tsconfig.json @@ -4,6 +4,8 @@ "rootDir": "src", "outDir": "build" }, - // TODO: include `babel-preset-jest` even though we don't care about its types - "references": [{"path": "../jest-types"}] + // TODO: include `babel-preset-jest` if it's ever in TS even though we don't care about its types + "references": [ + {"path": "../jest-types"} + ] } diff --git a/packages/babel-plugin-jest-hoist/package.json b/packages/babel-plugin-jest-hoist/package.json index b02241e5c3fc..33c652d33a19 100644 --- a/packages/babel-plugin-jest-hoist/package.json +++ b/packages/babel-plugin-jest-hoist/package.json @@ -11,5 +11,12 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", + "dependencies": { + "@types/babel__traverse": "^7.0.6" + }, + "devDependencies": { + "@babel/types": "^7.3.3" + }, "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" } diff --git a/packages/babel-plugin-jest-hoist/src/index.js b/packages/babel-plugin-jest-hoist/src/index.js deleted file mode 100644 index 9445cffde661..000000000000 --- a/packages/babel-plugin-jest-hoist/src/index.js +++ /dev/null @@ -1,168 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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 - */ - -function invariant(condition, message) { - if (!condition) { - throw new Error('babel-plugin-jest-hoist: ' + message); - } -} - -// We allow `jest`, `expect`, `require`, all default Node.js globals and all -// ES2015 built-ins to be used inside of a `jest.mock` factory. -// We also allow variables prefixed with `mock` as an escape-hatch. -const WHITELISTED_IDENTIFIERS = { - Array: true, - ArrayBuffer: true, - Boolean: true, - DataView: true, - Date: true, - Error: true, - EvalError: true, - Float32Array: true, - Float64Array: true, - Function: true, - Generator: true, - GeneratorFunction: true, - Infinity: true, - Int16Array: true, - Int32Array: true, - Int8Array: true, - InternalError: true, - Intl: true, - JSON: true, - Map: true, - Math: true, - NaN: true, - Number: true, - Object: true, - Promise: true, - Proxy: true, - RangeError: true, - ReferenceError: true, - Reflect: true, - RegExp: true, - Set: true, - String: true, - Symbol: true, - SyntaxError: true, - TypeError: true, - URIError: true, - Uint16Array: true, - Uint32Array: true, - Uint8Array: true, - Uint8ClampedArray: true, - WeakMap: true, - WeakSet: true, - arguments: true, - console: true, - expect: true, - isNaN: true, - jest: true, - parseFloat: true, - parseInt: true, - require: true, - undefined: true, -}; -Object.keys(global).forEach(name => (WHITELISTED_IDENTIFIERS[name] = true)); - -const JEST_GLOBAL = {name: 'jest'}; -const IDVisitor = { - ReferencedIdentifier(path) { - this.ids.add(path); - }, - blacklist: ['TypeAnnotation', 'TSTypeAnnotation', 'TSTypeReference'], -}; - -const FUNCTIONS: Object = Object.create(null); -FUNCTIONS.mock = args => { - if (args.length === 1) { - return args[0].isStringLiteral() || args[0].isLiteral(); - } else if (args.length === 2 || args.length === 3) { - const moduleFactory = args[1]; - invariant( - moduleFactory.isFunction(), - 'The second argument of `jest.mock` must be an inline function.', - ); - - const ids = new Set(); - const parentScope = moduleFactory.parentPath.scope; - moduleFactory.traverse(IDVisitor, {ids}); - for (const id of ids) { - const name = id.node.name; - let found = false; - let scope = id.scope; - - while (scope !== parentScope) { - if (scope.bindings[name]) { - found = true; - break; - } - - scope = scope.parent; - } - - if (!found) { - invariant( - (scope.hasGlobal(name) && WHITELISTED_IDENTIFIERS[name]) || - /^mock/i.test(name) || - // Allow istanbul's coverage variable to pass. - /^(?:__)?cov/.test(name), - 'The module factory of `jest.mock()` is not allowed to ' + - 'reference any out-of-scope variables.\n' + - 'Invalid variable access: ' + - name + - '\n' + - 'Whitelisted objects: ' + - Object.keys(WHITELISTED_IDENTIFIERS).join(', ') + - '.\n' + - 'Note: This is a precaution to guard against uninitialized mock ' + - 'variables. If it is ensured that the mock is required lazily, ' + - 'variable names prefixed with `mock` (case insensitive) are permitted.', - ); - } - } - - return true; - } - return false; -}; - -FUNCTIONS.unmock = args => args.length === 1 && args[0].isStringLiteral(); -FUNCTIONS.deepUnmock = args => args.length === 1 && args[0].isStringLiteral(); - -FUNCTIONS.disableAutomock = FUNCTIONS.enableAutomock = args => - args.length === 0; - -module.exports = () => { - const shouldHoistExpression = expr => { - if (!expr.isCallExpression()) { - return false; - } - - const callee = expr.get('callee'); - const object = callee.get('object'); - const property = callee.get('property'); - return ( - property.isIdentifier() && - FUNCTIONS[property.node.name] && - (object.isIdentifier(JEST_GLOBAL) || - (callee.isMemberExpression() && shouldHoistExpression(object))) && - FUNCTIONS[property.node.name](expr.get('arguments')) - ); - }; - return { - visitor: { - ExpressionStatement(path: any) { - if (shouldHoistExpression(path.get('expression'))) { - path.node._blockHoist = Infinity; - } - }, - }, - }; -}; diff --git a/packages/babel-plugin-jest-hoist/src/index.ts b/packages/babel-plugin-jest-hoist/src/index.ts new file mode 100644 index 000000000000..f99fa9675691 --- /dev/null +++ b/packages/babel-plugin-jest-hoist/src/index.ts @@ -0,0 +1,186 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + * + */ + +// Only used for types +// eslint-disable-next-line +import {NodePath, Visitor} from '@babel/traverse'; +// eslint-disable-next-line +import {Identifier} from '@babel/types'; + +const invariant = (condition: unknown, message: string) => { + if (!condition) { + throw new Error('babel-plugin-jest-hoist: ' + message); + } +}; + +// We allow `jest`, `expect`, `require`, all default Node.js globals and all +// ES2015 built-ins to be used inside of a `jest.mock` factory. +// We also allow variables prefixed with `mock` as an escape-hatch. +const WHITELISTED_IDENTIFIERS: Set = new Set([ + 'Array', + 'ArrayBuffer', + 'Boolean', + 'DataView', + 'Date', + 'Error', + 'EvalError', + 'Float32Array', + 'Float64Array', + 'Function', + 'Generator', + 'GeneratorFunction', + 'Infinity', + 'Int16Array', + 'Int32Array', + 'Int8Array', + 'InternalError', + 'Intl', + 'JSON', + 'Map', + 'Math', + 'NaN', + 'Number', + 'Object', + 'Promise', + 'Proxy', + 'RangeError', + 'ReferenceError', + 'Reflect', + 'RegExp', + 'Set', + 'String', + 'Symbol', + 'SyntaxError', + 'TypeError', + 'URIError', + 'Uint16Array', + 'Uint32Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'WeakMap', + 'WeakSet', + 'arguments', + 'console', + 'expect', + 'isNaN', + 'jest', + 'parseFloat', + 'parseInt', + 'require', + 'undefined', +]); +Object.keys(global).forEach(name => { + WHITELISTED_IDENTIFIERS.add(name); +}); + +const JEST_GLOBAL = {name: 'jest'}; +// TODO: Should be Visitor<{ids: Set>}>, but `ReferencedIdentifier` doesn't exist +const IDVisitor = { + ReferencedIdentifier(path: NodePath) { + // @ts-ignore: passed as Visitor State + this.ids.add(path); + }, + blacklist: ['TypeAnnotation', 'TSTypeAnnotation', 'TSTypeReference'], +}; + +const FUNCTIONS: { + [key: string]: (args: Array) => boolean; +} = Object.create(null); + +FUNCTIONS.mock = (args: Array) => { + if (args.length === 1) { + return args[0].isStringLiteral() || args[0].isLiteral(); + } else if (args.length === 2 || args.length === 3) { + const moduleFactory = args[1]; + invariant( + moduleFactory.isFunction(), + 'The second argument of `jest.mock` must be an inline function.', + ); + + const ids: Set> = new Set(); + const parentScope = moduleFactory.parentPath.scope; + // @ts-ignore: Same as above: ReferencedIdentifier doesn't exist + moduleFactory.traverse(IDVisitor, {ids}); + for (const id of ids) { + const {name} = id.node; + let found = false; + let scope = id.scope; + + while (scope !== parentScope) { + if (scope.bindings[name]) { + found = true; + break; + } + + scope = scope.parent; + } + + if (!found) { + invariant( + (scope.hasGlobal(name) && WHITELISTED_IDENTIFIERS.has(name)) || + /^mock/i.test(name) || + // Allow istanbul's coverage variable to pass. + /^(?:__)?cov/.test(name), + 'The module factory of `jest.mock()` is not allowed to ' + + 'reference any out-of-scope variables.\n' + + 'Invalid variable access: ' + + name + + '\n' + + 'Whitelisted objects: ' + + Array.from(WHITELISTED_IDENTIFIERS).join(', ') + + '.\n' + + 'Note: This is a precaution to guard against uninitialized mock ' + + 'variables. If it is ensured that the mock is required lazily, ' + + 'variable names prefixed with `mock` (case insensitive) are permitted.', + ); + } + } + + return true; + } + return false; +}; + +FUNCTIONS.unmock = (args: Array) => + args.length === 1 && args[0].isStringLiteral(); +FUNCTIONS.deepUnmock = (args: Array) => + args.length === 1 && args[0].isStringLiteral(); +FUNCTIONS.disableAutomock = FUNCTIONS.enableAutomock = ( + args: Array, +) => args.length === 0; + +export = () => { + const shouldHoistExpression = (expr: NodePath): boolean => { + if (!expr.isCallExpression()) { + return false; + } + + const callee = expr.get('callee'); + // TODO: avoid type casts - the types can be arrays (is it possible to ignore that without casting?) + const object = callee.get('object') as NodePath; + const property = callee.get('property') as NodePath; + return ( + property.isIdentifier() && + FUNCTIONS[property.node.name] && + (object.isIdentifier(JEST_GLOBAL) || + (callee.isMemberExpression() && shouldHoistExpression(object))) && + FUNCTIONS[property.node.name](expr.get('arguments')) + ); + }; + + const visitor: Visitor = { + ExpressionStatement(path) { + if (shouldHoistExpression(path.get('expression'))) { + // @ts-ignore: private, magical property + path.node._blockHoist = Infinity; + } + }, + }; + + return {visitor}; +}; diff --git a/packages/babel-plugin-jest-hoist/tsconfig.json b/packages/babel-plugin-jest-hoist/tsconfig.json new file mode 100644 index 000000000000..7bb06bce6d20 --- /dev/null +++ b/packages/babel-plugin-jest-hoist/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + } +} diff --git a/yarn.lock b/yarn.lock index 467177c5e637..a78b50b6fcd2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -838,13 +838,13 @@ globals "^11.1.0" lodash "^4.17.10" -"@babel/types@^7.0.0", "@babel/types@^7.1.2", "@babel/types@^7.2.0", "@babel/types@^7.2.2": - version "7.2.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.2.2.tgz#44e10fc24e33af524488b716cdaee5360ea8ed1e" - integrity sha512-fKCuD6UFUMkR541eDWL+2ih/xFZBXPOg/7EQFeTluMDebfqR4jrpaCjLhkWlQS4hT6nRa2PMEgXKbRB5/H2fpg== +"@babel/types@^7.0.0", "@babel/types@^7.1.2", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.3.0": + version "7.3.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.3.3.tgz#6c44d1cdac2a7625b624216657d5bc6c107ab436" + integrity sha512-2tACZ80Wg09UnPg5uGAOUvvInaqLk3l/IAhQzlxLQOIXacr6bMsra5SH6AWw/hIDRCSbCdHP2KzSOD+cT7TzMQ== dependencies: esutils "^2.0.2" - lodash "^4.17.10" + lodash "^4.17.11" to-fast-properties "^2.0.0" "@lerna/add@3.10.5": @@ -1514,12 +1514,12 @@ "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@^7.0.4": - version "7.0.4" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.4.tgz#de652399bd8493ab712e4d6b68031b7a2f72ad5f" - integrity sha512-2vARZYqR4Flf59WtqMfa9GgbOjK04xLZaN9+CMf7Cs+4cAhxZBP3K9LYRzsWxOQe402VvqX9+7DdXxB72ujEOg== +"@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.6.tgz#328dd1a8fc4cfe3c8458be9477b219ea158fd7b2" + integrity sha512-XYVgHF2sQ0YblLRMLNPB3CkFMewzFmlDsH/TneZFHUXDlABQgh88uOxuez7ZcXxayLFrqLwtDH1t+FmlFwNZxw== dependencies: - "@babel/types" "^7.0.0" + "@babel/types" "^7.3.0" "@types/braces@*": version "2.3.0" From 8d6c6cefcc486e98dbad2cc43b39dfd12dbeb4de Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sat, 16 Feb 2019 23:08:08 +0100 Subject: [PATCH 067/107] create @jest/core package (#7696) --- CHANGELOG.md | 1 + jest.config.js | 6 +- packages/jest-cli/package.json | 34 +-- packages/jest-cli/src/cli/index.js | 279 +++--------------- packages/jest-cli/src/index.js | 13 + .../__tests__/__snapshots__/init.test.js.snap | 0 .../modify_package_json.test.js.snap | 0 .../has_jest_config_file/jest.config.js | 0 .../has_jest_config_file/package.json | 0 .../package.json | 0 .../fixtures/no_package_json/index.js | 0 .../fixtures/only_package_json/package.json | 0 .../test_script_configured/package.json | 0 .../typescript_in_dependencies/package.json | 0 .../package.json | 0 .../src/{lib => init}/__tests__/init.test.js | 2 +- .../__tests__/modify_package_json.test.js | 2 +- packages/jest-cli/src/{ => init}/constants.js | 0 .../jest-cli/src/{lib => }/init/errors.js | 0 .../{lib => }/init/generate_config_file.js | 0 packages/jest-cli/src/{lib => }/init/index.js | 2 +- .../src/{lib => }/init/modify_package_json.js | 0 .../jest-cli/src/{lib => }/init/questions.js | 0 packages/jest-cli/src/jest.js | 23 -- packages/jest-core/.npmignore | 3 + packages/jest-core/README.md | 3 + packages/jest-core/package.json | 78 +++++ .../src/FailedTestsCache.js | 0 .../src/ReporterDispatcher.js | 0 .../src/SearchSource.js | 0 .../src/SnapshotInteractiveMode.js | 0 .../src/TestNamePatternPrompt.js | 0 .../src/TestPathPatternPrompt.js | 0 .../src/TestScheduler.js | 0 .../src/TestSequencer.js | 0 .../src/TestWatcher.js | 0 .../src/__tests__/FailedTestsCache.test.js | 0 .../src/__tests__/SearchSource.test.js | 6 +- .../__tests__/SnapshotInteractiveMode.test.js | 0 .../src/__tests__/TestScheduler.test.js | 0 .../__tests__/__fixtures__/watch_plugin.js | 0 .../__tests__/__fixtures__/watch_plugin2.js | 0 .../SnapshotInteractiveMode.test.js.snap | 0 .../__snapshots__/watch.test.js.snap | 0 .../watch_filename_pattern_mode.test.js.snap | 0 .../watch_test_name_pattern_mode.test.js.snap | 0 .../src/__tests__/globals.test.js | 0 .../src/__tests__/run_jest.test.js | 0 .../src/__tests__/testSchedulerHelper.test.js | 0 .../.hiddenFolder/not-really-a-test.txt | 0 .../__testtests__/do-not-match-me.txt | 0 .../__testtests__/not-really-a-test.txt | 0 .../test_root/__testtests__/test.foobar | 0 .../__tests__/test_root/__testtests__/test.js | 0 .../test_root/__testtests__/test.jsx | 0 .../src/__tests__/test_root/module.foobar | 0 .../src/__tests__/test_root/module.jsx | 0 .../src/__tests__/test_root/no_tests.js | 0 .../__testtests__/test.js | 0 .../test_root_with_(parentheses)/module.jsx | 0 .../src/__tests__/test_sequencer.test.js | 0 .../src/__tests__/watch.test.js | 0 .../watch_filename_pattern_mode.test.js | 0 .../watch_test_name_pattern_mode.test.js | 0 .../src/assets/jest_logo.png | Bin packages/jest-core/src/cli/index.js | 211 +++++++++++++ .../src/collectHandles.js | 0 .../src/coverage.template | 0 .../src/getChangedFilesPromise.js | 0 .../src/getNoTestFound.js | 0 .../src/getNoTestFoundFailed.js | 0 .../getNoTestFoundRelatedToChangedFiles.js | 0 .../src/getNoTestFoundVerbose.js | 0 .../src/getNoTestsFoundMessage.js | 0 packages/jest-core/src/jest.js | 13 + .../log_debug_messages.test.js.snap | 0 .../src/lib/__tests__/is_valid_path.test.js | 0 .../lib/__tests__/log_debug_messages.test.js | 0 .../src/lib/active_filters_message.js | 0 .../src/lib/create_context.js | 0 .../src/lib/handle_deprecation_warnings.js | 0 .../src/lib/is_valid_path.js | 0 .../src/lib/log_debug_messages.js | 0 .../src/lib/update_global_config.js | 0 .../src/lib/watch_plugins_helpers.js | 0 .../src/plugins/quit.js | 0 .../src/plugins/test_name_pattern.js | 0 .../src/plugins/test_path_pattern.js | 0 .../src/plugins/update_snapshots.js | 0 .../plugins/update_snapshots_interactive.js | 0 .../{jest-cli => jest-core}/src/pluralize.js | 0 .../src/runGlobalHook.js | 0 .../{jest-cli => jest-core}/src/runJest.js | 0 .../src/testResultHelpers.js | 0 .../src/testSchedulerHelper.js | 0 packages/{jest-cli => jest-core}/src/types.js | 0 packages/{jest-cli => jest-core}/src/watch.js | 0 .../src/__tests__/testRunner.test.js | 7 +- packages/jest/src/jest.js | 2 +- types/TestRunner.js | 8 +- yarn.lock | 9 +- 101 files changed, 385 insertions(+), 317 deletions(-) create mode 100644 packages/jest-cli/src/index.js rename packages/jest-cli/src/{lib => init}/__tests__/__snapshots__/init.test.js.snap (100%) rename packages/jest-cli/src/{lib => init}/__tests__/__snapshots__/modify_package_json.test.js.snap (100%) rename packages/jest-cli/src/{lib => init}/__tests__/fixtures/has_jest_config_file/jest.config.js (100%) rename packages/jest-cli/src/{lib => init}/__tests__/fixtures/has_jest_config_file/package.json (100%) rename packages/jest-cli/src/{lib => init}/__tests__/fixtures/has_jest_config_in_package_json/package.json (100%) rename packages/jest-cli/src/{lib => init}/__tests__/fixtures/no_package_json/index.js (100%) rename packages/jest-cli/src/{lib => init}/__tests__/fixtures/only_package_json/package.json (100%) rename packages/jest-cli/src/{lib => init}/__tests__/fixtures/test_script_configured/package.json (100%) rename packages/jest-cli/src/{lib => init}/__tests__/fixtures/typescript_in_dependencies/package.json (100%) rename packages/jest-cli/src/{lib => init}/__tests__/fixtures/typescript_in_dev_dependencies/package.json (100%) rename packages/jest-cli/src/{lib => init}/__tests__/init.test.js (99%) rename packages/jest-cli/src/{lib => init}/__tests__/modify_package_json.test.js (94%) rename packages/jest-cli/src/{ => init}/constants.js (100%) rename packages/jest-cli/src/{lib => }/init/errors.js (100%) rename packages/jest-cli/src/{lib => }/init/generate_config_file.js (100%) rename packages/jest-cli/src/{lib => }/init/index.js (98%) rename packages/jest-cli/src/{lib => }/init/modify_package_json.js (100%) rename packages/jest-cli/src/{lib => }/init/questions.js (100%) delete mode 100644 packages/jest-cli/src/jest.js create mode 100644 packages/jest-core/.npmignore create mode 100644 packages/jest-core/README.md create mode 100644 packages/jest-core/package.json rename packages/{jest-cli => jest-core}/src/FailedTestsCache.js (100%) rename packages/{jest-cli => jest-core}/src/ReporterDispatcher.js (100%) rename packages/{jest-cli => jest-core}/src/SearchSource.js (100%) rename packages/{jest-cli => jest-core}/src/SnapshotInteractiveMode.js (100%) rename packages/{jest-cli => jest-core}/src/TestNamePatternPrompt.js (100%) rename packages/{jest-cli => jest-core}/src/TestPathPatternPrompt.js (100%) rename packages/{jest-cli => jest-core}/src/TestScheduler.js (100%) rename packages/{jest-cli => jest-core}/src/TestSequencer.js (100%) rename packages/{jest-cli => jest-core}/src/TestWatcher.js (100%) rename packages/{jest-cli => jest-core}/src/__tests__/FailedTestsCache.test.js (100%) rename packages/{jest-cli => jest-core}/src/__tests__/SearchSource.test.js (98%) rename packages/{jest-cli => jest-core}/src/__tests__/SnapshotInteractiveMode.test.js (100%) rename packages/{jest-cli => jest-core}/src/__tests__/TestScheduler.test.js (100%) rename packages/{jest-cli => jest-core}/src/__tests__/__fixtures__/watch_plugin.js (100%) rename packages/{jest-cli => jest-core}/src/__tests__/__fixtures__/watch_plugin2.js (100%) rename packages/{jest-cli => jest-core}/src/__tests__/__snapshots__/SnapshotInteractiveMode.test.js.snap (100%) rename packages/{jest-cli => jest-core}/src/__tests__/__snapshots__/watch.test.js.snap (100%) rename packages/{jest-cli => jest-core}/src/__tests__/__snapshots__/watch_filename_pattern_mode.test.js.snap (100%) rename packages/{jest-cli => jest-core}/src/__tests__/__snapshots__/watch_test_name_pattern_mode.test.js.snap (100%) rename packages/{jest-cli => jest-core}/src/__tests__/globals.test.js (100%) rename packages/{jest-cli => jest-core}/src/__tests__/run_jest.test.js (100%) rename packages/{jest-cli => jest-core}/src/__tests__/testSchedulerHelper.test.js (100%) rename packages/{jest-cli => jest-core}/src/__tests__/test_root/.hiddenFolder/not-really-a-test.txt (100%) rename packages/{jest-cli => jest-core}/src/__tests__/test_root/__testtests__/do-not-match-me.txt (100%) rename packages/{jest-cli => jest-core}/src/__tests__/test_root/__testtests__/not-really-a-test.txt (100%) rename packages/{jest-cli => jest-core}/src/__tests__/test_root/__testtests__/test.foobar (100%) rename packages/{jest-cli => jest-core}/src/__tests__/test_root/__testtests__/test.js (100%) rename packages/{jest-cli => jest-core}/src/__tests__/test_root/__testtests__/test.jsx (100%) rename packages/{jest-cli => jest-core}/src/__tests__/test_root/module.foobar (100%) rename packages/{jest-cli => jest-core}/src/__tests__/test_root/module.jsx (100%) rename packages/{jest-cli => jest-core}/src/__tests__/test_root/no_tests.js (100%) rename packages/{jest-cli => jest-core}/src/__tests__/test_root_with_(parentheses)/__testtests__/test.js (100%) rename packages/{jest-cli => jest-core}/src/__tests__/test_root_with_(parentheses)/module.jsx (100%) rename packages/{jest-cli => jest-core}/src/__tests__/test_sequencer.test.js (100%) rename packages/{jest-cli => jest-core}/src/__tests__/watch.test.js (100%) rename packages/{jest-cli => jest-core}/src/__tests__/watch_filename_pattern_mode.test.js (100%) rename packages/{jest-cli => jest-core}/src/__tests__/watch_test_name_pattern_mode.test.js (100%) rename packages/{jest-cli => jest-core}/src/assets/jest_logo.png (100%) create mode 100644 packages/jest-core/src/cli/index.js rename packages/{jest-cli => jest-core}/src/collectHandles.js (100%) rename packages/{jest-cli => jest-core}/src/coverage.template (100%) rename packages/{jest-cli => jest-core}/src/getChangedFilesPromise.js (100%) rename packages/{jest-cli => jest-core}/src/getNoTestFound.js (100%) rename packages/{jest-cli => jest-core}/src/getNoTestFoundFailed.js (100%) rename packages/{jest-cli => jest-core}/src/getNoTestFoundRelatedToChangedFiles.js (100%) rename packages/{jest-cli => jest-core}/src/getNoTestFoundVerbose.js (100%) rename packages/{jest-cli => jest-core}/src/getNoTestsFoundMessage.js (100%) create mode 100644 packages/jest-core/src/jest.js rename packages/{jest-cli => jest-core}/src/lib/__tests__/__snapshots__/log_debug_messages.test.js.snap (100%) rename packages/{jest-cli => jest-core}/src/lib/__tests__/is_valid_path.test.js (100%) rename packages/{jest-cli => jest-core}/src/lib/__tests__/log_debug_messages.test.js (100%) rename packages/{jest-cli => jest-core}/src/lib/active_filters_message.js (100%) rename packages/{jest-cli => jest-core}/src/lib/create_context.js (100%) rename packages/{jest-cli => jest-core}/src/lib/handle_deprecation_warnings.js (100%) rename packages/{jest-cli => jest-core}/src/lib/is_valid_path.js (100%) rename packages/{jest-cli => jest-core}/src/lib/log_debug_messages.js (100%) rename packages/{jest-cli => jest-core}/src/lib/update_global_config.js (100%) rename packages/{jest-cli => jest-core}/src/lib/watch_plugins_helpers.js (100%) rename packages/{jest-cli => jest-core}/src/plugins/quit.js (100%) rename packages/{jest-cli => jest-core}/src/plugins/test_name_pattern.js (100%) rename packages/{jest-cli => jest-core}/src/plugins/test_path_pattern.js (100%) rename packages/{jest-cli => jest-core}/src/plugins/update_snapshots.js (100%) rename packages/{jest-cli => jest-core}/src/plugins/update_snapshots_interactive.js (100%) rename packages/{jest-cli => jest-core}/src/pluralize.js (100%) rename packages/{jest-cli => jest-core}/src/runGlobalHook.js (100%) rename packages/{jest-cli => jest-core}/src/runJest.js (100%) rename packages/{jest-cli => jest-core}/src/testResultHelpers.js (100%) rename packages/{jest-cli => jest-core}/src/testSchedulerHelper.js (100%) rename packages/{jest-cli => jest-core}/src/types.js (100%) rename packages/{jest-cli => jest-core}/src/watch.js (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index b490b297a6f5..d23db1531297 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ - `[jest-snapshot]`: Migrate to TypeScript ([#7899](https://github.com/facebook/jest/pull/7899)) - `[@jest/transform]`: New package extracted from `jest-runtime` ([#7915](https://github.com/facebook/jest/pull/7915)) - `[babel-plugin-jest-hoist]`: Migrate to TypeScript ([#7898](https://github.com/facebook/jest/pull/7898)) +- `[@jest/core]` Create new package, which is `jest-cli` minus `yargs` and `prompts` ([#7696](https://github.com/facebook/jest/pull/7696)) ### Performance diff --git a/jest.config.js b/jest.config.js index db041ad091ae..7bda2df4675a 100644 --- a/jest.config.js +++ b/jest.config.js @@ -39,9 +39,9 @@ module.exports = { '/packages/.*/build', '/packages/.*/build-es5', '/packages/.*/src/__tests__/setPrettyPrint.ts', - '/packages/jest-cli/src/__tests__/test_root', - '/packages/jest-cli/src/__tests__/__fixtures__/', - '/packages/jest-cli/src/lib/__tests__/fixtures/', + '/packages/jest-core/src/__tests__/test_root', + '/packages/jest-core/src/__tests__/__fixtures__/', + '/packages/jest-cli/src/init/__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__/', diff --git a/packages/jest-cli/package.json b/packages/jest-cli/package.json index 4267412020a5..3b37a7743958 100644 --- a/packages/jest-cli/package.json +++ b/packages/jest-cli/package.json @@ -2,54 +2,24 @@ "name": "jest-cli", "description": "Delightful JavaScript Testing.", "version": "24.1.0", - "main": "build/jest.js", + "main": "build/index.js", "dependencies": { - "@jest/reporters": "^24.1.0", - "@jest/transform": "^24.1.0", - "ansi-escapes": "^3.0.0", + "@jest/core": "^24.1.0", "chalk": "^2.0.1", "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.1.15", "import-local": "^2.0.0", "is-ci": "^2.0.0", - "jest-changed-files": "^24.0.0", "jest-config": "^24.1.0", - "jest-environment-jsdom": "^24.0.0", - "jest-get-type": "^24.0.0", - "jest-haste-map": "^24.0.0", - "jest-message-util": "^24.0.0", - "jest-regex-util": "^24.0.0", - "jest-resolve-dependencies": "^24.1.0", - "jest-runner": "^24.1.0", - "jest-runtime": "^24.1.0", - "jest-snapshot": "^24.1.0", "jest-util": "^24.0.0", "jest-validate": "^24.0.0", - "jest-watcher": "^24.0.0", - "micromatch": "^3.1.10", - "p-each-series": "^1.0.0", - "pirates": "^4.0.0", "prompts": "^2.0.1", "realpath-native": "^1.1.0", - "rimraf": "^2.5.4", - "string-length": "^2.0.0", - "strip-ansi": "^5.0.0", - "which": "^1.2.12", "yargs": "^12.0.2" }, "devDependencies": { - "@types/ansi-escapes": "^3.0.0", "@types/exit": "^0.1.30", - "@types/glob": "^7.1.1", - "@types/graceful-fs": "^4.1.2", "@types/is-ci": "^1.1.0", - "@types/micromatch": "^3.1.0", "@types/prompts": "^1.2.0", - "@types/rimraf": "^2.0.2", - "@types/string-length": "^2.0.0", - "@types/strip-ansi": "^3.0.0", - "@types/which": "^1.3.1", "@types/yargs": "^12.0.2" }, "bin": { diff --git a/packages/jest-cli/src/cli/index.js b/packages/jest-cli/src/cli/index.js index 69a3ba889d78..910cc7f10fa8 100644 --- a/packages/jest-cli/src/cli/index.js +++ b/packages/jest-cli/src/cli/index.js @@ -12,34 +12,23 @@ import type {Argv} from 'types/Argv'; import type {GlobalConfig, Path} from 'types/Config'; import path from 'path'; -import {Console, clearLine, createDirectory, preRunMessage} from 'jest-util'; +import {clearLine} from 'jest-util'; import {validateCLIOptions} from 'jest-validate'; -import {readConfigs, deprecationEntries} from 'jest-config'; +import {deprecationEntries} from 'jest-config'; +import {runCLI} from '@jest/core'; import * as args from './args'; import chalk from 'chalk'; -import createContext from '../lib/create_context'; import exit from 'exit'; -import getChangedFilesPromise from '../getChangedFilesPromise'; -import {formatHandleErrors} from '../collectHandles'; -import handleDeprecationWarnings from '../lib/handle_deprecation_warnings'; -import runJest from '../runJest'; -import Runtime from 'jest-runtime'; -import TestWatcher from '../TestWatcher'; -import watch from '../watch'; -import pluralize from '../pluralize'; import yargs from 'yargs'; -import rimraf from 'rimraf'; import {sync as realpath} from 'realpath-native'; -import init from '../lib/init'; -import logDebugMessages from '../lib/log_debug_messages'; -import getVersion from '../version'; +import init from '../init'; -const {print: preRunMessagePrint} = preRunMessage; +import {version as VERSION} from '../../package.json'; export async function run(maybeArgv?: Argv, project?: Path) { try { // $FlowFixMe:`allow reduced return - const argv: Argv = buildArgv(maybeArgv, project); + const argv: Argv = buildArgv(maybeArgv); if (argv.init) { await init(); @@ -59,129 +48,9 @@ export async function run(maybeArgv?: Argv, project?: Path) { } } -export const runCLI = async ( - argv: Argv, - projects: Array, -): Promise<{results: AggregatedResult, globalConfig: GlobalConfig}> => { - const realFs = require('fs'); - const fs = require('graceful-fs'); - fs.gracefulify(realFs); - - let results; - - // If we output a JSON object, we can't write anything to stdout, since - // it'll break the JSON structure and it won't be valid. - const outputStream = - argv.json || argv.useStderr ? process.stderr : process.stdout; - - const {globalConfig, configs, hasDeprecationWarnings} = readConfigs( - argv, - projects, - ); - - if (argv.debug) { - logDebugMessages(globalConfig, configs, outputStream); - } - - if (argv.showConfig) { - logDebugMessages(globalConfig, configs, process.stdout); - exit(0); - } - - if (argv.clearCache) { - configs.forEach(config => { - rimraf.sync(config.cacheDirectory); - process.stdout.write(`Cleared ${config.cacheDirectory}\n`); - }); - - exit(0); - } - - await _run( - globalConfig, - configs, - hasDeprecationWarnings, - outputStream, - (r: AggregatedResult) => (results = r), - ); - - if (argv.watch || argv.watchAll) { - // If in watch mode, return the promise that will never resolve. - // If the watch mode is interrupted, watch should handle the process - // shutdown. - return new Promise(() => {}); - } - - if (!results) { - throw new Error( - 'AggregatedResult must be present after test run is complete', - ); - } - - const {openHandles} = results; - - if (openHandles && openHandles.length) { - const formatted = formatHandleErrors(openHandles, configs[0]); - - const openHandlesString = pluralize('open handle', formatted.length, 's'); - - const message = - chalk.red( - `\nJest has detected the following ${openHandlesString} potentially keeping Jest from exiting:\n\n`, - ) + formatted.join('\n\n'); - - console.error(message); - } - - return Promise.resolve({globalConfig, results}); -}; - -const readResultsAndExit = ( - result: ?AggregatedResult, - globalConfig: GlobalConfig, -) => { - const code = !result || result.success ? 0 : globalConfig.testFailureExitCode; - - // Only exit if needed - process.on('exit', () => { - if (typeof code === 'number' && code !== 0) { - process.exitCode = code; - } - }); - - if (globalConfig.forceExit) { - if (!globalConfig.detectOpenHandles) { - console.error( - chalk.red.bold('Force exiting Jest\n\n') + - chalk.red( - 'Have you considered using `--detectOpenHandles` to detect ' + - 'async operations that kept running after all tests finished?', - ), - ); - } - - exit(code); - } else if (!globalConfig.detectOpenHandles) { - setTimeout(() => { - console.error( - chalk.red.bold( - 'Jest did not exit one second after the test run has completed.\n\n', - ) + - chalk.red( - 'This usually means that there are asynchronous operations that ' + - "weren't stopped in your tests. Consider running Jest with " + - '`--detectOpenHandles` to troubleshoot this issue.', - ), - ); - // $FlowFixMe: `unref` exists in Node - }, 1000).unref(); - } -}; - -export const buildArgv = (maybeArgv: ?Argv, project: ?Path) => { +export const buildArgv = (maybeArgv: ?Argv) => { const version = - getVersion() + - (__dirname.includes(`packages${path.sep}jest-cli`) ? '-dev' : ''); + VERSION + (__dirname.includes(`packages${path.sep}jest-cli`) ? '-dev' : ''); const rawArgv: Argv | string[] = maybeArgv || process.argv.slice(2); const argv: Argv = yargs(rawArgv) @@ -234,108 +103,44 @@ const getProjectListFromCLIArgs = (argv, project: ?Path) => { return projects; }; -const buildContextsAndHasteMaps = async ( - configs, - globalConfig, - outputStream, -) => { - const hasteMapInstances = Array(configs.length); - const contexts = await Promise.all( - configs.map(async (config, index) => { - createDirectory(config.cacheDirectory); - const hasteMapInstance = Runtime.createHasteMap(config, { - console: new Console(outputStream, outputStream), - maxWorkers: globalConfig.maxWorkers, - resetCache: !config.cache, - watch: globalConfig.watch || globalConfig.watchAll, - watchman: globalConfig.watchman, - }); - hasteMapInstances[index] = hasteMapInstance; - return createContext(config, await hasteMapInstance.build()); - }), - ); - - return {contexts, hasteMapInstances}; -}; - -const _run = async ( - globalConfig, - configs, - hasDeprecationWarnings, - outputStream, - onComplete, +const readResultsAndExit = ( + result: ?AggregatedResult, + globalConfig: GlobalConfig, ) => { - // Queries to hg/git can take a while, so we need to start the process - // as soon as possible, so by the time we need the result it's already there. - const changedFilesPromise = getChangedFilesPromise(globalConfig, configs); + const code = !result || result.success ? 0 : globalConfig.testFailureExitCode; - const {contexts, hasteMapInstances} = await buildContextsAndHasteMaps( - configs, - globalConfig, - outputStream, - ); + // Only exit if needed + process.on('exit', () => { + if (typeof code === 'number' && code !== 0) { + process.exitCode = code; + } + }); - globalConfig.watch || globalConfig.watchAll - ? await runWatch( - contexts, - configs, - hasDeprecationWarnings, - globalConfig, - outputStream, - hasteMapInstances, - changedFilesPromise, - ) - : await runWithoutWatch( - globalConfig, - contexts, - outputStream, - onComplete, - changedFilesPromise, + if (globalConfig.forceExit) { + if (!globalConfig.detectOpenHandles) { + console.error( + chalk.red.bold('Force exiting Jest\n\n') + + chalk.red( + 'Have you considered using `--detectOpenHandles` to detect ' + + 'async operations that kept running after all tests finished?', + ), ); -}; - -const runWatch = async ( - contexts, - configs, - hasDeprecationWarnings, - globalConfig, - outputStream, - hasteMapInstances, - changedFilesPromise, -) => { - if (hasDeprecationWarnings) { - try { - await handleDeprecationWarnings(outputStream, process.stdin); - return watch(globalConfig, contexts, outputStream, hasteMapInstances); - } catch (e) { - exit(0); } - } - - return watch(globalConfig, contexts, outputStream, hasteMapInstances); -}; -const runWithoutWatch = async ( - globalConfig, - contexts, - outputStream, - onComplete, - changedFilesPromise, -) => { - const startRun = async () => { - if (!globalConfig.listTests) { - preRunMessagePrint(outputStream); - } - return await runJest({ - changedFilesPromise, - contexts, - failedTestsCache: null, - globalConfig, - onComplete, - outputStream, - startRun, - testWatcher: new TestWatcher({isWatchMode: false}), - }); - }; - return await startRun(); + exit(code); + } else if (!globalConfig.detectOpenHandles) { + setTimeout(() => { + console.error( + chalk.red.bold( + 'Jest did not exit one second after the test run has completed.\n\n', + ) + + chalk.red( + 'This usually means that there are asynchronous operations that ' + + "weren't stopped in your tests. Consider running Jest with " + + '`--detectOpenHandles` to troubleshoot this issue.', + ), + ); + // $FlowFixMe: `unref` exists in Node + }, 1000).unref(); + } }; diff --git a/packages/jest-cli/src/index.js b/packages/jest-cli/src/index.js new file mode 100644 index 000000000000..7ebc603595c4 --- /dev/null +++ b/packages/jest-cli/src/index.js @@ -0,0 +1,13 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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 + */ + +// TODO: remove exports for the next major +export {runCLI, SearchSource, TestScheduler, TestWatcher} from '@jest/core'; +export {run} from './cli'; +export {default as getVersion} from './version'; diff --git a/packages/jest-cli/src/lib/__tests__/__snapshots__/init.test.js.snap b/packages/jest-cli/src/init/__tests__/__snapshots__/init.test.js.snap similarity index 100% rename from packages/jest-cli/src/lib/__tests__/__snapshots__/init.test.js.snap rename to packages/jest-cli/src/init/__tests__/__snapshots__/init.test.js.snap diff --git a/packages/jest-cli/src/lib/__tests__/__snapshots__/modify_package_json.test.js.snap b/packages/jest-cli/src/init/__tests__/__snapshots__/modify_package_json.test.js.snap similarity index 100% rename from packages/jest-cli/src/lib/__tests__/__snapshots__/modify_package_json.test.js.snap rename to packages/jest-cli/src/init/__tests__/__snapshots__/modify_package_json.test.js.snap diff --git a/packages/jest-cli/src/lib/__tests__/fixtures/has_jest_config_file/jest.config.js b/packages/jest-cli/src/init/__tests__/fixtures/has_jest_config_file/jest.config.js similarity index 100% rename from packages/jest-cli/src/lib/__tests__/fixtures/has_jest_config_file/jest.config.js rename to packages/jest-cli/src/init/__tests__/fixtures/has_jest_config_file/jest.config.js diff --git a/packages/jest-cli/src/lib/__tests__/fixtures/has_jest_config_file/package.json b/packages/jest-cli/src/init/__tests__/fixtures/has_jest_config_file/package.json similarity index 100% rename from packages/jest-cli/src/lib/__tests__/fixtures/has_jest_config_file/package.json rename to packages/jest-cli/src/init/__tests__/fixtures/has_jest_config_file/package.json diff --git a/packages/jest-cli/src/lib/__tests__/fixtures/has_jest_config_in_package_json/package.json b/packages/jest-cli/src/init/__tests__/fixtures/has_jest_config_in_package_json/package.json similarity index 100% rename from packages/jest-cli/src/lib/__tests__/fixtures/has_jest_config_in_package_json/package.json rename to packages/jest-cli/src/init/__tests__/fixtures/has_jest_config_in_package_json/package.json diff --git a/packages/jest-cli/src/lib/__tests__/fixtures/no_package_json/index.js b/packages/jest-cli/src/init/__tests__/fixtures/no_package_json/index.js similarity index 100% rename from packages/jest-cli/src/lib/__tests__/fixtures/no_package_json/index.js rename to packages/jest-cli/src/init/__tests__/fixtures/no_package_json/index.js diff --git a/packages/jest-cli/src/lib/__tests__/fixtures/only_package_json/package.json b/packages/jest-cli/src/init/__tests__/fixtures/only_package_json/package.json similarity index 100% rename from packages/jest-cli/src/lib/__tests__/fixtures/only_package_json/package.json rename to packages/jest-cli/src/init/__tests__/fixtures/only_package_json/package.json diff --git a/packages/jest-cli/src/lib/__tests__/fixtures/test_script_configured/package.json b/packages/jest-cli/src/init/__tests__/fixtures/test_script_configured/package.json similarity index 100% rename from packages/jest-cli/src/lib/__tests__/fixtures/test_script_configured/package.json rename to packages/jest-cli/src/init/__tests__/fixtures/test_script_configured/package.json diff --git a/packages/jest-cli/src/lib/__tests__/fixtures/typescript_in_dependencies/package.json b/packages/jest-cli/src/init/__tests__/fixtures/typescript_in_dependencies/package.json similarity index 100% rename from packages/jest-cli/src/lib/__tests__/fixtures/typescript_in_dependencies/package.json rename to packages/jest-cli/src/init/__tests__/fixtures/typescript_in_dependencies/package.json diff --git a/packages/jest-cli/src/lib/__tests__/fixtures/typescript_in_dev_dependencies/package.json b/packages/jest-cli/src/init/__tests__/fixtures/typescript_in_dev_dependencies/package.json similarity index 100% rename from packages/jest-cli/src/lib/__tests__/fixtures/typescript_in_dev_dependencies/package.json rename to packages/jest-cli/src/init/__tests__/fixtures/typescript_in_dev_dependencies/package.json diff --git a/packages/jest-cli/src/lib/__tests__/init.test.js b/packages/jest-cli/src/init/__tests__/init.test.js similarity index 99% rename from packages/jest-cli/src/lib/__tests__/init.test.js rename to packages/jest-cli/src/init/__tests__/init.test.js index 277fa89d5ffc..d10629e6153a 100644 --- a/packages/jest-cli/src/lib/__tests__/init.test.js +++ b/packages/jest-cli/src/init/__tests__/init.test.js @@ -11,7 +11,7 @@ import fs from 'fs'; import path from 'path'; import prompts from 'prompts'; -import init from '../init'; +import init from '../'; jest.mock('prompts'); jest.mock('../../../../jest-config/build/getCacheDirectory', () => () => diff --git a/packages/jest-cli/src/lib/__tests__/modify_package_json.test.js b/packages/jest-cli/src/init/__tests__/modify_package_json.test.js similarity index 94% rename from packages/jest-cli/src/lib/__tests__/modify_package_json.test.js rename to packages/jest-cli/src/init/__tests__/modify_package_json.test.js index bd1ce0cf0a21..aa2f8f4641f4 100644 --- a/packages/jest-cli/src/lib/__tests__/modify_package_json.test.js +++ b/packages/jest-cli/src/init/__tests__/modify_package_json.test.js @@ -6,7 +6,7 @@ * */ -import modifyPackageJson from '../init/modify_package_json'; +import modifyPackageJson from '../modify_package_json'; test('should remove jest config if exists', () => { expect( diff --git a/packages/jest-cli/src/constants.js b/packages/jest-cli/src/init/constants.js similarity index 100% rename from packages/jest-cli/src/constants.js rename to packages/jest-cli/src/init/constants.js diff --git a/packages/jest-cli/src/lib/init/errors.js b/packages/jest-cli/src/init/errors.js similarity index 100% rename from packages/jest-cli/src/lib/init/errors.js rename to packages/jest-cli/src/init/errors.js diff --git a/packages/jest-cli/src/lib/init/generate_config_file.js b/packages/jest-cli/src/init/generate_config_file.js similarity index 100% rename from packages/jest-cli/src/lib/init/generate_config_file.js rename to packages/jest-cli/src/init/generate_config_file.js diff --git a/packages/jest-cli/src/lib/init/index.js b/packages/jest-cli/src/init/index.js similarity index 98% rename from packages/jest-cli/src/lib/init/index.js rename to packages/jest-cli/src/init/index.js index 4ae03318470e..68dd9f4fa640 100644 --- a/packages/jest-cli/src/lib/init/index.js +++ b/packages/jest-cli/src/init/index.js @@ -14,7 +14,7 @@ import prompts from 'prompts'; import {sync as realpath} from 'realpath-native'; import defaultQuestions, {testScriptQuestion} from './questions'; import {NotFoundPackageJsonError, MalformedPackageJsonError} from './errors'; -import {PACKAGE_JSON, JEST_CONFIG} from '../../constants'; +import {PACKAGE_JSON, JEST_CONFIG} from './constants'; import generateConfigFile from './generate_config_file'; import modifyPackageJson from './modify_package_json'; diff --git a/packages/jest-cli/src/lib/init/modify_package_json.js b/packages/jest-cli/src/init/modify_package_json.js similarity index 100% rename from packages/jest-cli/src/lib/init/modify_package_json.js rename to packages/jest-cli/src/init/modify_package_json.js diff --git a/packages/jest-cli/src/lib/init/questions.js b/packages/jest-cli/src/init/questions.js similarity index 100% rename from packages/jest-cli/src/lib/init/questions.js rename to packages/jest-cli/src/init/questions.js diff --git a/packages/jest-cli/src/jest.js b/packages/jest-cli/src/jest.js deleted file mode 100644 index 22210bef096e..000000000000 --- a/packages/jest-cli/src/jest.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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 SearchSource from './SearchSource'; -import TestScheduler from './TestScheduler'; -import TestWatcher from './TestWatcher'; -import {run, runCLI} from './cli'; -import getVersion from './version'; - -module.exports = { - SearchSource, - TestScheduler, - TestWatcher, - getVersion, - run, - runCLI, -}; diff --git a/packages/jest-core/.npmignore b/packages/jest-core/.npmignore new file mode 100644 index 000000000000..85e48fe7b0a4 --- /dev/null +++ b/packages/jest-core/.npmignore @@ -0,0 +1,3 @@ +**/__mocks__/** +**/__tests__/** +src diff --git a/packages/jest-core/README.md b/packages/jest-core/README.md new file mode 100644 index 000000000000..29dd758bb3c8 --- /dev/null +++ b/packages/jest-core/README.md @@ -0,0 +1,3 @@ +# @jest/core + +Jest is currently working on providing a programmatic API. This is under developemnt, and usage of this package directly is currently not supported. diff --git a/packages/jest-core/package.json b/packages/jest-core/package.json new file mode 100644 index 000000000000..4d835cd058dc --- /dev/null +++ b/packages/jest-core/package.json @@ -0,0 +1,78 @@ +{ + "name": "@jest/core", + "description": "Delightful JavaScript Testing.", + "version": "24.1.0", + "main": "build/jest.js", + "dependencies": { + "@jest/reporters": "^24.1.0", + "@jest/transform": "^24.1.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-changed-files": "^24.0.0", + "jest-config": "^24.1.0", + "jest-haste-map": "^24.0.0", + "jest-message-util": "^24.0.0", + "jest-regex-util": "^24.0.0", + "jest-resolve-dependencies": "^24.1.0", + "jest-runner": "^24.1.0", + "jest-runtime": "^24.1.0", + "jest-snapshot": "^24.1.0", + "jest-util": "^24.0.0", + "jest-validate": "^24.0.0", + "jest-watcher": "^24.0.0", + "micromatch": "^3.1.10", + "p-each-series": "^1.0.0", + "pirates": "^4.0.0", + "realpath-native": "^1.1.0", + "rimraf": "^2.5.4", + "strip-ansi": "^5.0.0" + }, + "devDependencies": { + "@types/ansi-escapes": "^3.0.0", + "@types/exit": "^0.1.30", + "@types/graceful-fs": "^4.1.2", + "@types/micromatch": "^3.1.0", + "@types/rimraf": "^2.0.2" + }, + "engines": { + "node": ">= 6" + }, + "repository": { + "type": "git", + "url": "https://github.com/facebook/jest", + "directory": "packages/jest-core" + }, + "bugs": { + "url": "https://github.com/facebook/jest/issues" + }, + "homepage": "https://jestjs.io/", + "license": "MIT", + "keywords": [ + "ava", + "babel", + "coverage", + "easy", + "expect", + "facebook", + "immersive", + "instant", + "jasmine", + "jest", + "jsdom", + "mocha", + "mocking", + "painless", + "qunit", + "runner", + "sandboxed", + "snapshot", + "tap", + "tape", + "test", + "testing", + "typescript", + "watch" + ] +} diff --git a/packages/jest-cli/src/FailedTestsCache.js b/packages/jest-core/src/FailedTestsCache.js similarity index 100% rename from packages/jest-cli/src/FailedTestsCache.js rename to packages/jest-core/src/FailedTestsCache.js diff --git a/packages/jest-cli/src/ReporterDispatcher.js b/packages/jest-core/src/ReporterDispatcher.js similarity index 100% rename from packages/jest-cli/src/ReporterDispatcher.js rename to packages/jest-core/src/ReporterDispatcher.js diff --git a/packages/jest-cli/src/SearchSource.js b/packages/jest-core/src/SearchSource.js similarity index 100% rename from packages/jest-cli/src/SearchSource.js rename to packages/jest-core/src/SearchSource.js diff --git a/packages/jest-cli/src/SnapshotInteractiveMode.js b/packages/jest-core/src/SnapshotInteractiveMode.js similarity index 100% rename from packages/jest-cli/src/SnapshotInteractiveMode.js rename to packages/jest-core/src/SnapshotInteractiveMode.js diff --git a/packages/jest-cli/src/TestNamePatternPrompt.js b/packages/jest-core/src/TestNamePatternPrompt.js similarity index 100% rename from packages/jest-cli/src/TestNamePatternPrompt.js rename to packages/jest-core/src/TestNamePatternPrompt.js diff --git a/packages/jest-cli/src/TestPathPatternPrompt.js b/packages/jest-core/src/TestPathPatternPrompt.js similarity index 100% rename from packages/jest-cli/src/TestPathPatternPrompt.js rename to packages/jest-core/src/TestPathPatternPrompt.js diff --git a/packages/jest-cli/src/TestScheduler.js b/packages/jest-core/src/TestScheduler.js similarity index 100% rename from packages/jest-cli/src/TestScheduler.js rename to packages/jest-core/src/TestScheduler.js diff --git a/packages/jest-cli/src/TestSequencer.js b/packages/jest-core/src/TestSequencer.js similarity index 100% rename from packages/jest-cli/src/TestSequencer.js rename to packages/jest-core/src/TestSequencer.js diff --git a/packages/jest-cli/src/TestWatcher.js b/packages/jest-core/src/TestWatcher.js similarity index 100% rename from packages/jest-cli/src/TestWatcher.js rename to packages/jest-core/src/TestWatcher.js diff --git a/packages/jest-cli/src/__tests__/FailedTestsCache.test.js b/packages/jest-core/src/__tests__/FailedTestsCache.test.js similarity index 100% rename from packages/jest-cli/src/__tests__/FailedTestsCache.test.js rename to packages/jest-core/src/__tests__/FailedTestsCache.test.js diff --git a/packages/jest-cli/src/__tests__/SearchSource.test.js b/packages/jest-core/src/__tests__/SearchSource.test.js similarity index 98% rename from packages/jest-cli/src/__tests__/SearchSource.test.js rename to packages/jest-core/src/__tests__/SearchSource.test.js index 0f010a136e3d..d3ed9b7117a0 100644 --- a/packages/jest-cli/src/__tests__/SearchSource.test.js +++ b/packages/jest-core/src/__tests__/SearchSource.test.js @@ -471,7 +471,7 @@ describe('SearchSource', () => { }); it('finds tests for a single file', () => { - const input = ['packages/jest-cli/src/__tests__/test_root/module.jsx']; + const input = ['packages/jest-core/src/__tests__/test_root/module.jsx']; const data = searchSource.findRelatedTestsFromPattern(input); expect(toPaths(data.tests).sort()).toEqual([ path.join(rootDir, '__testtests__', 'test.js'), @@ -481,8 +481,8 @@ describe('SearchSource', () => { it('finds tests for multiple files', () => { const input = [ - 'packages/jest-cli/src/__tests__/test_root/module.jsx', - 'packages/jest-cli/src/__tests__/test_root/module.foobar', + 'packages/jest-core/src/__tests__/test_root/module.jsx', + 'packages/jest-core/src/__tests__/test_root/module.foobar', ]; const data = searchSource.findRelatedTestsFromPattern(input); expect(toPaths(data.tests).sort()).toEqual([ diff --git a/packages/jest-cli/src/__tests__/SnapshotInteractiveMode.test.js b/packages/jest-core/src/__tests__/SnapshotInteractiveMode.test.js similarity index 100% rename from packages/jest-cli/src/__tests__/SnapshotInteractiveMode.test.js rename to packages/jest-core/src/__tests__/SnapshotInteractiveMode.test.js diff --git a/packages/jest-cli/src/__tests__/TestScheduler.test.js b/packages/jest-core/src/__tests__/TestScheduler.test.js similarity index 100% rename from packages/jest-cli/src/__tests__/TestScheduler.test.js rename to packages/jest-core/src/__tests__/TestScheduler.test.js diff --git a/packages/jest-cli/src/__tests__/__fixtures__/watch_plugin.js b/packages/jest-core/src/__tests__/__fixtures__/watch_plugin.js similarity index 100% rename from packages/jest-cli/src/__tests__/__fixtures__/watch_plugin.js rename to packages/jest-core/src/__tests__/__fixtures__/watch_plugin.js diff --git a/packages/jest-cli/src/__tests__/__fixtures__/watch_plugin2.js b/packages/jest-core/src/__tests__/__fixtures__/watch_plugin2.js similarity index 100% rename from packages/jest-cli/src/__tests__/__fixtures__/watch_plugin2.js rename to packages/jest-core/src/__tests__/__fixtures__/watch_plugin2.js diff --git a/packages/jest-cli/src/__tests__/__snapshots__/SnapshotInteractiveMode.test.js.snap b/packages/jest-core/src/__tests__/__snapshots__/SnapshotInteractiveMode.test.js.snap similarity index 100% rename from packages/jest-cli/src/__tests__/__snapshots__/SnapshotInteractiveMode.test.js.snap rename to packages/jest-core/src/__tests__/__snapshots__/SnapshotInteractiveMode.test.js.snap diff --git a/packages/jest-cli/src/__tests__/__snapshots__/watch.test.js.snap b/packages/jest-core/src/__tests__/__snapshots__/watch.test.js.snap similarity index 100% rename from packages/jest-cli/src/__tests__/__snapshots__/watch.test.js.snap rename to packages/jest-core/src/__tests__/__snapshots__/watch.test.js.snap diff --git a/packages/jest-cli/src/__tests__/__snapshots__/watch_filename_pattern_mode.test.js.snap b/packages/jest-core/src/__tests__/__snapshots__/watch_filename_pattern_mode.test.js.snap similarity index 100% rename from packages/jest-cli/src/__tests__/__snapshots__/watch_filename_pattern_mode.test.js.snap rename to packages/jest-core/src/__tests__/__snapshots__/watch_filename_pattern_mode.test.js.snap diff --git a/packages/jest-cli/src/__tests__/__snapshots__/watch_test_name_pattern_mode.test.js.snap b/packages/jest-core/src/__tests__/__snapshots__/watch_test_name_pattern_mode.test.js.snap similarity index 100% rename from packages/jest-cli/src/__tests__/__snapshots__/watch_test_name_pattern_mode.test.js.snap rename to packages/jest-core/src/__tests__/__snapshots__/watch_test_name_pattern_mode.test.js.snap diff --git a/packages/jest-cli/src/__tests__/globals.test.js b/packages/jest-core/src/__tests__/globals.test.js similarity index 100% rename from packages/jest-cli/src/__tests__/globals.test.js rename to packages/jest-core/src/__tests__/globals.test.js diff --git a/packages/jest-cli/src/__tests__/run_jest.test.js b/packages/jest-core/src/__tests__/run_jest.test.js similarity index 100% rename from packages/jest-cli/src/__tests__/run_jest.test.js rename to packages/jest-core/src/__tests__/run_jest.test.js diff --git a/packages/jest-cli/src/__tests__/testSchedulerHelper.test.js b/packages/jest-core/src/__tests__/testSchedulerHelper.test.js similarity index 100% rename from packages/jest-cli/src/__tests__/testSchedulerHelper.test.js rename to packages/jest-core/src/__tests__/testSchedulerHelper.test.js diff --git a/packages/jest-cli/src/__tests__/test_root/.hiddenFolder/not-really-a-test.txt b/packages/jest-core/src/__tests__/test_root/.hiddenFolder/not-really-a-test.txt similarity index 100% rename from packages/jest-cli/src/__tests__/test_root/.hiddenFolder/not-really-a-test.txt rename to packages/jest-core/src/__tests__/test_root/.hiddenFolder/not-really-a-test.txt diff --git a/packages/jest-cli/src/__tests__/test_root/__testtests__/do-not-match-me.txt b/packages/jest-core/src/__tests__/test_root/__testtests__/do-not-match-me.txt similarity index 100% rename from packages/jest-cli/src/__tests__/test_root/__testtests__/do-not-match-me.txt rename to packages/jest-core/src/__tests__/test_root/__testtests__/do-not-match-me.txt diff --git a/packages/jest-cli/src/__tests__/test_root/__testtests__/not-really-a-test.txt b/packages/jest-core/src/__tests__/test_root/__testtests__/not-really-a-test.txt similarity index 100% rename from packages/jest-cli/src/__tests__/test_root/__testtests__/not-really-a-test.txt rename to packages/jest-core/src/__tests__/test_root/__testtests__/not-really-a-test.txt diff --git a/packages/jest-cli/src/__tests__/test_root/__testtests__/test.foobar b/packages/jest-core/src/__tests__/test_root/__testtests__/test.foobar similarity index 100% rename from packages/jest-cli/src/__tests__/test_root/__testtests__/test.foobar rename to packages/jest-core/src/__tests__/test_root/__testtests__/test.foobar diff --git a/packages/jest-cli/src/__tests__/test_root/__testtests__/test.js b/packages/jest-core/src/__tests__/test_root/__testtests__/test.js similarity index 100% rename from packages/jest-cli/src/__tests__/test_root/__testtests__/test.js rename to packages/jest-core/src/__tests__/test_root/__testtests__/test.js diff --git a/packages/jest-cli/src/__tests__/test_root/__testtests__/test.jsx b/packages/jest-core/src/__tests__/test_root/__testtests__/test.jsx similarity index 100% rename from packages/jest-cli/src/__tests__/test_root/__testtests__/test.jsx rename to packages/jest-core/src/__tests__/test_root/__testtests__/test.jsx diff --git a/packages/jest-cli/src/__tests__/test_root/module.foobar b/packages/jest-core/src/__tests__/test_root/module.foobar similarity index 100% rename from packages/jest-cli/src/__tests__/test_root/module.foobar rename to packages/jest-core/src/__tests__/test_root/module.foobar diff --git a/packages/jest-cli/src/__tests__/test_root/module.jsx b/packages/jest-core/src/__tests__/test_root/module.jsx similarity index 100% rename from packages/jest-cli/src/__tests__/test_root/module.jsx rename to packages/jest-core/src/__tests__/test_root/module.jsx diff --git a/packages/jest-cli/src/__tests__/test_root/no_tests.js b/packages/jest-core/src/__tests__/test_root/no_tests.js similarity index 100% rename from packages/jest-cli/src/__tests__/test_root/no_tests.js rename to packages/jest-core/src/__tests__/test_root/no_tests.js diff --git a/packages/jest-cli/src/__tests__/test_root_with_(parentheses)/__testtests__/test.js b/packages/jest-core/src/__tests__/test_root_with_(parentheses)/__testtests__/test.js similarity index 100% rename from packages/jest-cli/src/__tests__/test_root_with_(parentheses)/__testtests__/test.js rename to packages/jest-core/src/__tests__/test_root_with_(parentheses)/__testtests__/test.js diff --git a/packages/jest-cli/src/__tests__/test_root_with_(parentheses)/module.jsx b/packages/jest-core/src/__tests__/test_root_with_(parentheses)/module.jsx similarity index 100% rename from packages/jest-cli/src/__tests__/test_root_with_(parentheses)/module.jsx rename to packages/jest-core/src/__tests__/test_root_with_(parentheses)/module.jsx diff --git a/packages/jest-cli/src/__tests__/test_sequencer.test.js b/packages/jest-core/src/__tests__/test_sequencer.test.js similarity index 100% rename from packages/jest-cli/src/__tests__/test_sequencer.test.js rename to packages/jest-core/src/__tests__/test_sequencer.test.js diff --git a/packages/jest-cli/src/__tests__/watch.test.js b/packages/jest-core/src/__tests__/watch.test.js similarity index 100% rename from packages/jest-cli/src/__tests__/watch.test.js rename to packages/jest-core/src/__tests__/watch.test.js diff --git a/packages/jest-cli/src/__tests__/watch_filename_pattern_mode.test.js b/packages/jest-core/src/__tests__/watch_filename_pattern_mode.test.js similarity index 100% rename from packages/jest-cli/src/__tests__/watch_filename_pattern_mode.test.js rename to packages/jest-core/src/__tests__/watch_filename_pattern_mode.test.js diff --git a/packages/jest-cli/src/__tests__/watch_test_name_pattern_mode.test.js b/packages/jest-core/src/__tests__/watch_test_name_pattern_mode.test.js similarity index 100% rename from packages/jest-cli/src/__tests__/watch_test_name_pattern_mode.test.js rename to packages/jest-core/src/__tests__/watch_test_name_pattern_mode.test.js diff --git a/packages/jest-cli/src/assets/jest_logo.png b/packages/jest-core/src/assets/jest_logo.png similarity index 100% rename from packages/jest-cli/src/assets/jest_logo.png rename to packages/jest-core/src/assets/jest_logo.png diff --git a/packages/jest-core/src/cli/index.js b/packages/jest-core/src/cli/index.js new file mode 100644 index 000000000000..b71054c167de --- /dev/null +++ b/packages/jest-core/src/cli/index.js @@ -0,0 +1,211 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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 type {AggregatedResult} from 'types/TestResult'; +import type {Argv} from 'types/Argv'; +import type {GlobalConfig, Path} from 'types/Config'; + +import {Console, createDirectory, preRunMessage} from 'jest-util'; +import {readConfigs} from 'jest-config'; +import chalk from 'chalk'; +import createContext from '../lib/create_context'; +import exit from 'exit'; +import getChangedFilesPromise from '../getChangedFilesPromise'; +import {formatHandleErrors} from '../collectHandles'; +import handleDeprecationWarnings from '../lib/handle_deprecation_warnings'; +import runJest from '../runJest'; +import Runtime from 'jest-runtime'; +import TestWatcher from '../TestWatcher'; +import watch from '../watch'; +import pluralize from '../pluralize'; +import rimraf from 'rimraf'; +import logDebugMessages from '../lib/log_debug_messages'; + +const {print: preRunMessagePrint} = preRunMessage; + +export const runCLI = async ( + argv: Argv, + projects: Array, +): Promise<{results: AggregatedResult, globalConfig: GlobalConfig}> => { + const realFs = require('fs'); + const fs = require('graceful-fs'); + fs.gracefulify(realFs); + + let results; + + // If we output a JSON object, we can't write anything to stdout, since + // it'll break the JSON structure and it won't be valid. + const outputStream = + argv.json || argv.useStderr ? process.stderr : process.stdout; + + const {globalConfig, configs, hasDeprecationWarnings} = readConfigs( + argv, + projects, + ); + + if (argv.debug) { + logDebugMessages(globalConfig, configs, outputStream); + } + + if (argv.showConfig) { + logDebugMessages(globalConfig, configs, process.stdout); + exit(0); + } + + if (argv.clearCache) { + configs.forEach(config => { + rimraf.sync(config.cacheDirectory); + process.stdout.write(`Cleared ${config.cacheDirectory}\n`); + }); + + exit(0); + } + + await _run( + globalConfig, + configs, + hasDeprecationWarnings, + outputStream, + (r: AggregatedResult) => (results = r), + ); + + if (argv.watch || argv.watchAll) { + // If in watch mode, return the promise that will never resolve. + // If the watch mode is interrupted, watch should handle the process + // shutdown. + return new Promise(() => {}); + } + + if (!results) { + throw new Error( + 'AggregatedResult must be present after test run is complete', + ); + } + + const {openHandles} = results; + + if (openHandles && openHandles.length) { + const formatted = formatHandleErrors(openHandles, configs[0]); + + const openHandlesString = pluralize('open handle', formatted.length, 's'); + + const message = + chalk.red( + `\nJest has detected the following ${openHandlesString} potentially keeping Jest from exiting:\n\n`, + ) + formatted.join('\n\n'); + + console.error(message); + } + + return {globalConfig, results}; +}; + +const buildContextsAndHasteMaps = async ( + configs, + globalConfig, + outputStream, +) => { + const hasteMapInstances = Array(configs.length); + const contexts = await Promise.all( + configs.map(async (config, index) => { + createDirectory(config.cacheDirectory); + const hasteMapInstance = Runtime.createHasteMap(config, { + console: new Console(outputStream, outputStream), + maxWorkers: globalConfig.maxWorkers, + resetCache: !config.cache, + watch: globalConfig.watch || globalConfig.watchAll, + watchman: globalConfig.watchman, + }); + hasteMapInstances[index] = hasteMapInstance; + return createContext(config, await hasteMapInstance.build()); + }), + ); + + return {contexts, hasteMapInstances}; +}; + +const _run = async ( + globalConfig, + configs, + hasDeprecationWarnings, + outputStream, + onComplete, +) => { + // Queries to hg/git can take a while, so we need to start the process + // as soon as possible, so by the time we need the result it's already there. + const changedFilesPromise = getChangedFilesPromise(globalConfig, configs); + + const {contexts, hasteMapInstances} = await buildContextsAndHasteMaps( + configs, + globalConfig, + outputStream, + ); + + globalConfig.watch || globalConfig.watchAll + ? await runWatch( + contexts, + configs, + hasDeprecationWarnings, + globalConfig, + outputStream, + hasteMapInstances, + ) + : await runWithoutWatch( + globalConfig, + contexts, + outputStream, + onComplete, + changedFilesPromise, + ); +}; + +const runWatch = async ( + contexts, + configs, + hasDeprecationWarnings, + globalConfig, + outputStream, + hasteMapInstances, +) => { + if (hasDeprecationWarnings) { + try { + await handleDeprecationWarnings(outputStream, process.stdin); + return watch(globalConfig, contexts, outputStream, hasteMapInstances); + } catch (e) { + exit(0); + } + } + + return watch(globalConfig, contexts, outputStream, hasteMapInstances); +}; + +const runWithoutWatch = async ( + globalConfig, + contexts, + outputStream, + onComplete, + changedFilesPromise, +) => { + const startRun = async () => { + if (!globalConfig.listTests) { + preRunMessagePrint(outputStream); + } + return runJest({ + changedFilesPromise, + contexts, + failedTestsCache: null, + globalConfig, + onComplete, + outputStream, + startRun, + testWatcher: new TestWatcher({isWatchMode: false}), + }); + }; + return startRun(); +}; diff --git a/packages/jest-cli/src/collectHandles.js b/packages/jest-core/src/collectHandles.js similarity index 100% rename from packages/jest-cli/src/collectHandles.js rename to packages/jest-core/src/collectHandles.js diff --git a/packages/jest-cli/src/coverage.template b/packages/jest-core/src/coverage.template similarity index 100% rename from packages/jest-cli/src/coverage.template rename to packages/jest-core/src/coverage.template diff --git a/packages/jest-cli/src/getChangedFilesPromise.js b/packages/jest-core/src/getChangedFilesPromise.js similarity index 100% rename from packages/jest-cli/src/getChangedFilesPromise.js rename to packages/jest-core/src/getChangedFilesPromise.js diff --git a/packages/jest-cli/src/getNoTestFound.js b/packages/jest-core/src/getNoTestFound.js similarity index 100% rename from packages/jest-cli/src/getNoTestFound.js rename to packages/jest-core/src/getNoTestFound.js diff --git a/packages/jest-cli/src/getNoTestFoundFailed.js b/packages/jest-core/src/getNoTestFoundFailed.js similarity index 100% rename from packages/jest-cli/src/getNoTestFoundFailed.js rename to packages/jest-core/src/getNoTestFoundFailed.js diff --git a/packages/jest-cli/src/getNoTestFoundRelatedToChangedFiles.js b/packages/jest-core/src/getNoTestFoundRelatedToChangedFiles.js similarity index 100% rename from packages/jest-cli/src/getNoTestFoundRelatedToChangedFiles.js rename to packages/jest-core/src/getNoTestFoundRelatedToChangedFiles.js diff --git a/packages/jest-cli/src/getNoTestFoundVerbose.js b/packages/jest-core/src/getNoTestFoundVerbose.js similarity index 100% rename from packages/jest-cli/src/getNoTestFoundVerbose.js rename to packages/jest-core/src/getNoTestFoundVerbose.js diff --git a/packages/jest-cli/src/getNoTestsFoundMessage.js b/packages/jest-core/src/getNoTestsFoundMessage.js similarity index 100% rename from packages/jest-cli/src/getNoTestsFoundMessage.js rename to packages/jest-core/src/getNoTestsFoundMessage.js diff --git a/packages/jest-core/src/jest.js b/packages/jest-core/src/jest.js new file mode 100644 index 000000000000..ca6fe2d0d479 --- /dev/null +++ b/packages/jest-core/src/jest.js @@ -0,0 +1,13 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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 as SearchSource} from './SearchSource'; +export {default as TestScheduler} from './TestScheduler'; +export {default as TestWatcher} from './TestWatcher'; +export {runCLI} from './cli'; diff --git a/packages/jest-cli/src/lib/__tests__/__snapshots__/log_debug_messages.test.js.snap b/packages/jest-core/src/lib/__tests__/__snapshots__/log_debug_messages.test.js.snap similarity index 100% rename from packages/jest-cli/src/lib/__tests__/__snapshots__/log_debug_messages.test.js.snap rename to packages/jest-core/src/lib/__tests__/__snapshots__/log_debug_messages.test.js.snap diff --git a/packages/jest-cli/src/lib/__tests__/is_valid_path.test.js b/packages/jest-core/src/lib/__tests__/is_valid_path.test.js similarity index 100% rename from packages/jest-cli/src/lib/__tests__/is_valid_path.test.js rename to packages/jest-core/src/lib/__tests__/is_valid_path.test.js diff --git a/packages/jest-cli/src/lib/__tests__/log_debug_messages.test.js b/packages/jest-core/src/lib/__tests__/log_debug_messages.test.js similarity index 100% rename from packages/jest-cli/src/lib/__tests__/log_debug_messages.test.js rename to packages/jest-core/src/lib/__tests__/log_debug_messages.test.js diff --git a/packages/jest-cli/src/lib/active_filters_message.js b/packages/jest-core/src/lib/active_filters_message.js similarity index 100% rename from packages/jest-cli/src/lib/active_filters_message.js rename to packages/jest-core/src/lib/active_filters_message.js diff --git a/packages/jest-cli/src/lib/create_context.js b/packages/jest-core/src/lib/create_context.js similarity index 100% rename from packages/jest-cli/src/lib/create_context.js rename to packages/jest-core/src/lib/create_context.js diff --git a/packages/jest-cli/src/lib/handle_deprecation_warnings.js b/packages/jest-core/src/lib/handle_deprecation_warnings.js similarity index 100% rename from packages/jest-cli/src/lib/handle_deprecation_warnings.js rename to packages/jest-core/src/lib/handle_deprecation_warnings.js diff --git a/packages/jest-cli/src/lib/is_valid_path.js b/packages/jest-core/src/lib/is_valid_path.js similarity index 100% rename from packages/jest-cli/src/lib/is_valid_path.js rename to packages/jest-core/src/lib/is_valid_path.js diff --git a/packages/jest-cli/src/lib/log_debug_messages.js b/packages/jest-core/src/lib/log_debug_messages.js similarity index 100% rename from packages/jest-cli/src/lib/log_debug_messages.js rename to packages/jest-core/src/lib/log_debug_messages.js diff --git a/packages/jest-cli/src/lib/update_global_config.js b/packages/jest-core/src/lib/update_global_config.js similarity index 100% rename from packages/jest-cli/src/lib/update_global_config.js rename to packages/jest-core/src/lib/update_global_config.js diff --git a/packages/jest-cli/src/lib/watch_plugins_helpers.js b/packages/jest-core/src/lib/watch_plugins_helpers.js similarity index 100% rename from packages/jest-cli/src/lib/watch_plugins_helpers.js rename to packages/jest-core/src/lib/watch_plugins_helpers.js diff --git a/packages/jest-cli/src/plugins/quit.js b/packages/jest-core/src/plugins/quit.js similarity index 100% rename from packages/jest-cli/src/plugins/quit.js rename to packages/jest-core/src/plugins/quit.js diff --git a/packages/jest-cli/src/plugins/test_name_pattern.js b/packages/jest-core/src/plugins/test_name_pattern.js similarity index 100% rename from packages/jest-cli/src/plugins/test_name_pattern.js rename to packages/jest-core/src/plugins/test_name_pattern.js diff --git a/packages/jest-cli/src/plugins/test_path_pattern.js b/packages/jest-core/src/plugins/test_path_pattern.js similarity index 100% rename from packages/jest-cli/src/plugins/test_path_pattern.js rename to packages/jest-core/src/plugins/test_path_pattern.js diff --git a/packages/jest-cli/src/plugins/update_snapshots.js b/packages/jest-core/src/plugins/update_snapshots.js similarity index 100% rename from packages/jest-cli/src/plugins/update_snapshots.js rename to packages/jest-core/src/plugins/update_snapshots.js diff --git a/packages/jest-cli/src/plugins/update_snapshots_interactive.js b/packages/jest-core/src/plugins/update_snapshots_interactive.js similarity index 100% rename from packages/jest-cli/src/plugins/update_snapshots_interactive.js rename to packages/jest-core/src/plugins/update_snapshots_interactive.js diff --git a/packages/jest-cli/src/pluralize.js b/packages/jest-core/src/pluralize.js similarity index 100% rename from packages/jest-cli/src/pluralize.js rename to packages/jest-core/src/pluralize.js diff --git a/packages/jest-cli/src/runGlobalHook.js b/packages/jest-core/src/runGlobalHook.js similarity index 100% rename from packages/jest-cli/src/runGlobalHook.js rename to packages/jest-core/src/runGlobalHook.js diff --git a/packages/jest-cli/src/runJest.js b/packages/jest-core/src/runJest.js similarity index 100% rename from packages/jest-cli/src/runJest.js rename to packages/jest-core/src/runJest.js diff --git a/packages/jest-cli/src/testResultHelpers.js b/packages/jest-core/src/testResultHelpers.js similarity index 100% rename from packages/jest-cli/src/testResultHelpers.js rename to packages/jest-core/src/testResultHelpers.js diff --git a/packages/jest-cli/src/testSchedulerHelper.js b/packages/jest-core/src/testSchedulerHelper.js similarity index 100% rename from packages/jest-cli/src/testSchedulerHelper.js rename to packages/jest-core/src/testSchedulerHelper.js diff --git a/packages/jest-cli/src/types.js b/packages/jest-core/src/types.js similarity index 100% rename from packages/jest-cli/src/types.js rename to packages/jest-core/src/types.js diff --git a/packages/jest-cli/src/watch.js b/packages/jest-core/src/watch.js similarity index 100% rename from packages/jest-cli/src/watch.js rename to packages/jest-core/src/watch.js diff --git a/packages/jest-runner/src/__tests__/testRunner.test.js b/packages/jest-runner/src/__tests__/testRunner.test.js index c4ce341398cd..9601969f012d 100644 --- a/packages/jest-runner/src/__tests__/testRunner.test.js +++ b/packages/jest-runner/src/__tests__/testRunner.test.js @@ -6,10 +6,9 @@ * */ -'use strict'; - -const TestRunner = require('../index'); -const {TestWatcher} = require('jest-cli'); +import {TestWatcher} from '@jest/core'; +// eslint-disable-next-line import/default +import TestRunner from '../index'; let mockWorkerFarm; diff --git a/packages/jest/src/jest.js b/packages/jest/src/jest.js index 318e1641c408..fe580071a094 100644 --- a/packages/jest/src/jest.js +++ b/packages/jest/src/jest.js @@ -7,6 +7,6 @@ * @flow */ -import cli from 'jest-cli'; +import * as cli from 'jest-cli'; module.exports = cli; diff --git a/types/TestRunner.js b/types/TestRunner.js index 2aa7005c6f26..2588e1a29296 100644 --- a/types/TestRunner.js +++ b/types/TestRunner.js @@ -8,16 +8,16 @@ */ import type {Context} from './Context'; -import type {Environment} from 'types/Environment'; +import type {Environment} from './Environment'; import type {GlobalConfig, Path, ProjectConfig} from './Config'; -import type {ReporterOnStartOptions} from 'types/Reporters'; +import type {ReporterOnStartOptions} from './Reporters'; import type { AggregatedResult, SerializableError, TestResult, -} from 'types/TestResult'; +} from './TestResult'; import type Runtime from 'jest-runtime'; -import type {TestWatcher as _TestWatcher} from 'jest-cli'; +import type {TestWatcher as _TestWatcher} from '@jest/core'; export type Test = {| context: Context, diff --git a/yarn.lock b/yarn.lock index a78b50b6fcd2..2ca7de58e22a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -838,7 +838,7 @@ globals "^11.1.0" lodash "^4.17.10" -"@babel/types@^7.0.0", "@babel/types@^7.1.2", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.3.0": +"@babel/types@^7.0.0", "@babel/types@^7.1.2", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.3.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.3.3.tgz#6c44d1cdac2a7625b624216657d5bc6c107ab436" integrity sha512-2tACZ80Wg09UnPg5uGAOUvvInaqLk3l/IAhQzlxLQOIXacr6bMsra5SH6AWw/hIDRCSbCdHP2KzSOD+cT7TzMQ== @@ -1823,11 +1823,6 @@ dependencies: "@types/node" "*" -"@types/which@^1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@types/which/-/which-1.3.1.tgz#7802c380887986ca909008afea4e08025b130f8d" - integrity sha512-ZrJDWpvg75LTGX4XwuneY9s6bF3OeZcGTpoGh3zDV9ytzcHMFsRrMIaLBRJZQMBoGyKs6unBQfVdrLZiYfb1zQ== - "@types/write-file-atomic@^2.1.1": version "2.1.1" resolved "https://registry.yarnpkg.com/@types/write-file-atomic/-/write-file-atomic-2.1.1.tgz#7f9fcd6c5c8d194dba03472e3fa6cb29a839764c" @@ -13307,7 +13302,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@1, which@^1.2.1, which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: +which@1, which@^1.2.1, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== From fb534d33257da553c9d87567d654afa082e77aa5 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sun, 17 Feb 2019 01:36:03 +0100 Subject: [PATCH 068/107] chore: collect coverage from TS files --- jest.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/jest.config.js b/jest.config.js index 7bda2df4675a..81f208271834 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,6 +5,7 @@ module.exports = { collectCoverageFrom: [ '**/packages/*/**/*.js', + '**/packages/*/**/*.ts', '!**/bin/**', '!**/cli/**', '!**/perf/**', From f5e04f432855b3a4f9451e7d7850c36a1c35c95e Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sun, 17 Feb 2019 11:45:56 +0100 Subject: [PATCH 069/107] chore: migrate @jest/transform to TypeScript (#7918) --- CHANGELOG.md | 1 + packages/babel-jest/package.json | 3 +- packages/babel-jest/src/__tests__/index.ts | 6 +- packages/babel-jest/src/index.ts | 52 ++++--- packages/babel-jest/tsconfig.json | 1 + packages/jest-core/package.json | 4 +- packages/jest-transform/package.json | 3 + ...iptTransformer.js => ScriptTransformer.ts} | 130 ++++++++++-------- ...ge.js => enhanceUnexpectedTokenMessage.ts} | 2 - .../jest-transform/src/{index.js => index.ts} | 3 +- ...houldInstrument.js => shouldInstrument.ts} | 15 +- packages/jest-transform/src/types.js | 21 --- .../src/types.ts} | 30 ++-- packages/jest-transform/tsconfig.json | 13 ++ packages/jest-types/package.json | 5 +- packages/jest-types/src/index.ts | 12 +- yarn.lock | 10 ++ 17 files changed, 163 insertions(+), 148 deletions(-) rename packages/jest-transform/src/{ScriptTransformer.js => ScriptTransformer.ts} (84%) rename packages/jest-transform/src/{enhanceUnexpectedTokenMessage.js => enhanceUnexpectedTokenMessage.ts} (99%) rename packages/jest-transform/src/{index.js => index.ts} (90%) rename packages/jest-transform/src/{shouldInstrument.js => shouldInstrument.ts} (88%) delete mode 100644 packages/jest-transform/src/types.js rename packages/{jest-types/src/Transform.ts => jest-transform/src/types.ts} (68%) create mode 100644 packages/jest-transform/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index d23db1531297..5d980e89930b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ - `[@jest/transform]`: New package extracted from `jest-runtime` ([#7915](https://github.com/facebook/jest/pull/7915)) - `[babel-plugin-jest-hoist]`: Migrate to TypeScript ([#7898](https://github.com/facebook/jest/pull/7898)) - `[@jest/core]` Create new package, which is `jest-cli` minus `yargs` and `prompts` ([#7696](https://github.com/facebook/jest/pull/7696)) +- `[@jest/transform]`: Migrate to TypeScript ([#7918](https://github.com/facebook/jest/pull/7918)) ### Performance diff --git a/packages/babel-jest/package.json b/packages/babel-jest/package.json index a827589d2319..226e23853ce9 100644 --- a/packages/babel-jest/package.json +++ b/packages/babel-jest/package.json @@ -11,7 +11,9 @@ "main": "build/index.js", "types": "build/index.d.ts", "dependencies": { + "@jest/transform": "^24.1.0", "@jest/types": "^24.1.0", + "@types/babel__core": "^7.0.4", "babel-plugin-istanbul": "^5.1.0", "babel-preset-jest": "^24.1.0", "chalk": "^2.4.2", @@ -19,7 +21,6 @@ }, "devDependencies": { "@babel/core": "^7.1.0", - "@types/babel__core": "^7.0.4", "@types/slash": "^2.0.0" }, "peerDependencies": { diff --git a/packages/babel-jest/src/__tests__/index.ts b/packages/babel-jest/src/__tests__/index.ts index 01fbd54b92c9..6b3d264a883f 100644 --- a/packages/babel-jest/src/__tests__/index.ts +++ b/packages/babel-jest/src/__tests__/index.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {Config, Transform} from '@jest/types'; +import {Config} from '@jest/types'; import babelJest from '../index'; //Mock data for all the tests @@ -30,14 +30,12 @@ test(`Returns source string with inline maps when no transformOptions is passed` sourceString, 'dummy_path.js', (mockConfig as unknown) as Config.ProjectConfig, - ) as Transform.TransformedSource; + ) as any; expect(typeof result).toBe('object'); expect(result.code).toBeDefined(); expect(result.map).toBeDefined(); expect(result.code).toMatch('//# sourceMappingURL'); expect(result.code).toMatch('customMultiply'); - // @ts-ignore: it's fine if we get wrong types, the tests will fail then expect(result.map.sources).toEqual(['dummy_path.js']); - // @ts-ignore: it's fine if we get wrong types, the tests will fail then expect(JSON.stringify(result.map.sourcesContent)).toMatch('customMultiply'); }); diff --git a/packages/babel-jest/src/index.ts b/packages/babel-jest/src/index.ts index ea8a5bde75cd..2df36fe37d22 100644 --- a/packages/babel-jest/src/index.ts +++ b/packages/babel-jest/src/index.ts @@ -8,12 +8,13 @@ import crypto from 'crypto'; import fs from 'fs'; import path from 'path'; -import {Config, Transform} from '@jest/types'; +import {Transformer} from '@jest/transform'; +import {Config} from '@jest/types'; import { - transformSync as babelTransform, loadPartialConfig, - TransformOptions, PartialConfig, + TransformOptions, + transformSync as babelTransform, } from '@babel/core'; import chalk from 'chalk'; import slash from 'slash'; @@ -22,12 +23,18 @@ const THIS_FILE = fs.readFileSync(__filename); const jestPresetPath = require.resolve('babel-preset-jest'); const babelIstanbulPlugin = require.resolve('babel-plugin-istanbul'); +// Narrow down the types +interface BabelJestTransformer extends Transformer { + canInstrument: true; + createTransformer: (options?: TransformOptions) => BabelJestTransformer; +} + const createTransformer = ( options: TransformOptions = {}, -): Transform.Transformer => { +): BabelJestTransformer => { options = { ...options, - // @ts-ignore: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/32955 + // @ts-ignore: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/33118 caller: { name: 'babel-jest', supportsStaticESM: false, @@ -58,18 +65,15 @@ const createTransformer = ( return babelConfig; } - return { + const transformer: BabelJestTransformer = { canInstrument: true, + createTransformer, getCacheKey( - fileData: string, - filename: Config.Path, - configString: string, - { - config, - instrument, - rootDir, - }: {config: Config.ProjectConfig} & Transform.CacheKeyOptions, - ): string { + fileData, + filename, + configString, + {config, instrument, rootDir}, + ) { const babelOptions = loadBabelConfig(config.cwd, filename); const configPath = [ babelOptions.config || '', @@ -97,12 +101,7 @@ const createTransformer = ( .update(process.env.BABEL_ENV || '') .digest('hex'); }, - process( - src: string, - filename: Config.Path, - config: Config.ProjectConfig, - transformOptions?: Transform.TransformOptions, - ): string | Transform.TransformedSource { + process(src, filename, config, transformOptions) { const babelOptions = {...loadBabelConfig(config.cwd, filename).options}; if (transformOptions && transformOptions.instrument) { @@ -132,13 +131,8 @@ const createTransformer = ( return src; }, }; -}; -const transformer = createTransformer(); - -// FIXME: This is not part of the exported TS types. When fixed, remember to -// move @types/babel__core to `dependencies` instead of `devDependencies` -// (one fix is to use ESM, maybe for Jest 25?) -transformer.createTransformer = createTransformer; + return transformer; +}; -export = transformer; +export = createTransformer(); diff --git a/packages/babel-jest/tsconfig.json b/packages/babel-jest/tsconfig.json index 75c28b2f54a9..c11e5a122222 100644 --- a/packages/babel-jest/tsconfig.json +++ b/packages/babel-jest/tsconfig.json @@ -6,6 +6,7 @@ }, // TODO: include `babel-preset-jest` if it's ever in TS even though we don't care about its types "references": [ + {"path": "../jest-transform"}, {"path": "../jest-types"} ] } diff --git a/packages/jest-core/package.json b/packages/jest-core/package.json index 4d835cd058dc..adc6256996b9 100644 --- a/packages/jest-core/package.json +++ b/packages/jest-core/package.json @@ -34,7 +34,9 @@ "@types/exit": "^0.1.30", "@types/graceful-fs": "^4.1.2", "@types/micromatch": "^3.1.0", - "@types/rimraf": "^2.0.2" + "@types/p-each-series": "^1.0.0", + "@types/rimraf": "^2.0.2", + "@types/strip-ansi": "^3.0.0" }, "engines": { "node": ">= 6" diff --git a/packages/jest-transform/package.json b/packages/jest-transform/package.json index 345321986283..61152b5281f7 100644 --- a/packages/jest-transform/package.json +++ b/packages/jest-transform/package.json @@ -10,6 +10,7 @@ "main": "build/index.js", "dependencies": { "@babel/core": "^7.1.0", + "@jest/types": "^24.1.0", "babel-plugin-istanbul": "^5.1.0", "chalk": "^2.0.1", "convert-source-map": "^1.4.0", @@ -21,11 +22,13 @@ "micromatch": "^3.1.10", "realpath-native": "^1.1.0", "slash": "^2.0.0", + "source-map": "^0.6.1", "write-file-atomic": "2.4.1" }, "devDependencies": { "@types/babel__core": "^7.0.4", "@types/convert-source-map": "^1.5.1", + "@types/fast-json-stable-stringify": "^2.0.0", "@types/graceful-fs": "^4.1.2", "@types/micromatch": "^3.1.0", "@types/write-file-atomic": "^2.1.1" diff --git a/packages/jest-transform/src/ScriptTransformer.js b/packages/jest-transform/src/ScriptTransformer.ts similarity index 84% rename from packages/jest-transform/src/ScriptTransformer.js rename to packages/jest-transform/src/ScriptTransformer.ts index 44b5959fb39e..350616c327ad 100644 --- a/packages/jest-transform/src/ScriptTransformer.js +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -3,57 +3,59 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Path, ProjectConfig} from 'types/Config'; -import type { - Transformer, - TransformedSource, - TransformResult, -} from 'types/Transform'; -import type {ErrorWithCode} from 'types/Errors'; -import type {Options} from './types'; - import crypto from 'crypto'; import path from 'path'; import vm from 'vm'; +import {Config} from '@jest/types'; import {createDirectory} from 'jest-util'; import fs from 'graceful-fs'; import {transformSync as babelTransform} from '@babel/core'; +// @ts-ignore: should just be `require.resolve`, but the tests mess that up import babelPluginIstanbul from 'babel-plugin-istanbul'; import convertSourceMap from 'convert-source-map'; import HasteMap from 'jest-haste-map'; import stableStringify from 'fast-json-stable-stringify'; import slash from 'slash'; -import {version as VERSION} from '../package.json'; -import shouldInstrument from './shouldInstrument'; import writeFileAtomic from 'write-file-atomic'; import {sync as realpath} from 'realpath-native'; +import { + Options, + Transformer, + TransformedSource, + TransformResult, +} from './types'; +import shouldInstrument from './shouldInstrument'; import enhanceUnexpectedTokenMessage from './enhanceUnexpectedTokenMessage'; -type ProjectCache = {| - configString: string, - ignorePatternsRegExp: ?RegExp, - transformedFiles: Map, -|}; +type ProjectCache = { + configString: string; + ignorePatternsRegExp: RegExp | null; + transformedFiles: Map; +}; + +// Use `require` to avoid TS rootDir +const {version: VERSION} = require('../package.json'); // This data structure is used to avoid recalculating some data every time that // we need to transform a file. Since ScriptTransformer is instantiated for each // file we need to keep this object in the local scope of this module. -const projectCaches: WeakMap = new WeakMap(); +const projectCaches: WeakMap< + Config.ProjectConfig, + ProjectCache +> = new WeakMap(); // To reset the cache for specific changesets (rather than package version). const CACHE_VERSION = '1'; export default class ScriptTransformer { static EVAL_RESULT_VARIABLE: string; - _cache: ProjectCache; - _config: ProjectConfig; - _transformCache: Map; + private _cache: ProjectCache; + private _config: Config.ProjectConfig; + private _transformCache: Map; - constructor(config: ProjectConfig) { + constructor(config: Config.ProjectConfig) { this._config = config; this._transformCache = new Map(); @@ -72,7 +74,11 @@ export default class ScriptTransformer { this._cache = projectCache; } - _getCacheKey(fileData: string, filename: Path, instrument: boolean): string { + private _getCacheKey( + fileData: string, + filename: Config.Path, + instrument: boolean, + ): string { const configString = this._cache.configString; const transformer = this._getTransformer(filename); @@ -99,11 +105,12 @@ export default class ScriptTransformer { } } - _getFileCachePath( - filename: Path, + private _getFileCachePath( + filename: Config.Path, content: string, instrument: boolean, - ): Path { + ): Config.Path { + // @ts-ignore: not properly exported (needs ESM) const baseCacheDir = HasteMap.getCacheFilePath( this._config.cacheDirectory, 'jest-transform-cache-' + this._config.name, @@ -124,7 +131,7 @@ export default class ScriptTransformer { return cachePath; } - _getTransformPath(filename: Path) { + private _getTransformPath(filename: Config.Path) { for (let i = 0; i < this._config.transform.length; i++) { if (new RegExp(this._config.transform[i][0]).test(filename)) { return this._config.transform[i][1]; @@ -133,8 +140,8 @@ export default class ScriptTransformer { return null; } - _getTransformer(filename: Path) { - let transform: ?Transformer; + private _getTransformer(filename: Config.Path) { + let transform: Transformer | null = null; if (!this._config.transform || !this._config.transform.length) { return null; } @@ -146,8 +153,7 @@ export default class ScriptTransformer { return transformer; } - // $FlowFixMe - transform = (require(transformPath): Transformer); + transform = require(transformPath) as Transformer; if (typeof transform.createTransformer === 'function') { transform = transform.createTransformer(); } @@ -161,10 +167,11 @@ export default class ScriptTransformer { return transform; } - _instrumentFile(filename: Path, content: string): string { + private _instrumentFile(filename: Config.Path, content: string): string { const result = babelTransform(content, { auxiliaryCommentBefore: ' istanbul ignore next ', babelrc: false, + // @ts-ignore: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/33118 caller: { name: '@jest/transform', supportsStaticESM: false, @@ -185,10 +192,18 @@ export default class ScriptTransformer { ], }); - return result ? result.code : content; + if (result) { + const {code} = result; + + if (code) { + return code; + } + } + + return content; } - _getRealPath(filepath: Path): Path { + private _getRealPath(filepath: Config.Path): Config.Path { try { return realpath(filepath) || filepath; } catch (err) { @@ -198,15 +213,15 @@ export default class ScriptTransformer { // We don't want to expose transformers to the outside - this function is just // to warm up `this._transformCache` - preloadTransformer(filepath: Path): void { + preloadTransformer(filepath: Config.Path): void { this._getTransformer(filepath); } - transformSource(filepath: Path, content: string, instrument: boolean) { + transformSource(filepath: Config.Path, content: string, instrument: boolean) { const filename = this._getRealPath(filepath); const transform = this._getTransformer(filename); const cacheFilePath = this._getFileCachePath(filename, content, instrument); - let sourceMapPath = cacheFilePath + '.map'; + let sourceMapPath: Config.Path | null = cacheFilePath + '.map'; // Ignore cache if `config.cache` is set (--no-cache) let code = this._config.cache ? readCodeCacheFile(cacheFilePath) : null; @@ -290,9 +305,9 @@ export default class ScriptTransformer { }; } - _transformAndBuildScript( - filename: Path, - options: ?Options, + private _transformAndBuildScript( + filename: Config.Path, + options: Options | null, instrument: boolean, fileSource?: string, ): TransformResult { @@ -303,7 +318,7 @@ export default class ScriptTransformer { ); let wrappedCode: string; - let sourceMapPath: ?string = null; + let sourceMapPath: string | null = null; let mapCoverage = false; const willTransform = @@ -354,13 +369,13 @@ export default class ScriptTransformer { } transform( - filename: Path, + filename: Config.Path, options: Options, fileSource?: string, ): TransformResult { let scriptCacheKey = null; let instrument = false; - let result = ''; + let result: TransformResult | undefined; if (!options.isCoreModule) { instrument = shouldInstrument(filename, options, this._config); @@ -386,7 +401,7 @@ export default class ScriptTransformer { return result; } - _shouldTransform(filename: Path): boolean { + private _shouldTransform(filename: Config.Path): boolean { const ignoreRegexp = this._cache.ignorePatternsRegExp; const isIgnored = ignoreRegexp ? ignoreRegexp.test(filename) : false; @@ -396,13 +411,13 @@ export default class ScriptTransformer { } } -const removeFile = (path: Path) => { +const removeFile = (path: Config.Path) => { try { fs.unlinkSync(path); } catch (e) {} }; -const stripShebang = content => { +const stripShebang = (content: string) => { // If the file data starts with a shebang remove it. Leaves the empty line // to keep stack trace line numbers correct. if (content.startsWith('#!')) { @@ -419,7 +434,7 @@ const stripShebang = content => { * it right away. This is not a great system, because source map cache file * could get corrupted, out-of-sync, etc. */ -function writeCodeCacheFile(cachePath: Path, code: string) { +function writeCodeCacheFile(cachePath: Config.Path, code: string) { const checksum = crypto .createHash('md5') .update(code) @@ -433,7 +448,7 @@ function writeCodeCacheFile(cachePath: Path, code: string) { * could happen if an older version of `jest-runtime` writes non-atomically to * the same cache, for example. */ -function readCodeCacheFile(cachePath: Path): ?string { +function readCodeCacheFile(cachePath: Config.Path): string | null { const content = readCacheFile(cachePath); if (content == null) { return null; @@ -455,7 +470,7 @@ function readCodeCacheFile(cachePath: Path): ?string { * two processes to write to the same file at the same time. It also reduces * the risk of reading a file that's being overwritten at the same time. */ -const writeCacheFile = (cachePath: Path, fileData: string) => { +const writeCacheFile = (cachePath: Config.Path, fileData: string) => { try { writeFileAtomic.sync(cachePath, fileData, {encoding: 'utf8'}); } catch (e) { @@ -479,12 +494,15 @@ const writeCacheFile = (cachePath: Path, fileData: string) => { * If the target file exists we can be reasonably sure another process has * legitimately won a cache write race and ignore the error. */ -const cacheWriteErrorSafeToIgnore = (e: ErrorWithCode, cachePath: Path) => +const cacheWriteErrorSafeToIgnore = ( + e: Error & {code: string}, + cachePath: Config.Path, +) => process.platform === 'win32' && e.code === 'EPERM' && fs.existsSync(cachePath); -const readCacheFile = (cachePath: Path): ?string => { +const readCacheFile = (cachePath: Config.Path): string | null => { if (!fs.existsSync(cachePath)) { return null; } @@ -510,12 +528,14 @@ const readCacheFile = (cachePath: Path): ?string => { return fileData; }; -const getScriptCacheKey = (filename, instrument: boolean) => { +const getScriptCacheKey = (filename: Config.Path, instrument: boolean) => { const mtime = fs.statSync(filename).mtime; return filename + '_' + mtime.getTime() + (instrument ? '_instrumented' : ''); }; -const calcIgnorePatternRegexp = (config: ProjectConfig): ?RegExp => { +const calcIgnorePatternRegexp = ( + config: Config.ProjectConfig, +): RegExp | null => { if ( !config.transformIgnorePatterns || config.transformIgnorePatterns.length === 0 @@ -526,7 +546,7 @@ const calcIgnorePatternRegexp = (config: ProjectConfig): ?RegExp => { return new RegExp(config.transformIgnorePatterns.join('|')); }; -const wrap = (content, ...extras) => { +const wrap = (content: string, ...extras: Array) => { const globals = new Set([ 'module', 'exports', diff --git a/packages/jest-transform/src/enhanceUnexpectedTokenMessage.js b/packages/jest-transform/src/enhanceUnexpectedTokenMessage.ts similarity index 99% rename from packages/jest-transform/src/enhanceUnexpectedTokenMessage.js rename to packages/jest-transform/src/enhanceUnexpectedTokenMessage.ts index af941fa7cf53..e16129fe7c95 100644 --- a/packages/jest-transform/src/enhanceUnexpectedTokenMessage.js +++ b/packages/jest-transform/src/enhanceUnexpectedTokenMessage.ts @@ -1,7 +1,5 @@ // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. -// @flow - import chalk from 'chalk'; const DOT = ' \u2022 '; diff --git a/packages/jest-transform/src/index.js b/packages/jest-transform/src/index.ts similarity index 90% rename from packages/jest-transform/src/index.js rename to packages/jest-transform/src/index.ts index 37cbb258b92a..6f10151c3051 100644 --- a/packages/jest-transform/src/index.js +++ b/packages/jest-transform/src/index.ts @@ -3,9 +3,8 @@ * * 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 as ScriptTransformer} from './ScriptTransformer'; export {default as shouldInstrument} from './shouldInstrument'; +export {Transformer} from './types'; diff --git a/packages/jest-transform/src/shouldInstrument.js b/packages/jest-transform/src/shouldInstrument.ts similarity index 88% rename from packages/jest-transform/src/shouldInstrument.js rename to packages/jest-transform/src/shouldInstrument.ts index c0b869d4318b..d8585c37a8b2 100644 --- a/packages/jest-transform/src/shouldInstrument.js +++ b/packages/jest-transform/src/shouldInstrument.ts @@ -3,26 +3,23 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Path, ProjectConfig} from 'types/Config'; -import type {Options} from './types'; - import path from 'path'; +import {Config} from '@jest/types'; import {escapePathForRegex} from 'jest-regex-util'; import {replacePathSepForGlob} from 'jest-util'; import micromatch from 'micromatch'; +import {Options} from './types'; const MOCKS_PATTERN = new RegExp( escapePathForRegex(path.sep + '__mocks__' + path.sep), ); export default function shouldInstrument( - filename: Path, + filename: Config.Path, options: Options, - config: ProjectConfig, + config: Config.ProjectConfig, ): boolean { if (!options.collectCoverage) { return false; @@ -38,7 +35,7 @@ export default function shouldInstrument( if ( !config.testPathIgnorePatterns || - !config.testPathIgnorePatterns.some(pattern => filename.match(pattern)) + !config.testPathIgnorePatterns.some(pattern => !!filename.match(pattern)) ) { if ( config.testRegex && @@ -79,7 +76,7 @@ export default function shouldInstrument( if ( config.coveragePathIgnorePatterns && - config.coveragePathIgnorePatterns.some(pattern => filename.match(pattern)) + config.coveragePathIgnorePatterns.some(pattern => !!filename.match(pattern)) ) { return false; } diff --git a/packages/jest-transform/src/types.js b/packages/jest-transform/src/types.js deleted file mode 100644 index 00f577bdad80..000000000000 --- a/packages/jest-transform/src/types.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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 type {Glob, Path} from 'types/Config'; - -// TODO: Pick from `GlobalConfig` -export type Options = {| - changedFiles: ?Set, - collectCoverage: boolean, - collectCoverageFrom: Array, - collectCoverageOnlyFrom: ?{[key: string]: boolean, __proto__: null}, - extraGlobals?: Array, - isCoreModule?: boolean, - isInternalModule?: boolean, -|}; diff --git a/packages/jest-types/src/Transform.ts b/packages/jest-transform/src/types.ts similarity index 68% rename from packages/jest-types/src/Transform.ts rename to packages/jest-transform/src/types.ts index 473434637f90..91df3f394de9 100644 --- a/packages/jest-types/src/Transform.ts +++ b/packages/jest-transform/src/types.ts @@ -7,7 +7,19 @@ import {Script} from 'vm'; import {RawSourceMap} from 'source-map'; -import {Path, ProjectConfig} from './Config'; +import {Config} from '@jest/types'; + +export type Options = Pick< + Config.GlobalConfig, + | 'collectCoverage' + | 'collectCoverageFrom' + | 'collectCoverageOnlyFrom' + | 'extraGlobals' +> & { + changedFiles: Set | undefined; + isCoreModule?: boolean; + isInternalModule?: boolean; +}; // https://stackoverflow.com/a/48216010/1850276 type Omit = Pick>; @@ -26,7 +38,7 @@ export type TransformedSource = { export type TransformResult = { script: Script; mapCoverage: boolean; - sourceMapPath?: string; + sourceMapPath: string | null; }; export type TransformOptions = { @@ -34,26 +46,26 @@ export type TransformOptions = { }; export type CacheKeyOptions = { - config: ProjectConfig; + config: Config.ProjectConfig; instrument: boolean; rootDir: string; }; -export type Transformer = { +export interface Transformer { canInstrument?: boolean; - createTransformer?: (options: any) => Transformer; + createTransformer?: (options?: any) => Transformer; getCacheKey: ( fileData: string, - filePath: Path, + filePath: Config.Path, configStr: string, options: CacheKeyOptions, ) => string; process: ( sourceText: string, - sourcePath: Path, - config: ProjectConfig, + sourcePath: Config.Path, + config: Config.ProjectConfig, options?: TransformOptions, ) => string | TransformedSource; -}; +} diff --git a/packages/jest-transform/tsconfig.json b/packages/jest-transform/tsconfig.json new file mode 100644 index 000000000000..73d865a32cda --- /dev/null +++ b/packages/jest-transform/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [ + {"path": "../jest-haste-map"}, + {"path": "../jest-regex-util"}, + {"path": "../jest-types"}, + {"path": "../jest-util"} + ] +} diff --git a/packages/jest-types/package.json b/packages/jest-types/package.json index 572e2b791882..86018bd9b822 100644 --- a/packages/jest-types/package.json +++ b/packages/jest-types/package.json @@ -11,8 +11,5 @@ }, "license": "MIT", "main": "build/index.js", - "types": "build/index.d.ts", - "dependencies": { - "source-map": "^0.6.1" - } + "types": "build/index.d.ts" } diff --git a/packages/jest-types/src/index.ts b/packages/jest-types/src/index.ts index 5f72f2e75feb..70361152f936 100644 --- a/packages/jest-types/src/index.ts +++ b/packages/jest-types/src/index.ts @@ -10,17 +10,7 @@ import * as Console from './Console'; import * as SourceMaps from './SourceMaps'; import * as TestResult from './TestResult'; import * as Mocks from './Mocks'; -import * as Transform from './Transform'; import * as PrettyFormat from './PrettyFormat'; import * as Matchers from './Matchers'; -export { - Config, - Console, - SourceMaps, - TestResult, - Mocks, - Transform, - PrettyFormat, - Matchers, -}; +export {Config, Console, SourceMaps, TestResult, Mocks, PrettyFormat, Matchers}; diff --git a/yarn.lock b/yarn.lock index 2ca7de58e22a..a11c96da8288 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1575,6 +1575,11 @@ resolved "https://registry.yarnpkg.com/@types/exit/-/exit-0.1.30.tgz#7078b736a7d166c80b6394dc0d9de1577ca76daf" integrity sha1-cHi3NqfRZsgLY5TcDZ3hV3ynba8= +"@types/fast-json-stable-stringify@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#40363bb847cb86b2c2e1599f1398d11e8329c921" + integrity sha512-mky/O83TXmGY39P1H9YbUpjV6l6voRYlufqfFCvel8l1phuy8HRjdWc1rrPuN53ITBJlbyMSV6z3niOySO5pgQ== + "@types/fb-watchman@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/fb-watchman/-/fb-watchman-2.0.0.tgz#ca60ded406baa8c81c65ac1f86763a5d00aa7c55" @@ -1705,6 +1710,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.24.tgz#b13564af612a22a20b5d95ca40f1bffb3af315cf" integrity sha512-GWWbvt+z9G5otRBW8rssOFgRY87J9N/qbhqfjMZ+gUuL6zoL+Hm6gP/8qQBG4jjimqdaNLCehcVapZ/Fs2WjCQ== +"@types/p-each-series@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/p-each-series/-/p-each-series-1.0.0.tgz#04d8a62a63a2fb82a1673ec98edabaf58dd73072" + integrity sha512-bqQAn+tAs1QwGQYNIbv8a0XT8Pzl6Z+6SVpca+vJngcvwRwws7eJj9P2rTJDpjwOSyX1iRNSlIokUlusV0mP0A== + "@types/prettier@^1.16.1": version "1.16.1" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.16.1.tgz#328d1c9b54402e44119398bcb6a31b7bbd606d59" From 9cbafef422c9b908e3c3e5c17ac8891ac6bcb21f Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sun, 17 Feb 2019 13:52:11 +0100 Subject: [PATCH 070/107] chore: upgrade flow --- .flowconfig | 2 +- package.json | 4 ++-- packages/expect/src/jasmineUtils.js | 1 - yarn.lock | 16 ++++++++-------- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.flowconfig b/.flowconfig index e7de44ffb18f..cd4e73947413 100644 --- a/.flowconfig +++ b/.flowconfig @@ -13,7 +13,7 @@ untyped-import untyped-type-import [version] -^0.91.0 +^0.93.0 [options] module.name_mapper='^pretty-format$' -> '/packages/pretty-format/src/index.js' diff --git a/package.json b/package.json index 109352dede9e..a1d279fd9e1a 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "eslint-plugin-react": "^7.1.0", "eslint-plugin-relay": "~0.0.19", "execa": "^1.0.0", - "flow-bin": "^0.91.0", + "flow-bin": "^0.93.0", "glob": "^7.1.1", "graceful-fs": "^4.1.15", "isbinaryfile": "^3.0.3", @@ -68,7 +68,7 @@ "slash": "^2.0.0", "string-length": "^2.0.0", "strip-ansi": "^5.0.0", - "typescript": "^3.3.1", + "typescript": "^3.3.3", "webpack": "^4.28.4" }, "scripts": { diff --git a/packages/expect/src/jasmineUtils.js b/packages/expect/src/jasmineUtils.js index b558741ae553..e4438ed60293 100644 --- a/packages/expect/src/jasmineUtils.js +++ b/packages/expect/src/jasmineUtils.js @@ -215,7 +215,6 @@ function keys(obj, isArray, hasKey) { } for (var x = 0; x < allKeys.length; x++) { - //$FlowFixMe if (typeof allKeys[x] === 'symbol' || !allKeys[x].match(/^[0-9]+$/)) { extraKeys.push(allKeys[x]); } diff --git a/yarn.lock b/yarn.lock index a11c96da8288..3429cadc7ab8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5869,10 +5869,10 @@ flatten@^1.0.2: resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I= -flow-bin@^0.91.0: - version "0.91.0" - resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.91.0.tgz#f5c89729f74b2ccbd47df6fbfadbdcc89cc1e478" - integrity sha512-j+L+xNiUYnZZ27MjVI0y2c9474ZHOvdSQq0Tjwh56mEA7tfxYqp5Dcb6aZSwvs3tGMTjCrZow9aUlZf3OoRyDQ== +flow-bin@^0.93.0: + version "0.93.0" + resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.93.0.tgz#9192a08d88db2a8da0ff55e42420f44539791430" + integrity sha512-p8yq4ocOlpyJgOEBEj0v0GzCP25c9WP0ilFQ8hXSbrTR7RPKuR+Whr+OitlVyp8ocdX0j1MrIwQ8x28dacy1pg== flush-write-stream@^1.0.0: version "1.0.3" @@ -12767,10 +12767,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^3.0.0, typescript@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.1.tgz#6de14e1db4b8a006ac535e482c8ba018c55f750b" - integrity sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA== +typescript@^3.0.0, typescript@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.3.tgz#f1657fc7daa27e1a8930758ace9ae8da31403221" + integrity sha512-Y21Xqe54TBVp+VDSNbuDYdGw0BpoR/Q6wo/+35M8PAU0vipahnyduJWirxxdxjsAkS7hue53x2zp8gz7F05u0A== ua-parser-js@^0.7.18: version "0.7.19" From 64980c890bce4dc9c326dd3b31605fbdd0ff8977 Mon Sep 17 00:00:00 2001 From: Kornel Dubieniecki Date: Mon, 18 Feb 2019 02:12:19 +0100 Subject: [PATCH 071/107] Remove unnecessary config checks (#7801) --- CHANGELOG.md | 1 + TestUtils.js => TestUtils.ts | 21 +++---- .../src/lib/__tests__/is_valid_path.test.js | 1 + .../__tests__/generateEmptyCoverage.test.js | 1 + .../src/__tests__/instrumentation.test.js | 5 +- .../script_transformer.test.js.snap | 56 ++++++++++++++++++- .../src/__tests__/script_transformer.test.js | 6 +- .../src/__tests__/should_instrument.test.js | 36 ++++++------ .../jest-transform/src/shouldInstrument.ts | 26 ++------- 9 files changed, 95 insertions(+), 58 deletions(-) rename TestUtils.js => TestUtils.ts (87%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d980e89930b..2b64b89c05ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - `[jest-changed-files]` Improve default file selection for Mercurial repos ([#7880](https://github.com/facebook/jest/pull/7880)) - `[jest-validate]` Fix validating async functions ([#7894](https://github.com/facebook/jest/issues/7894)) - `[jest-circus]` Fix bug with test.only ([#7888](https://github.com/facebook/jest/pull/7888)) +- `[jest-transform]` Normalize config and remove unecessary checks, convert `TestUtils.js` to TypeScript ([#7801](https://github.com/facebook/jest/pull/7801) ### Chore & Maintenance diff --git a/TestUtils.js b/TestUtils.ts similarity index 87% rename from TestUtils.js rename to TestUtils.ts index 1c588f05822a..ba80887bda29 100644 --- a/TestUtils.js +++ b/TestUtils.ts @@ -3,15 +3,12 @@ * * 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 type {GlobalConfig, ProjectConfig} from 'types/Config'; +// eslint-disable-next-line import/no-extraneous-dependencies +import {Config} from '@jest/types'; -const DEFAULT_GLOBAL_CONFIG: GlobalConfig = { +const DEFAULT_GLOBAL_CONFIG: Config.GlobalConfig = { bail: 0, changedFilesWithAncestor: false, changedSince: '', @@ -67,7 +64,7 @@ const DEFAULT_GLOBAL_CONFIG: GlobalConfig = { watchman: false, }; -const DEFAULT_PROJECT_CONFIG: ProjectConfig = { +const DEFAULT_PROJECT_CONFIG: Config.ProjectConfig = { automock: false, browser: false, cache: false, @@ -124,7 +121,9 @@ const DEFAULT_PROJECT_CONFIG: ProjectConfig = { watchPathIgnorePatterns: [], }; -export const makeGlobalConfig = (overrides: Object = {}): GlobalConfig => { +export const makeGlobalConfig = ( + overrides: Partial = {}, +): Config.GlobalConfig => { const overridesKeys = new Set(Object.keys(overrides)); Object.keys(DEFAULT_GLOBAL_CONFIG).forEach(key => overridesKeys.delete(key)); @@ -138,7 +137,9 @@ export const makeGlobalConfig = (overrides: Object = {}): GlobalConfig => { return {...DEFAULT_GLOBAL_CONFIG, ...overrides}; }; -export const makeProjectConfig = (overrides: Object = {}): ProjectConfig => { +export const makeProjectConfig = ( + overrides: Partial = {}, +): Config.ProjectConfig => { const overridesKeys = new Set(Object.keys(overrides)); Object.keys(DEFAULT_PROJECT_CONFIG).forEach(key => overridesKeys.delete(key)); @@ -149,5 +150,5 @@ export const makeProjectConfig = (overrides: Object = {}): ProjectConfig => { `); } - return {...DEFAULT_GLOBAL_CONFIG, ...overrides}; + return {...DEFAULT_PROJECT_CONFIG, ...overrides}; }; diff --git a/packages/jest-core/src/lib/__tests__/is_valid_path.test.js b/packages/jest-core/src/lib/__tests__/is_valid_path.test.js index 2dbf4aaade1a..06320576c7c3 100644 --- a/packages/jest-core/src/lib/__tests__/is_valid_path.test.js +++ b/packages/jest-core/src/lib/__tests__/is_valid_path.test.js @@ -9,6 +9,7 @@ import isValidPath from '../is_valid_path'; import path from 'path'; +//$FlowFixMe: Converted to TS import {makeGlobalConfig} from '../../../../../TestUtils'; const rootDir = path.resolve(path.sep, 'root'); diff --git a/packages/jest-reporters/src/__tests__/generateEmptyCoverage.test.js b/packages/jest-reporters/src/__tests__/generateEmptyCoverage.test.js index de5b901249fd..00ae3f7a7ad8 100644 --- a/packages/jest-reporters/src/__tests__/generateEmptyCoverage.test.js +++ b/packages/jest-reporters/src/__tests__/generateEmptyCoverage.test.js @@ -13,6 +13,7 @@ import generateEmptyCoverage from '../generateEmptyCoverage'; import path from 'path'; import os from 'os'; +//$FlowFixMe: Converted to TS import {makeGlobalConfig, makeProjectConfig} from '../../../../TestUtils'; jest.mock('@jest/transform', () => ({ diff --git a/packages/jest-runtime/src/__tests__/instrumentation.test.js b/packages/jest-runtime/src/__tests__/instrumentation.test.js index 8f6188d86ca3..3d229f5105e1 100644 --- a/packages/jest-runtime/src/__tests__/instrumentation.test.js +++ b/packages/jest-runtime/src/__tests__/instrumentation.test.js @@ -12,6 +12,7 @@ import vm from 'vm'; import path from 'path'; import os from 'os'; import {ScriptTransformer} from '@jest/transform'; +import {makeProjectConfig} from '../../../../TestUtils'; jest.mock('vm'); @@ -21,11 +22,11 @@ const FILE_PATH_TO_INSTRUMENT = path.resolve( ); it('instruments files', () => { - const config = { + const config = makeProjectConfig({ cache: false, cacheDirectory: os.tmpdir(), rootDir: '/', - }; + }); const instrumented = new ScriptTransformer(config).transform( FILE_PATH_TO_INSTRUMENT, {collectCoverage: true}, diff --git a/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap b/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap index d2d473e38b9f..92cba297e22e 100644 --- a/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap +++ b/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap @@ -3,10 +3,60 @@ exports[`ScriptTransformer passes expected transform options to getCacheKey 1`] = ` Object { "config": Object { + "automock": false, + "browser": false, "cache": true, "cacheDirectory": "/cache/", + "clearMocks": false, + "coveragePathIgnorePatterns": Array [], + "cwd": "/test_root_dir/", + "detectLeaks": false, + "detectOpenHandles": false, + "displayName": undefined, + "errorOnDeprecated": false, + "extraGlobals": Array [], + "filter": null, + "forceCoverageMatch": Array [], + "globalSetup": null, + "globalTeardown": null, + "globals": Object {}, + "haste": Object { + "providesModuleNodeModules": Array [], + }, + "moduleDirectories": Array [], + "moduleFileExtensions": Array [ + "js", + ], + "moduleLoader": "/test_module_loader_path", + "moduleNameMapper": Array [], + "modulePathIgnorePatterns": Array [], + "modulePaths": Array [], "name": "test", + "prettierPath": "prettier", + "resetMocks": false, + "resetModules": false, + "resolver": null, + "restoreMocks": false, "rootDir": "/", + "roots": Array [], + "runner": "jest-runner", + "setupFiles": Array [], + "setupFilesAfterEnv": Array [], + "skipFilter": false, + "skipNodeResolution": false, + "snapshotResolver": null, + "snapshotSerializers": Array [], + "testEnvironment": "node", + "testEnvironmentOptions": Object {}, + "testLocationInResults": false, + "testMatch": Array [], + "testPathIgnorePatterns": Array [], + "testRegex": Array [ + "\\\\.test\\\\.js$", + ], + "testRunner": "jest-jasmine2", + "testURL": "", + "timers": "real", "transform": Array [ Array [ "^.+\\\\.js$", @@ -16,6 +66,8 @@ Object { "transformIgnorePatterns": Array [ "/node_modules/", ], + "unmockedModulePathPatterns": null, + "watchPathIgnorePatterns": Array [], }, "instrument": true, "rootDir": "/", @@ -172,7 +224,7 @@ exports[`ScriptTransformer uses multiple preprocessors 1`] = ` const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = \\"banana\\";', - config: '{\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"name\\":\\"test\\",\\"rootDir\\":\\"/\\",\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"transform\\":[[\\"^.+\\\\\\\\.js$\\",\\"test_preprocessor\\"],[\\"^.+\\\\\\\\.css$\\",\\"css-preprocessor\\"]]}', + config: '{\\"automock\\":false,\\"browser\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extraGlobals\\":[],\\"filter\\":null,\\"forceCoverageMatch\\":[],\\"globalSetup\\":null,\\"globalTeardown\\":null,\\"globals\\":{},\\"haste\\":{\\"providesModuleNodeModules\\":[]},\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"resolver\\":null,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"snapshotResolver\\":null,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-jasmine2\\",\\"testURL\\":\\"\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"^.+\\\\\\\\.js$\\",\\"test_preprocessor\\"],[\\"^.+\\\\\\\\.css$\\",\\"css-preprocessor\\"]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"unmockedModulePathPatterns\\":null,\\"watchPathIgnorePatterns\\":[]}', }; }});" @@ -198,7 +250,7 @@ exports[`ScriptTransformer uses the supplied preprocessor 1`] = ` const TRANSFORMED = { filename: '/fruits/banana.js', script: 'module.exports = \\"banana\\";', - config: '{\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"name\\":\\"test\\",\\"rootDir\\":\\"/\\",\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"transform\\":[[\\"^.+\\\\\\\\.js$\\",\\"test_preprocessor\\"]]}', + config: '{\\"automock\\":false,\\"browser\\":false,\\"cache\\":true,\\"cacheDirectory\\":\\"/cache/\\",\\"clearMocks\\":false,\\"coveragePathIgnorePatterns\\":[],\\"cwd\\":\\"/test_root_dir/\\",\\"detectLeaks\\":false,\\"detectOpenHandles\\":false,\\"errorOnDeprecated\\":false,\\"extraGlobals\\":[],\\"filter\\":null,\\"forceCoverageMatch\\":[],\\"globalSetup\\":null,\\"globalTeardown\\":null,\\"globals\\":{},\\"haste\\":{\\"providesModuleNodeModules\\":[]},\\"moduleDirectories\\":[],\\"moduleFileExtensions\\":[\\"js\\"],\\"moduleLoader\\":\\"/test_module_loader_path\\",\\"moduleNameMapper\\":[],\\"modulePathIgnorePatterns\\":[],\\"modulePaths\\":[],\\"name\\":\\"test\\",\\"prettierPath\\":\\"prettier\\",\\"resetMocks\\":false,\\"resetModules\\":false,\\"resolver\\":null,\\"restoreMocks\\":false,\\"rootDir\\":\\"/\\",\\"roots\\":[],\\"runner\\":\\"jest-runner\\",\\"setupFiles\\":[],\\"setupFilesAfterEnv\\":[],\\"skipFilter\\":false,\\"skipNodeResolution\\":false,\\"snapshotResolver\\":null,\\"snapshotSerializers\\":[],\\"testEnvironment\\":\\"node\\",\\"testEnvironmentOptions\\":{},\\"testLocationInResults\\":false,\\"testMatch\\":[],\\"testPathIgnorePatterns\\":[],\\"testRegex\\":[\\"\\\\\\\\.test\\\\\\\\.js$\\"],\\"testRunner\\":\\"jest-jasmine2\\",\\"testURL\\":\\"\\",\\"timers\\":\\"real\\",\\"transform\\":[[\\"^.+\\\\\\\\.js$\\",\\"test_preprocessor\\"]],\\"transformIgnorePatterns\\":[\\"/node_modules/\\"],\\"unmockedModulePathPatterns\\":null,\\"watchPathIgnorePatterns\\":[]}', }; }});" diff --git a/packages/jest-transform/src/__tests__/script_transformer.test.js b/packages/jest-transform/src/__tests__/script_transformer.test.js index bc61eaa04b1b..994d28d7066c 100644 --- a/packages/jest-transform/src/__tests__/script_transformer.test.js +++ b/packages/jest-transform/src/__tests__/script_transformer.test.js @@ -8,6 +8,8 @@ 'use strict'; +import {makeProjectConfig} from '../../../../TestUtils'; + jest .mock('fs', () => // Node 10.5.x compatibility @@ -185,13 +187,13 @@ describe('ScriptTransformer', () => { writeFileAtomic = require('write-file-atomic'); - config = { + config = makeProjectConfig({ cache: true, cacheDirectory: '/cache/', name: 'test', rootDir: '/', transformIgnorePatterns: ['/node_modules/'], - }; + }); ScriptTransformer = require('../ScriptTransformer').default; }; diff --git a/packages/jest-transform/src/__tests__/should_instrument.test.js b/packages/jest-transform/src/__tests__/should_instrument.test.js index c05dacc5677e..38bf9388417c 100644 --- a/packages/jest-transform/src/__tests__/should_instrument.test.js +++ b/packages/jest-transform/src/__tests__/should_instrument.test.js @@ -7,22 +7,25 @@ */ import shouldInstrument from '../shouldInstrument'; -import {makeGlobalConfig} from '../../../../TestUtils'; +import {makeProjectConfig} from '../../../../TestUtils'; describe('shouldInstrument', () => { const defaultFilename = 'source_file.test.js'; const defaultOptions = { collectCoverage: true, }; - const defaultConfig = makeGlobalConfig({rootDir: '/'}); - + const defaultConfig = makeProjectConfig({rootDir: '/'}); describe('should return true', () => { const testShouldInstrument = ( filename = defaultFilename, - options = defaultOptions, - config = defaultConfig, + options, + config, ) => { - const result = shouldInstrument(filename, options, config); + const result = shouldInstrument( + filename, + {...defaultOptions, ...options}, + {...defaultConfig, ...config}, + ); expect(result).toBe(true); }; @@ -74,7 +77,6 @@ describe('shouldInstrument', () => { testShouldInstrument( 'collect/only/from/here.js', { - collectCoverage: true, collectCoverageOnlyFrom: {'collect/only/from/here.js': true}, }, defaultConfig, @@ -85,7 +87,6 @@ describe('shouldInstrument', () => { testShouldInstrument( 'do/collect/coverage.js', { - collectCoverage: true, collectCoverageFrom: ['!**/dont/**/*.js', '**/do/**/*.js'], }, defaultConfig, @@ -95,14 +96,12 @@ describe('shouldInstrument', () => { it('should return true if the file is not in coveragePathIgnorePatterns', () => { testShouldInstrument('do/collect/coverage.js', defaultOptions, { coveragePathIgnorePatterns: ['dont'], - rootDir: '/', }); }); it('should return true if file is a testfile but forceCoverageMatch is set', () => { testShouldInstrument('do/collect/sum.coverage.test.js', defaultOptions, { forceCoverageMatch: ['**/*.(coverage).(test).js'], - rootDir: '/', testRegex: ['.*\\.(test)\\.(js)$'], }); }); @@ -111,10 +110,14 @@ describe('shouldInstrument', () => { describe('should return false', () => { const testShouldInstrument = ( filename = defaultFilename, - options = defaultOptions, - config = defaultConfig, + options, + config, ) => { - const result = shouldInstrument(filename, options, config); + const result = shouldInstrument( + filename, + {...defaultOptions, ...options}, + {...defaultConfig, ...config}, + ); expect(result).toBe(false); }; @@ -164,7 +167,6 @@ describe('shouldInstrument', () => { testShouldInstrument( 'source_file.js', { - collectCoverage: true, collectCoverageOnlyFrom: {'collect/only/from/here.js': true}, }, defaultConfig, @@ -175,7 +177,6 @@ describe('shouldInstrument', () => { testShouldInstrument( 'dont/collect/coverage.js', { - collectCoverage: true, collectCoverageFrom: ['!**/dont/**/*.js', '**/do/**/*.js'], }, defaultConfig, @@ -185,7 +186,6 @@ describe('shouldInstrument', () => { it('if the file is in coveragePathIgnorePatterns', () => { testShouldInstrument('dont/collect/coverage.js', defaultOptions, { coveragePathIgnorePatterns: ['dont'], - rootDir: '/', }); }); @@ -201,27 +201,23 @@ describe('shouldInstrument', () => { it('if file is a globalSetup file', () => { testShouldInstrument('globalSetup.js', defaultOptions, { globalSetup: 'globalSetup.js', - rootDir: '/', }); }); it('if file is globalTeardown file', () => { testShouldInstrument('globalTeardown.js', defaultOptions, { globalTeardown: 'globalTeardown.js', - rootDir: '/', }); }); it('if file is in setupFiles', () => { testShouldInstrument('setupTest.js', defaultOptions, { - rootDir: '/', setupFiles: ['setupTest.js'], }); }); it('if file is in setupFilesAfterEnv', () => { testShouldInstrument('setupTest.js', defaultOptions, { - rootDir: '/', setupFilesAfterEnv: ['setupTest.js'], }); }); diff --git a/packages/jest-transform/src/shouldInstrument.ts b/packages/jest-transform/src/shouldInstrument.ts index d8585c37a8b2..2020e724ddb5 100644 --- a/packages/jest-transform/src/shouldInstrument.ts +++ b/packages/jest-transform/src/shouldInstrument.ts @@ -26,7 +26,6 @@ export default function shouldInstrument( } if ( - config.forceCoverageMatch && config.forceCoverageMatch.length && micromatch.any(filename, config.forceCoverageMatch) ) { @@ -34,21 +33,13 @@ export default function shouldInstrument( } if ( - !config.testPathIgnorePatterns || !config.testPathIgnorePatterns.some(pattern => !!filename.match(pattern)) ) { - if ( - config.testRegex && - config.testRegex.some(regex => new RegExp(regex).test(filename)) - ) { + if (config.testRegex.some(regex => new RegExp(regex).test(filename))) { return false; } - if ( - config.testMatch && - config.testMatch.length && - micromatch.some(replacePathSepForGlob(filename), config.testMatch) - ) { + if (micromatch.some(replacePathSepForGlob(filename), config.testMatch)) { return false; } } @@ -75,7 +66,6 @@ export default function shouldInstrument( } if ( - config.coveragePathIgnorePatterns && config.coveragePathIgnorePatterns.some(pattern => !!filename.match(pattern)) ) { return false; @@ -89,19 +79,11 @@ export default function shouldInstrument( return false; } - if ( - //TODO: Remove additional check when normalized config provided in unit test - config.setupFiles && - config.setupFiles.some(setupFile => setupFile === filename) - ) { + if (config.setupFiles.some(setupFile => setupFile === filename)) { return false; } - if ( - //TODO: Remove additional check when normalized config provided in unit test - config.setupFilesAfterEnv && - config.setupFilesAfterEnv.some(setupFile => setupFile === filename) - ) { + if (config.setupFilesAfterEnv.some(setupFile => setupFile === filename)) { return false; } From deedf4c903671eea4a9970454f8925458548eec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Jim=C3=A9nez=20Es=C3=BAn?= Date: Mon, 18 Feb 2019 11:52:38 +0000 Subject: [PATCH 072/107] Align readme (#7927) #impacc --- packages/jest-worker/src/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/jest-worker/src/index.ts b/packages/jest-worker/src/index.ts index 812d41db2b32..6e8aa4f839ba 100644 --- a/packages/jest-worker/src/index.ts +++ b/packages/jest-worker/src/index.ts @@ -53,10 +53,10 @@ function getExposedMethods( * so they will get executed as soon as they can. * * - Sticky method: if a "computeWorkerKey" method is provided within the - * config, the resulting string of this method will be used as a key. - * Every time this key is returned, it is guaranteed that your job will be - * processed by the same worker. This is specially useful if your workers are - * caching results. + * config, the resulting string of this method will be used as a key. + * Every time this key is returned, it is guaranteed that your job will be + * processed by the same worker. This is specially useful if your workers + * are caching results. */ export default class JestWorker { private _ending: boolean; From dd45b252507751aae6d59ca925932998b9a9fec4 Mon Sep 17 00:00:00 2001 From: egdbear <1176374+egdbear@users.noreply.github.com> Date: Mon, 18 Feb 2019 18:02:13 +0100 Subject: [PATCH 073/107] Add missing import to docs (#7928) --- CHANGELOG.md | 1 + docs/Es6ClassMocks.md | 2 ++ website/versioned_docs/version-23.x/Es6ClassMocks.md | 2 ++ website/versioned_docs/version-24.0/Es6ClassMocks.md | 2 ++ 4 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b64b89c05ce..00c2bcf8e278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ - `[babel-plugin-jest-hoist]`: Migrate to TypeScript ([#7898](https://github.com/facebook/jest/pull/7898)) - `[@jest/core]` Create new package, which is `jest-cli` minus `yargs` and `prompts` ([#7696](https://github.com/facebook/jest/pull/7696)) - `[@jest/transform]`: Migrate to TypeScript ([#7918](https://github.com/facebook/jest/pull/7918)) +- `[docs]` Add missing import to docs ([#7928](https://github.com/facebook/jest/pull/7928)) ### Performance diff --git a/docs/Es6ClassMocks.md b/docs/Es6ClassMocks.md index 31ce34423fa2..a0b744eb5cdc 100644 --- a/docs/Es6ClassMocks.md +++ b/docs/Es6ClassMocks.md @@ -155,6 +155,8 @@ Calls to jest.mock are hoisted to the top of the code. You can specify a mock la ```javascript import SoundPlayer from './sound-player'; +import SoundPlayerConsumer from './sound-player-consumer'; + jest.mock('./sound-player'); describe('When SoundPlayer throws an error', () => { diff --git a/website/versioned_docs/version-23.x/Es6ClassMocks.md b/website/versioned_docs/version-23.x/Es6ClassMocks.md index d949b05a31fb..32a864d69195 100644 --- a/website/versioned_docs/version-23.x/Es6ClassMocks.md +++ b/website/versioned_docs/version-23.x/Es6ClassMocks.md @@ -156,6 +156,8 @@ Calls to jest.mock are hoisted to the top of the code. You can specify a mock la ```javascript import SoundPlayer from './sound-player'; +import SoundPlayerConsumer from './sound-player-consumer'; + jest.mock('./sound-player'); describe('When SoundPlayer throws an error', () => { diff --git a/website/versioned_docs/version-24.0/Es6ClassMocks.md b/website/versioned_docs/version-24.0/Es6ClassMocks.md index e3bda02092b5..97e43754114f 100644 --- a/website/versioned_docs/version-24.0/Es6ClassMocks.md +++ b/website/versioned_docs/version-24.0/Es6ClassMocks.md @@ -156,6 +156,8 @@ Calls to jest.mock are hoisted to the top of the code. You can specify a mock la ```javascript import SoundPlayer from './sound-player'; +import SoundPlayerConsumer from './sound-player-consumer'; + jest.mock('./sound-player'); describe('When SoundPlayer throws an error', () => { From ab08116b3db851e68390a41fbbef124ab382e34e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Nison?= Date: Mon, 18 Feb 2019 19:49:15 +0100 Subject: [PATCH 074/107] Adds @babel/core to the peer dependencies (#7930) --- packages/babel-preset-jest/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/babel-preset-jest/package.json b/packages/babel-preset-jest/package.json index 2def73a20e35..dfb315b09364 100644 --- a/packages/babel-preset-jest/package.json +++ b/packages/babel-preset-jest/package.json @@ -12,6 +12,9 @@ "@babel/plugin-syntax-object-rest-spread": "^7.0.0", "babel-plugin-jest-hoist": "^24.1.0" }, + "peerDependencies": { + "@babel/core": "^7.0.0" + }, "engines": { "node": ">= 6" }, From 6db372f174505fd25a01c4a1a9c1c947f7f6c466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Jim=C3=A9nez=20Es=C3=BAn?= Date: Tue, 19 Feb 2019 14:47:03 +0000 Subject: [PATCH 075/107] Fix queue management in jest-worker (#7934) --- CHANGELOG.md | 3 +- packages/jest-worker/src/Farm.ts | 55 ++++++------ .../jest-worker/src/__tests__/Farm.test.js | 90 ++++++++++++++++--- packages/jest-worker/src/types.ts | 7 +- 4 files changed, 115 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00c2bcf8e278..4d58df44f24c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,8 @@ - `[jest-changed-files]` Improve default file selection for Mercurial repos ([#7880](https://github.com/facebook/jest/pull/7880)) - `[jest-validate]` Fix validating async functions ([#7894](https://github.com/facebook/jest/issues/7894)) - `[jest-circus]` Fix bug with test.only ([#7888](https://github.com/facebook/jest/pull/7888)) -- `[jest-transform]` Normalize config and remove unecessary checks, convert `TestUtils.js` to TypeScript ([#7801](https://github.com/facebook/jest/pull/7801) +- `[jest-transform]` Normalize config and remove unecessary checks, convert `TestUtils.js` to TypeScript ([#7801](https://github.com/facebook/jest/pull/7801)) +- `[jest-worker]` Fix `jest-worker` when using pre-allocated jobs ([#7934](https://github.com/facebook/jest/pull/7934)) ### Chore & Maintenance diff --git a/packages/jest-worker/src/Farm.ts b/packages/jest-worker/src/Farm.ts index 1006f05e94bf..522561fdfec0 100644 --- a/packages/jest-worker/src/Farm.ts +++ b/packages/jest-worker/src/Farm.ts @@ -9,6 +9,7 @@ import { ChildMessage, FarmOptions, QueueChildMessage, + QueueItem, WorkerInterface, OnStart, OnEnd, @@ -19,24 +20,25 @@ export default class Farm { private _computeWorkerKey: FarmOptions['computeWorkerKey']; private _cacheKeys: {[key: string]: WorkerInterface}; private _callback: Function; - private _last: Array; + private _last: Array; private _locks: Array; private _numOfWorkers: number; private _offset: number; - private _queue: Array; + private _queue: Array; constructor( numOfWorkers: number, callback: Function, computeWorkerKey?: FarmOptions['computeWorkerKey'], ) { - this._callback = callback; - this._numOfWorkers = numOfWorkers; this._cacheKeys = Object.create(null); - this._queue = []; + this._callback = callback; this._last = []; this._locks = []; + this._numOfWorkers = numOfWorkers; this._offset = 0; + this._queue = []; + if (computeWorkerKey) { this._computeWorkerKey = computeWorkerKey; } @@ -70,6 +72,7 @@ export default class Farm { }; const task = {onEnd, onStart, request}; + if (worker) { this._enqueue(task, worker.getWorkerId()); } else { @@ -78,56 +81,58 @@ export default class Farm { }); } - private _getNextJob(workerId: number): QueueChildMessage | null { + private _getNextTask(workerId: number): QueueChildMessage | null { let queueHead = this._queue[workerId]; - while (queueHead && queueHead.request[1]) { + while (queueHead && queueHead.task.request[1]) { queueHead = queueHead.next || null; } this._queue[workerId] = queueHead; - return queueHead; + return queueHead && queueHead.task; } private _process(workerId: number): Farm { - if (this.isLocked(workerId)) { + if (this._isLocked(workerId)) { return this; } - const job = this._getNextJob(workerId); + const task = this._getNextTask(workerId); - if (!job) { + if (!task) { return this; } const onEnd = (error: Error | null, result: unknown) => { - job.onEnd(error, result); - this.unlock(workerId); + task.onEnd(error, result); + + this._unlock(workerId); this._process(workerId); }; - this.lock(workerId); + task.request[1] = true; - this._callback(workerId, job.request, job.onStart, onEnd); - - job.request[1] = true; + this._lock(workerId); + this._callback(workerId, task.request, task.onStart, onEnd); return this; } private _enqueue(task: QueueChildMessage, workerId: number): Farm { + const item = {next: null, task}; + if (task.request[1]) { return this; } if (this._queue[workerId]) { - this._last[workerId].next = task; + this._last[workerId].next = item; } else { - this._queue[workerId] = task; + this._queue[workerId] = item; } - this._last[workerId] = task; + this._last[workerId] = item; this._process(workerId); return this; @@ -135,23 +140,23 @@ export default class Farm { private _push(task: QueueChildMessage): Farm { for (let i = 0; i < this._numOfWorkers; i++) { - const workerIdx = (this._offset + i) % this._numOfWorkers; - this._enqueue(task, workerIdx); + this._enqueue(task, (this._offset + i) % this._numOfWorkers); } + this._offset++; return this; } - lock(workerId: number): void { + private _lock(workerId: number): void { this._locks[workerId] = true; } - unlock(workerId: number): void { + private _unlock(workerId: number): void { this._locks[workerId] = false; } - isLocked(workerId: number): boolean { + private _isLocked(workerId: number): boolean { return this._locks[workerId]; } } diff --git a/packages/jest-worker/src/__tests__/Farm.test.js b/packages/jest-worker/src/__tests__/Farm.test.js index 3615cef62492..e502b1a987ca 100644 --- a/packages/jest-worker/src/__tests__/Farm.test.js +++ b/packages/jest-worker/src/__tests__/Farm.test.js @@ -200,18 +200,19 @@ describe('Farm', () => { workerReply(0, null, 17); await p0; - // Note that the stickiness is not created by the method name or the arguments - // it is solely controlled by the provided "computeWorkerKey" method, which in - // the test example always returns the same key, so all calls should be - // redirected to worker 1 (which is the one that resolved the first call). + // Note that the stickiness is not created by the method name or the + // arguments it is solely controlled by the provided "computeWorkerKey" + // method, which in the test example always returns the same key, so all + // calls should be redirected to worker 1 (which is the one that resolved + // the first call). const p1 = farm.doWork('foo', 'bar'); workerReply(1, null, 17); await p1; - // The first time, a call with a "1234567890abcdef" hash had never been done - // earlier ("foo" call), so it got queued to all workers. Later, since the one - // that resolved the call was the one in position 1, all subsequent calls are - // only redirected to that worker. + // The first time, a call with a "1234567890abcdef" hash had never been + // done earlier ("foo" call), so it got queued to all workers. Later, since + // the one that resolved the call was the one in position 1, all subsequent + // calls are only redirected to that worker. expect(callback).toHaveBeenCalledTimes(2); // Only "foo". expect(callback).toHaveBeenNthCalledWith( 1, @@ -248,11 +249,11 @@ describe('Farm', () => { workerReply(1, null, 17); await p1; - // Both requests are send to the same worker - // The first time, a call with a "1234567890abcdef" hash had never been done - // earlier ("foo" call), so it got queued to all workers. Later, since the one - // that resolved the call was the one in position 1, all subsequent calls are - // only redirected to that worker. + // Both requests are send to the same worker. The first time, a call with + // a "1234567890abcdef" hash had never been done earlier ("foo" call), so + // it got queued to all workers. Later, since the one that resolved the + // call was the one in position 1, all subsequent calls are only redirected + // to that worker. expect(callback).toHaveBeenCalledTimes(2); expect(callback).toHaveBeenNthCalledWith( 1, @@ -269,4 +270,67 @@ describe('Farm', () => { expect.any(Function), ); }); + + it('checks that locking works, and jobs are never lost', async () => { + const hash = jest + .fn() + // This will go to both queues, but picked by the first worker. + .mockReturnValueOnce(0) + // This will go to both queues too, but picked by the second worker. + .mockReturnValueOnce(1) + // This will go to worker 0, now only assigned to it. + .mockReturnValueOnce(0) + // This will go to worker 1, now only assigned to it. + .mockReturnValueOnce(1) + // This will go to both queues too, but will wait, since workers are busy. + .mockReturnValueOnce(2) + // This will only go to the first queue. + .mockReturnValueOnce(0) + // This will be gone if the queue implementation is wrong. + .mockReturnValueOnce(0) + // Push onto the second queue; potentially wiping the earlier job. + .mockReturnValueOnce(1); + + const farm = new Farm(2, callback, hash); + + // First and second jobs get resolved, so that their hash is sticked to + // the right worker: worker assignment is performed when workers reply, not + // when the call is made. + const p0 = farm.doWork('work-0'); + const p1 = farm.doWork('work-1'); + workerReply(0, null, 'response-0'); + await p0; + workerReply(1, null, 'response-1'); + await p1; + + // Now we perform the rest of the calls (7 resolves before 5 and 6, since 4 + // is in both queues, and as soon as you resolve 4, 7 will be picked). + const p2 = farm.doWork('work-2'); + const p3 = farm.doWork('work-3'); + const p4 = farm.doWork('work-4'); + const p5 = farm.doWork('work-5'); + const p6 = farm.doWork('work-6'); + const p7 = farm.doWork('work-7'); + workerReply(2, null, 'response-2'); + await p2; + workerReply(3, null, 'response-3'); + await p3; + workerReply(4, null, 'response-4'); + await p4; + workerReply(5, null, 'response-7'); + await p7; + workerReply(6, null, 'response-5'); + await p5; + workerReply(7, null, 'response-6'); + await p6; + + await expect(p0).resolves.toBe('response-0'); + await expect(p1).resolves.toBe('response-1'); + await expect(p2).resolves.toBe('response-2'); + await expect(p3).resolves.toBe('response-3'); + await expect(p4).resolves.toBe('response-4'); + await expect(p5).resolves.toBe('response-5'); + await expect(p6).resolves.toBe('response-6'); + await expect(p7).resolves.toBe('response-7'); + }); }); diff --git a/packages/jest-worker/src/types.ts b/packages/jest-worker/src/types.ts index 5c0e7f144cdc..f0f4d86aecae 100644 --- a/packages/jest-worker/src/types.ts +++ b/packages/jest-worker/src/types.ts @@ -147,6 +147,7 @@ export type ParentMessageError = [ export type ParentMessage = ParentMessageOk | ParentMessageError; // Queue types. + export type OnStart = (worker: WorkerInterface) => void; export type OnEnd = (err: Error | null, result: unknown) => void; @@ -154,5 +155,9 @@ export type QueueChildMessage = { request: ChildMessage; onStart: OnStart; onEnd: OnEnd; - next?: QueueChildMessage; +}; + +export type QueueItem = { + task: QueueChildMessage; + next: QueueItem | null; }; From 3e15abc5f9db266cce4db656260292b1d7e370fb Mon Sep 17 00:00:00 2001 From: Tho Date: Tue, 19 Feb 2019 18:22:12 +0100 Subject: [PATCH 076/107] feat: print stack on preset normalize error (#7935) --- CHANGELOG.md | 1 + .../src/__tests__/normalize.test.js | 37 ++++++++++++++++++- packages/jest-config/src/normalize.js | 6 ++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d58df44f24c..b33fe1e4bf73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - `[expect]`: Improve report when matcher fails, part 7 ([#7866](https://github.com/facebook/jest/pull/7866)) - `[expect]`: Improve report when matcher fails, part 8 ([#7876](https://github.com/facebook/jest/pull/7876)) - `[pretty-format]` Support `React.memo` ([#7891](https://github.com/facebook/jest/pull/7891)) +- `[jest-config]` Print error information on preset normalization error ([#7935](https://github.com/facebook/jest/pull/7935)) ### Fixes diff --git a/packages/jest-config/src/__tests__/normalize.test.js b/packages/jest-config/src/__tests__/normalize.test.js index e1e24827dade..9ccb14ddb43a 100644 --- a/packages/jest-config/src/__tests__/normalize.test.js +++ b/packages/jest-config/src/__tests__/normalize.test.js @@ -934,6 +934,10 @@ describe('preset', () => { return '/node_modules/react-native/jest-preset.json'; } + if (name === 'react-native-js-preset/jest-preset') { + return '/node_modules/react-native-js-preset/jest-preset.js'; + } + if (name === 'doesnt-exist') { return null; } @@ -951,6 +955,15 @@ describe('preset', () => { }), {virtual: true}, ); + jest.doMock( + '/node_modules/react-native-js-preset/jest-preset.js', + () => ({ + moduleNameMapper: { + json: true, + }, + }), + {virtual: true}, + ); jest.mock( '/node_modules/with-json-ext/jest-preset.json', () => ({ @@ -1021,7 +1034,29 @@ describe('preset', () => { }, {}, ); - }).toThrowError(/Unexpected token }/); + }).toThrowError(/Unexpected token } in JSON at position 104[\s\S]* at /); + }); + + test('throws when preset evaluation throws type error', () => { + jest.doMock( + '/node_modules/react-native-js-preset/jest-preset.js', + () => ({ + transform: {}.nonExistingProp.call(), + }), + {virtual: true}, + ); + + expect(() => { + normalize( + { + preset: 'react-native-js-preset', + rootDir: '/root/path/foo', + }, + {}, + ); + }).toThrowError( + /TypeError: Cannot read property 'call' of undefined[\s\S]* at /, + ); }); test('works with "react-native"', () => { diff --git a/packages/jest-config/src/normalize.js b/packages/jest-config/src/normalize.js index 4c2bee56f3cc..f938d520e03d 100644 --- a/packages/jest-config/src/normalize.js +++ b/packages/jest-config/src/normalize.js @@ -95,9 +95,11 @@ const setupPreset = ( // $FlowFixMe preset = (require(presetModule): InitialOptions); } catch (error) { - if (error instanceof SyntaxError) { + if (error instanceof SyntaxError || error instanceof TypeError) { throw createConfigError( - ` Preset ${chalk.bold(presetPath)} is invalid:\n ${error.message}`, + ` Preset ${chalk.bold(presetPath)} is invalid:\n\n ${ + error.message + }\n ${error.stack}`, ); } From df295890eef151ff48d83c2e3f58cb0362cddef3 Mon Sep 17 00:00:00 2001 From: Tim Seckinger Date: Tue, 19 Feb 2019 20:09:08 +0100 Subject: [PATCH 077/107] chore: migrate jest-resolve-dependencies to TypeScript (#7922) --- CHANGELOG.md | 1 + .../jest-resolve-dependencies/package.json | 11 +++ ...er.test.js => dependency_resolver.test.ts} | 49 ++++++------- .../src/{index.js => index.ts} | 68 ++++++++++--------- .../jest-resolve-dependencies/tsconfig.json | 12 ++++ packages/jest-resolve/src/index.ts | 11 +-- packages/jest-snapshot/src/index.ts | 5 +- .../jest-snapshot/src/snapshot_resolver.ts | 24 +++---- packages/jest-types/src/Resolve.ts | 17 +++++ packages/jest-types/src/Snapshot.ts | 14 ++++ packages/jest-types/src/index.ts | 20 ++++-- 11 files changed, 146 insertions(+), 86 deletions(-) rename packages/jest-resolve-dependencies/src/__tests__/{dependency_resolver.test.js => dependency_resolver.test.ts} (73%) rename packages/jest-resolve-dependencies/src/{index.js => index.ts} (63%) create mode 100644 packages/jest-resolve-dependencies/tsconfig.json create mode 100644 packages/jest-types/src/Resolve.ts create mode 100644 packages/jest-types/src/Snapshot.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index b33fe1e4bf73..2dfdb20b44cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ - `[@jest/core]` Create new package, which is `jest-cli` minus `yargs` and `prompts` ([#7696](https://github.com/facebook/jest/pull/7696)) - `[@jest/transform]`: Migrate to TypeScript ([#7918](https://github.com/facebook/jest/pull/7918)) - `[docs]` Add missing import to docs ([#7928](https://github.com/facebook/jest/pull/7928)) +- `[jest-resolve-dependencies]`: Migrate to TypeScript ([#7922](https://github.com/facebook/jest/pull/7922)) ### Performance diff --git a/packages/jest-resolve-dependencies/package.json b/packages/jest-resolve-dependencies/package.json index 3e4bd3dbaa75..047647ee7dc9 100644 --- a/packages/jest-resolve-dependencies/package.json +++ b/packages/jest-resolve-dependencies/package.json @@ -8,10 +8,21 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { + "@jest/types": "^24.1.0", "jest-regex-util": "^24.0.0", "jest-snapshot": "^24.1.0" }, + "devDependencies": { + "jest-haste-map": "^24.0.0", + "jest-resolve": "^24.1.0", + "jest-runtime": "^24.1.0" + }, + "peerDependencies": { + "jest-haste-map": "^24.0.0", + "jest-resolve": "^24.1.0" + }, "engines": { "node": ">= 6" }, diff --git a/packages/jest-resolve-dependencies/src/__tests__/dependency_resolver.test.js b/packages/jest-resolve-dependencies/src/__tests__/dependency_resolver.test.ts similarity index 73% rename from packages/jest-resolve-dependencies/src/__tests__/dependency_resolver.test.js rename to packages/jest-resolve-dependencies/src/__tests__/dependency_resolver.test.ts index 3725c9bc951f..c2f50429462e 100644 --- a/packages/jest-resolve-dependencies/src/__tests__/dependency_resolver.test.js +++ b/packages/jest-resolve-dependencies/src/__tests__/dependency_resolver.test.ts @@ -3,41 +3,44 @@ * * 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 path = require('path'); -const {normalize} = require('jest-config'); -const {buildSnapshotResolver} = require('jest-snapshot'); -const DependencyResolver = require('../index'); +import {tmpdir} from 'os'; +import path from 'path'; +import {Config} from '@jest/types'; +import {buildSnapshotResolver} from 'jest-snapshot'; +import {makeProjectConfig} from '../../../../TestUtils'; + +import DependencyResolver from '../index'; const maxWorkers = 1; -let dependencyResolver; +let dependencyResolver: DependencyResolver; let Runtime; -let config; -const cases = { +let config: Config.ProjectConfig; +const cases: {[key: string]: jest.Mock} = { fancyCondition: jest.fn(path => path.length > 10), testRegex: jest.fn(path => /.test.js$/.test(path)), }; -const filter = path => Object.keys(cases).every(key => cases[key](path)); +const filter = (path: Config.Path) => + Object.keys(cases).every(key => cases[key](path)); beforeEach(() => { Runtime = require('jest-runtime'); - config = normalize( - { - rootDir: '.', - roots: ['./packages/jest-resolve-dependencies'], - }, - {}, - ).options; - return Runtime.createContext(config, {maxWorkers}).then(hasteMap => { - dependencyResolver = new DependencyResolver( - hasteMap.resolver, - hasteMap.hasteFS, - buildSnapshotResolver(config), - ); + config = makeProjectConfig({ + cacheDirectory: path.resolve(tmpdir(), 'jest-resolve-dependencies-test'), + moduleDirectories: ['node_modules'], + rootDir: '.', + roots: ['./packages/jest-resolve-dependencies'], }); + return Runtime.createContext(config, {maxWorkers, watchman: false}).then( + (hasteMap: any) => { + dependencyResolver = new DependencyResolver( + hasteMap.resolver, + hasteMap.hasteFS, + buildSnapshotResolver(config), + ); + }, + ); }); test('resolves no dependencies for non-existent path', () => { diff --git a/packages/jest-resolve-dependencies/src/index.js b/packages/jest-resolve-dependencies/src/index.ts similarity index 63% rename from packages/jest-resolve-dependencies/src/index.js rename to packages/jest-resolve-dependencies/src/index.ts index bebb7a76e212..583ab2f91f85 100644 --- a/packages/jest-resolve-dependencies/src/index.js +++ b/packages/jest-resolve-dependencies/src/index.ts @@ -3,18 +3,11 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {HasteFS} from 'types/HasteMap'; -import type {Path} from 'types/Config'; -import type { - Resolver, - ResolveModuleConfig, - ResolvedModule, -} from 'types/Resolve'; -import type {SnapshotResolver} from 'types/SnapshotResolver'; +import {Config, Resolve, Snapshot} from '@jest/types'; +import {FS as HasteFS} from 'jest-haste-map'; +import Resolver from 'jest-resolve'; import {isSnapshotPath} from 'jest-snapshot'; /** @@ -22,27 +15,30 @@ import {isSnapshotPath} from 'jest-snapshot'; * to retrieve a list of all transitive inverse dependencies. */ class DependencyResolver { - _hasteFS: HasteFS; - _resolver: Resolver; - _snapshotResolver: SnapshotResolver; + private _hasteFS: HasteFS; + private _resolver: Resolver; + private _snapshotResolver: Snapshot.SnapshotResolver; constructor( resolver: Resolver, hasteFS: HasteFS, - snapshotResolver: SnapshotResolver, + snapshotResolver: Snapshot.SnapshotResolver, ) { this._resolver = resolver; this._hasteFS = hasteFS; this._snapshotResolver = snapshotResolver; } - resolve(file: Path, options?: ResolveModuleConfig): Array { + resolve( + file: Config.Path, + options?: Resolve.ResolveModuleConfig, + ): Array { const dependencies = this._hasteFS.getDependencies(file); if (!dependencies) { return []; } - return dependencies.reduce((acc, dependency) => { + return dependencies.reduce>((acc, dependency) => { if (this._resolver.isCoreModule(dependency)) { return acc; } @@ -66,23 +62,27 @@ class DependencyResolver { } resolveInverseModuleMap( - paths: Set, - filter: (file: Path) => boolean, - options?: ResolveModuleConfig, - ): Array { + paths: Set, + filter: (file: Config.Path) => boolean, + options?: Resolve.ResolveModuleConfig, + ): Array { if (!paths.size) { return []; } - const collectModules = (related, moduleMap, changed) => { + const collectModules = ( + related: Set, + moduleMap: Array, + changed: Set, + ) => { const visitedModules = new Set(); - const result: Array = []; + const result: Array = []; while (changed.size) { changed = new Set( - moduleMap.reduce((acc, module) => { + moduleMap.reduce>((acc, module) => { if ( visitedModules.has(module.file) || - !module.dependencies.some(dep => dep && changed.has(dep)) + !module.dependencies.some(dep => changed.has(dep)) ) { return acc; } @@ -98,11 +98,13 @@ class DependencyResolver { }, []), ); } - return result.concat(Array.from(related).map(file => ({file}))); + return result.concat( + Array.from(related).map(file => ({dependencies: [], file})), + ); }; - const relatedPaths = new Set(); - const changed = new Set(); + const relatedPaths = new Set(); + const changed: Set = new Set(); for (const path of paths) { if (this._hasteFS.exists(path)) { const modulePath = isSnapshotPath(path) @@ -114,7 +116,7 @@ class DependencyResolver { } } } - const modules = []; + const modules: Array = []; for (const file of this._hasteFS.getAbsoluteFileIterator()) { modules.push({ dependencies: this.resolve(file, options), @@ -125,14 +127,14 @@ class DependencyResolver { } resolveInverse( - paths: Set, - filter: (file: Path) => boolean, - options?: ResolveModuleConfig, - ): Array { + paths: Set, + filter: (file: Config.Path) => boolean, + options?: Resolve.ResolveModuleConfig, + ): Array { return this.resolveInverseModuleMap(paths, filter, options).map( module => module.file, ); } } -module.exports = DependencyResolver; +export = DependencyResolver; diff --git a/packages/jest-resolve-dependencies/tsconfig.json b/packages/jest-resolve-dependencies/tsconfig.json new file mode 100644 index 000000000000..330ebb703e00 --- /dev/null +++ b/packages/jest-resolve-dependencies/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [ + {"path": "../jest-regex-util"}, + {"path": "../jest-snapshot"}, + {"path": "../jest-types"} + ] +} diff --git a/packages/jest-resolve/src/index.ts b/packages/jest-resolve/src/index.ts index cfc053f08001..666116b2db5d 100644 --- a/packages/jest-resolve/src/index.ts +++ b/packages/jest-resolve/src/index.ts @@ -6,7 +6,7 @@ */ import path from 'path'; -import {Config} from '@jest/types'; +import {Config, Resolve} from '@jest/types'; import {ModuleMap} from 'jest-haste-map'; import {sync as realpath} from 'realpath-native'; import chalk from 'chalk'; @@ -15,11 +15,6 @@ import isBuiltinModule from './isBuiltinModule'; import defaultResolver from './defaultResolver'; import {ResolverConfig} from './types'; -type ResolveModuleConfig = { - skipNodeResolution?: boolean; - paths?: Config.Path[]; -}; - type FindNodeModuleConfig = { basedir: Config.Path; browser?: boolean; @@ -102,7 +97,7 @@ class Resolver { resolveModuleFromDirIfExists( dirname: Config.Path, moduleName: string, - options?: ResolveModuleConfig, + options?: Resolve.ResolveModuleConfig, ): Config.Path | null { const paths = (options && options.paths) || this._options.modulePaths; const moduleDirectory = this._options.moduleDirectories; @@ -185,7 +180,7 @@ class Resolver { resolveModule( from: Config.Path, moduleName: string, - options?: ResolveModuleConfig, + options?: Resolve.ResolveModuleConfig, ): Config.Path { const dirname = path.dirname(from); const module = this.resolveModuleFromDirIfExists( diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index bc22a7e4b9d9..b9b60ec9bab8 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -6,7 +6,7 @@ */ import fs from 'fs'; -import {Config, Matchers} from '@jest/types'; +import {Config, Matchers, Snapshot} from '@jest/types'; import {FS as HasteFS} from 'jest-haste-map'; import diff from 'jest-diff'; @@ -14,7 +14,6 @@ import {EXPECTED_COLOR, matcherHint, RECEIVED_COLOR} from 'jest-matcher-utils'; import { buildSnapshotResolver, isSnapshotPath, - SnapshotResolver, EXTENSION, } from './snapshot_resolver'; import SnapshotState from './State'; @@ -31,7 +30,7 @@ const fileExists = (filePath: Config.Path, hasteFS: HasteFS): boolean => const cleanup = ( hasteFS: HasteFS, update: Config.SnapshotUpdateState, - snapshotResolver: SnapshotResolver, + snapshotResolver: Snapshot.SnapshotResolver, ) => { const pattern = '\\.' + EXTENSION + '$'; const files = hasteFS.matchFiles(pattern); diff --git a/packages/jest-snapshot/src/snapshot_resolver.ts b/packages/jest-snapshot/src/snapshot_resolver.ts index 824fb5307dfa..838345c30b23 100644 --- a/packages/jest-snapshot/src/snapshot_resolver.ts +++ b/packages/jest-snapshot/src/snapshot_resolver.ts @@ -6,25 +6,19 @@ */ import path from 'path'; -import {Config} from '@jest/types'; +import {Config, Snapshot} from '@jest/types'; import chalk from 'chalk'; -export type SnapshotResolver = { - testPathForConsistencyCheck: string; - resolveSnapshotPath(testPath: Config.Path, extension?: string): Config.Path; - resolveTestPath(snapshotPath: Config.Path, extension?: string): Config.Path; -}; - export const EXTENSION = 'snap'; export const DOT_EXTENSION = '.' + EXTENSION; export const isSnapshotPath = (path: string): boolean => path.endsWith(DOT_EXTENSION); -const cache: Map = new Map(); +const cache: Map = new Map(); export const buildSnapshotResolver = ( config: Config.ProjectConfig, -): SnapshotResolver => { +): Snapshot.SnapshotResolver => { const key = config.rootDir; if (!cache.has(key)) { cache.set(key, createSnapshotResolver(config.snapshotResolver)); @@ -34,13 +28,13 @@ export const buildSnapshotResolver = ( function createSnapshotResolver( snapshotResolverPath?: Config.Path | null, -): SnapshotResolver { +): Snapshot.SnapshotResolver { return typeof snapshotResolverPath === 'string' ? createCustomSnapshotResolver(snapshotResolverPath) : createDefaultSnapshotResolver(); } -function createDefaultSnapshotResolver(): SnapshotResolver { +function createDefaultSnapshotResolver(): Snapshot.SnapshotResolver { return { resolveSnapshotPath: (testPath: Config.Path) => path.join( @@ -65,10 +59,10 @@ function createDefaultSnapshotResolver(): SnapshotResolver { function createCustomSnapshotResolver( snapshotResolverPath: Config.Path, -): SnapshotResolver { - const custom: SnapshotResolver = require(snapshotResolverPath); +): Snapshot.SnapshotResolver { + const custom: Snapshot.SnapshotResolver = require(snapshotResolverPath); - const keys: [keyof SnapshotResolver, string][] = [ + const keys: [keyof Snapshot.SnapshotResolver, string][] = [ ['resolveSnapshotPath', 'function'], ['resolveTestPath', 'function'], ['testPathForConsistencyCheck', 'string'], @@ -101,7 +95,7 @@ function mustImplement(propName: string, requiredType: string) { ); } -function verifyConsistentTransformations(custom: SnapshotResolver) { +function verifyConsistentTransformations(custom: Snapshot.SnapshotResolver) { const resolvedSnapshotPath = custom.resolveSnapshotPath( custom.testPathForConsistencyCheck, ); diff --git a/packages/jest-types/src/Resolve.ts b/packages/jest-types/src/Resolve.ts new file mode 100644 index 000000000000..61ce0329cb95 --- /dev/null +++ b/packages/jest-types/src/Resolve.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import {Path} from './Config'; + +export type ResolveModuleConfig = { + skipNodeResolution?: boolean; + paths?: Path[]; +}; +export type ResolvedModule = { + file: Path; + dependencies: Path[]; +}; diff --git a/packages/jest-types/src/Snapshot.ts b/packages/jest-types/src/Snapshot.ts new file mode 100644 index 000000000000..c372b924d750 --- /dev/null +++ b/packages/jest-types/src/Snapshot.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import {Path} from './Config'; + +export type SnapshotResolver = { + testPathForConsistencyCheck: string; + resolveSnapshotPath(testPath: Path, extension?: string): Path; + resolveTestPath(snapshotPath: Path, extension?: string): Path; +}; diff --git a/packages/jest-types/src/index.ts b/packages/jest-types/src/index.ts index 70361152f936..0226a0a00820 100644 --- a/packages/jest-types/src/index.ts +++ b/packages/jest-types/src/index.ts @@ -7,10 +7,22 @@ import * as Config from './Config'; import * as Console from './Console'; -import * as SourceMaps from './SourceMaps'; -import * as TestResult from './TestResult'; +import * as Matchers from './Matchers'; import * as Mocks from './Mocks'; import * as PrettyFormat from './PrettyFormat'; -import * as Matchers from './Matchers'; +import * as Resolve from './Resolve'; +import * as Snapshot from './Snapshot'; +import * as SourceMaps from './SourceMaps'; +import * as TestResult from './TestResult'; -export {Config, Console, SourceMaps, TestResult, Mocks, PrettyFormat, Matchers}; +export { + Config, + Console, + Matchers, + Mocks, + PrettyFormat, + Resolve, + Snapshot, + SourceMaps, + TestResult, +}; From ee1955eaa1353b8d2cc19cd515708f86cec928f0 Mon Sep 17 00:00:00 2001 From: Alcedo Nathaniel De Guzman Jr Date: Wed, 20 Feb 2019 21:42:06 +0800 Subject: [PATCH 078/107] Migrate expect to typescript (#7919) --- CHANGELOG.md | 1 + packages/expect/package.json | 2 + ...etricMatchers.js => asymmetricMatchers.ts} | 72 +++++------ ....js => extractExpectedAssertionsErrors.ts} | 1 - .../expect/src/{fakeChalk.js => fakeChalk.ts} | 5 +- packages/expect/src/{index.js => index.ts} | 60 +++++---- .../src/{jasmineUtils.js => jasmineUtils.ts} | 43 ++++--- ...atchersObject.js => jestMatchersObject.ts} | 42 +++---- .../expect/src/{matchers.js => matchers.ts} | 119 +++++++++++------- .../src/{spyMatchers.js => spyMatchers.ts} | 103 ++++++++------- ...{toThrowMatchers.js => toThrowMatchers.ts} | 52 ++++---- packages/expect/src/types.ts | 103 +++++++++++++++ packages/expect/src/{utils.js => utils.ts} | 47 +++---- packages/expect/tsconfig.json | 14 +++ packages/jest-snapshot/src/index.ts | 1 + packages/jest-types/src/Matchers.ts | 2 +- scripts/checkCopyrightHeaders.js | 2 +- 17 files changed, 421 insertions(+), 248 deletions(-) rename packages/expect/src/{asymmetricMatchers.js => asymmetricMatchers.ts} (81%) rename packages/expect/src/{extractExpectedAssertionsErrors.js => extractExpectedAssertionsErrors.ts} (99%) rename packages/expect/src/{fakeChalk.js => fakeChalk.ts} (88%) rename packages/expect/src/{index.js => index.ts} (89%) rename packages/expect/src/{jasmineUtils.js => jasmineUtils.ts} (90%) rename packages/expect/src/{jestMatchersObject.js => jestMatchersObject.ts} (66%) rename packages/expect/src/{matchers.js => matchers.ts} (89%) rename packages/expect/src/{spyMatchers.js => spyMatchers.ts} (89%) rename packages/expect/src/{toThrowMatchers.js => toThrowMatchers.ts} (92%) create mode 100644 packages/expect/src/types.ts rename packages/expect/src/{utils.js => utils.ts} (87%) create mode 100644 packages/expect/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dfdb20b44cc..570249abec1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ - `[@jest/transform]`: Migrate to TypeScript ([#7918](https://github.com/facebook/jest/pull/7918)) - `[docs]` Add missing import to docs ([#7928](https://github.com/facebook/jest/pull/7928)) - `[jest-resolve-dependencies]`: Migrate to TypeScript ([#7922](https://github.com/facebook/jest/pull/7922)) +- `[expect]`: Migrate to TypeScript ([#7919](https://github.com/facebook/jest/pull/7919)) ### Performance diff --git a/packages/expect/package.json b/packages/expect/package.json index 3f74a119c97e..1cf724969380 100644 --- a/packages/expect/package.json +++ b/packages/expect/package.json @@ -8,8 +8,10 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "browser": "build-es5/index.js", "dependencies": { + "@jest/types": "^24.1.0", "ansi-styles": "^3.2.0", "jest-get-type": "^24.0.0", "jest-matcher-utils": "^24.0.0", diff --git a/packages/expect/src/asymmetricMatchers.js b/packages/expect/src/asymmetricMatchers.ts similarity index 81% rename from packages/expect/src/asymmetricMatchers.js rename to packages/expect/src/asymmetricMatchers.ts index 404cb3eb8cc9..1742cf7711ae 100644 --- a/packages/expect/src/asymmetricMatchers.js +++ b/packages/expect/src/asymmetricMatchers.ts @@ -4,37 +4,35 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ import {equals, fnNameFor, hasProperty, isA, isUndefined} from './jasmineUtils'; import {emptyObject} from './utils'; -export class AsymmetricMatcher { +export class AsymmetricMatcher { + protected sample: T; $$typeof: Symbol; - inverse: boolean; + inverse?: boolean; - constructor() { + constructor(sample: T) { this.$$typeof = Symbol.for('jest.asymmetricMatcher'); + this.sample = sample; } } -class Any extends AsymmetricMatcher { - sample: any; - - constructor(sample: any) { - super(); +class Any extends AsymmetricMatcher { + constructor(sample: unknown) { if (typeof sample === 'undefined') { throw new TypeError( 'any() expects to be passed a constructor function. ' + 'Please pass one or use anything() to match any object.', ); } - this.sample = sample; + super(sample); } - asymmetricMatch(other: any) { + asymmetricMatch(other: unknown) { if (this.sample == String) { return typeof other == 'string' || other instanceof String; } @@ -91,8 +89,8 @@ class Any extends AsymmetricMatcher { } } -class Anything extends AsymmetricMatcher { - asymmetricMatch(other: any) { +class Anything extends AsymmetricMatcher { + asymmetricMatch(other: unknown) { return !isUndefined(other) && other !== null; } @@ -107,16 +105,13 @@ class Anything extends AsymmetricMatcher { } } -class ArrayContaining extends AsymmetricMatcher { - sample: Array; - - constructor(sample: Array, inverse: boolean = false) { - super(); - this.sample = sample; +class ArrayContaining extends AsymmetricMatcher> { + constructor(sample: Array, inverse: boolean = false) { + super(sample); this.inverse = inverse; } - asymmetricMatch(other: Array) { + asymmetricMatch(other: Array) { if (!Array.isArray(this.sample)) { throw new Error( `You must provide an array to ${this.toString()}, not '` + @@ -144,16 +139,13 @@ class ArrayContaining extends AsymmetricMatcher { } } -class ObjectContaining extends AsymmetricMatcher { - sample: Object; - +class ObjectContaining extends AsymmetricMatcher { constructor(sample: Object, inverse: boolean = false) { - super(); - this.sample = sample; + super(sample); this.inverse = inverse; } - asymmetricMatch(other: Object) { + asymmetricMatch(other: any) { if (typeof this.sample !== 'object') { throw new Error( `You must provide an object to ${this.toString()}, not '` + @@ -166,8 +158,8 @@ class ObjectContaining extends AsymmetricMatcher { for (const property in this.sample) { if ( hasProperty(other, property) && - equals(this.sample[property], other[property]) && - !emptyObject(this.sample[property]) && + equals((this.sample as any)[property], other[property]) && + !emptyObject((this.sample as any)[property]) && !emptyObject(other[property]) ) { return false; @@ -179,7 +171,7 @@ class ObjectContaining extends AsymmetricMatcher { for (const property in this.sample) { if ( !hasProperty(other, property) || - !equals(this.sample[property], other[property]) + !equals((this.sample as any)[property], other[property]) ) { return false; } @@ -198,19 +190,16 @@ class ObjectContaining extends AsymmetricMatcher { } } -class StringContaining extends AsymmetricMatcher { - sample: string; - +class StringContaining extends AsymmetricMatcher { constructor(sample: string, inverse: boolean = false) { - super(); if (!isA('String', sample)) { throw new Error('Expected is not a string'); } - this.sample = sample; + super(sample); this.inverse = inverse; } - asymmetricMatch(other: any) { + asymmetricMatch(other: string) { const result = isA('String', other) && other.includes(this.sample); return this.inverse ? !result : result; @@ -225,20 +214,17 @@ class StringContaining extends AsymmetricMatcher { } } -class StringMatching extends AsymmetricMatcher { - sample: RegExp; - +class StringMatching extends AsymmetricMatcher { constructor(sample: string | RegExp, inverse: boolean = false) { - super(); if (!isA('String', sample) && !isA('RegExp', sample)) { throw new Error('Expected is not a String or a RegExp'); } + super(new RegExp(sample)); - this.sample = new RegExp(sample); this.inverse = inverse; } - asymmetricMatch(other: any) { + asymmetricMatch(other: string) { const result = isA('String', other) && this.sample.test(other); return this.inverse ? !result : result; @@ -255,9 +241,9 @@ class StringMatching extends AsymmetricMatcher { export const any = (expectedObject: any) => new Any(expectedObject); export const anything = () => new Anything(); -export const arrayContaining = (sample: Array) => +export const arrayContaining = (sample: Array) => new ArrayContaining(sample); -export const arrayNotContaining = (sample: Array) => +export const arrayNotContaining = (sample: Array) => new ArrayContaining(sample, true); export const objectContaining = (sample: Object) => new ObjectContaining(sample); diff --git a/packages/expect/src/extractExpectedAssertionsErrors.js b/packages/expect/src/extractExpectedAssertionsErrors.ts similarity index 99% rename from packages/expect/src/extractExpectedAssertionsErrors.js rename to packages/expect/src/extractExpectedAssertionsErrors.ts index 1a52a8424760..de97acc2dc4d 100644 --- a/packages/expect/src/extractExpectedAssertionsErrors.js +++ b/packages/expect/src/extractExpectedAssertionsErrors.ts @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ import { diff --git a/packages/expect/src/fakeChalk.js b/packages/expect/src/fakeChalk.ts similarity index 88% rename from packages/expect/src/fakeChalk.js rename to packages/expect/src/fakeChalk.ts index c113b8db0703..a01ba4d1d566 100644 --- a/packages/expect/src/fakeChalk.js +++ b/packages/expect/src/fakeChalk.ts @@ -3,12 +3,11 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * @flow */ import ansiStyles from 'ansi-styles'; -const returnInput = str => str; +const returnInput = (str: string) => str; const allColorsAsFunc = Object.keys(ansiStyles) .map(style => ({[style]: returnInput})) @@ -21,4 +20,4 @@ Object.keys(allColorsAsFunc) Object.assign(returnInput, style); }); -module.exports = allColorsAsFunc; +export = allColorsAsFunc; diff --git a/packages/expect/src/index.js b/packages/expect/src/index.ts similarity index 89% rename from packages/expect/src/index.js rename to packages/expect/src/index.ts index 1f50866a4e67..500b2f19945c 100644 --- a/packages/expect/src/index.js +++ b/packages/expect/src/index.ts @@ -4,12 +4,10 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ -import type { - Expect, - ExpectationObject, +import * as matcherUtils from 'jest-matcher-utils'; +import { AsyncExpectationResult, SyncExpectationResult, ExpectationResult, @@ -18,9 +16,10 @@ import type { RawMatcherFn, ThrowingMatcherFn, PromiseMatcherFn, -} from 'types/Matchers'; + ExpectationObject, + Expect, +} from './types'; -import * as matcherUtils from 'jest-matcher-utils'; import {iterableEquality, subsetEquality} from './utils'; import matchers from './matchers'; import spyMatchers from './spyMatchers'; @@ -50,21 +49,27 @@ import { import extractExpectedAssertionsErrors from './extractExpectedAssertionsErrors'; class JestAssertionError extends Error { - matcherResult: any; + matcherResult?: SyncExpectationResult; } -const isPromise = obj => +const isPromise = (obj: any): obj is PromiseLike => !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'; -const createToThrowErrorMatchingSnapshotMatcher = function(matcher) { - return function(received: any, testNameOrInlineSnapshot?: string) { +const createToThrowErrorMatchingSnapshotMatcher = function( + matcher: RawMatcherFn, +) { + return function( + this: MatcherState, + received: any, + testNameOrInlineSnapshot?: string, + ) { return matcher.apply(this, [received, testNameOrInlineSnapshot, true]); }; }; -const getPromiseMatcher = (name, matcher) => { +const getPromiseMatcher = (name: string, matcher: any) => { if (name === 'toThrow' || name === 'toThrowError') { return createThrowMatcher(name, true); } else if ( @@ -77,13 +82,13 @@ const getPromiseMatcher = (name, matcher) => { return null; }; -const expect = (actual: any, ...rest): ExpectationObject => { +const expect: any = (actual: any, ...rest: Array): ExpectationObject => { if (rest.length !== 0) { throw new Error('Expect takes at most one argument.'); } const allMatchers = getMatchers(); - const expectation = { + const expectation: any = { not: {}, rejects: {not: {}}, resolves: {not: {}}, @@ -131,7 +136,7 @@ const expect = (actual: any, ...rest): ExpectationObject => { return expectation; }; -const getMessage = message => +const getMessage = (message?: () => string) => (message && message()) || matcherUtils.RECEIVED_COLOR('No message was specified for this matcher.'); @@ -294,7 +299,7 @@ const makeThrowingMatcher = ( const handlError = (error: Error) => { if ( - matcher[INTERNAL_MATCHER_FLAG] === true && + (matcher as any)[INTERNAL_MATCHER_FLAG] === true && !(error instanceof JestAssertionError) && error.name !== 'PrettyFormatPluginError' && // Guard for some environments (browsers) that do not support this feature. @@ -309,10 +314,13 @@ const makeThrowingMatcher = ( let potentialResult: ExpectationResult; try { - potentialResult = matcher.apply(matcherContext, [actual].concat(args)); + potentialResult = matcher.apply( + matcherContext, + ([actual] as any).concat(args), + ); - if (isPromise((potentialResult: any))) { - const asyncResult = ((potentialResult: any): AsyncExpectationResult); + if (isPromise(potentialResult)) { + const asyncResult = potentialResult as AsyncExpectationResult; const asyncError = new JestAssertionError(); if (Error.captureStackTrace) { Error.captureStackTrace(asyncError, throwingMatcher); @@ -322,7 +330,7 @@ const makeThrowingMatcher = ( .then(aResult => processResult(aResult, asyncError)) .catch(error => handlError(error)); } else { - const syncResult = ((potentialResult: any): SyncExpectationResult); + const syncResult = potentialResult as SyncExpectationResult; return processResult(syncResult); } @@ -332,7 +340,7 @@ const makeThrowingMatcher = ( }; expect.extend = (matchers: MatchersObject): void => - setMatchers(matchers, false, expect); + setMatchers(matchers, false, expect as any); expect.anything = anything; expect.any = any; @@ -349,7 +357,7 @@ expect.arrayContaining = arrayContaining; expect.stringContaining = stringContaining; expect.stringMatching = stringMatching; -const _validateResult = result => { +const _validateResult = (result: any) => { if ( typeof result !== 'object' || typeof result.pass !== 'boolean' || @@ -376,7 +384,7 @@ function assertions(expected: number) { getState().expectedAssertionsNumber = expected; getState().expectedAssertionsNumberError = error; } -function hasAssertions(...args) { +function hasAssertions(...args: Array) { const error = new Error(); if (Error.captureStackTrace) { Error.captureStackTrace(error, hasAssertions); @@ -388,9 +396,9 @@ function hasAssertions(...args) { } // add default jest matchers -setMatchers(matchers, true, expect); -setMatchers(spyMatchers, true, expect); -setMatchers(toThrowMatchers, true, expect); +setMatchers(matchers, true, expect as Expect); +setMatchers(spyMatchers, true, expect as Expect); +setMatchers(toThrowMatchers, true, expect as Expect); expect.addSnapshotSerializer = () => void 0; expect.assertions = assertions; @@ -399,4 +407,4 @@ expect.getState = getState; expect.setState = setState; expect.extractExpectedAssertionsErrors = extractExpectedAssertionsErrors; -module.exports = (expect: Expect); +export = expect as Expect; diff --git a/packages/expect/src/jasmineUtils.js b/packages/expect/src/jasmineUtils.ts similarity index 90% rename from packages/expect/src/jasmineUtils.js rename to packages/expect/src/jasmineUtils.ts index e4438ed60293..cb8b6deb9e16 100644 --- a/packages/expect/src/jasmineUtils.js +++ b/packages/expect/src/jasmineUtils.ts @@ -20,17 +20,16 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -@flow */ /* eslint-disable */ -type Tester = (a: any, b: any) => boolean | typeof undefined; +import {Tester} from './types'; // Extracted out of jasmine 2.5.2 export function equals( - a: any, - b: any, + a: unknown, + b: unknown, customTesters?: Array, strictCheck?: boolean, ): boolean { @@ -38,11 +37,11 @@ export function equals( return eq(a, b, [], [], customTesters, strictCheck ? hasKey : hasDefinedKey); } -function isAsymmetric(obj) { +function isAsymmetric(obj: any) { return !!obj && isA('Function', obj.asymmetricMatch); } -function asymmetricMatch(a, b) { +function asymmetricMatch(a: any, b: any) { var asymmetricA = isAsymmetric(a), asymmetricB = isAsymmetric(b); @@ -62,7 +61,14 @@ function asymmetricMatch(a, b) { // Equality function lovingly adapted from isEqual in // [Underscore](http://underscorejs.org) -function eq(a, b, aStack, bStack, customTesters, hasKey): boolean { +function eq( + a: any, + b: any, + aStack: any, + bStack: any, + customTesters: any, + hasKey: any, +): boolean { var result = true; var asymmetricResult = asymmetricMatch(a, b); @@ -189,7 +195,11 @@ function eq(a, b, aStack, bStack, customTesters, hasKey): boolean { return result; } -function keys(obj, isArray, hasKey) { +function keys( + obj: object, + isArray: boolean, + hasKey: (obj: object, key: string) => boolean, +) { var allKeys = (function(o) { var keys = []; for (var key in o) { @@ -198,9 +208,10 @@ function keys(obj, isArray, hasKey) { } } return keys.concat( - (Object.getOwnPropertySymbols(o): Array).filter( + (Object.getOwnPropertySymbols(o) as Array).filter( //$FlowFixMe Jest complains about nullability, but we know for sure that property 'symbol' does exist. - symbol => Object.getOwnPropertyDescriptor(o, symbol).enumerable, + symbol => + (Object.getOwnPropertyDescriptor(o, symbol) as any).enumerable, ), ); })(obj); @@ -223,19 +234,19 @@ function keys(obj, isArray, hasKey) { return extraKeys; } -function hasDefinedKey(obj, key) { +function hasDefinedKey(obj: any, key: string) { return hasKey(obj, key) && obj[key] !== undefined; } -function hasKey(obj, key) { +function hasKey(obj: any, key: string) { return Object.prototype.hasOwnProperty.call(obj, key); } -export function isA(typeName: string, value: any) { +export function isA(typeName: string, value: unknown) { return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; } -function isDomNode(obj) { +function isDomNode(obj: any): obj is Node { // In some test environments (e.g. "node") there is no `Node` even though // we might be comparing things that look like DOM nodes. return typeof Node !== 'undefined' && obj instanceof Node; @@ -254,7 +265,7 @@ export function isUndefined(obj: any) { return obj === void 0; } -function getPrototype(obj) { +function getPrototype(obj: object) { if (Object.getPrototypeOf) { return Object.getPrototypeOf(obj); } @@ -266,7 +277,7 @@ function getPrototype(obj) { return obj.constructor.prototype; } -export function hasProperty(obj: Object | null, property: string) { +export function hasProperty(obj: object | null, property: string): boolean { if (!obj) { return false; } diff --git a/packages/expect/src/jestMatchersObject.js b/packages/expect/src/jestMatchersObject.ts similarity index 66% rename from packages/expect/src/jestMatchersObject.js rename to packages/expect/src/jestMatchersObject.ts index c2fc9600f5eb..e9563113c219 100644 --- a/packages/expect/src/jestMatchersObject.js +++ b/packages/expect/src/jestMatchersObject.ts @@ -4,15 +4,10 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ import {AsymmetricMatcher} from './asymmetricMatchers'; -import type { - Expect, - MatchersObject, - SyncExpectationResult, -} from 'types/Matchers'; +import {Expect, MatchersObject, SyncExpectationResult} from './types'; // Global matchers object holds the list of available matchers and // the state, that can hold matcher specific values that change over time. @@ -22,7 +17,7 @@ const JEST_MATCHERS_OBJECT = Symbol.for('$$jest-matchers-object'); // Jest may override the stack trace of Errors thrown by internal matchers. export const INTERNAL_MATCHER_FLAG = Symbol.for('$$jest-internal-matcher'); -if (!global[JEST_MATCHERS_OBJECT]) { +if (!(global as any)[JEST_MATCHERS_OBJECT]) { Object.defineProperty(global, JEST_MATCHERS_OBJECT, { value: { matchers: Object.create(null), @@ -36,13 +31,13 @@ if (!global[JEST_MATCHERS_OBJECT]) { }); } -export const getState = () => global[JEST_MATCHERS_OBJECT].state; +export const getState = () => (global as any)[JEST_MATCHERS_OBJECT].state; -export const setState = (state: Object) => { - Object.assign(global[JEST_MATCHERS_OBJECT].state, state); +export const setState = (state: object) => { + Object.assign((global as any)[JEST_MATCHERS_OBJECT].state, state); }; -export const getMatchers = () => global[JEST_MATCHERS_OBJECT].matchers; +export const getMatchers = () => (global as any)[JEST_MATCHERS_OBJECT].matchers; export const setMatchers = ( matchers: MatchersObject, @@ -58,20 +53,17 @@ export const setMatchers = ( if (!isInternal) { // expect is defined - class CustomMatcher extends AsymmetricMatcher { - sample: Array; - - constructor(inverse: boolean = false, ...sample: Array) { - super(); + class CustomMatcher extends AsymmetricMatcher<[unknown, unknown]> { + constructor(inverse: boolean = false, ...sample: [unknown, unknown]) { + super(sample); this.inverse = inverse; - this.sample = sample; } - asymmetricMatch(other: any) { - const {pass} = ((matcher( - (other: any), - ...(this.sample: any), - ): any): SyncExpectationResult); + asymmetricMatch(other: unknown) { + const {pass} = matcher( + other, + ...this.sample, + ) as SyncExpectationResult; return this.inverse ? !pass : pass; } @@ -89,15 +81,15 @@ export const setMatchers = ( } } - expect[key] = (...sample: Array) => + expect[key] = (...sample: [unknown, unknown]) => new CustomMatcher(false, ...sample); if (!expect.not) { expect.not = {}; } - expect.not[key] = (...sample: Array) => + expect.not[key] = (...sample: [unknown, unknown]) => new CustomMatcher(true, ...sample); } }); - Object.assign(global[JEST_MATCHERS_OBJECT].matchers, matchers); + Object.assign((global as any)[JEST_MATCHERS_OBJECT].matchers, matchers); }; diff --git a/packages/expect/src/matchers.js b/packages/expect/src/matchers.ts similarity index 89% rename from packages/expect/src/matchers.js rename to packages/expect/src/matchers.ts index bc093152ca15..6154f33d7873 100644 --- a/packages/expect/src/matchers.js +++ b/packages/expect/src/matchers.ts @@ -4,11 +4,8 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ -import type {MatchersObject} from 'types/Matchers'; - import getType from 'jest-get-type'; import {escapeStrForRegex} from 'jest-regex-util'; import { @@ -25,7 +22,9 @@ import { printReceived, printExpected, printWithType, + MatcherHintOptions, } from 'jest-matcher-utils'; +import {MatchersObject, MatcherState} from './types'; import { getObjectSubset, getPath, @@ -38,14 +37,14 @@ import { import {equals} from './jasmineUtils'; type ContainIterable = - | Array - | Set - | NodeList + | Array + | Set + | NodeListOf | DOMTokenList - | HTMLCollection; + | HTMLCollectionOf; const matchers: MatchersObject = { - toBe(received: any, expected: any) { + toBe(this: MatcherState, received: unknown, expected: unknown) { const comment = 'Object.is equality'; const pass = Object.is(received, expected); @@ -87,10 +86,15 @@ const matchers: MatchersObject = { return {actual: received, expected, message, name: 'toBe', pass}; }, - toBeCloseTo(received: number, expected: number, precision?: number = 2) { - const secondArgument = arguments.length === 3 ? 'precision' : null; + toBeCloseTo( + this: MatcherState, + received: number, + expected: number, + precision: number = 2, + ) { + const secondArgument = arguments.length === 3 ? 'precision' : undefined; const isNot = this.isNot; - const options = { + const options: MatcherHintOptions = { isNot, promise: this.promise, secondArgument, @@ -136,8 +140,8 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeDefined(received: any, expected: void) { - const options = { + toBeDefined(this: MatcherState, received: unknown, expected: void) { + const options: MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; @@ -153,8 +157,8 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeFalsy(received: any, expected: void) { - const options = { + toBeFalsy(this: MatcherState, received: unknown, expected: void) { + const options: MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; @@ -170,9 +174,9 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeGreaterThan(received: number, expected: number) { + toBeGreaterThan(this: MatcherState, received: number, expected: number) { const isNot = this.isNot; - const options = { + const options: MatcherHintOptions = { isNot, promise: this.promise, }; @@ -189,9 +193,13 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeGreaterThanOrEqual(received: number, expected: number) { + toBeGreaterThanOrEqual( + this: MatcherState, + received: number, + expected: number, + ) { const isNot = this.isNot; - const options = { + const options: MatcherHintOptions = { isNot, promise: this.promise, }; @@ -208,7 +216,7 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeInstanceOf(received: any, constructor: Function) { + toBeInstanceOf(this: MatcherState, received: any, constructor: Function) { const constType = getType(constructor); if (constType !== 'function') { @@ -252,9 +260,9 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeLessThan(received: number, expected: number) { + toBeLessThan(this: MatcherState, received: number, expected: number) { const isNot = this.isNot; - const options = { + const options: MatcherHintOptions = { isNot, promise: this.promise, }; @@ -271,9 +279,9 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeLessThanOrEqual(received: number, expected: number) { + toBeLessThanOrEqual(this: MatcherState, received: number, expected: number) { const isNot = this.isNot; - const options = { + const options: MatcherHintOptions = { isNot, promise: this.promise, }; @@ -290,8 +298,8 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeNaN(received: any, expected: void) { - const options = { + toBeNaN(this: MatcherState, received: any, expected: void) { + const options: MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; @@ -307,8 +315,8 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeNull(received: any, expected: void) { - const options = { + toBeNull(this: MatcherState, received: unknown, expected: void) { + const options: MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; @@ -324,8 +332,8 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeTruthy(received: any, expected: void) { - const options = { + toBeTruthy(this: MatcherState, received: unknown, expected: void) { + const options: MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; @@ -341,8 +349,8 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeUndefined(received: any, expected: void) { - const options = { + toBeUndefined(this: MatcherState, received: unknown, expected: void) { + const options: MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; @@ -358,10 +366,14 @@ const matchers: MatchersObject = { return {message, pass}; }, - toContain(collection: ContainIterable | string, value: any) { + toContain( + this: MatcherState, + collection: ContainIterable | string, + value: unknown, + ) { const collectionType = getType(collection); - let converted = null; + let converted: any = null; if (Array.isArray(collection) || typeof collection === 'string') { // strings have `indexOf` so we don't need to convert // arrays have `indexOf` and we don't want to make a copy @@ -413,7 +425,11 @@ const matchers: MatchersObject = { return {message, pass}; }, - toContainEqual(collection: ContainIterable, value: any) { + toContainEqual( + this: MatcherState, + collection: ContainIterable, + value: unknown, + ) { const collectionType = getType(collection); let converted = null; if (Array.isArray(collection)) { @@ -458,7 +474,7 @@ const matchers: MatchersObject = { return {message, pass}; }, - toEqual(received: any, expected: any) { + toEqual(this: MatcherState, received: unknown, expected: unknown) { const pass = equals(received, expected, [iterableEquality]); const message = pass @@ -490,7 +506,7 @@ const matchers: MatchersObject = { return {actual: received, expected, message, name: 'toEqual', pass}; }, - toHaveLength(received: any, length: number) { + toHaveLength(this: MatcherState, received: any, length: number) { if ( typeof received !== 'string' && (!received || typeof received.length !== 'number') @@ -547,7 +563,12 @@ const matchers: MatchersObject = { return {message, pass}; }, - toHaveProperty(object: Object, keyPath: string | Array, value?: any) { + toHaveProperty( + this: MatcherState, + object: object, + keyPath: string | Array, + value?: unknown, + ) { const valuePassed = arguments.length === 3; const secondArgument = valuePassed ? 'value' : null; @@ -557,7 +578,7 @@ const matchers: MatchersObject = { matcherHint('.toHaveProperty', undefined, 'path', { isNot: this.isNot, secondArgument, - }), + } as MatcherHintOptions), `${RECEIVED_COLOR('received')} value must not be null nor undefined`, printWithType('Received', object, printReceived), ), @@ -572,7 +593,7 @@ const matchers: MatchersObject = { matcherHint('.toHaveProperty', undefined, 'path', { isNot: this.isNot, secondArgument, - }), + } as MatcherHintOptions), `${EXPECTED_COLOR('expected')} path must be a string or array`, printWithType('Expected', keyPath, printExpected), ), @@ -592,7 +613,7 @@ const matchers: MatchersObject = { ? () => matcherHint('.not.toHaveProperty', 'object', 'path', { secondArgument, - }) + + } as MatcherHintOptions) + '\n\n' + `Expected the object:\n` + ` ${printReceived(object)}\n` + @@ -607,7 +628,7 @@ const matchers: MatchersObject = { return ( matcherHint('.toHaveProperty', 'object', 'path', { secondArgument, - }) + + } as MatcherHintOptions) + '\n\n' + `Expected the object:\n` + ` ${printReceived(object)}\n` + @@ -634,7 +655,7 @@ const matchers: MatchersObject = { return {message, pass}; }, - toMatch(received: string, expected: string | RegExp) { + toMatch(this: MatcherState, received: string, expected: string | RegExp) { if (typeof received !== 'string') { throw new Error( matcherErrorMessage( @@ -648,8 +669,8 @@ const matchers: MatchersObject = { } if ( - !(expected && typeof expected.test === 'function') && - !(typeof expected === 'string') + !(typeof expected === 'string') && + !(expected && typeof expected.test === 'function') ) { throw new Error( matcherErrorMessage( @@ -684,7 +705,11 @@ const matchers: MatchersObject = { return {message, pass}; }, - toMatchObject(receivedObject: Object, expectedObject: Object) { + toMatchObject( + this: MatcherState, + receivedObject: object, + expectedObject: object, + ) { if (typeof receivedObject !== 'object' || receivedObject === null) { throw new Error( matcherErrorMessage( @@ -742,7 +767,7 @@ const matchers: MatchersObject = { return {message, pass}; }, - toStrictEqual(received: any, expected: any) { + toStrictEqual(this: MatcherState, received: unknown, expected: unknown) { const pass = equals( received, expected, diff --git a/packages/expect/src/spyMatchers.js b/packages/expect/src/spyMatchers.ts similarity index 89% rename from packages/expect/src/spyMatchers.js rename to packages/expect/src/spyMatchers.ts index 50f623d9c770..60e5606d6a6a 100644 --- a/packages/expect/src/spyMatchers.js +++ b/packages/expect/src/spyMatchers.ts @@ -4,14 +4,8 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ -import type {MatchersObject} from 'types/Matchers'; - -const CALL_PRINT_LIMIT = 3; -const RETURN_PRINT_LIMIT = 5; -const LAST_CALL_PRINT_LIMIT = 1; import { diff, ensureExpectedIsNumber, @@ -25,10 +19,18 @@ import { printWithType, RECEIVED_COLOR, } from 'jest-matcher-utils'; +import {MatchersObject, SyncExpectationResult} from './types'; import {equals} from './jasmineUtils'; import {iterableEquality, partition, isOneline} from './utils'; -const createToBeCalledMatcher = matcherName => (received, expected) => { +const CALL_PRINT_LIMIT = 3; +const RETURN_PRINT_LIMIT = 5; +const LAST_CALL_PRINT_LIMIT = 1; + +const createToBeCalledMatcher = (matcherName: string) => ( + received: any, + expected: unknown, +): SyncExpectationResult => { ensureNoExpected(expected, matcherName); ensureMock(received, matcherName); @@ -43,7 +45,7 @@ const createToBeCalledMatcher = matcherName => (received, expected) => { ? received.calls.count() : received.mock.calls.length; const calls = receivedIsSpy - ? received.calls.all().map(x => x.args) + ? received.calls.all().map((x: any) => x.args) : received.mock.calls; const pass = count > 0; const message = pass @@ -60,7 +62,10 @@ const createToBeCalledMatcher = matcherName => (received, expected) => { return {message, pass}; }; -const createToReturnMatcher = matcherName => (received, expected) => { +const createToReturnMatcher = (matcherName: string) => ( + received: any, + expected: unknown, +): SyncExpectationResult => { ensureNoExpected(expected, matcherName); ensureMock(received, matcherName); @@ -72,8 +77,8 @@ const createToReturnMatcher = matcherName => (received, expected) => { // List of return values that correspond only to calls that returned const returnValues = received.mock.results - .filter(result => result.type === 'return') - .map(result => result.value); + .filter((result: any) => result.type === 'return') + .map((result: any) => result.value); const count = returnValues.length; const pass = count > 0; @@ -95,7 +100,7 @@ const createToReturnMatcher = matcherName => (received, expected) => { const createToBeCalledTimesMatcher = (matcherName: string) => ( received: any, expected: number, -) => { +): SyncExpectationResult => { ensureExpectedIsNumber(expected, matcherName); ensureMock(received, matcherName); @@ -130,7 +135,7 @@ const createToBeCalledTimesMatcher = (matcherName: string) => ( const createToReturnTimesMatcher = (matcherName: string) => ( received: any, expected: number, -) => { +): SyncExpectationResult => { ensureExpectedIsNumber(expected, matcherName); ensureMock(received, matcherName); @@ -142,7 +147,7 @@ const createToReturnTimesMatcher = (matcherName: string) => ( // List of return results that correspond only to calls that returned const returnResults = received.mock.results.filter( - result => result.type === 'return', + (result: any) => result.type === 'return', ); const count = returnResults.length; @@ -165,10 +170,10 @@ const createToReturnTimesMatcher = (matcherName: string) => ( return {message, pass}; }; -const createToBeCalledWithMatcher = matcherName => ( +const createToBeCalledWithMatcher = (matcherName: string) => ( received: any, - ...expected: any -) => { + ...expected: Array +): SyncExpectationResult => { ensureMock(received, matcherName); const receivedIsSpy = isSpy(received); @@ -180,7 +185,7 @@ const createToBeCalledWithMatcher = matcherName => ( : `${type} "${receivedName}"`; const calls = receivedIsSpy - ? received.calls.all().map(x => x.args) + ? received.calls.all().map((x: any) => x.args) : received.mock.calls; const [match, fail] = partition(calls, call => @@ -203,10 +208,10 @@ const createToBeCalledWithMatcher = matcherName => ( return {message, pass}; }; -const createToReturnWithMatcher = matcherName => ( +const createToReturnWithMatcher = (matcherName: string) => ( received: any, - expected: any, -) => { + expected: unknown, +): SyncExpectationResult => { ensureMock(received, matcherName); const receivedName = received.getMockName(); @@ -217,8 +222,8 @@ const createToReturnWithMatcher = matcherName => ( // List of return values that correspond only to calls that returned const returnValues = received.mock.results - .filter(result => result.type === 'return') - .map(result => result.value); + .filter((result: any) => result.type === 'return') + .map((result: any) => result.value); const [match] = partition(returnValues, value => equals(expected, value, [iterableEquality]), @@ -246,10 +251,10 @@ const createToReturnWithMatcher = matcherName => ( return {message, pass}; }; -const createLastCalledWithMatcher = matcherName => ( +const createLastCalledWithMatcher = (matcherName: string) => ( received: any, - ...expected: any -) => { + ...expected: Array +): SyncExpectationResult => { ensureMock(received, matcherName); const receivedIsSpy = isSpy(received); @@ -260,7 +265,7 @@ const createLastCalledWithMatcher = matcherName => ( ? type : `${type} "${receivedName}"`; const calls = receivedIsSpy - ? received.calls.all().map(x => x.args) + ? received.calls.all().map((x: any) => x.args) : received.mock.calls; const pass = equals(calls[calls.length - 1], expected, [iterableEquality]); @@ -279,10 +284,10 @@ const createLastCalledWithMatcher = matcherName => ( return {message, pass}; }; -const createLastReturnedMatcher = matcherName => ( +const createLastReturnedMatcher = (matcherName: string) => ( received: any, - expected: any, -) => { + expected: unknown, +): SyncExpectationResult => { ensureMock(received, matcherName); const receivedName = received.getMockName(); @@ -327,13 +332,14 @@ const createLastReturnedMatcher = matcherName => ( const createNthCalledWithMatcher = (matcherName: string) => ( received: any, nth: number, - ...expected: any -) => { + ...expected: Array +): SyncExpectationResult => { ensureMock(received, matcherName); const receivedIsSpy = isSpy(received); const type = receivedIsSpy ? 'spy' : 'mock function'; + // @ts-ignore if (typeof nth !== 'number' || parseInt(nth, 10) !== nth || nth < 1) { const message = () => `nth value ${printReceived( @@ -349,7 +355,7 @@ const createNthCalledWithMatcher = (matcherName: string) => ( ? type : `${type} "${receivedName}"`; const calls = receivedIsSpy - ? received.calls.all().map(x => x.args) + ? received.calls.all().map((x: any) => x.args) : received.mock.calls; const pass = equals(calls[nth - 1], expected, [iterableEquality]); @@ -379,10 +385,11 @@ const createNthCalledWithMatcher = (matcherName: string) => ( const createNthReturnedWithMatcher = (matcherName: string) => ( received: any, nth: number, - expected: any, -) => { + expected: unknown, +): SyncExpectationResult => { ensureMock(received, matcherName); + //@ts-ignore if (typeof nth !== 'number' || parseInt(nth, 10) !== nth || nth < 1) { const message = () => `nth value ${printReceived( @@ -462,9 +469,9 @@ const spyMatchers: MatchersObject = { toReturnWith: createToReturnWithMatcher('.toReturnWith'), }; -const isSpy = spy => spy.calls && typeof spy.calls.count === 'function'; +const isSpy = (spy: any) => spy.calls && typeof spy.calls.count === 'function'; -const ensureMock = (mockOrSpy, matcherName) => { +const ensureMock = (mockOrSpy: any, matcherName: any) => { if ( !mockOrSpy || ((mockOrSpy.calls === undefined || mockOrSpy.calls.all === undefined) && @@ -510,7 +517,11 @@ const getPrintedReturnValues = (calls: any[], limit: number): string => { return result.join('\n\n '); }; -const formatReceivedCalls = (calls, limit, options) => { +const formatReceivedCalls = ( + calls: Array, + limit: number, + options: any, +) => { if (calls.length) { const but = options && options.sameSentence ? 'but' : 'But'; const count = calls.length - limit; @@ -528,7 +539,11 @@ const formatReceivedCalls = (calls, limit, options) => { } }; -const formatMismatchedCalls = (calls, expected, limit) => { +const formatMismatchedCalls = ( + calls: Array, + expected: any, + limit: number, +): string => { if (calls.length) { return getPrintedCalls( calls, @@ -544,7 +559,11 @@ const formatMismatchedCalls = (calls, expected, limit) => { } }; -const formatMismatchedReturnValues = (returnValues, expected, limit) => { +const formatMismatchedReturnValues = ( + returnValues: Array, + expected: any, + limit: number, +): string => { if (returnValues.length) { return ( ` ${printExpected(expected)}\n` + @@ -559,7 +578,7 @@ const formatMismatchedReturnValues = (returnValues, expected, limit) => { } }; -const formatMismatchedArgs = (expected, received) => { +const formatMismatchedArgs = (expected: any, received: any): string => { const length = Math.max(expected.length, received.length); const printedArgs = []; @@ -584,7 +603,7 @@ const formatMismatchedArgs = (expected, received) => { return printedArgs.join('\n'); }; -const nthToString = (nth: number) => { +const nthToString = (nth: number): string => { switch (nth) { case 1: return 'first'; diff --git a/packages/expect/src/toThrowMatchers.js b/packages/expect/src/toThrowMatchers.ts similarity index 92% rename from packages/expect/src/toThrowMatchers.js rename to packages/expect/src/toThrowMatchers.ts index a53e6979e461..150e1c7fe725 100644 --- a/packages/expect/src/toThrowMatchers.js +++ b/packages/expect/src/toThrowMatchers.ts @@ -4,11 +4,8 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ -import type {MatcherHintOptions, MatchersObject} from 'types/Matchers'; - import {formatStackTrace, separateMessageFromStack} from 'jest-message-util'; import { EXPECTED_COLOR, @@ -18,23 +15,30 @@ import { printExpected, printReceived, printWithType, + MatcherHintOptions, } from 'jest-matcher-utils'; +import { + MatchersObject, + MatcherState, + RawMatcherFn, + SyncExpectationResult, +} from './types'; import {isError} from './utils'; const DID_NOT_THROW = 'Received function did not throw'; type Thrown = | { - hasMessage: true, - isError: true, - message: string, - value: Error, + hasMessage: true; + isError: true; + message: string; + value: Error; } | { - hasMessage: boolean, - isError: false, - message: string, - value: any, + hasMessage: boolean; + isError: false; + message: string; + value: any; }; const getThrown = (e: any): Thrown => { @@ -58,8 +62,11 @@ const getThrown = (e: any): Thrown => { }; }; -export const createMatcher = (matcherName: string, fromPromise?: boolean) => - function(received: Function, expected: any) { +export const createMatcher = ( + matcherName: string, + fromPromise?: boolean, +): RawMatcherFn => + function(this: MatcherState, received: Function, expected: any) { const options = { isNot: this.isNot, promise: this.promise, @@ -128,7 +135,7 @@ const toThrowExpectedRegExp = ( options: MatcherHintOptions, thrown: Thrown | null, expected: RegExp, -) => { +): SyncExpectationResult => { const pass = thrown !== null && expected.test(thrown.message); const message = pass @@ -155,7 +162,7 @@ const toThrowExpectedRegExp = ( }; type AsymmetricMatcher = { - asymmetricMatch: (received: any) => boolean, + asymmetricMatch: (received: unknown) => boolean; }; const toThrowExpectedAsymmetric = ( @@ -163,7 +170,7 @@ const toThrowExpectedAsymmetric = ( options: MatcherHintOptions, thrown: Thrown | null, expected: AsymmetricMatcher, -) => { +): SyncExpectationResult => { const pass = thrown !== null && expected.asymmetricMatch(thrown.value); const message = pass @@ -197,8 +204,8 @@ const toThrowExpectedObject = ( matcherName: string, options: MatcherHintOptions, thrown: Thrown | null, - expected: Object, -) => { + expected: any, +): SyncExpectationResult => { const pass = thrown !== null && thrown.message === expected.message; const message = pass @@ -229,7 +236,7 @@ const toThrowExpectedClass = ( options: MatcherHintOptions, thrown: Thrown | null, expected: Function, -) => { +): SyncExpectationResult => { const pass = thrown !== null && thrown.value instanceof expected; const message = pass @@ -264,7 +271,7 @@ const toThrowExpectedString = ( options: MatcherHintOptions, thrown: Thrown | null, expected: string, -) => { +): SyncExpectationResult => { const pass = thrown !== null && thrown.message.includes(expected); const message = pass @@ -294,7 +301,7 @@ const toThrow = ( matcherName: string, options: MatcherHintOptions, thrown: Thrown | null, -) => { +): SyncExpectationResult => { const pass = thrown !== null; const message = pass @@ -314,7 +321,7 @@ const toThrow = ( return {message, pass}; }; -const formatExpected = (label: string, expected: any) => +const formatExpected = (label: string, expected: unknown) => label + printExpected(expected) + '\n'; const formatReceived = (label: string, thrown: Thrown | null, key: string) => { @@ -343,6 +350,7 @@ const formatStack = (thrown: Thrown | null) => thrown === null || !thrown.isError ? '' : formatStackTrace( + // @ts-ignore separateMessageFromStack(thrown.value.stack).stack, { rootDir: process.cwd(), diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts new file mode 100644 index 000000000000..106abcb03995 --- /dev/null +++ b/packages/expect/src/types.ts @@ -0,0 +1,103 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + * + */ +import {Config} from '@jest/types'; +import * as jestMatcherUtils from 'jest-matcher-utils'; + +export type SyncExpectationResult = { + pass: boolean; + message: () => string; +}; + +export type AsyncExpectationResult = Promise; + +export type ExpectationResult = SyncExpectationResult | AsyncExpectationResult; + +export type RawMatcherFn = ( + expected: any, + actual: any, + options?: any, +) => ExpectationResult; + +export type ThrowingMatcherFn = (actual: any) => void; +export type PromiseMatcherFn = (actual: any) => Promise; + +export type Tester = (a: any, b: any) => boolean | undefined; + +export type MatcherState = { + assertionCalls: number; + currentTestName?: string; + dontThrow?: () => void; + error?: Error; + equals: ( + a: unknown, + b: unknown, + customTesters?: Array, + strictCheck?: boolean, + ) => boolean; + expand?: boolean; + expectedAssertionsNumber?: number; + isExpectingAssertions?: boolean; + isNot: boolean; + promise: string; + suppressedErrors: Array; + testPath?: Config.Path; + utils: typeof jestMatcherUtils & { + iterableEquality: Tester; + subsetEquality: Tester; + }; +}; + +export type AsymmetricMatcher = Object; +export type MatchersObject = {[id: string]: RawMatcherFn}; +export type Expect = { + (expected: any): ExpectationObject; + addSnapshotSerializer(arg0: any): void; + assertions(arg0: number): void; + extend(arg0: any): void; + extractExpectedAssertionsErrors: () => Array<{ + actual: string | number; + error: Error; + expected: string; + }>; + getState(): MatcherState; + hasAssertions(): void; + setState(arg0: any): void; + + any(expectedObject: any): AsymmetricMatcher; + anything(): AsymmetricMatcher; + arrayContaining(sample: Array): AsymmetricMatcher; + objectContaining(sample: Object): AsymmetricMatcher; + stringContaining(expected: string): AsymmetricMatcher; + stringMatching(expected: string | RegExp): AsymmetricMatcher; + [id: string]: AsymmetricMatcher; + not: {[id: string]: AsymmetricMatcher}; +}; + +type resolvesFn = { + [id: string]: PromiseMatcherFn; +} & { + not: {[id: string]: PromiseMatcherFn}; +}; + +type rejectsFn = { + [id: string]: PromiseMatcherFn; +} & { + not: {[id: string]: PromiseMatcherFn}; +}; + +type notFn = { + [id: string]: ThrowingMatcherFn; +}; + +export type ExpectationObject = { + [id: string]: ThrowingMatcherFn; +} & { + resolves: resolvesFn; + rejects: rejectsFn; + not: notFn; +}; diff --git a/packages/expect/src/utils.js b/packages/expect/src/utils.ts similarity index 87% rename from packages/expect/src/utils.js rename to packages/expect/src/utils.ts index 680c5faa0c1b..42384bbb8bc2 100644 --- a/packages/expect/src/utils.js +++ b/packages/expect/src/utils.ts @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ import { @@ -15,14 +14,14 @@ import { } from './jasmineUtils'; type GetPath = { - hasEndProp?: boolean, - lastTraversedObject: ?Object, - traversedPath: Array, - value?: any, + hasEndProp?: boolean; + lastTraversedObject: unknown; + traversedPath: Array; + value?: unknown; }; // Return whether object instance inherits getter from its class. -const hasGetterFromConstructor = (object: Object, key: string) => { +const hasGetterFromConstructor = (object: object, key: string) => { const constructor = object.constructor; if (constructor === Object) { // A literal object has Object as constructor. @@ -44,22 +43,22 @@ const hasGetterFromConstructor = (object: Object, key: string) => { return descriptor !== undefined && typeof descriptor.get === 'function'; }; -export const hasOwnProperty = (object: Object, key: string) => +export const hasOwnProperty = (object: object, key: string) => Object.prototype.hasOwnProperty.call(object, key) || hasGetterFromConstructor(object, key); export const getPath = ( - object: Object, + object: object, propertyPath: string | Array, ): GetPath => { if (!Array.isArray(propertyPath)) { - propertyPath = propertyPath.split('.'); + propertyPath = (propertyPath as string).split('.'); } if (propertyPath.length) { const lastProp = propertyPath.length === 1; const prop = propertyPath[0]; - const newObject = object[prop]; + const newObject = (object as any)[prop]; if (!lastProp && (newObject === null || newObject === undefined)) { // This is not the last prop in the chain. If we keep recursing it will @@ -99,10 +98,12 @@ export const getPath = ( // Strip properties from object that are not present in the subset. Useful for // printing the diff for toMatchObject() without adding unrelated noise. -export const getObjectSubset = (object: Object, subset: Object) => { +export const getObjectSubset = (object: any, subset: any): any => { if (Array.isArray(object)) { if (Array.isArray(subset) && subset.length === object.length) { - return subset.map((sub, i) => getObjectSubset(object[i], sub)); + return subset.map((sub: any, i: number) => + getObjectSubset(object[i], sub), + ); } } else if (object instanceof Date) { return object; @@ -112,7 +113,7 @@ export const getObjectSubset = (object: Object, subset: Object) => { typeof subset === 'object' && subset !== null ) { - const trimmed = {}; + const trimmed: any = {}; Object.keys(subset) .filter(key => hasOwnProperty(object, key)) .forEach( @@ -128,7 +129,8 @@ export const getObjectSubset = (object: Object, subset: Object) => { const IteratorSymbol = Symbol.iterator; -const hasIterator = object => !!(object != null && object[IteratorSymbol]); +const hasIterator = (object: any) => + !!(object != null && object[IteratorSymbol]); export const iterableEquality = (a: any, b: any) => { if ( typeof a !== 'object' || @@ -215,14 +217,17 @@ export const iterableEquality = (a: any, b: any) => { return true; }; -const isObjectWithKeys = a => +const isObjectWithKeys = (a: any) => a !== null && typeof a === 'object' && !(a instanceof Error) && !(a instanceof Array) && !(a instanceof Date); -export const subsetEquality = (object: Object, subset: Object) => { +export const subsetEquality = ( + object: any, + subset: any, +): undefined | boolean => { if (!isObjectWithKeys(subset)) { return undefined; } @@ -243,7 +248,7 @@ export const typeEquality = (a: any, b: any) => { return false; }; -export const sparseArrayEquality = (a: any, b: any) => { +export const sparseArrayEquality = (a: unknown, b: unknown) => { if (!Array.isArray(a) || !Array.isArray(b)) { return undefined; } @@ -256,9 +261,9 @@ export const sparseArrayEquality = (a: any, b: any) => { export const partition = ( items: Array, - predicate: T => boolean, + predicate: (arg: T) => boolean, ): [Array, Array] => { - const result = [[], []]; + const result: [Array, Array] = [[], []]; items.forEach(item => result[predicate(item) ? 0 : 1].push(item)); @@ -266,7 +271,7 @@ export const partition = ( }; // Copied from https://github.com/graingert/angular.js/blob/a43574052e9775cbc1d7dd8a086752c979b0f020/src/Angular.js#L685-L693 -export const isError = (value: any) => { +export const isError = (value: unknown) => { switch (Object.prototype.toString.call(value)) { case '[object Error]': return true; @@ -285,7 +290,7 @@ export function emptyObject(obj: any) { const MULTILINE_REGEXP = /[\r\n]/; -export const isOneline = (expected: any, received: any) => +export const isOneline = (expected: any, received: any): boolean => typeof expected === 'string' && typeof received === 'string' && (!MULTILINE_REGEXP.test(expected) || !MULTILINE_REGEXP.test(received)); diff --git a/packages/expect/tsconfig.json b/packages/expect/tsconfig.json new file mode 100644 index 000000000000..9bc3707b280d --- /dev/null +++ b/packages/expect/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [ + {"path": "../jest-get-type"}, + {"path": "../jest-matcher-utils"}, + {"path": "../jest-message-util"}, + {"path": "../jest-regex-util"}, + {"path": "../jest-types"} + ] +} diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index b9b60ec9bab8..68a42e200c0a 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -20,6 +20,7 @@ import SnapshotState from './State'; import {addSerializer, getSerializers} from './plugins'; import * as utils from './utils'; +// TODO: use MatcherState directly from `expect` once whole project is migrated type Context = Matchers.MatcherState & { snapshotState: SnapshotState; }; diff --git a/packages/jest-types/src/Matchers.ts b/packages/jest-types/src/Matchers.ts index e4e419e0ae23..f1a4b9b94ce9 100644 --- a/packages/jest-types/src/Matchers.ts +++ b/packages/jest-types/src/Matchers.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -// TODO: Move this to `expect` when it's migrated +// TODO: Remove this when whole project is migrated import {Path} from './Config'; diff --git a/scripts/checkCopyrightHeaders.js b/scripts/checkCopyrightHeaders.js index 2cd3c0623a17..73eeffc644ba 100755 --- a/scripts/checkCopyrightHeaders.js +++ b/scripts/checkCopyrightHeaders.js @@ -99,7 +99,7 @@ const CUSTOM_IGNORED_PATTERNS = [ '\\.(example|map)$', '^examples/.*', '^flow-typed/.*', - '^packages/expect/src/jasmineUtils\\.js$', + '^packages/expect/src/jasmineUtils\\.ts$', '^packages/jest-config/src/vendor/jsonlint\\.js$', ].map(createRegExp); From 9018f3cdfc794db6111fecd7051457863905643b Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Wed, 20 Feb 2019 20:15:58 +0100 Subject: [PATCH 079/107] `toStrictEqual` does not consider arrays with objects having undefined values correctly (#7938) ## Summary Fixes #7937: `toStrictEqual` does not consider arrays with objects having undefined values correctly ## Test plan Before that change, the following assertion was failing: ```js expect([{a: undefined}]).not.toStrictEqual([{}]); ``` Now it passes as expected. --- CHANGELOG.md | 1 + packages/expect/src/__tests__/matchers.test.js | 8 ++++++++ packages/expect/src/utils.ts | 4 +++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 570249abec1d..bdf022c9ad6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - `[jest-cli]` Fix prototype pollution vulnerability in dependency ([#7904](https://github.com/facebook/jest/pull/7904)) - `[jest-cli]` Refactor `-o` and `--coverage` combined ([#7611](https://github.com/facebook/jest/pull/7611)) - `[expect]` Fix custom async matcher stack trace ([#7652](https://github.com/facebook/jest/pull/7652)) +- `[expect]` Fix `toStrictEqual` not considering arrays with objects having undefined values correctly ([#7938](https://github.com/facebook/jest/pull/7938)) - `[jest-changed-files]` Improve default file selection for Mercurial repos ([#7880](https://github.com/facebook/jest/pull/7880)) - `[jest-validate]` Fix validating async functions ([#7894](https://github.com/facebook/jest/issues/7894)) - `[jest-circus]` Fix bug with test.only ([#7888](https://github.com/facebook/jest/pull/7888)) diff --git a/packages/expect/src/__tests__/matchers.test.js b/packages/expect/src/__tests__/matchers.test.js index afcce46e1c7e..3e7b52b6f26d 100644 --- a/packages/expect/src/__tests__/matchers.test.js +++ b/packages/expect/src/__tests__/matchers.test.js @@ -281,6 +281,14 @@ describe('.toStrictEqual()', () => { }).not.toStrictEqual({b: 2}); }); + it('does not ignore keys with undefined values inside an array', () => { + expect([{a: undefined}]).not.toStrictEqual([{}]); + }); + + it('does not ignore keys with undefined values deep inside an object', () => { + expect([{a: [{a: undefined}]}]).not.toStrictEqual([{a: [{}]}]); + }); + it('passes when comparing same type', () => { expect({ test: new TestClassA(1, 2), diff --git a/packages/expect/src/utils.ts b/packages/expect/src/utils.ts index 42384bbb8bc2..23d1fe1af797 100644 --- a/packages/expect/src/utils.ts +++ b/packages/expect/src/utils.ts @@ -256,7 +256,9 @@ export const sparseArrayEquality = (a: unknown, b: unknown) => { // A sparse array [, , 1] will have keys ["2"] whereas [undefined, undefined, 1] will have keys ["0", "1", "2"] const aKeys = Object.keys(a); const bKeys = Object.keys(b); - return equals(a, b) && equals(aKeys, bKeys); + return ( + equals(a, b, [iterableEquality, typeEquality], true) && equals(aKeys, bKeys) + ); }; export const partition = ( From 82d25a50ebd7cd42670dc49a7a19ed73f98ff9b2 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 21 Feb 2019 18:45:36 +0100 Subject: [PATCH 080/107] chore: upgrade babel-core typings --- packages/babel-jest/package.json | 2 +- packages/babel-jest/src/index.ts | 1 - packages/jest-transform/package.json | 2 +- .../jest-transform/src/ScriptTransformer.ts | 1 - yarn.lock | 35 ++++++++++--------- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/packages/babel-jest/package.json b/packages/babel-jest/package.json index 226e23853ce9..ea5f3c6cd554 100644 --- a/packages/babel-jest/package.json +++ b/packages/babel-jest/package.json @@ -13,7 +13,7 @@ "dependencies": { "@jest/transform": "^24.1.0", "@jest/types": "^24.1.0", - "@types/babel__core": "^7.0.4", + "@types/babel__core": "^7.1.0", "babel-plugin-istanbul": "^5.1.0", "babel-preset-jest": "^24.1.0", "chalk": "^2.4.2", diff --git a/packages/babel-jest/src/index.ts b/packages/babel-jest/src/index.ts index 2df36fe37d22..480cb6cfaca1 100644 --- a/packages/babel-jest/src/index.ts +++ b/packages/babel-jest/src/index.ts @@ -34,7 +34,6 @@ const createTransformer = ( ): BabelJestTransformer => { options = { ...options, - // @ts-ignore: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/33118 caller: { name: 'babel-jest', supportsStaticESM: false, diff --git a/packages/jest-transform/package.json b/packages/jest-transform/package.json index 61152b5281f7..5d3364d9f08b 100644 --- a/packages/jest-transform/package.json +++ b/packages/jest-transform/package.json @@ -26,7 +26,7 @@ "write-file-atomic": "2.4.1" }, "devDependencies": { - "@types/babel__core": "^7.0.4", + "@types/babel__core": "^7.1.0", "@types/convert-source-map": "^1.5.1", "@types/fast-json-stable-stringify": "^2.0.0", "@types/graceful-fs": "^4.1.2", diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index 350616c327ad..690a97c7da33 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -171,7 +171,6 @@ export default class ScriptTransformer { const result = babelTransform(content, { auxiliaryCommentBefore: ' istanbul ignore next ', babelrc: false, - // @ts-ignore: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/33118 caller: { name: '@jest/transform', supportsStaticESM: false, diff --git a/yarn.lock b/yarn.lock index 3429cadc7ab8..02da8691c7e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1491,30 +1491,33 @@ resolved "https://registry.yarnpkg.com/@types/babel__code-frame/-/babel__code-frame-7.0.0.tgz#d5658827984c1e386c1b4ef30699b344d3f732a2" integrity sha512-Rt0KuqopAdtpyvxO0JbKbwgtw7B+y0NQpTXns+tiz1lqGXywOESSKIiiQt0wnCreqEKvmrArH7KZ6bQg+w8BoA== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.0.4": - version "7.0.4" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.0.4.tgz#14b30c11113bad353cabfaea73e327b48edb0f0e" - integrity sha512-2Y2RK1BN5BRFfhneGfQA8mmFmTANbzGgS5uQPluoRqGNWb6uAcefqxzNbqgxPpmPkLqKapQfmYcyyl5iAQV+fA== +"@types/babel__core@^7.0.0", "@types/babel__core@^7.0.4", "@types/babel__core@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.0.tgz#710f2487dda4dcfd010ca6abb2b4dc7394365c51" + integrity sha512-wJTeJRt7BToFx3USrCDs2BhEi4ijBInTQjOIukj6a/5tEkwpFMVZ+1ppgmE+Q/FQyc5P/VWUbx7I9NELrKruHA== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" -"@types/babel__generator@^7.0.0": - version "7.0.1" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.0.1.tgz#533bd37e7be8af27ebf8fcc0a053cfa4c03f4c5c" - integrity sha512-QZ+oM5v3lAh/4DqWDyCL0rVi/BJlKRANDEpShxt3Lg+rhbFA78Ec7N+8lcAIpziA/Mfp/1ShDJj2Ti3HJ/JPxA== +"@types/babel__generator@*", "@types/babel__generator@^7.0.0": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.0.2.tgz#d2112a6b21fad600d7674274293c85dce0cb47fc" + integrity sha512-NHcOfab3Zw4q5sEE2COkpfXjoE7o+PmqD9DQW4koUT3roNxwziUdXGnRndMat/LJNUtePwn1TlP4do3uoe3KZQ== dependencies: "@babel/types" "^7.0.0" -"@types/babel__template@^7.0.0": - version "7.0.1" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.1.tgz#8de6effa729bf0b0f65eb72399b629a3998a7f10" - integrity sha512-LT1zwkpL0/2oBP+npLaOCYSWv47cxbdAvONutjalMdCIBfjtfzVNnT8rgllBmsr15QAdAZDOmYw1CPhLB2JCtA== +"@types/babel__template@*", "@types/babel__template@^7.0.0": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.2.tgz#4ff63d6b52eddac1de7b975a5223ed32ecea9307" + integrity sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": version "7.0.6" resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.6.tgz#328dd1a8fc4cfe3c8458be9477b219ea158fd7b2" integrity sha512-XYVgHF2sQ0YblLRMLNPB3CkFMewzFmlDsH/TneZFHUXDlABQgh88uOxuez7ZcXxayLFrqLwtDH1t+FmlFwNZxw== @@ -1841,9 +1844,9 @@ "@types/node" "*" "@types/yargs@^12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.2.tgz#7569157b2601d49d4442023c0d22f81d23f62d1e" - integrity sha512-NUyxaQW48vjeDHybmhy7CFx/6of1+PoaEGPMI+0PzBVr5s3BTELT7gV4lNaxUsKllNS1YAjkeTEhBtzIDWs2WQ== + version "12.0.9" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.9.tgz#693e76a52f61a2f1e7fb48c0eef167b95ea4ffd0" + integrity sha512-sCZy4SxP9rN2w30Hlmg5dtdRwgYQfYRiLo9usw8X9cxlf+H4FqM1xX7+sNH7NNKVdbXMJWqva7iyy+fxh/V7fA== "@typescript-eslint/eslint-plugin@^1.3.0": version "1.3.0" From 11077a8a8e7575dd1e1e1637076092d8fedf575d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Thu, 21 Feb 2019 17:53:55 +0000 Subject: [PATCH 081/107] Fix transformer signature in babel-jest (#7945) * Assigned createTransformer function only to the transformer exported by babel-jest * Improved typing * Updated changelog * Added e2e test --- CHANGELOG.md | 2 +- e2e/__tests__/transform.test.js | 15 +++++++++ .../__tests__/babelJest.test.js | 15 +++++++++ e2e/transform/babel-jest-manual/foo.js | 10 ++++++ e2e/transform/babel-jest-manual/package.json | 11 +++++++ .../babel-jest-manual/transformer.js | 13 ++++++++ e2e/transform/babel-jest-manual/yarn.lock | 31 +++++++++++++++++++ packages/babel-jest/src/index.ts | 15 ++++++--- 8 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 e2e/transform/babel-jest-manual/__tests__/babelJest.test.js create mode 100644 e2e/transform/babel-jest-manual/foo.js create mode 100644 e2e/transform/babel-jest-manual/package.json create mode 100644 e2e/transform/babel-jest-manual/transformer.js create mode 100644 e2e/transform/babel-jest-manual/yarn.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index bdf022c9ad6e..b95746b8ceb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,7 +47,7 @@ - `[@jest/transform]`: New package extracted from `jest-runtime` ([#7915](https://github.com/facebook/jest/pull/7915)) - `[babel-plugin-jest-hoist]`: Migrate to TypeScript ([#7898](https://github.com/facebook/jest/pull/7898)) - `[@jest/core]` Create new package, which is `jest-cli` minus `yargs` and `prompts` ([#7696](https://github.com/facebook/jest/pull/7696)) -- `[@jest/transform]`: Migrate to TypeScript ([#7918](https://github.com/facebook/jest/pull/7918)) +- `[@jest/transform]`: Migrate to TypeScript ([#7918](https://github.com/facebook/jest/pull/7918), [#7945](https://github.com/facebook/jest/pull/7945)) - `[docs]` Add missing import to docs ([#7928](https://github.com/facebook/jest/pull/7928)) - `[jest-resolve-dependencies]`: Migrate to TypeScript ([#7922](https://github.com/facebook/jest/pull/7922)) - `[expect]`: Migrate to TypeScript ([#7919](https://github.com/facebook/jest/pull/7919)) diff --git a/e2e/__tests__/transform.test.js b/e2e/__tests__/transform.test.js index c9f12a0c406a..8adc624a0249 100644 --- a/e2e/__tests__/transform.test.js +++ b/e2e/__tests__/transform.test.js @@ -57,6 +57,21 @@ describe('babel-jest ignored', () => { }); }); +describe('babel-jest with manual transformer', () => { + const dir = path.resolve(__dirname, '..', 'transform/babel-jest-manual'); + + beforeEach(() => { + run('yarn', dir); + }); + + it('runs transpiled code', () => { + // --no-cache because babel can cache stuff and result in false green + const {json} = runWithJson(dir, ['--no-cache']); + expect(json.success).toBe(true); + expect(json.numTotalTests).toBeGreaterThanOrEqual(1); + }); +}); + // babel-jest is automatically linked at the root because it is a workspace now // a way to test this in isolation is to move the test suite into a temp folder describe('no babel-jest', () => { diff --git a/e2e/transform/babel-jest-manual/__tests__/babelJest.test.js b/e2e/transform/babel-jest-manual/__tests__/babelJest.test.js new file mode 100644 index 000000000000..49af46513702 --- /dev/null +++ b/e2e/transform/babel-jest-manual/__tests__/babelJest.test.js @@ -0,0 +1,15 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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'; + +require('../foo'); + +it('strips flowtypes using babel-jest and .babelrc', () => { + const a: string = 'a'; + expect(a).toBe('a'); +}); diff --git a/e2e/transform/babel-jest-manual/foo.js b/e2e/transform/babel-jest-manual/foo.js new file mode 100644 index 000000000000..7e8e2873685b --- /dev/null +++ b/e2e/transform/babel-jest-manual/foo.js @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +const someFunction = (): null => null; + +module.exports = someFunction(); diff --git a/e2e/transform/babel-jest-manual/package.json b/e2e/transform/babel-jest-manual/package.json new file mode 100644 index 000000000000..6f6e06a0b254 --- /dev/null +++ b/e2e/transform/babel-jest-manual/package.json @@ -0,0 +1,11 @@ +{ + "dependencies": { + "@babel/preset-flow": "^7.0.0" + }, + "jest": { + "testEnvironment": "node", + "transform": { + "\\.js$": "/transformer.js" + } + } +} diff --git a/e2e/transform/babel-jest-manual/transformer.js b/e2e/transform/babel-jest-manual/transformer.js new file mode 100644 index 000000000000..597b36bc9ae5 --- /dev/null +++ b/e2e/transform/babel-jest-manual/transformer.js @@ -0,0 +1,13 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +const {createTransformer} = require('babel-jest'); + +module.exports = createTransformer({ + presets: ['@babel/preset-flow'], + root: __dirname, +}); diff --git a/e2e/transform/babel-jest-manual/yarn.lock b/e2e/transform/babel-jest-manual/yarn.lock new file mode 100644 index 000000000000..a698b6900465 --- /dev/null +++ b/e2e/transform/babel-jest-manual/yarn.lock @@ -0,0 +1,31 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/helper-plugin-utils@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" + integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== + +"@babel/plugin-syntax-flow@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.0.0.tgz#70638aeaad9ee426bc532e51523cff8ff02f6f17" + integrity sha512-zGcuZWiWWDa5qTZ6iAnpG0fnX/GOu49pGR5PFvkQ9GmKNaSphXQnlNXh/LG20sqWtNrx/eB6krzfEzcwvUyeFA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-flow-strip-types@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.0.0.tgz#c40ced34c2783985d90d9f9ac77a13e6fb396a01" + integrity sha512-WhXUNb4It5a19RsgKKbQPrjmy4yWOY1KynpEbNw7bnd1QTcrT/EIl3MJvnGgpgvrKyKbqX7nUNOJfkpLOnoDKA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-flow" "^7.0.0" + +"@babel/preset-flow@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.0.0.tgz#afd764835d9535ec63d8c7d4caf1c06457263da2" + integrity sha512-bJOHrYOPqJZCkPVbG1Lot2r5OSsB+iUOaxiHdlOeB1yPWS6evswVHwvkDLZ54WTaTRIk89ds0iHmGZSnxlPejQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-transform-flow-strip-types" "^7.0.0" diff --git a/packages/babel-jest/src/index.ts b/packages/babel-jest/src/index.ts index 480cb6cfaca1..8c072abadc4e 100644 --- a/packages/babel-jest/src/index.ts +++ b/packages/babel-jest/src/index.ts @@ -26,7 +26,6 @@ const babelIstanbulPlugin = require.resolve('babel-plugin-istanbul'); // Narrow down the types interface BabelJestTransformer extends Transformer { canInstrument: true; - createTransformer: (options?: TransformOptions) => BabelJestTransformer; } const createTransformer = ( @@ -64,9 +63,8 @@ const createTransformer = ( return babelConfig; } - const transformer: BabelJestTransformer = { + return { canInstrument: true, - createTransformer, getCacheKey( fileData, filename, @@ -130,8 +128,15 @@ const createTransformer = ( return src; }, }; +}; - return transformer; +const transformer: BabelJestTransformer & { + createTransformer: (options?: TransformOptions) => BabelJestTransformer; +} = { + ...createTransformer(), + // Assigned here so only the exported transformer has `createTransformer`, + // instead of all created transformers by the function + createTransformer, }; -export = createTransformer(); +export = transformer; From aa03cb0984f5d383b4f1a0e64f46b20175b78558 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Thu, 21 Feb 2019 19:12:28 -0500 Subject: [PATCH 082/107] expect, jest-matcher-utils: Improve report when matcher fails, part 9 (#7940) --- CHANGELOG.md | 1 + .../__snapshots__/matchers.test.js.snap | 52 ++++++++++++ .../expect/src/__tests__/matchers.test.js | 53 ++++++++++++ packages/expect/src/matchers.ts | 13 ++- .../__snapshots__/index.test.ts.snap | 60 +++++++++++++- .../src/__tests__/index.test.ts | 80 ++++++++++++++++++- packages/jest-matcher-utils/src/index.ts | 22 +++-- 7 files changed, 259 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b95746b8ceb7..bcfc390b745f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - `[expect]`: Improve report when matcher fails, part 7 ([#7866](https://github.com/facebook/jest/pull/7866)) - `[expect]`: Improve report when matcher fails, part 8 ([#7876](https://github.com/facebook/jest/pull/7876)) +- `[expect]`: Improve report when matcher fails, part 9 ([#7940](https://github.com/facebook/jest/pull/7940)) - `[pretty-format]` Support `React.memo` ([#7891](https://github.com/facebook/jest/pull/7891)) - `[jest-config]` Print error information on preset normalization error ([#7935](https://github.com/facebook/jest/pull/7935)) diff --git a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap index 422a60a6b004..ce7887e1b2e4 100644 --- a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap @@ -563,6 +563,58 @@ Expected difference: < 0.005 Received difference: 0.005000099999999952" `; +exports[`.toBeCloseTo() throws: Matcher error promise empty isNot false received 1`] = ` +"expect(received).toBeCloseTo(expected, precision) + +Matcher error: received value must be a number + +Received has type: string +Received has value: \\"\\"" +`; + +exports[`.toBeCloseTo() throws: Matcher error promise empty isNot true expected 1`] = ` +"expect(received).not.toBeCloseTo(expected) + +Matcher error: expected value must be a number + +Expected has value: undefined" +`; + +exports[`.toBeCloseTo() throws: Matcher error promise rejects isNot false expected 1`] = ` +"expect(received).rejects.toBeCloseTo(expected) + +Matcher error: expected value must be a number + +Expected has type: string +Expected has value: \\"0\\"" +`; + +exports[`.toBeCloseTo() throws: Matcher error promise rejects isNot true received 1`] = ` +"expect(received).rejects.not.toBeCloseTo(expected) + +Matcher error: received value must be a number + +Received has type: symbol +Received has value: Symbol(0.1)" +`; + +exports[`.toBeCloseTo() throws: Matcher error promise resolves isNot false received 1`] = ` +"expect(received).resolves.toBeCloseTo(expected, precision) + +Matcher error: received value must be a number + +Received has type: boolean +Received has value: false" +`; + +exports[`.toBeCloseTo() throws: Matcher error promise resolves isNot true expected 1`] = ` +"expect(received).resolves.not.toBeCloseTo(expected, precision) + +Matcher error: expected value must be a number + +Expected has value: null" +`; + exports[`.toBeDefined(), .toBeUndefined() '"a"' is defined 1`] = ` "expect(received).not.toBeDefined() diff --git a/packages/expect/src/__tests__/matchers.test.js b/packages/expect/src/__tests__/matchers.test.js index 3e7b52b6f26d..c6be356dac0d 100644 --- a/packages/expect/src/__tests__/matchers.test.js +++ b/packages/expect/src/__tests__/matchers.test.js @@ -1026,6 +1026,59 @@ describe('.toBeCloseTo()', () => { ).toThrowErrorMatchingSnapshot(); }); }); + + describe('throws: Matcher error', () => { + test('promise empty isNot false received', () => { + const precision = 3; + const expected = 0; + const received = ''; + expect(() => { + jestExpect(received).toBeCloseTo(expected, precision); + }).toThrowErrorMatchingSnapshot(); + }); + + test('promise empty isNot true expected', () => { + const received = 0.1; + // expected is undefined + expect(() => { + jestExpect(received).not.toBeCloseTo(); + }).toThrowErrorMatchingSnapshot(); + }); + + test('promise rejects isNot false expected', () => { + const expected = '0'; + const received = Promise.reject(0.01); + return expect( + jestExpect(received).rejects.toBeCloseTo(expected), + ).rejects.toThrowErrorMatchingSnapshot(); + }); + + test('promise rejects isNot true received', () => { + const expected = 0; + const received = Promise.reject(Symbol('0.1')); + return expect( + jestExpect(received).rejects.not.toBeCloseTo(expected), + ).rejects.toThrowErrorMatchingSnapshot(); + }); + + test('promise resolves isNot false received', () => { + const precision = 3; + const expected = 0; + const received = Promise.resolve(false); + return expect( + jestExpect(received).resolves.toBeCloseTo(expected, precision), + ).rejects.toThrowErrorMatchingSnapshot(); + }); + + test('promise resolves isNot true expected', () => { + const precision = 3; + const expected = null; + const received = Promise.resolve(0.1); + expect( + jestExpect(received).resolves.not.toBeCloseTo(expected, precision), + ).rejects.toThrowErrorMatchingSnapshot(); + }); + }); }); describe('.toMatch()', () => { diff --git a/packages/expect/src/matchers.ts b/packages/expect/src/matchers.ts index 6154f33d7873..adb628dd4231 100644 --- a/packages/expect/src/matchers.ts +++ b/packages/expect/src/matchers.ts @@ -93,13 +93,12 @@ const matchers: MatchersObject = { precision: number = 2, ) { const secondArgument = arguments.length === 3 ? 'precision' : undefined; - const isNot = this.isNot; const options: MatcherHintOptions = { - isNot, + isNot: this.isNot, promise: this.promise, secondArgument, }; - ensureNumbers(received, expected, '.toBeCloseTo'); + ensureNumbers(received, expected, 'toBeCloseTo', options); let pass = false; let expectedDiff = 0; @@ -180,7 +179,7 @@ const matchers: MatchersObject = { isNot, promise: this.promise, }; - ensureNumbers(received, expected, '.toBeGreaterThan'); + ensureNumbers(received, expected, 'toBeGreaterThan', options); const pass = received > expected; @@ -203,7 +202,7 @@ const matchers: MatchersObject = { isNot, promise: this.promise, }; - ensureNumbers(received, expected, '.toBeGreaterThanOrEqual'); + ensureNumbers(received, expected, 'toBeGreaterThanOrEqual', options); const pass = received >= expected; @@ -266,7 +265,7 @@ const matchers: MatchersObject = { isNot, promise: this.promise, }; - ensureNumbers(received, expected, '.toBeLessThan'); + ensureNumbers(received, expected, 'toBeLessThan', options); const pass = received < expected; @@ -285,7 +284,7 @@ const matchers: MatchersObject = { isNot, promise: this.promise, }; - ensureNumbers(received, expected, '.toBeLessThanOrEqual'); + ensureNumbers(received, expected, 'toBeLessThanOrEqual', options); const pass = received <= expected; diff --git a/packages/jest-matcher-utils/src/__tests__/__snapshots__/index.test.ts.snap b/packages/jest-matcher-utils/src/__tests__/__snapshots__/index.test.ts.snap index 6a3d31c4315c..208598d4c2f2 100644 --- a/packages/jest-matcher-utils/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/jest-matcher-utils/src/__tests__/__snapshots__/index.test.ts.snap @@ -18,8 +18,8 @@ Expected has type: object Expected has value: {\\"a\\": 1}" `; -exports[`.ensureNumbers() throws error when expected is not a number 1`] = ` -"expect(received)[.not]This matcher(expected) +exports[`.ensureNumbers() throws error when expected is not a number (backward compatibility) 1`] = ` +"expect(received)[.not].toBeCloseTo(expected) Matcher error: expected value must be a number @@ -27,8 +27,8 @@ Expected has type: string Expected has value: \\"not_a_number\\"" `; -exports[`.ensureNumbers() throws error when received is not a number 1`] = ` -"expect(received)[.not]This matcher(expected) +exports[`.ensureNumbers() throws error when received is not a number (backward compatibility) 1`] = ` +"expect(received)[.not].toBeCloseTo(expected) Matcher error: received value must be a number @@ -36,6 +36,58 @@ Received has type: string Received has value: \\"not_a_number\\"" `; +exports[`.ensureNumbers() with options promise empty isNot false received 1`] = ` +"expect(received).toBeCloseTo(expected, precision) + +Matcher error: received value must be a number + +Received has type: string +Received has value: \\"\\"" +`; + +exports[`.ensureNumbers() with options promise empty isNot true expected 1`] = ` +"expect(received).not.toBeCloseTo(expected) + +Matcher error: expected value must be a number + +Expected has value: undefined" +`; + +exports[`.ensureNumbers() with options promise rejects isNot false expected 1`] = ` +"expect(received).rejects.toBeCloseTo(expected) + +Matcher error: expected value must be a number + +Expected has type: string +Expected has value: \\"0\\"" +`; + +exports[`.ensureNumbers() with options promise rejects isNot true received 1`] = ` +"expect(received).rejects.not.toBeCloseTo(expected) + +Matcher error: received value must be a number + +Received has type: symbol +Received has value: Symbol(0.1)" +`; + +exports[`.ensureNumbers() with options promise resolves isNot false received 1`] = ` +"expect(received).resolves.toBeCloseTo(expected) + +Matcher error: received value must be a number + +Received has type: boolean +Received has value: false" +`; + +exports[`.ensureNumbers() with options promise resolves isNot true expected 1`] = ` +"expect(received).resolves.not.toBeCloseTo(expected) + +Matcher error: expected value must be a number + +Expected has value: null" +`; + exports[`.stringify() reduces maxDepth if stringifying very large objects 1`] = `"{\\"a\\": 1, \\"b\\": [Object]}"`; exports[`.stringify() reduces maxDepth if stringifying very large objects 2`] = `"{\\"a\\": 1, \\"b\\": {\\"0\\": \\"test\\", \\"1\\": \\"test\\", \\"2\\": \\"test\\", \\"3\\": \\"test\\", \\"4\\": \\"test\\", \\"5\\": \\"test\\", \\"6\\": \\"test\\", \\"7\\": \\"test\\", \\"8\\": \\"test\\", \\"9\\": \\"test\\"}}"`; diff --git a/packages/jest-matcher-utils/src/__tests__/index.test.ts b/packages/jest-matcher-utils/src/__tests__/index.test.ts index a78976c11a3c..e5f0ad94cca1 100644 --- a/packages/jest-matcher-utils/src/__tests__/index.test.ts +++ b/packages/jest-matcher-utils/src/__tests__/index.test.ts @@ -13,6 +13,7 @@ import { getLabelPrinter, pluralize, stringify, + MatcherHintOptions, } from '../'; describe('.stringify()', () => { @@ -98,19 +99,90 @@ describe('.ensureNumbers()', () => { }).not.toThrow(); }); - test('throws error when expected is not a number', () => { + test('throws error when expected is not a number (backward compatibility)', () => { expect(() => { // @ts-ignore - ensureNumbers(1, 'not_a_number'); + ensureNumbers(1, 'not_a_number', '.toBeCloseTo'); }).toThrowErrorMatchingSnapshot(); }); - test('throws error when received is not a number', () => { + test('throws error when received is not a number (backward compatibility)', () => { expect(() => { // @ts-ignore - ensureNumbers('not_a_number', 3); + ensureNumbers('not_a_number', 3, '.toBeCloseTo'); }).toThrowErrorMatchingSnapshot(); }); + + describe('with options', () => { + const matcherName = 'toBeCloseTo'; + + test('promise empty isNot false received', () => { + const options: MatcherHintOptions = { + isNot: false, + promise: '', + secondArgument: 'precision', + }; + expect(() => { + // @ts-ignore + ensureNumbers('', 0, matcherName, options); + }).toThrowErrorMatchingSnapshot(); + }); + + test('promise empty isNot true expected', () => { + const options: MatcherHintOptions = { + isNot: true, + // promise undefined is equivalent to empty string + }; + expect(() => { + // @ts-ignore + ensureNumbers(0.1, undefined, matcherName, options); + }).toThrowErrorMatchingSnapshot(); + }); + + test('promise rejects isNot false expected', () => { + const options: MatcherHintOptions = { + isNot: false, + promise: 'rejects', + }; + expect(() => { + // @ts-ignore + ensureNumbers(0.01, '0', matcherName, options); + }).toThrowErrorMatchingSnapshot(); + }); + + test('promise rejects isNot true received', () => { + const options: MatcherHintOptions = { + isNot: true, + promise: 'rejects', + }; + expect(() => { + // @ts-ignore + ensureNumbers(Symbol('0.1'), 0, matcherName, options); + }).toThrowErrorMatchingSnapshot(); + }); + + test('promise resolves isNot false received', () => { + const options: MatcherHintOptions = { + isNot: false, + promise: 'resolves', + }; + expect(() => { + // @ts-ignore + ensureNumbers(false, 0, matcherName, options); + }).toThrowErrorMatchingSnapshot(); + }); + + test('promise resolves isNot true expected', () => { + const options: MatcherHintOptions = { + isNot: true, + promise: 'resolves', + }; + expect(() => { + // @ts-ignore + ensureNumbers(0.1, null, matcherName, options); + }).toThrowErrorMatchingSnapshot(); + }); + }); }); describe('.ensureNoExpected()', () => { diff --git a/packages/jest-matcher-utils/src/index.ts b/packages/jest-matcher-utils/src/index.ts index ef5121d7210a..293c93e8d344 100644 --- a/packages/jest-matcher-utils/src/index.ts +++ b/packages/jest-matcher-utils/src/index.ts @@ -130,12 +130,17 @@ export const ensureNoExpected = ( } }; -export const ensureActualIsNumber = (actual: unknown, matcherName: string) => { - matcherName || (matcherName = 'This matcher'); +export const ensureActualIsNumber = ( + actual: unknown, + matcherName: string, + options?: MatcherHintOptions, +) => { if (typeof actual !== 'number') { + // Prepend maybe not only for backward compatibility. + const matcherString = (options ? '' : '[.not]') + matcherName; throw new Error( matcherErrorMessage( - matcherHint('[.not]' + matcherName), + matcherHint(matcherString, undefined, undefined, options), `${RECEIVED_COLOR('received')} value must be a number`, printWithType('Received', actual, printReceived), ), @@ -146,12 +151,14 @@ export const ensureActualIsNumber = (actual: unknown, matcherName: string) => { export const ensureExpectedIsNumber = ( expected: unknown, matcherName: string, + options?: MatcherHintOptions, ) => { - matcherName || (matcherName = 'This matcher'); if (typeof expected !== 'number') { + // Prepend maybe not only for backward compatibility. + const matcherString = (options ? '' : '[.not]') + matcherName; throw new Error( matcherErrorMessage( - matcherHint('[.not]' + matcherName), + matcherHint(matcherString, undefined, undefined, options), `${EXPECTED_COLOR('expected')} value must be a number`, printWithType('Expected', expected, printExpected), ), @@ -163,9 +170,10 @@ export const ensureNumbers = ( actual: unknown, expected: unknown, matcherName: string, + options?: MatcherHintOptions, ) => { - ensureActualIsNumber(actual, matcherName); - ensureExpectedIsNumber(expected, matcherName); + ensureActualIsNumber(actual, matcherName, options); + ensureExpectedIsNumber(expected, matcherName, options); }; // Sometimes, e.g. when comparing two numbers, the output from jest-diff From 713ddc08dd9f0d548878b1014c3a131f1819aa96 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Fri, 22 Feb 2019 01:13:49 +0100 Subject: [PATCH 083/107] chore: update failing snapshot --- e2e/__tests__/__snapshots__/transform.test.js.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/__tests__/__snapshots__/transform.test.js.snap b/e2e/__tests__/__snapshots__/transform.test.js.snap index 00c81c270f78..a8a139c79177 100644 --- a/e2e/__tests__/__snapshots__/transform.test.js.snap +++ b/e2e/__tests__/__snapshots__/transform.test.js.snap @@ -6,7 +6,7 @@ FAIL __tests__/ignoredFile.test.js babel-jest: Babel ignores __tests__/ignoredFile.test.js - make sure to include the file in Jest's transformIgnorePatterns as well. - at loadBabelConfig (../../../packages/babel-jest/build/index.js:133:13) + at loadBabelConfig (../../../packages/babel-jest/build/index.js:132:13) `; exports[`babel-jest instruments only specific files and collects coverage 1`] = ` From fcb73c41a8e7aa38adbcf40550ee1fbe6ffa1fde Mon Sep 17 00:00:00 2001 From: Tim Seckinger Date: Fri, 22 Feb 2019 01:17:37 +0100 Subject: [PATCH 084/107] fix jest-haste-map-types (#7951) --- CHANGELOG.md | 4 ++-- babel.config.js | 1 + package.json | 1 + packages/jest-haste-map/src/index.ts | 13 ++++++++----- .../src/__tests__/dependency_resolver.test.ts | 6 +++--- packages/jest-resolve/src/__tests__/resolve.test.ts | 5 +---- packages/jest-transform/src/ScriptTransformer.ts | 1 - yarn.lock | 5 +++++ 8 files changed, 21 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcfc390b745f..4d6ca4619aa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ ### Chore & Maintenance -- `[*]`: Setup building, linting and testing of TypeScript ([#7808](https://github.com/facebook/jest/pull/7808), [#7855](https://github.com/facebook/jest/pull/7855)) +- `[*]`: Setup building, linting and testing of TypeScript ([#7808](https://github.com/facebook/jest/pull/7808), [#7855](https://github.com/facebook/jest/pull/7855), [#7951](https://github.com/facebook/jest/pull/7951)) - `[pretty-format]`: Migrate to TypeScript ([#7809](https://github.com/facebook/jest/pull/7809)) - `[diff-sequences]`: Migrate to Typescript ([#7820](https://github.com/facebook/jest/pull/7820)) - `[jest-get-type]`: Migrate to TypeScript ([#7818](https://github.com/facebook/jest/pull/7818)) @@ -39,7 +39,7 @@ - `[jest-watcher]`: Migrate to TypeScript ([#7843](https://github.com/facebook/jest/pull/7843)) - `[jest-mock]`: Migrate to TypeScript ([#7847](https://github.com/facebook/jest/pull/7847), [#7850](https://github.com/facebook/jest/pull/7850)) - `[jest-worker]`: Migrate to TypeScript ([#7853](https://github.com/facebook/jest/pull/7853)) -- `[jest-haste-map]`: Migrate to TypeScript ([#7854](https://github.com/facebook/jest/pull/7854)) +- `[jest-haste-map]`: Migrate to TypeScript ([#7854](https://github.com/facebook/jest/pull/7854), [#7951](https://github.com/facebook/jest/pull/7951)) - `[docs]`: Fix image paths in SnapshotTesting.md for current and version 24 ([#7872](https://github.com/facebook/jest/pull/7872)) - `[babel-jest]`: Migrate to TypeScript ([#7862](https://github.com/facebook/jest/pull/7862)) - `[jest-resolve]`: Migrate to TypeScript ([#7871](https://github.com/facebook/jest/pull/7871)) diff --git a/babel.config.js b/babel.config.js index e295ec832457..5b6610d3801e 100644 --- a/babel.config.js +++ b/babel.config.js @@ -9,6 +9,7 @@ module.exports = { }, { plugins: [ + 'babel-plugin-typescript-strip-namespaces', require.resolve( './scripts/babel-plugin-jest-replace-ts-export-assignment.js' ), diff --git a/package.json b/package.json index a1d279fd9e1a..12bab1f44dbf 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "ansi-styles": "^3.2.0", "babel-eslint": "^9.0.0", "babel-loader": "^8.0.5", + "babel-plugin-typescript-strip-namespaces": "^1.1.0", "camelcase": "^5.0.0", "chalk": "^2.0.1", "codecov": "^3.0.0", diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index 0bf7125abaf8..f809a46df037 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -98,9 +98,12 @@ type Watcher = { type WorkerInterface = {worker: typeof worker; getSha1: typeof getSha1}; -export type ModuleMap = HasteModuleMap; -export type SerializableModuleMap = HasteSerializableModuleMap; -export type FS = HasteFS; +// TODO: Ditch namespace when this module exports ESM +namespace HasteMap { + export type ModuleMap = HasteModuleMap; + export type SerializableModuleMap = HasteSerializableModuleMap; + export type FS = HasteFS; +} const CHANGE_INTERVAL = 30; const MAX_WAIT_TIME = 240000; @@ -368,7 +371,7 @@ class HasteMap extends EventEmitter { return hasteMap; } - readModuleMap(): ModuleMap { + readModuleMap(): HasteModuleMap { const data = this.read(); return new HasteModuleMap({ duplicates: data.duplicates, @@ -1079,4 +1082,4 @@ function copyMap(input: Map): Map { HasteMap.H = H; HasteMap.ModuleMap = HasteModuleMap; -module.exports = HasteMap; +export = HasteMap; diff --git a/packages/jest-resolve-dependencies/src/__tests__/dependency_resolver.test.ts b/packages/jest-resolve-dependencies/src/__tests__/dependency_resolver.test.ts index c2f50429462e..f6bdea51c0ba 100644 --- a/packages/jest-resolve-dependencies/src/__tests__/dependency_resolver.test.ts +++ b/packages/jest-resolve-dependencies/src/__tests__/dependency_resolver.test.ts @@ -33,10 +33,10 @@ beforeEach(() => { roots: ['./packages/jest-resolve-dependencies'], }); return Runtime.createContext(config, {maxWorkers, watchman: false}).then( - (hasteMap: any) => { + (runtimeContext: any) => { dependencyResolver = new DependencyResolver( - hasteMap.resolver, - hasteMap.hasteFS, + runtimeContext.resolver, + runtimeContext.hasteFS, buildSnapshotResolver(config), ); }, diff --git a/packages/jest-resolve/src/__tests__/resolve.test.ts b/packages/jest-resolve/src/__tests__/resolve.test.ts index 014407bc47c7..021ba08aeeac 100644 --- a/packages/jest-resolve/src/__tests__/resolve.test.ts +++ b/packages/jest-resolve/src/__tests__/resolve.test.ts @@ -8,7 +8,7 @@ import fs from 'fs'; import path from 'path'; -import HasteMap from 'jest-haste-map'; +import {ModuleMap} from 'jest-haste-map'; import Resolver from '../'; // @ts-ignore: js file import userResolver from '../__mocks__/userResolver'; @@ -16,9 +16,6 @@ import nodeModulesPaths from '../nodeModulesPaths'; import defaultResolver from '../defaultResolver'; import {ResolverConfig} from '../types'; -// @ts-ignore: types are wrong. not sure how... -const {ModuleMap} = HasteMap; - jest.mock('../__mocks__/userResolver'); beforeEach(() => { diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index 690a97c7da33..3d92b9f94cee 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -110,7 +110,6 @@ export default class ScriptTransformer { content: string, instrument: boolean, ): Config.Path { - // @ts-ignore: not properly exported (needs ESM) const baseCacheDir = HasteMap.getCacheFilePath( this._config.cacheDirectory, 'jest-transform-cache-' + this._config.name, diff --git a/yarn.lock b/yarn.lock index 02da8691c7e2..a16c547fc344 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2610,6 +2610,11 @@ babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== +babel-plugin-typescript-strip-namespaces@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-typescript-strip-namespaces/-/babel-plugin-typescript-strip-namespaces-1.1.0.tgz#7f8b022505bc742905801f9c41dd93f08f34ffc4" + integrity sha512-69kdF5HJoSIdgTVtHDor6XGJzVcxF5Hh92Rnh+rcitBZpj0pVU3Go0CrNJdPog6uoTcB90Ifk9O55FPZg0XN4w== + babel-polyfill@6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d" From 5aaf200ba357d3a200e13a1e9938bf1ac5662d23 Mon Sep 17 00:00:00 2001 From: Tim Seckinger Date: Fri, 22 Feb 2019 10:43:14 +0100 Subject: [PATCH 085/107] fix Node 6 build (#7955) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 12bab1f44dbf..7c9a0c6d15c0 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "ansi-styles": "^3.2.0", "babel-eslint": "^9.0.0", "babel-loader": "^8.0.5", - "babel-plugin-typescript-strip-namespaces": "^1.1.0", + "babel-plugin-typescript-strip-namespaces": "^1.1.1", "camelcase": "^5.0.0", "chalk": "^2.0.1", "codecov": "^3.0.0", diff --git a/yarn.lock b/yarn.lock index a16c547fc344..cfedeb1ee38a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2610,10 +2610,10 @@ babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== -babel-plugin-typescript-strip-namespaces@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/babel-plugin-typescript-strip-namespaces/-/babel-plugin-typescript-strip-namespaces-1.1.0.tgz#7f8b022505bc742905801f9c41dd93f08f34ffc4" - integrity sha512-69kdF5HJoSIdgTVtHDor6XGJzVcxF5Hh92Rnh+rcitBZpj0pVU3Go0CrNJdPog6uoTcB90Ifk9O55FPZg0XN4w== +babel-plugin-typescript-strip-namespaces@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-typescript-strip-namespaces/-/babel-plugin-typescript-strip-namespaces-1.1.1.tgz#160433b17e424b57cf72e3b4d8f08195ad28d7fd" + integrity sha512-dVB9caEANbEVwUylL8g3lsYU5JjaXE2KNIVLib3KVcGJF32QunxvQqP6kf+lzW/fyDed/zWD/e/hdyimyc/79Q== babel-polyfill@6.23.0: version "6.23.0" From 76bc0d01e2f84e9e7d585483ae2430f0a1a27b2c Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Fri, 22 Feb 2019 14:44:26 +0100 Subject: [PATCH 086/107] chore: add missing npmignore to jest-circus --- packages/jest-circus/.npmignore | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 packages/jest-circus/.npmignore diff --git a/packages/jest-circus/.npmignore b/packages/jest-circus/.npmignore new file mode 100644 index 000000000000..85e48fe7b0a4 --- /dev/null +++ b/packages/jest-circus/.npmignore @@ -0,0 +1,3 @@ +**/__mocks__/** +**/__tests__/** +src From c5fd56115a43726edd92e7442490bed9d15cef70 Mon Sep 17 00:00:00 2001 From: Mark Pedrotti Date: Fri, 22 Feb 2019 16:42:15 -0500 Subject: [PATCH 087/107] expect: Improve report when matcher fails, part 10 (#7960) --- CHANGELOG.md | 1 + .../__snapshots__/matchers.test.js.snap | 93 ++++++++++++------- .../expect/src/__tests__/matchers.test.js | 46 ++++++++- packages/expect/src/matchers.ts | 58 ++++++------ packages/jest-matcher-utils/src/index.ts | 22 +++++ 5 files changed, 155 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d6ca4619aa6..7da8428efb9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - `[expect]`: Improve report when matcher fails, part 7 ([#7866](https://github.com/facebook/jest/pull/7866)) - `[expect]`: Improve report when matcher fails, part 8 ([#7876](https://github.com/facebook/jest/pull/7876)) - `[expect]`: Improve report when matcher fails, part 9 ([#7940](https://github.com/facebook/jest/pull/7940)) +- `[expect]`: Improve report when matcher fails, part 10 ([#7960](https://github.com/facebook/jest/pull/7960)) - `[pretty-format]` Support `React.memo` ([#7891](https://github.com/facebook/jest/pull/7891)) - `[jest-config]` Print error information on preset normalization error ([#7935](https://github.com/facebook/jest/pull/7935)) diff --git a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap index ce7887e1b2e4..9a46e02d761a 100644 --- a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap @@ -2625,7 +2625,7 @@ Received: {\\"a\\": 1}" `; exports[`.toHaveLength {pass: false} expect("").toHaveLength(1) 1`] = ` -"expect(received).toHaveLength(length) +"expect(received).toHaveLength(expected) Expected length: 1 Received length: 0 @@ -2633,7 +2633,7 @@ Received string: \\"\\"" `; exports[`.toHaveLength {pass: false} expect("abc").toHaveLength(66) 1`] = ` -"expect(received).toHaveLength(length) +"expect(received).toHaveLength(expected) Expected length: 66 Received length: 3 @@ -2641,7 +2641,7 @@ Received string: \\"abc\\"" `; exports[`.toHaveLength {pass: false} expect(["a", "b"]).toHaveLength(99) 1`] = ` -"expect(received).toHaveLength(length) +"expect(received).toHaveLength(expected) Expected length: 99 Received length: 2 @@ -2649,7 +2649,7 @@ Received array: [\\"a\\", \\"b\\"]" `; exports[`.toHaveLength {pass: false} expect([]).toHaveLength(1) 1`] = ` -"expect(received).toHaveLength(length) +"expect(received).toHaveLength(expected) Expected length: 1 Received length: 0 @@ -2657,7 +2657,7 @@ Received array: []" `; exports[`.toHaveLength {pass: false} expect([1, 2]).toHaveLength(3) 1`] = ` -"expect(received).toHaveLength(length) +"expect(received).toHaveLength(expected) Expected length: 3 Received length: 2 @@ -2665,47 +2665,42 @@ Received array: [1, 2]" `; exports[`.toHaveLength {pass: true} expect("").toHaveLength(0) 1`] = ` -"expect(received).not.toHaveLength(length) +"expect(received).not.toHaveLength(expected) -Expected length: 0 -Received length: 0 -Received string: \\"\\"" +Expected length: not 0 +Received string: \\"\\"" `; exports[`.toHaveLength {pass: true} expect("abc").toHaveLength(3) 1`] = ` -"expect(received).not.toHaveLength(length) +"expect(received).not.toHaveLength(expected) -Expected length: 3 -Received length: 3 -Received string: \\"abc\\"" +Expected length: not 3 +Received string: \\"abc\\"" `; exports[`.toHaveLength {pass: true} expect(["a", "b"]).toHaveLength(2) 1`] = ` -"expect(received).not.toHaveLength(length) +"expect(received).not.toHaveLength(expected) -Expected length: 2 -Received length: 2 -Received array: [\\"a\\", \\"b\\"]" +Expected length: not 2 +Received array: [\\"a\\", \\"b\\"]" `; exports[`.toHaveLength {pass: true} expect([]).toHaveLength(0) 1`] = ` -"expect(received).not.toHaveLength(length) +"expect(received).not.toHaveLength(expected) -Expected length: 0 -Received length: 0 -Received array: []" +Expected length: not 0 +Received array: []" `; exports[`.toHaveLength {pass: true} expect([1, 2]).toHaveLength(2) 1`] = ` -"expect(received).not.toHaveLength(length) +"expect(received).not.toHaveLength(expected) -Expected length: 2 -Received length: 2 -Received array: [1, 2]" +Expected length: not 2 +Received array: [1, 2]" `; exports[`.toHaveLength error cases 1`] = ` -"expect(received).toHaveLength(expected) +"expect(received).toHaveLength(expected) Matcher error: received value must have a length property whose value must be a number @@ -2714,7 +2709,7 @@ Received has value: {\\"a\\": 9}" `; exports[`.toHaveLength error cases 2`] = ` -"expect(received).toHaveLength(expected) +"expect(received).toHaveLength(expected) Matcher error: received value must have a length property whose value must be a number @@ -2723,22 +2718,58 @@ Received has value: 0" `; exports[`.toHaveLength error cases 3`] = ` -"expect(received).toHaveLength(expected) +"expect(received).not.toHaveLength(expected) Matcher error: received value must have a length property whose value must be a number Received has value: undefined" `; -exports[`.toHaveLength matcher error expected length 1`] = ` -"expect(received).toHaveLength(expected) +exports[`.toHaveLength matcher error expected length not number 1`] = ` +"expect(received).not.toHaveLength(expected) -Matcher error: expected value must be a number +Matcher error: expected value must be a non-negative integer Expected has type: string Expected has value: \\"3\\"" `; +exports[`.toHaveLength matcher error expected length number Infinity 1`] = ` +"expect(received).rejects.toHaveLength(expected) + +Matcher error: expected value must be a non-negative integer + +Expected has type: number +Expected has value: Infinity" +`; + +exports[`.toHaveLength matcher error expected length number NaN 1`] = ` +"expect(received).rejects.not.toHaveLength(expected) + +Matcher error: expected value must be a non-negative integer + +Expected has type: number +Expected has value: NaN" +`; + +exports[`.toHaveLength matcher error expected length number float 1`] = ` +"expect(received).resolves.toHaveLength(expected) + +Matcher error: expected value must be a non-negative integer + +Expected has type: number +Expected has value: 0.5" +`; + +exports[`.toHaveLength matcher error expected length number negative integer 1`] = ` +"expect(received).resolves.not.toHaveLength(expected) + +Matcher error: expected value must be a non-negative integer + +Expected has type: number +Expected has value: -3" +`; + exports[`.toHaveProperty() {error} expect({"a": {"b": {}}}).toHaveProperty('1') 1`] = ` "expect(received).toHaveProperty(path) diff --git a/packages/expect/src/__tests__/matchers.test.js b/packages/expect/src/__tests__/matchers.test.js index c6be356dac0d..70ec53f66fa1 100644 --- a/packages/expect/src/__tests__/matchers.test.js +++ b/packages/expect/src/__tests__/matchers.test.js @@ -1171,14 +1171,50 @@ describe('.toHaveLength', () => { ).toThrowErrorMatchingSnapshot(); expect(() => jestExpect(0).toHaveLength(1)).toThrowErrorMatchingSnapshot(); expect(() => - jestExpect(undefined).toHaveLength(1), + jestExpect(undefined).not.toHaveLength(1), ).toThrowErrorMatchingSnapshot(); }); - test('matcher error expected length', () => { - expect(() => - jestExpect('abc').toHaveLength('3'), - ).toThrowErrorMatchingSnapshot(); + describe('matcher error expected length', () => { + test('not number', () => { + const expected = '3'; + const received = 'abc'; + expect(() => { + jestExpect(received).not.toHaveLength(expected); + }).toThrowErrorMatchingSnapshot(); + }); + + test('number Infinity', () => { + const expected = Infinity; + const received = Promise.reject('abc'); + return expect( + jestExpect(received).rejects.toHaveLength(expected), + ).rejects.toThrowErrorMatchingSnapshot(); + }); + + test('number NaN', () => { + const expected = NaN; + const received = Promise.reject('abc'); + return expect( + jestExpect(received).rejects.not.toHaveLength(expected), + ).rejects.toThrowErrorMatchingSnapshot(); + }); + + test('number float', () => { + const expected = 0.5; + const received = Promise.resolve('abc'); + return expect( + jestExpect(received).resolves.toHaveLength(expected), + ).rejects.toThrowErrorMatchingSnapshot(); + }); + + test('number negative integer', () => { + const expected = -3; + const received = Promise.resolve('abc'); + return expect( + jestExpect(received).resolves.not.toHaveLength(expected), + ).rejects.toThrowErrorMatchingSnapshot(); + }); }); }); diff --git a/packages/expect/src/matchers.ts b/packages/expect/src/matchers.ts index adb628dd4231..e76bd64c63c6 100644 --- a/packages/expect/src/matchers.ts +++ b/packages/expect/src/matchers.ts @@ -14,6 +14,7 @@ import { SUGGEST_TO_EQUAL, SUGGEST_TO_CONTAIN_EQUAL, diff, + ensureExpectedIsNonNegativeInteger, ensureNoExpected, ensureNumbers, getLabelPrinter, @@ -505,16 +506,20 @@ const matchers: MatchersObject = { return {actual: received, expected, message, name: 'toEqual', pass}; }, - toHaveLength(this: MatcherState, received: any, length: number) { + toHaveLength(this: MatcherState, received: any, expected: number) { + const isNot = this.isNot; + const options: MatcherHintOptions = { + isNot, + promise: this.promise, + }; + if ( typeof received !== 'string' && (!received || typeof received.length !== 'number') ) { throw new Error( matcherErrorMessage( - matcherHint('.toHaveLength', undefined, undefined, { - isNot: this.isNot, - }), + matcherHint('toHaveLength', undefined, undefined, options), `${RECEIVED_COLOR( 'received', )} value must have a length property whose value must be a number`, @@ -523,39 +528,34 @@ const matchers: MatchersObject = { ); } - if (typeof length !== 'number') { - throw new Error( - matcherErrorMessage( - matcherHint('.toHaveLength', undefined, undefined, { - isNot: this.isNot, - }), - `${EXPECTED_COLOR('expected')} value must be a number`, - printWithType('Expected', length, printExpected), - ), - ); - } + ensureExpectedIsNonNegativeInteger(expected, 'toHaveLength', options); + + const pass = received.length === expected; - const pass = received.length === length; const message = () => { - const stringExpected = 'Expected length'; - const stringReceivedLength = 'Received length'; - const stringReceivedValue = `Received ${getType(received)}`; + const labelExpected = 'Expected length'; + const labelReceivedLength = 'Received length'; + const labelReceivedValue = `Received ${getType(received)}`; const printLabel = getLabelPrinter( - stringExpected, - stringReceivedLength, - stringReceivedValue, + labelExpected, + labelReceivedLength, + labelReceivedValue, ); return ( - matcherHint('.toHaveLength', 'received', 'length', { - isNot: this.isNot, - }) + + matcherHint('toHaveLength', undefined, undefined, options) + '\n\n' + - `${printLabel(stringExpected)}${printExpected(length)}\n` + - `${printLabel(stringReceivedLength)}${printReceived( - received.length, + `${printLabel(labelExpected)}${isNot ? 'not ' : ''}${printExpected( + expected, )}\n` + - `${printLabel(stringReceivedValue)}${printReceived(received)}` + (isNot + ? '' + : `${printLabel(labelReceivedLength)}${printReceived( + received.length, + )}\n`) + + `${printLabel(labelReceivedValue)}${isNot ? ' ' : ''}${printReceived( + received, + )}` ); }; diff --git a/packages/jest-matcher-utils/src/index.ts b/packages/jest-matcher-utils/src/index.ts index 293c93e8d344..6207960d5af7 100644 --- a/packages/jest-matcher-utils/src/index.ts +++ b/packages/jest-matcher-utils/src/index.ts @@ -176,6 +176,28 @@ export const ensureNumbers = ( ensureExpectedIsNumber(expected, matcherName, options); }; +export const ensureExpectedIsNonNegativeInteger = ( + expected: unknown, + matcherName: string, + options?: MatcherHintOptions, +) => { + if ( + typeof expected !== 'number' || + !Number.isSafeInteger(expected) || + expected < 0 + ) { + // Prepend maybe not only for backward compatibility. + const matcherString = (options ? '' : '[.not]') + matcherName; + throw new Error( + matcherErrorMessage( + matcherHint(matcherString, undefined, undefined, options), + `${EXPECTED_COLOR('expected')} value must be a non-negative integer`, + printWithType('Expected', expected, printExpected), + ), + ); + } +}; + // Sometimes, e.g. when comparing two numbers, the output from jest-diff // does not contain more information than the `Expected:` / `Received:` already gives. // In those cases, we do not print a diff to make the output shorter and not redundant. From 50f1ef34d9041020464457f1f573c603c811a90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20M=C3=B8ller?= Date: Fri, 22 Feb 2019 22:45:23 +0100 Subject: [PATCH 088/107] fix(jest-changed-files): only return files from `getChangedFilesFromRoots` (#7961) --- CHANGELOG.md | 1 + e2e/__tests__/jestChangedFiles.test.js | 15 ++++++++++++--- packages/jest-changed-files/src/git.ts | 6 ++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7da8428efb9a..532347008eaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - `[jest-circus]` Fix bug with test.only ([#7888](https://github.com/facebook/jest/pull/7888)) - `[jest-transform]` Normalize config and remove unecessary checks, convert `TestUtils.js` to TypeScript ([#7801](https://github.com/facebook/jest/pull/7801)) - `[jest-worker]` Fix `jest-worker` when using pre-allocated jobs ([#7934](https://github.com/facebook/jest/pull/7934)) +- `[jest-changed-files]` Fix `getChangedFilesFromRoots` to not return parts of the commit messages as if they were files, when the commit messages contained multiple paragraphs ([#7961](https://github.com/facebook/jest/pull/7961)) ### Chore & Maintenance diff --git a/e2e/__tests__/jestChangedFiles.test.js b/e2e/__tests__/jestChangedFiles.test.js index d4efe1838964..9736299bb2ac 100644 --- a/e2e/__tests__/jestChangedFiles.test.js +++ b/e2e/__tests__/jestChangedFiles.test.js @@ -148,7 +148,11 @@ test('gets changed files for git', async () => { ).toEqual(['file1.txt', 'file2.txt', 'file3.txt']); run(`${GIT} add .`, DIR); - run(`${GIT} commit --no-gpg-sign -m "test"`, DIR); + + // Uses multiple `-m` to make the commit message have multiple + // paragraphs. This is done to ensure that `changedFiles` only + // returns files and not parts of commit messages. + run(`${GIT} commit --no-gpg-sign -m "test" -m "extra-line"`, DIR); ({changedFiles: files} = await getChangedFilesForRoots(roots, {})); expect(Array.from(files)).toEqual([]); @@ -266,8 +270,13 @@ test('gets changed files for hg', async () => { // skip this test and run it only locally. return; } + + // file1.txt is used to make a multi-line commit message + // with `hg commit -l file1.txt`. + // This is done to ensure that `changedFiles` only returns files + // and not parts of commit messages. writeFiles(DIR, { - 'file1.txt': 'file1', + 'file1.txt': 'file1\n\nextra-line', 'nested-dir/file2.txt': 'file2', 'nested-dir/second-nested-dir/file3.txt': 'file3', }); @@ -286,7 +295,7 @@ test('gets changed files for hg', async () => { ).toEqual(['file1.txt', 'file2.txt', 'file3.txt']); run(`${HG} add .`, DIR); - run(`${HG} commit -m "test"`, DIR); + run(`${HG} commit -l file1.txt`, DIR); ({changedFiles: files} = await getChangedFilesForRoots(roots, {})); expect(Array.from(files)).toEqual([]); diff --git a/packages/jest-changed-files/src/git.ts b/packages/jest-changed-files/src/git.ts index 27124ef57c33..463ad88647da 100644 --- a/packages/jest-changed-files/src/git.ts +++ b/packages/jest-changed-files/src/git.ts @@ -35,7 +35,9 @@ const adapter: SCMAdapter = { if (options && options.lastCommit) { return findChangedFilesUsingCommand( - ['show', '--name-only', '--pretty=%b', 'HEAD'].concat(includePaths), + ['show', '--name-only', '--pretty=format:', 'HEAD'].concat( + includePaths, + ), cwd, ); } else if (changedSince) { @@ -43,7 +45,7 @@ const adapter: SCMAdapter = { [ 'log', '--name-only', - '--pretty=%b', + '--pretty=format:', 'HEAD', `^${changedSince}`, ].concat(includePaths), From ce545e3b064405875dd913eadc26e5f39eeb142b Mon Sep 17 00:00:00 2001 From: doniyor2109 Date: Sat, 23 Feb 2019 02:56:44 +0500 Subject: [PATCH 089/107] TS migration `jest-circus` (#7916) * Migration to ts (part 1) * Migration to ts (part 2) * Migration to ts (part 3) * Minor tweaks * TS migration (part 4) * Wrong dts * dts for co * Added project references * Remove not ts module * Replace custom co dts with @types/co * No index file Co-Authored-By: doniyor2109 * Some tweaks * Some tweaks * Temp DiffOptions type * Remove extra eslint disable * Workaround for global vars * Resolves * Move @types/co to devDeps * Update packages/jest-circus/src/run.ts Co-Authored-By: doniyor2109 * Update packages/jest-circus/src/utils.ts Co-Authored-By: doniyor2109 * Tweaks * Remove extra types * Fix failing test Cannot run ts via node cli * TS migration part (4) - Added Global and Environment types - Workaround for RawMatcherFn type * Fix linter errors * Fix types for tests * Fix @jest/types cannot be found in test * Detailed comment for flowfix * Ignore ts errors for non migrated modules * `import =` is not supported by @babel/plugin-transform-typescript * Fix weired ts error Exported variable 'jestAdapter' has or is using name '$JestEnvironment' from external module "packages/jest-types/build/Environment" but cannot be named. https://github.com/Microsoft/TypeScript/issues/5711 * Fix linter errors * Remove extra eslint disables * Move expect types to @jest/types * tweaks * remove jest config change * fix test * Added reminder for replacing Each type * keep comments from old tests * Update CHANGELOG.md --- CHANGELOG.md | 1 + packages/jest-circus/package.json | 4 + ...estEventHandler.js => testEventHandler.ts} | 6 +- .../__mocks__/{testUtils.js => testUtils.ts} | 20 +- ...All.test.js.snap => afterAll.test.ts.snap} | 0 ...est.test.js.snap => baseTest.test.ts.snap} | 0 ...{hooks.test.js.snap => hooks.test.ts.snap} | 0 .../{afterAll.test.js => afterAll.test.ts} | 4 - .../{baseTest.test.js => baseTest.test.ts} | 4 - ...rror.test.js => circusItTestError.test.ts} | 22 +- ....test.js => circusItTodoTestError.test.ts} | 12 +- .../{hooks.test.js => hooks.test.ts} | 4 - ...{hooksError.test.js => hooksError.test.ts} | 11 +- .../src/{eventHandler.js => eventHandler.ts} | 18 +- ...ertErrors.js => formatNodeAssertErrors.ts} | 48 ++-- ...rrorHandlers.js => globalErrorHandlers.ts} | 6 +- packages/jest-circus/src/index.js | 151 ----------- packages/jest-circus/src/index.ts | 172 +++++++++++++ .../{jestAdapter.js => jestAdapter.ts} | 28 +- ...{jestAdapterInit.js => jestAdapterInit.ts} | 157 +++++------ .../{jestExpect.js => jestExpect.ts} | 12 +- packages/jest-circus/src/{run.js => run.ts} | 21 +- .../jest-circus/src/{state.js => state.ts} | 5 +- packages/jest-circus/src/types.ts | 243 ++++++++++++++++++ .../jest-circus/src/{utils.js => utils.ts} | 136 +++++----- packages/jest-circus/tsconfig.json | 8 + packages/jest-types/src/Environment.ts | 44 ++++ packages/jest-types/src/Global.ts | 75 ++++++ packages/jest-types/src/index.ts | 4 + yarn.lock | 5 + 30 files changed, 799 insertions(+), 422 deletions(-) rename packages/jest-circus/src/__mocks__/{testEventHandler.js => testEventHandler.ts} (94%) rename packages/jest-circus/src/__mocks__/{testUtils.js => testUtils.ts} (85%) rename packages/jest-circus/src/__tests__/__snapshots__/{afterAll.test.js.snap => afterAll.test.ts.snap} (100%) rename packages/jest-circus/src/__tests__/__snapshots__/{baseTest.test.js.snap => baseTest.test.ts.snap} (100%) rename packages/jest-circus/src/__tests__/__snapshots__/{hooks.test.js.snap => hooks.test.ts.snap} (100%) rename packages/jest-circus/src/__tests__/{afterAll.test.js => afterAll.test.ts} (97%) rename packages/jest-circus/src/__tests__/{baseTest.test.js => baseTest.test.ts} (95%) rename packages/jest-circus/src/__tests__/{circusItTestError.test.js => circusItTestError.test.ts} (82%) rename packages/jest-circus/src/__tests__/{circusItTodoTestError.test.js => circusItTodoTestError.test.ts} (84%) rename packages/jest-circus/src/__tests__/{hooks.test.js => hooks.test.ts} (98%) rename packages/jest-circus/src/__tests__/{hooksError.test.js => hooksError.test.ts} (81%) rename packages/jest-circus/src/{eventHandler.js => eventHandler.ts} (92%) rename packages/jest-circus/src/{formatNodeAssertErrors.js => formatNodeAssertErrors.ts} (81%) rename packages/jest-circus/src/{globalErrorHandlers.js => globalErrorHandlers.ts} (94%) delete mode 100644 packages/jest-circus/src/index.js create mode 100644 packages/jest-circus/src/index.ts rename packages/jest-circus/src/legacy-code-todo-rewrite/{jestAdapter.js => jestAdapter.ts} (81%) rename packages/jest-circus/src/legacy-code-todo-rewrite/{jestAdapterInit.js => jestAdapterInit.ts} (61%) rename packages/jest-circus/src/legacy-code-todo-rewrite/{jestExpect.js => jestExpect.ts} (73%) rename packages/jest-circus/src/{run.js => run.ts} (92%) rename packages/jest-circus/src/{state.js => state.ts} (91%) create mode 100644 packages/jest-circus/src/types.ts rename packages/jest-circus/src/{utils.js => utils.ts} (77%) create mode 100644 packages/jest-circus/tsconfig.json create mode 100644 packages/jest-types/src/Environment.ts create mode 100644 packages/jest-types/src/Global.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 532347008eaa..aa078a389e3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ - `[docs]` Add missing import to docs ([#7928](https://github.com/facebook/jest/pull/7928)) - `[jest-resolve-dependencies]`: Migrate to TypeScript ([#7922](https://github.com/facebook/jest/pull/7922)) - `[expect]`: Migrate to TypeScript ([#7919](https://github.com/facebook/jest/pull/7919)) +- `[jest-circus]`: Migrate to TypeScript ([#7916](https://github.com/facebook/jest/pull/7916)) ### Performance diff --git a/packages/jest-circus/package.json b/packages/jest-circus/package.json index 7ce23e74d4a0..1986ebe211cf 100644 --- a/packages/jest-circus/package.json +++ b/packages/jest-circus/package.json @@ -8,8 +8,11 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { "@babel/traverse": "^7.1.0", + "@jest/types": "^24.1.0", + "@types/node": "*", "chalk": "^2.0.1", "co": "^4.6.0", "expect": "^24.1.0", @@ -25,6 +28,7 @@ }, "devDependencies": { "@types/babel__traverse": "^7.0.4", + "@types/co": "^4.6.0", "@types/stack-utils": "^1.0.1", "execa": "^1.0.0", "jest-diff": "^24.0.0", diff --git a/packages/jest-circus/src/__mocks__/testEventHandler.js b/packages/jest-circus/src/__mocks__/testEventHandler.ts similarity index 94% rename from packages/jest-circus/src/__mocks__/testEventHandler.js rename to packages/jest-circus/src/__mocks__/testEventHandler.ts index 7455a4fcc42a..1bc877b075f9 100644 --- a/packages/jest-circus/src/__mocks__/testEventHandler.js +++ b/packages/jest-circus/src/__mocks__/testEventHandler.ts @@ -4,13 +4,9 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow scrict-local */ -'use strict'; - -import type {EventHandler} from 'types/Circus'; +import {EventHandler} from '../types'; const testEventHandler: EventHandler = (event, state) => { switch (event.name) { diff --git a/packages/jest-circus/src/__mocks__/testUtils.js b/packages/jest-circus/src/__mocks__/testUtils.ts similarity index 85% rename from packages/jest-circus/src/__mocks__/testUtils.js rename to packages/jest-circus/src/__mocks__/testUtils.ts index fd71d097c5f5..6a53bb586938 100644 --- a/packages/jest-circus/src/__mocks__/testUtils.js +++ b/packages/jest-circus/src/__mocks__/testUtils.ts @@ -4,20 +4,17 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict-local */ -'use strict'; -// $FlowFixMe - execa is untyped -import {sync as spawnSync} from 'execa'; import fs from 'fs'; import os from 'os'; import path from 'path'; import crypto from 'crypto'; +import {sync as spawnSync, ExecaReturns} from 'execa'; +// @ts-ignore import {skipSuiteOnWindows} from '../../../../scripts/ConditionalTest'; -const CIRCUS_PATH = require.resolve('../../build/index'); +const CIRCUS_PATH = require.resolve('../../build'); const CIRCUS_RUN_PATH = require.resolve('../../build/run'); const CIRCUS_STATE_PATH = require.resolve('../../build/state'); const TEST_EVENT_HANDLER_PATH = require.resolve('./testEventHandler'); @@ -25,6 +22,11 @@ const BABEL_REGISTER_PATH = require.resolve('@babel/register'); skipSuiteOnWindows(); +interface Result extends ExecaReturns { + status: number; + error: string; +} + export const runTest = (source: string) => { const filename = crypto .createHash('md5') @@ -33,7 +35,7 @@ export const runTest = (source: string) => { const tmpFilename = path.join(os.tmpdir(), filename); const content = ` - require('${BABEL_REGISTER_PATH}'); + require('${BABEL_REGISTER_PATH}')({extensions: [".js", ".ts"]}); const circus = require('${CIRCUS_PATH}'); global.test = circus.test; global.describe = circus.describe; @@ -54,7 +56,9 @@ export const runTest = (source: string) => { `; fs.writeFileSync(tmpFilename, content); - const result = spawnSync('node', [tmpFilename], {cwd: process.cwd()}); + const result = spawnSync('node', [tmpFilename], { + cwd: process.cwd(), + }) as Result; // For compat with cross-spawn result.status = result.code; diff --git a/packages/jest-circus/src/__tests__/__snapshots__/afterAll.test.js.snap b/packages/jest-circus/src/__tests__/__snapshots__/afterAll.test.ts.snap similarity index 100% rename from packages/jest-circus/src/__tests__/__snapshots__/afterAll.test.js.snap rename to packages/jest-circus/src/__tests__/__snapshots__/afterAll.test.ts.snap diff --git a/packages/jest-circus/src/__tests__/__snapshots__/baseTest.test.js.snap b/packages/jest-circus/src/__tests__/__snapshots__/baseTest.test.ts.snap similarity index 100% rename from packages/jest-circus/src/__tests__/__snapshots__/baseTest.test.js.snap rename to packages/jest-circus/src/__tests__/__snapshots__/baseTest.test.ts.snap diff --git a/packages/jest-circus/src/__tests__/__snapshots__/hooks.test.js.snap b/packages/jest-circus/src/__tests__/__snapshots__/hooks.test.ts.snap similarity index 100% rename from packages/jest-circus/src/__tests__/__snapshots__/hooks.test.js.snap rename to packages/jest-circus/src/__tests__/__snapshots__/hooks.test.ts.snap diff --git a/packages/jest-circus/src/__tests__/afterAll.test.js b/packages/jest-circus/src/__tests__/afterAll.test.ts similarity index 97% rename from packages/jest-circus/src/__tests__/afterAll.test.js rename to packages/jest-circus/src/__tests__/afterAll.test.ts index a4eacd2e6ec1..386e6263e3ed 100644 --- a/packages/jest-circus/src/__tests__/afterAll.test.js +++ b/packages/jest-circus/src/__tests__/afterAll.test.ts @@ -4,12 +4,8 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict-local */ -'use strict'; - import {runTest} from '../__mocks__/testUtils'; test('tests are not marked done until their parent afterAll runs', () => { diff --git a/packages/jest-circus/src/__tests__/baseTest.test.js b/packages/jest-circus/src/__tests__/baseTest.test.ts similarity index 95% rename from packages/jest-circus/src/__tests__/baseTest.test.js rename to packages/jest-circus/src/__tests__/baseTest.test.ts index f8bbb457e84c..66d743f5c434 100644 --- a/packages/jest-circus/src/__tests__/baseTest.test.js +++ b/packages/jest-circus/src/__tests__/baseTest.test.ts @@ -4,12 +4,8 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict-local */ -'use strict'; - import {runTest} from '../__mocks__/testUtils'; test('simple test', () => { diff --git a/packages/jest-circus/src/__tests__/circusItTestError.test.js b/packages/jest-circus/src/__tests__/circusItTestError.test.ts similarity index 82% rename from packages/jest-circus/src/__tests__/circusItTestError.test.js rename to packages/jest-circus/src/__tests__/circusItTestError.test.ts index 4269b9c1bee3..05a4035decfb 100644 --- a/packages/jest-circus/src/__tests__/circusItTestError.test.js +++ b/packages/jest-circus/src/__tests__/circusItTestError.test.ts @@ -3,20 +3,18 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow strict-local */ -'use strict'; +import {Global} from '@jest/types'; -let circusIt; -let circusTest; +let circusIt: Global.It; +let circusTest: Global.It; // using jest-jasmine2's 'it' to test jest-circus's 'it'. Had to differentiate // the two with this alias. const aliasCircusIt = () => { - const {it, test} = require('../index.js'); + const {it, test} = require('../'); circusIt = it; circusTest = test; }; @@ -35,7 +33,7 @@ describe('test/it error throwing', () => { }); it(`it throws error with missing callback function`, () => { expect(() => { - // $FlowFixMe: Easy, we're testing runtime errors here + // @ts-ignore: Easy, we're testing runtime errors here circusIt('test2'); }).toThrowError( 'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.', @@ -43,13 +41,13 @@ describe('test/it error throwing', () => { }); it(`it throws an error when first argument isn't a string`, () => { expect(() => { - // $FlowFixMe: Easy, we're testing runtime errors here + // @ts-ignore: Easy, we're testing runtime errors here circusIt(() => {}); }).toThrowError('Invalid first argument, () => {}. It must be a string.'); }); it('it throws an error when callback function is not a function', () => { expect(() => { - // $FlowFixMe: Easy, we're testing runtime errors here + // @ts-ignore: Easy, we're testing runtime errors here circusIt('test4', 'test4b'); }).toThrowError( 'Invalid second argument, test4b. It must be a callback function.', @@ -62,7 +60,7 @@ describe('test/it error throwing', () => { }); it(`test throws error with missing callback function`, () => { expect(() => { - // $FlowFixMe: Easy, we're testing runtime errors here + // @ts-ignore: Easy, we're testing runtime errors here circusTest('test6'); }).toThrowError( 'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.', @@ -70,13 +68,13 @@ describe('test/it error throwing', () => { }); it(`test throws an error when first argument isn't a string`, () => { expect(() => { - // $FlowFixMe: Easy, we're testing runtime errors here + // @ts-ignore: Easy, we're testing runtime errors here circusTest(() => {}); }).toThrowError('Invalid first argument, () => {}. It must be a string.'); }); it('test throws an error when callback function is not a function', () => { expect(() => { - // $FlowFixMe: Easy, we're testing runtime errors here + // @ts-ignore: Easy, we're testing runtime errors here circusTest('test8', 'test8b'); }).toThrowError( 'Invalid second argument, test8b. It must be a callback function.', diff --git a/packages/jest-circus/src/__tests__/circusItTodoTestError.test.js b/packages/jest-circus/src/__tests__/circusItTodoTestError.test.ts similarity index 84% rename from packages/jest-circus/src/__tests__/circusItTodoTestError.test.js rename to packages/jest-circus/src/__tests__/circusItTodoTestError.test.ts index d7b017f6cc45..dfa31e2f9990 100644 --- a/packages/jest-circus/src/__tests__/circusItTodoTestError.test.js +++ b/packages/jest-circus/src/__tests__/circusItTodoTestError.test.ts @@ -3,19 +3,17 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow strict-local */ -'use strict'; +import {Global} from '@jest/types'; -let circusIt; +let circusIt: Global.It; // using jest-jasmine2's 'it' to test jest-circus's 'it'. Had to differentiate // the two with this alias. const aliasCircusIt = () => { - const {it} = require('../index.js'); + const {it} = require('../'); circusIt = it; }; @@ -24,7 +22,7 @@ aliasCircusIt(); describe('test/it.todo error throwing', () => { it('todo throws error when given no arguments', () => { expect(() => { - // $FlowFixMe: Testing runitme errors here + // @ts-ignore: Testing runtime errors here circusIt.todo(); }).toThrowError('Todo must be called with only a description.'); }); @@ -35,7 +33,7 @@ describe('test/it.todo error throwing', () => { }); it('todo throws error when given none string description', () => { expect(() => { - // $FlowFixMe: Testing runitme errors here + // @ts-ignore: Testing runtime errors here circusIt.todo(() => {}); }).toThrowError('Todo must be called with only a description.'); }); diff --git a/packages/jest-circus/src/__tests__/hooks.test.js b/packages/jest-circus/src/__tests__/hooks.test.ts similarity index 98% rename from packages/jest-circus/src/__tests__/hooks.test.js rename to packages/jest-circus/src/__tests__/hooks.test.ts index 52ce749949be..02b54ae8e515 100644 --- a/packages/jest-circus/src/__tests__/hooks.test.js +++ b/packages/jest-circus/src/__tests__/hooks.test.ts @@ -4,12 +4,8 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow strict-local */ -'use strict'; - import {runTest} from '../__mocks__/testUtils'; test('beforeEach is executed before each test in current/child describe blocks', () => { diff --git a/packages/jest-circus/src/__tests__/hooksError.test.js b/packages/jest-circus/src/__tests__/hooksError.test.ts similarity index 81% rename from packages/jest-circus/src/__tests__/hooksError.test.js rename to packages/jest-circus/src/__tests__/hooksError.test.ts index dcaac27a00be..5b39df11a43b 100644 --- a/packages/jest-circus/src/__tests__/hooksError.test.js +++ b/packages/jest-circus/src/__tests__/hooksError.test.ts @@ -3,17 +3,14 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -'use strict'; - -const circus = require('../index.js'); +import circus from '../'; +import {HookType} from '../types'; -describe.each([['beforeEach'], ['beforeAll'], ['afterEach'], ['afterAll']])( +describe.each(['beforeEach', 'beforeAll', 'afterEach', 'afterAll'])( '%s hooks error throwing', - fn => { + (fn: HookType) => { test.each([ ['String'], [1], diff --git a/packages/jest-circus/src/eventHandler.js b/packages/jest-circus/src/eventHandler.ts similarity index 92% rename from packages/jest-circus/src/eventHandler.js rename to packages/jest-circus/src/eventHandler.ts index 663249c689a8..a29b69828d7b 100644 --- a/packages/jest-circus/src/eventHandler.js +++ b/packages/jest-circus/src/eventHandler.ts @@ -3,11 +3,9 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow strict-local */ -import type {EventHandler, Exception} from 'types/Circus'; +import {EventHandler, TEST_TIMEOUT_SYMBOL} from './types'; import { addErrorToEachTestUnderDescribe, @@ -22,9 +20,6 @@ import { restoreGlobalErrorHandlers, } 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 eventHandler: EventHandler = (event, state): void => { switch (event.name) { case 'include_test_location_in_result': { @@ -112,14 +107,14 @@ const eventHandler: EventHandler = (event, state): void => { if (type === 'beforeAll') { invariant(describeBlock, 'always present for `*All` hooks'); - addErrorToEachTestUnderDescribe(describeBlock, error, asyncError); + addErrorToEachTestUnderDescribe(describeBlock!, error, asyncError); } else if (type === 'afterAll') { // Attaching `afterAll` errors to each test makes execution flow // too complicated, so we'll consider them to be global. state.unhandledErrors.push([error, asyncError]); } else { invariant(test, 'always present for `*Each` hooks'); - test.errors.push([error, asyncError]); + test!.errors.push([error, asyncError]); } break; } @@ -152,8 +147,7 @@ const eventHandler: EventHandler = (event, state): void => { break; } case 'test_retry': { - const errors: Array<[?Exception, Exception]> = []; - event.test.errors = errors; + event.test.errors = []; break; } case 'run_start': { @@ -185,8 +179,8 @@ const eventHandler: EventHandler = (event, state): void => { invariant(state.originalGlobalErrorHandlers); invariant(state.parentProcess); restoreGlobalErrorHandlers( - state.parentProcess, - state.originalGlobalErrorHandlers, + state.parentProcess!, + state.originalGlobalErrorHandlers!, ); break; } diff --git a/packages/jest-circus/src/formatNodeAssertErrors.js b/packages/jest-circus/src/formatNodeAssertErrors.ts similarity index 81% rename from packages/jest-circus/src/formatNodeAssertErrors.js rename to packages/jest-circus/src/formatNodeAssertErrors.ts index ef84986451b6..8310f8fac781 100644 --- a/packages/jest-circus/src/formatNodeAssertErrors.js +++ b/packages/jest-circus/src/formatNodeAssertErrors.ts @@ -3,38 +3,29 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow strict-local */ -// $FlowFixMe: Converted to TS. It's also not exported, but should be imported from `matcher-utils` -import type {DiffOptions} from 'jest-diff'; -import type {Event, State} from 'types/Circus'; - -// $FlowFixMe: Converted to TS +import {AssertionError} from 'assert'; import {diff, printExpected, printReceived} from 'jest-matcher-utils'; import chalk from 'chalk'; -// $FlowFixMe: Converted to TS import prettyFormat from 'pretty-format'; +import {Event, State, TestError} from './types'; + +// TODO replace with import {DiffOptions} from 'jest-matcher-utils'; +type DiffOptions = Parameters[2]; + +interface AssertionErrorWithStack extends AssertionError { + stack: string; +} -type AssertionError = {| - actual: ?string, - expected: ?string, - generatedMessage: boolean, - message: string, - name: string, - operator: ?string, - stack: string, -|}; - -const assertOperatorsMap = { +const assertOperatorsMap: {[key: string]: string} = { '!=': 'notEqual', '!==': 'notStrictEqual', '==': 'equal', '===': 'strictEqual', }; -const humanReadableOperators = { +const humanReadableOperators: {[key: string]: string} = { deepEqual: 'to deeply equal', deepStrictEqual: 'to deeply and strictly equal', equal: 'to be equal', @@ -48,7 +39,7 @@ const humanReadableOperators = { const formatNodeAssertErrors = (event: Event, state: State) => { switch (event.name) { case 'test_done': { - event.test.errors = event.test.errors.map(errors => { + event.test.errors = event.test.errors.map((errors: TestError) => { let error; if (Array.isArray(errors)) { const [originalError, asyncError] = errors; @@ -75,7 +66,7 @@ const formatNodeAssertErrors = (event: Event, state: State) => { } }; -const getOperatorName = (operator: ?string, stack: string) => { +const getOperatorName = (operator: string | undefined, stack: string) => { if (typeof operator === 'string') { return assertOperatorsMap[operator] || operator; } @@ -88,9 +79,8 @@ const getOperatorName = (operator: ?string, stack: string) => { return ''; }; -const operatorMessage = (operator: ?string) => { +const operatorMessage = (operator: string | undefined) => { const niceOperatorName = getOperatorName(operator, ''); - // $FlowFixMe: we default to the operator itself, so holes in the map doesn't matter const humanReadableOperator = humanReadableOperators[niceOperatorName]; return typeof operator === 'string' @@ -104,7 +94,10 @@ const assertThrowingMatcherHint = (operatorName: string) => chalk.red('function') + chalk.dim(')'); -const assertMatcherHint = (operator: ?string, operatorName: string) => { +const assertMatcherHint = ( + operator: string | undefined | null, + operatorName: string, +) => { let message = chalk.dim('assert') + chalk.dim('.' + operatorName + '(') + @@ -125,7 +118,10 @@ const assertMatcherHint = (operator: ?string, operatorName: string) => { return message; }; -function assertionErrorMessage(error: AssertionError, options: DiffOptions) { +function assertionErrorMessage( + error: AssertionErrorWithStack, + options: DiffOptions, +) { const {expected, actual, generatedMessage, message, operator, stack} = error; const diffString = diff(expected, actual, options); const hasCustomMessage = !generatedMessage; diff --git a/packages/jest-circus/src/globalErrorHandlers.js b/packages/jest-circus/src/globalErrorHandlers.ts similarity index 94% rename from packages/jest-circus/src/globalErrorHandlers.js rename to packages/jest-circus/src/globalErrorHandlers.ts index e9ca53b2780d..5acd9ffac9a9 100644 --- a/packages/jest-circus/src/globalErrorHandlers.js +++ b/packages/jest-circus/src/globalErrorHandlers.ts @@ -3,12 +3,12 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow strict-local */ import {dispatch} from './state'; -import type {GlobalErrorHandlers} from 'types/Circus'; +import {GlobalErrorHandlers} from './types'; + +type Process = NodeJS.Process; const uncaught = (error: Error) => { dispatch({error, name: 'error'}); diff --git a/packages/jest-circus/src/index.js b/packages/jest-circus/src/index.js deleted file mode 100644 index ca770f8f07ca..000000000000 --- a/packages/jest-circus/src/index.js +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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 strict-local - */ - -import type { - BlockFn, - HookFn, - HookType, - TestFn, - BlockMode, - BlockName, - TestName, - TestMode, -} from 'types/Circus'; -import {bind as bindEach} from 'jest-each'; -// $FlowFixMe: Converted to TS -import {ErrorWithStack} from 'jest-util'; -import {dispatch} from './state'; - -type THook = (fn: HookFn, timeout?: number) => void; - -const describe = (blockName: BlockName, blockFn: BlockFn) => - _dispatchDescribe(blockFn, blockName, describe); -describe.only = (blockName: BlockName, blockFn: BlockFn) => - _dispatchDescribe(blockFn, blockName, describe.only, 'only'); -describe.skip = (blockName: BlockName, blockFn: BlockFn) => - _dispatchDescribe(blockFn, blockName, describe.skip, 'skip'); - -const _dispatchDescribe = ( - blockFn, - blockName, - describeFn, - mode?: BlockMode, -) => { - const asyncError = new ErrorWithStack(undefined, describeFn); - if (blockFn === undefined) { - asyncError.message = `Missing second argument. It must be a callback function.`; - throw asyncError; - } - if (typeof blockFn !== 'function') { - asyncError.message = `Invalid second argument, ${blockFn}. It must be a callback function.`; - throw asyncError; - } - dispatch({ - asyncError, - blockName, - mode, - name: 'start_describe_definition', - }); - blockFn(); - dispatch({blockName, mode, name: 'finish_describe_definition'}); -}; - -const _addHook = (fn: HookFn, hookType: HookType, hookFn, timeout: ?number) => { - const asyncError = new ErrorWithStack(undefined, hookFn); - - if (typeof fn !== 'function') { - asyncError.message = - 'Invalid first argument. It must be a callback function.'; - - throw asyncError; - } - - dispatch({asyncError, fn, hookType, name: 'add_hook', timeout}); -}; - -// Hooks have to pass themselves to the HOF in order for us to trim stack traces. -const beforeEach: THook = (fn, timeout) => - _addHook(fn, 'beforeEach', beforeEach, timeout); -const beforeAll: THook = (fn, timeout) => - _addHook(fn, 'beforeAll', beforeAll, timeout); -const afterEach: THook = (fn, timeout) => - _addHook(fn, 'afterEach', afterEach, timeout); -const afterAll: THook = (fn, timeout) => - _addHook(fn, 'afterAll', afterAll, timeout); - -const test = (testName: TestName, fn: TestFn, timeout?: number) => - _addTest(testName, undefined, fn, test, timeout); -const it = test; -test.skip = (testName: TestName, fn?: TestFn, timeout?: number) => - _addTest(testName, 'skip', fn, test.skip, timeout); -test.only = (testName: TestName, fn: TestFn, timeout?: number) => - _addTest(testName, 'only', fn, test.only, timeout); -test.todo = (testName: TestName, ...rest: Array) => { - if (rest.length > 0 || typeof testName !== 'string') { - throw new ErrorWithStack( - 'Todo must be called with only a description.', - test.todo, - ); - } - return _addTest(testName, 'todo', () => {}, test.todo); -}; - -const _addTest = ( - testName: TestName, - mode: TestMode, - fn?: TestFn, - testFn, - timeout: ?number, -) => { - const asyncError = new ErrorWithStack(undefined, testFn); - - if (typeof testName !== 'string') { - asyncError.message = `Invalid first argument, ${testName}. It must be a string.`; - - throw asyncError; - } - if (fn === undefined) { - asyncError.message = - 'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.'; - - throw asyncError; - } - if (typeof fn !== 'function') { - asyncError.message = `Invalid second argument, ${fn}. It must be a callback function.`; - - throw asyncError; - } - - return dispatch({ - asyncError, - fn, - mode, - name: 'add_test', - testName, - timeout, - }); -}; - -test.each = bindEach(test); -test.only.each = bindEach(test.only); -test.skip.each = bindEach(test.skip); - -describe.each = bindEach(describe, false); -describe.only.each = bindEach(describe.only, false); -describe.skip.each = bindEach(describe.skip, false); - -module.exports = { - afterAll, - afterEach, - beforeAll, - beforeEach, - describe, - it, - test, -}; diff --git a/packages/jest-circus/src/index.ts b/packages/jest-circus/src/index.ts new file mode 100644 index 000000000000..d4ed333084b7 --- /dev/null +++ b/packages/jest-circus/src/index.ts @@ -0,0 +1,172 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ +// @ts-ignore TODO Remove ignore when jest-each is migrated to ts +import {bind as bindEach} from 'jest-each'; +import {ErrorWithStack} from 'jest-util'; +import {Global} from '@jest/types'; +import { + BlockFn, + HookFn, + HookType, + TestFn, + BlockMode, + BlockName, + TestName, + TestMode, +} from './types'; +import {dispatch} from './state'; + +type THook = (fn: HookFn, timeout?: number) => void; +type DescribeFn = (blockName: BlockName, blockFn: BlockFn) => void; + +const describe = (() => { + const describe = (blockName: BlockName, blockFn: BlockFn) => + _dispatchDescribe(blockFn, blockName, describe); + const only = (blockName: BlockName, blockFn: BlockFn) => + _dispatchDescribe(blockFn, blockName, only, 'only'); + const skip = (blockName: BlockName, blockFn: BlockFn) => + _dispatchDescribe(blockFn, blockName, skip, 'skip'); + + describe.each = bindEach(describe, false); + + only.each = bindEach(only, false); + skip.each = bindEach(skip, false); + + describe.only = only; + describe.skip = skip; + + return describe; +})(); + +const _dispatchDescribe = ( + blockFn: BlockFn, + blockName: BlockName, + describeFn: DescribeFn, + mode?: BlockMode, +) => { + const asyncError = new ErrorWithStack(undefined, describeFn); + if (blockFn === undefined) { + asyncError.message = `Missing second argument. It must be a callback function.`; + throw asyncError; + } + if (typeof blockFn !== 'function') { + asyncError.message = `Invalid second argument, ${blockFn}. It must be a callback function.`; + throw asyncError; + } + dispatch({ + asyncError, + blockName, + mode, + name: 'start_describe_definition', + }); + blockFn(); + dispatch({blockName, mode, name: 'finish_describe_definition'}); +}; + +const _addHook = ( + fn: HookFn, + hookType: HookType, + hookFn: THook, + timeout?: number, +) => { + const asyncError = new ErrorWithStack(undefined, hookFn); + + if (typeof fn !== 'function') { + asyncError.message = + 'Invalid first argument. It must be a callback function.'; + + throw asyncError; + } + + dispatch({asyncError, fn, hookType, name: 'add_hook', timeout}); +}; + +// Hooks have to pass themselves to the HOF in order for us to trim stack traces. +const beforeEach: THook = (fn, timeout) => + _addHook(fn, 'beforeEach', beforeEach, timeout); +const beforeAll: THook = (fn, timeout) => + _addHook(fn, 'beforeAll', beforeAll, timeout); +const afterEach: THook = (fn, timeout) => + _addHook(fn, 'afterEach', afterEach, timeout); +const afterAll: THook = (fn, timeout) => + _addHook(fn, 'afterAll', afterAll, timeout); + +const test: Global.It = (() => { + const test = (testName: TestName, fn: TestFn, timeout?: number): void => + _addTest(testName, undefined, fn, test, timeout); + const skip = (testName: TestName, fn?: TestFn, timeout?: number): void => + _addTest(testName, 'skip', fn, skip, timeout); + const only = (testName: TestName, fn: TestFn, timeout?: number): void => + _addTest(testName, 'only', fn, test.only, timeout); + + test.todo = (testName: TestName, ...rest: Array): void => { + if (rest.length > 0 || typeof testName !== 'string') { + throw new ErrorWithStack( + 'Todo must be called with only a description.', + test.todo, + ); + } + return _addTest(testName, 'todo', () => {}, test.todo); + }; + + const _addTest = ( + testName: TestName, + mode: TestMode, + fn: TestFn | undefined, + testFn: (testName: TestName, fn: TestFn, timeout?: number) => void, + timeout?: number, + ) => { + const asyncError = new ErrorWithStack(undefined, testFn); + + if (typeof testName !== 'string') { + asyncError.message = `Invalid first argument, ${testName}. It must be a string.`; + + throw asyncError; + } + if (fn === undefined) { + asyncError.message = + 'Missing second argument. It must be a callback function. Perhaps you want to use `test.todo` for a test placeholder.'; + + throw asyncError; + } + if (typeof fn !== 'function') { + asyncError.message = `Invalid second argument, ${fn}. It must be a callback function.`; + + throw asyncError; + } + + return dispatch({ + asyncError, + fn, + mode, + name: 'add_test', + testName, + timeout, + }); + }; + + test.each = bindEach(test); + only.each = bindEach(only); + skip.each = bindEach(skip); + + test.only = only; + test.skip = skip; + + return test; +})(); + +const it: Global.It = test; + +export = { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + it, + test, +}; diff --git a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.js b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts similarity index 81% rename from packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.js rename to packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts index 8cabcb8a9f76..84f32c39e2f4 100644 --- a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.js +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts @@ -3,25 +3,23 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow strict-local */ -import type {Environment} from 'types/Environment'; -import type {GlobalConfig, ProjectConfig} from 'types/Config'; -import type {TestResult} from 'types/TestResult'; -import type Runtime from 'jest-runtime'; +import path from 'path'; +import {Config, TestResult, Environment} from '@jest/types'; +// @ts-ignore TODO Remove ignore when jest-runtime is migrated to ts +import Runtime from 'jest-runtime'; // eslint-disable-line import/no-extraneous-dependencies +import {SnapshotState} from 'jest-snapshot'; const FRAMEWORK_INITIALIZER = require.resolve('./jestAdapterInit'); -import path from 'path'; const jestAdapter = async ( - globalConfig: GlobalConfig, - config: ProjectConfig, - environment: Environment, + globalConfig: Config.GlobalConfig, + config: Config.ProjectConfig, + environment: Environment.$JestEnvironment, runtime: Runtime, testPath: string, -): Promise => { +): Promise => { const { initialize, runAndTransformResultsToJestFormat, @@ -84,7 +82,11 @@ const jestAdapter = async ( return _addSnapshotData(results, snapshotState); }; -const _addSnapshotData = (results: TestResult, snapshotState) => { +const _addSnapshotData = ( + results: TestResult.TestResult, + // TODO: make just snapshotState: SnapshotState when `jest-snapshot` is ESM + snapshotState: typeof SnapshotState.prototype, +) => { results.testResults.forEach(({fullName, status}) => { if (status === 'pending' || status === 'failed') { // if test is skipped or failed, we don't want to mark @@ -111,4 +113,4 @@ const _addSnapshotData = (results: TestResult, snapshotState) => { return results; }; -module.exports = jestAdapter; +export = jestAdapter; diff --git a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.js b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts similarity index 61% rename from packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.js rename to packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts index 16473b066aab..577a5e94431c 100644 --- a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.js +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts @@ -3,13 +3,9 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {AssertionResult, TestResult, Status} from 'types/TestResult'; -import type {GlobalConfig, Path, ProjectConfig} from 'types/Config'; -import type {Event, RunResult, TestEntry} from 'types/Circus'; +import {Config, TestResult} from '@jest/types'; import {extractExpectedAssertionsErrors, getState, setState} from 'expect'; import {formatExecError, formatResultsErrors} from 'jest-message-util'; @@ -22,8 +18,10 @@ import throat from 'throat'; import {addEventHandler, dispatch, ROOT_DESCRIBE_BLOCK_NAME} from '../state'; import {getTestID} from '../utils'; import run from '../run'; -// eslint-disable-next-line import/default -import globals from '../index'; +import globals from '..'; +import {Event, RunResult, TestEntry} from '../types'; + +type Process = NodeJS.Process; export const initialize = ({ config, @@ -34,13 +32,13 @@ export const initialize = ({ parentProcess, testPath, }: { - config: ProjectConfig, - getPrettier: () => null | any, - getBabelTraverse: () => Function, - globalConfig: GlobalConfig, - localRequire: Path => any, - testPath: Path, - parentProcess: Process, + config: Config.ProjectConfig; + getPrettier: () => null | any; + getBabelTraverse: () => Function; + globalConfig: Config.GlobalConfig; + localRequire: (path: Config.Path) => any; + testPath: Config.Path; + parentProcess: Process; }) => { const mutex = throat(globalConfig.maxConcurrency); @@ -52,31 +50,36 @@ export const initialize = ({ global.fit = global.it.only; global.fdescribe = global.describe.only; - global.test.concurrent = ( - testName: string, - testFn: () => Promise, - timeout?: number, - ) => { - // For concurrent tests we first run the function that returns promise, and then register a - // nomral test that will be waiting on the returned promise (when we start the test, the promise - // will already be in the process of execution). - // Unfortunately at this stage there's no way to know if there are any `.only` tests in the suite - // that will result in this test to be skipped, so we'll be executing the promise function anyway, - // even if it ends up being skipped. - const promise = mutex(() => testFn()); - global.test(testName, () => promise, timeout); - }; + global.test.concurrent = (test => { + const concurrent = ( + testName: string, + testFn: () => Promise, + timeout?: number, + ) => { + // For concurrent tests we first run the function that returns promise, and then register a + // nomral test that will be waiting on the returned promise (when we start the test, the promise + // will already be in the process of execution). + // Unfortunately at this stage there's no way to know if there are any `.only` tests in the suite + // that will result in this test to be skipped, so we'll be executing the promise function anyway, + // even if it ends up being skipped. + const promise = mutex(() => testFn()); + global.test(testName, () => promise, timeout); + }; - global.test.concurrent.only = ( - testName: string, - testFn: () => Promise, - timeout?: number, - ) => { - const promise = mutex(() => testFn()); - global.test.only(testName, () => promise, timeout); - }; + concurrent.only = ( + testName: string, + testFn: () => Promise, + timeout?: number, + ) => { + const promise = mutex(() => testFn()); + // eslint-disable-next-line jest/no-focused-tests + test.only(testName, () => promise, timeout); + }; + + concurrent.skip = test.skip; - global.test.concurrent.skip = global.test.skip; + return concurrent; + })(global.test); addEventHandler(eventHandler); @@ -121,10 +124,10 @@ export const runAndTransformResultsToJestFormat = async ({ globalConfig, testPath, }: { - config: ProjectConfig, - globalConfig: GlobalConfig, - testPath: string, -}): Promise => { + config: Config.ProjectConfig; + globalConfig: Config.GlobalConfig; + testPath: string; +}): Promise => { const runResult: RunResult = await run(); let numFailingTests = 0; @@ -132,41 +135,43 @@ export const runAndTransformResultsToJestFormat = async ({ let numPendingTests = 0; let numTodoTests = 0; - const assertionResults: Array = runResult.testResults.map( - testResult => { - let status: Status; - if (testResult.status === 'skip') { - status = 'pending'; - numPendingTests += 1; - } else if (testResult.status === 'todo') { - status = 'todo'; - numTodoTests += 1; - } else if (testResult.errors.length) { - status = 'failed'; - numFailingTests += 1; - } else { - status = 'passed'; - numPassingTests += 1; - } - - const ancestorTitles = testResult.testPath.filter( - name => name !== ROOT_DESCRIBE_BLOCK_NAME, - ); - const title = ancestorTitles.pop(); - - return { - ancestorTitles, - duration: testResult.duration, - failureMessages: testResult.errors, - fullName: ancestorTitles.concat(title).join(' '), - invocations: testResult.invocations, - location: testResult.location, - numPassingAsserts: 0, - status, - title: testResult.testPath[testResult.testPath.length - 1], - }; - }, - ); + const assertionResults: Array< + TestResult.AssertionResult + > = runResult.testResults.map(testResult => { + let status: TestResult.Status; + if (testResult.status === 'skip') { + status = 'pending'; + numPendingTests += 1; + } else if (testResult.status === 'todo') { + status = 'todo'; + numTodoTests += 1; + } else if (testResult.errors.length) { + status = 'failed'; + numFailingTests += 1; + } else { + status = 'passed'; + numPassingTests += 1; + } + + const ancestorTitles = testResult.testPath.filter( + name => name !== ROOT_DESCRIBE_BLOCK_NAME, + ); + const title = ancestorTitles.pop(); + + return { + ancestorTitles, + duration: testResult.duration, + failureMessages: testResult.errors, + fullName: title + ? ancestorTitles.concat(title).join(' ') + : ancestorTitles.join(' '), + invocations: testResult.invocations, + location: testResult.location, + numPassingAsserts: 0, + status, + title: testResult.testPath[testResult.testPath.length - 1], + }; + }); let failureMessage = formatResultsErrors( assertionResults, diff --git a/packages/jest-circus/src/legacy-code-todo-rewrite/jestExpect.js b/packages/jest-circus/src/legacy-code-todo-rewrite/jestExpect.ts similarity index 73% rename from packages/jest-circus/src/legacy-code-todo-rewrite/jestExpect.js rename to packages/jest-circus/src/legacy-code-todo-rewrite/jestExpect.ts index 51b24afa4082..a78f4da4f4ae 100644 --- a/packages/jest-circus/src/legacy-code-todo-rewrite/jestExpect.js +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestExpect.ts @@ -3,12 +3,8 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {RawMatcherFn} from 'types/Matchers'; - import expect from 'expect'; import { @@ -19,12 +15,6 @@ import { toThrowErrorMatchingInlineSnapshot, } from 'jest-snapshot'; -type JasmineMatcher = { - (): JasmineMatcher, - compare: () => RawMatcherFn, - negativeCompare: () => RawMatcherFn, -}; - export default (config: {expand: boolean}) => { global.expect = expect; expect.setState({ @@ -37,5 +27,5 @@ export default (config: {expand: boolean}) => { toThrowErrorMatchingSnapshot, }); - (expect: Object).addSnapshotSerializer = addSerializer; + expect.addSnapshotSerializer = addSerializer; }; diff --git a/packages/jest-circus/src/run.js b/packages/jest-circus/src/run.ts similarity index 92% rename from packages/jest-circus/src/run.js rename to packages/jest-circus/src/run.ts index 27cadf1420bf..04d5b8459b8d 100644 --- a/packages/jest-circus/src/run.js +++ b/packages/jest-circus/src/run.ts @@ -3,17 +3,16 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow strict-local */ -import type { +import { RunResult, TestEntry, TestContext, Hook, DescribeBlock, -} from 'types/Circus'; + RETRY_TIMES, +} from './types'; import {getState, dispatch} from './state'; import { @@ -45,7 +44,7 @@ const _runTestsForDescribeBlock = async (describeBlock: DescribeBlock) => { } // Tests that fail and are retried we run after other tests - const retryTimes = parseInt(global[Symbol.for('RETRY_TIMES')], 10) || 0; + const retryTimes = parseInt(global[RETRY_TIMES], 10) || 0; const deferredRetryTests = []; for (const test of describeBlock.tests) { @@ -128,11 +127,11 @@ const _callCircusHook = ({ describeBlock, testContext, }: { - hook: Hook, - describeBlock?: DescribeBlock, - test?: TestEntry, - testContext?: TestContext, -}): Promise => { + hook: Hook; + describeBlock?: DescribeBlock; + test?: TestEntry; + testContext?: TestContext; +}): Promise => { dispatch({hook, name: 'hook_start'}); const timeout = hook.timeout || getState().testTimeout; return callAsyncCircusFn(hook.fn, testContext, {isHook: true, timeout}) @@ -155,7 +154,7 @@ const _callCircusTest = ( return Promise.resolve(); } - return callAsyncCircusFn(test.fn, testContext, {isHook: false, timeout}) + return callAsyncCircusFn(test.fn!, testContext, {isHook: false, timeout}) .then(() => dispatch({name: 'test_fn_success', test})) .catch(error => dispatch({error, name: 'test_fn_failure', test})); }; diff --git a/packages/jest-circus/src/state.js b/packages/jest-circus/src/state.ts similarity index 91% rename from packages/jest-circus/src/state.js rename to packages/jest-circus/src/state.ts index 8e353e7eb5c6..8f684d254d16 100644 --- a/packages/jest-circus/src/state.js +++ b/packages/jest-circus/src/state.ts @@ -3,11 +3,9 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow strict-local */ -import type {Event, State, EventHandler} from 'types/Circus'; +import {Event, State, EventHandler, STATE_SYM} from './types'; import {makeDescribe} from './utils'; import eventHandler from './eventHandler'; @@ -19,7 +17,6 @@ const eventHandlers: Array = [ ]; export const ROOT_DESCRIBE_BLOCK_NAME = 'ROOT_DESCRIBE_BLOCK'; -const STATE_SYM = Symbol('JEST_STATE_SYMBOL'); const ROOT_DESCRIBE_BLOCK = makeDescribe(ROOT_DESCRIBE_BLOCK_NAME); const INITIAL_STATE: State = { diff --git a/packages/jest-circus/src/types.ts b/packages/jest-circus/src/types.ts new file mode 100644 index 000000000000..bdc9cc8f2d1c --- /dev/null +++ b/packages/jest-circus/src/types.ts @@ -0,0 +1,243 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +// Used as type +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import expect from 'expect'; +import {Global} from '@jest/types'; + +type Process = NodeJS.Process; + +export type DoneFn = Global.DoneFn; +export type BlockFn = Global.BlockFn; +export type BlockName = Global.BlockName; +export type BlockMode = void | 'skip' | 'only' | 'todo'; +export type TestMode = BlockMode; +export type TestName = Global.TestName; +export type TestFn = Global.TestFn; +export type HookFn = (done?: DoneFn) => Promise | null | undefined; +export type AsyncFn = TestFn | HookFn; +export type SharedHookType = 'afterAll' | 'beforeAll'; +export type HookType = SharedHookType | 'afterEach' | 'beforeEach'; +export type TestContext = Object; +export type Exception = any; // Since in JS anything can be thrown as an error. +export type FormattedError = string; // String representation of error. +export type Hook = { + asyncError: Exception; + fn: HookFn; + type: HookType; + parent: DescribeBlock; + timeout: number | undefined | null; +}; + +export type EventHandler = (event: Event, state: State) => void; + +export type Event = + | { + name: 'include_test_location_in_result'; + } + | { + asyncError: Exception; + mode: BlockMode; + name: 'start_describe_definition'; + blockName: BlockName; + } + | { + mode: BlockMode; + name: 'finish_describe_definition'; + blockName: BlockName; + } + | { + asyncError: Exception; + name: 'add_hook'; + hookType: HookType; + fn: HookFn; + timeout: number | undefined; + } + | { + asyncError: Exception; + name: 'add_test'; + testName: TestName; + fn?: TestFn; + mode?: TestMode; + timeout: number | undefined; + } + | { + name: 'hook_start'; + hook: Hook; + } + | { + name: 'hook_success'; + describeBlock: DescribeBlock | undefined | null; + test: TestEntry | undefined | null; + hook: Hook; + } + | { + name: 'hook_failure'; + error: string | Exception; + describeBlock: DescribeBlock | undefined | null; + test: TestEntry | undefined | null; + hook: Hook; + } + | { + name: 'test_fn_start'; + test: TestEntry; + } + | { + name: 'test_fn_success'; + test: TestEntry; + } + | { + name: 'test_fn_failure'; + error: Exception; + test: TestEntry; + } + | { + name: 'test_retry'; + test: TestEntry; + } + | { + // the `test` in this case is all hooks + it/test function, not just the + // function passed to `it/test` + name: 'test_start'; + test: TestEntry; + } + | { + name: 'test_skip'; + test: TestEntry; + } + | { + name: 'test_todo'; + test: TestEntry; + } + | { + // test failure is defined by presence of errors in `test.errors`, + // `test_done` indicates that the test and all its hooks were run, + // and nothing else will change it's state in the future. (except third + // party extentions/plugins) + name: 'test_done'; + test: TestEntry; + } + | { + name: 'run_describe_start'; + describeBlock: DescribeBlock; + } + | { + name: 'run_describe_finish'; + describeBlock: DescribeBlock; + } + | { + name: 'run_start'; + } + | { + name: 'run_finish'; + } + | { + // Any unhandled error that happened outside of test/hooks (unless it is + // an `afterAll` hook) + name: 'error'; + error: Exception; + } + | { + // first action to dispatch. Good time to initialize all settings + name: 'setup'; + testNamePattern?: string; + parentProcess: Process; + } + | { + // Action dispatched after everything is finished and we're about to wrap + // things up and return test results to the parent process (caller). + name: 'teardown'; + }; + +export type TestStatus = 'skip' | 'done' | 'todo'; +export type TestResult = { + duration: number | null | undefined; + errors: Array; + invocations: number; + status: TestStatus; + location: {column: number; line: number} | null | undefined; + testPath: Array; +}; + +export type RunResult = { + unhandledErrors: Array; + testResults: TestResults; +}; + +export type TestResults = Array; + +export type GlobalErrorHandlers = { + uncaughtException: Array<(exception: Exception) => void>; + unhandledRejection: Array< + (exception: Exception, promise: Promise) => void + >; +}; + +export type State = { + currentDescribeBlock: DescribeBlock; + currentlyRunningTest: TestEntry | undefined | null; // including when hooks are being executed + expand?: boolean; // expand error messages + hasFocusedTests: boolean; // that are defined using test.only + // Store process error handlers. During the run we inject our own + // handlers (so we could fail tests on unhandled errors) and later restore + // the original ones. + originalGlobalErrorHandlers?: GlobalErrorHandlers; + parentProcess: Process | null; // process object from the outer scope + rootDescribeBlock: DescribeBlock; + testNamePattern: RegExp | undefined | null; + testTimeout: number; + unhandledErrors: Array; + includeTestLocationInResult: boolean; +}; + +export type DescribeBlock = { + children: Array; + hooks: Array; + mode: BlockMode; + name: BlockName; + parent: DescribeBlock | undefined | null; + tests: Array; +}; + +export type TestError = Exception | Array<[Exception | undefined, Exception]>; // the error from the test, as well as a backup error for async + +export type TestEntry = { + asyncError: Exception; // Used if the test failure contains no usable stack trace + errors: TestError; + fn: TestFn | undefined | null; + invocations: number; + mode: TestMode; + name: TestName; + parent: DescribeBlock; + startedAt: number | undefined | null; + duration: number | undefined | null; + status: TestStatus | undefined | null; // whether the test has been skipped or run already + timeout: number | undefined | null; +}; + +export const STATE_SYM = (Symbol( + 'JEST_STATE_SYMBOL', +) as unknown) as 'STATE_SYM_SYMBOL'; +export const RETRY_TIMES = (Symbol.for( + 'RETRY_TIMES', +) as unknown) as 'RETRY_TIMES_SYMBOL'; +// To pass this value from Runtime object to state we need to use global[sym] +export const TEST_TIMEOUT_SYMBOL = (Symbol.for( + 'TEST_TIMEOUT_SYMBOL', +) as unknown) as 'TEST_TIMEOUT_SYMBOL'; + +declare global { + module NodeJS { + interface Global { + STATE_SYM_SYMBOL: State; + RETRY_TIMES_SYMBOL: string; + TEST_TIMEOUT_SYMBOL: number; + expect: typeof expect; + } + } +} diff --git a/packages/jest-circus/src/utils.js b/packages/jest-circus/src/utils.ts similarity index 77% rename from packages/jest-circus/src/utils.js rename to packages/jest-circus/src/utils.ts index 0c56d8619b5b..7917d1356e9d 100644 --- a/packages/jest-circus/src/utils.js +++ b/packages/jest-circus/src/utils.ts @@ -3,11 +3,18 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow strict-local */ -import type { +import {convertDescriptorToString} from 'jest-util'; +import isGeneratorFn from 'is-generator-fn'; +import co from 'co'; + +import StackUtils from 'stack-utils'; + +import prettyFormat from 'pretty-format'; + +import {getState} from './state'; +import { AsyncFn, BlockMode, BlockName, @@ -21,24 +28,13 @@ import type { TestMode, TestName, TestResults, -} from 'types/Circus'; -// $FlowFixMe: Converted to TS -import {convertDescriptorToString} from 'jest-util'; -import isGeneratorFn from 'is-generator-fn'; -import co from 'co'; - -import StackUtils from 'stack-utils'; - -// $FlowFixMe: Converted to TS -import prettyFormat from 'pretty-format'; - -import {getState} from './state'; +} from './types'; const stackUtils = new StackUtils({cwd: 'A path that does not exist'}); export const makeDescribe = ( name: BlockName, - parent: ?DescribeBlock, + parent?: DescribeBlock, mode?: BlockMode, ): DescribeBlock => { let _mode = mode; @@ -58,29 +54,25 @@ export const makeDescribe = ( }; export const makeTest = ( - fn: ?TestFn, + fn: TestFn | undefined, mode: TestMode, name: TestName, parent: DescribeBlock, - timeout: ?number, + timeout: number | undefined, asyncError: Exception, -): TestEntry => { - const errors: Array<[?Exception, Exception]> = []; - - return { - asyncError, - duration: null, - errors, - fn, - invocations: 0, - mode, - name: convertDescriptorToString(name), - parent, - startedAt: null, - status: null, - timeout, - }; -}; +): TestEntry => ({ + asyncError, + duration: null, + errors: [], + fn, + invocations: 0, + mode, + name: convertDescriptorToString(name), + parent, + startedAt: null, + status: null, + timeout, +}); // Traverse the tree of describe blocks and return true if at least one describe // block has an enabled test. @@ -98,10 +90,14 @@ const hasEnabledTest = (describeBlock: DescribeBlock): boolean => { return hasOwnEnabledTests || describeBlock.children.some(hasEnabledTest); }; -export const getAllHooksForDescribe = ( - describe: DescribeBlock, -): {[key: 'beforeAll' | 'afterAll']: Array} => { - const result = {afterAll: [], beforeAll: []}; +export const getAllHooksForDescribe = (describe: DescribeBlock) => { + const result: { + beforeAll: Array; + afterAll: Array; + } = { + afterAll: [], + beforeAll: [], + }; if (hasEnabledTest(describe)) { for (const hook of describe.hooks) { @@ -119,11 +115,12 @@ export const getAllHooksForDescribe = ( return result; }; -export const getEachHooksForTest = ( - test: TestEntry, -): {[key: 'beforeEach' | 'afterEach']: Array} => { - const result = {afterEach: [], beforeEach: []}; - let {parent: block} = test; +export const getEachHooksForTest = (test: TestEntry) => { + const result: { + beforeEach: Array; + afterEach: Array; + } = {afterEach: [], beforeEach: []}; + let block: DescribeBlock | undefined | null = test.parent; do { const beforeEachForCurrentBlock = []; @@ -144,10 +141,10 @@ export const getEachHooksForTest = ( return result; }; -export const describeBlockHasTests = (describe: DescribeBlock) => - describe.tests.length || describe.children.some(describeBlockHasTests); +export const describeBlockHasTests = (describe: DescribeBlock): boolean => + describe.tests.length > 0 || describe.children.some(describeBlockHasTests); -const _makeTimeoutMessage = (timeout, isHook) => +const _makeTimeoutMessage = (timeout: number, isHook: boolean) => `Exceeded timeout of ${timeout}ms for a ${ isHook ? 'hook' : 'test' }.\nUse jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test.`; @@ -158,14 +155,14 @@ const {setTimeout, clearTimeout} = global; export const callAsyncCircusFn = ( fn: AsyncFn, - testContext: ?TestContext, - {isHook, timeout}: {isHook?: ?boolean, timeout: number}, -): Promise => { - let timeoutID; + testContext: TestContext | undefined, + {isHook, timeout}: {isHook?: boolean | null; timeout: number}, +): Promise => { + let timeoutID: NodeJS.Timeout; return new Promise((resolve, reject) => { timeoutID = setTimeout( - () => reject(_makeTimeoutMessage(timeout, isHook)), + () => reject(_makeTimeoutMessage(timeout, !!isHook)), timeout, ); @@ -173,8 +170,8 @@ export const callAsyncCircusFn = ( // soon as `done` called. if (fn.length) { const done = (reason?: Error | string): void => { - // $FlowFixMe: It doesn't approve of .stack - const isError = reason && reason.message && reason.stack; + const isError = + reason && (reason as Error).message && (reason as Error).stack; return reason ? reject( isError @@ -236,7 +233,7 @@ export const callAsyncCircusFn = ( }); }; -export const getTestDuration = (test: TestEntry): ?number => { +export const getTestDuration = (test: TestEntry): number | null => { const {startedAt} = test; return typeof startedAt === 'number' ? Date.now() - startedAt : null; }; @@ -249,12 +246,12 @@ export const makeRunResult = ( unhandledErrors: unhandledErrors.map(_formatError), }); -const makeTestResults = (describeBlock: DescribeBlock, config): TestResults => { +const makeTestResults = (describeBlock: DescribeBlock): TestResults => { const {includeTestLocationInResult} = getState(); - let testResults = []; + let testResults: TestResults = []; for (const test of describeBlock.tests) { const testPath = []; - let parent = test; + let parent: TestEntry | DescribeBlock = test; do { testPath.unshift(parent.name); } while ((parent = parent.parent)); @@ -268,8 +265,17 @@ const makeTestResults = (describeBlock: DescribeBlock, config): TestResults => { let location = null; if (includeTestLocationInResult) { const stackLine = test.asyncError.stack.split('\n')[1]; - const {line, column} = stackUtils.parseLine(stackLine); - location = {column, line}; + const parsedLine = stackUtils.parseLine(stackLine); + if ( + parsedLine && + typeof parsedLine.column === 'number' && + typeof parsedLine.line === 'number' + ) { + location = { + column: parsedLine.column, + line: parsedLine.line, + }; + } } testResults.push({ @@ -283,7 +289,7 @@ const makeTestResults = (describeBlock: DescribeBlock, config): TestResults => { } for (const child of describeBlock.children) { - testResults = testResults.concat(makeTestResults(child, config)); + testResults = testResults.concat(makeTestResults(child)); } return testResults; @@ -293,7 +299,7 @@ const makeTestResults = (describeBlock: DescribeBlock, config): TestResults => { // names + test title) export const getTestID = (test: TestEntry) => { const titles = []; - let parent = test; + let parent: TestEntry | DescribeBlock = test; do { titles.unshift(parent.name); } while ((parent = parent.parent)); @@ -302,7 +308,9 @@ export const getTestID = (test: TestEntry) => { return titles.join(' '); }; -const _formatError = (errors: ?Exception | [?Exception, Exception]): string => { +const _formatError = ( + errors?: Exception | [Exception | undefined, Exception], +): string => { let error; let asyncError; @@ -342,7 +350,7 @@ export const addErrorToEachTestUnderDescribe = ( } }; -export const invariant = (condition: *, message: string) => { +export const invariant = (condition: unknown, message?: string) => { if (!condition) { throw new Error(message); } diff --git a/packages/jest-circus/tsconfig.json b/packages/jest-circus/tsconfig.json new file mode 100644 index 000000000000..3cb4125e6ac7 --- /dev/null +++ b/packages/jest-circus/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig", + "compilerOptions": { + "outDir": "build", + "rootDir": "src" + }, + "references": [{"path": "../jest-types"}, {"path": "../jest-snapshot"}, {"path": "../jest-matcher-utils"}, {"path": "../jest-message-util"}, {"path": "../jest-util"}, {"path": "../pretty-format"}, {"path": "../jest-diff"}] +} diff --git a/packages/jest-types/src/Environment.ts b/packages/jest-types/src/Environment.ts new file mode 100644 index 000000000000..1af4493db851 --- /dev/null +++ b/packages/jest-types/src/Environment.ts @@ -0,0 +1,44 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import {Script} from 'vm'; +import {ProjectConfig} from './Config'; +import {Global} from './Global'; + +// TODO Fix jest-mock and @jest/types has circular dependency +// import {ModuleMocker} from 'jest-mock'; +type ModuleMocker = any; + +export type EnvironmentContext = { + console?: Object; + testPath?: string; +}; + +export declare class $JestEnvironment { + constructor(config: ProjectConfig, context?: EnvironmentContext); + runScript(script: Script): any; + global: Global; + fakeTimers: { + clearAllTimers(): void; + runAllImmediates(): void; + runAllTicks(): void; + runAllTimers(): void; + advanceTimersByTime(msToRun: number): void; + runOnlyPendingTimers(): void; + runWithRealTimers(callback: any): void; + getTimerCount(): number; + useFakeTimers(): void; + useRealTimers(): void; + }; + testFilePath: string; + moduleMocker: ModuleMocker; + setup(): Promise; + teardown(): Promise; +} + +export type Environment = $JestEnvironment; +export type EnvironmentClass = typeof $JestEnvironment; diff --git a/packages/jest-types/src/Global.ts b/packages/jest-types/src/Global.ts new file mode 100644 index 000000000000..ed7739615e14 --- /dev/null +++ b/packages/jest-types/src/Global.ts @@ -0,0 +1,75 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +export type DoneFn = (reason?: string | Error) => void; +export type TestName = string; +export type TestFn = (done?: DoneFn) => Promise | void | undefined; +export type BlockFn = () => void; +export type BlockName = string; + +// TODO Replace with actual type when `jest-each` is ready +type Each = () => void; + +export interface ItBase { + (testName: TestName, fn: TestFn, timeout?: number): void; + each: Each; +} + +export interface It extends ItBase { + only: ItBase; + skip: ItBase; + todo: (testName: TestName, ...rest: Array) => void; +} + +export interface ItConcurrentBase { + (testName: string, testFn: () => Promise, timeout?: number): void; +} + +export interface ItConcurrentExtended extends ItConcurrentBase { + only: ItConcurrentBase; + skip: ItConcurrentBase; +} + +export interface ItConcurrent extends It { + concurrent: ItConcurrentExtended; +} + +export interface DescribeBase { + (blockName: BlockName, blockFn: BlockFn): void; + each: Each; +} + +export interface Describe extends DescribeBase { + only: ItBase; + skip: ItBase; +} + +export interface Global { + it: It; + test: ItConcurrent; + fit: ItBase; + xit: ItBase; + xtest: ItBase; + describe: Describe; + xdescribe: DescribeBase; + fdescribe: DescribeBase; +} + +declare global { + module NodeJS { + interface Global { + it: It; + test: ItConcurrent; + fit: ItBase; + xit: ItBase; + xtest: ItBase; + describe: Describe; + xdescribe: DescribeBase; + fdescribe: DescribeBase; + } + } +} diff --git a/packages/jest-types/src/index.ts b/packages/jest-types/src/index.ts index 0226a0a00820..d845e25a2ff3 100644 --- a/packages/jest-types/src/index.ts +++ b/packages/jest-types/src/index.ts @@ -14,6 +14,8 @@ import * as Resolve from './Resolve'; import * as Snapshot from './Snapshot'; import * as SourceMaps from './SourceMaps'; import * as TestResult from './TestResult'; +import * as Global from './Global'; +import * as Environment from './Environment'; export { Config, @@ -25,4 +27,6 @@ export { Snapshot, SourceMaps, TestResult, + Global, + Environment, }; diff --git a/yarn.lock b/yarn.lock index cfedeb1ee38a..3a7d5cb3e17a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1546,6 +1546,11 @@ resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.10.tgz#780d552467824be4a241b29510a7873a7432c4a6" integrity sha512-fOM/Jhv51iyugY7KOBZz2ThfT1gwvsGCfWxpLpZDgkGjpEO4Le9cld07OdskikLjDUQJ43dzDaVRSFwQlpdqVg== +"@types/co@^4.6.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@types/co/-/co-4.6.0.tgz#fd7b669f3643e366d2d2114022be0571f3ddfc68" + integrity sha512-Ptqc3o9M/zuYT/AjM5pVO7xsAXPkal6P5xJ1nIbYYRzMlFYn7ZAVj0Wzz/Falort7jasH7GxuCkx5iueiiyjJA== + "@types/color-name@*": version "1.1.0" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.0.tgz#926f76f7e66f49cc59ad880bb15b030abbf0b66d" From 261fc655523ce27070edbcfb52d6f3f0fe632f91 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sat, 23 Feb 2019 13:30:46 +0100 Subject: [PATCH 090/107] chore: simplify types in jest-changed-files --- packages/jest-changed-files/package.json | 1 + packages/jest-changed-files/src/git.ts | 17 ++++++++--------- packages/jest-changed-files/src/hg.ts | 13 ++++++------- packages/jest-changed-files/src/index.ts | 21 ++++++++++++++------- packages/jest-changed-files/src/types.ts | 16 +++++++++------- packages/jest-changed-files/tsconfig.json | 5 ++++- 6 files changed, 42 insertions(+), 31 deletions(-) diff --git a/packages/jest-changed-files/package.json b/packages/jest-changed-files/package.json index 6aafc1373de5..26af22275c41 100644 --- a/packages/jest-changed-files/package.json +++ b/packages/jest-changed-files/package.json @@ -10,6 +10,7 @@ "main": "build/index.js", "types": "build/index.d.ts", "dependencies": { + "@jest/types": "^24.1.0", "execa": "^1.0.0", "throat": "^4.0.0" }, diff --git a/packages/jest-changed-files/src/git.ts b/packages/jest-changed-files/src/git.ts index 463ad88647da..ed0d4a9b90a1 100644 --- a/packages/jest-changed-files/src/git.ts +++ b/packages/jest-changed-files/src/git.ts @@ -8,13 +8,14 @@ import path from 'path'; import execa from 'execa'; +import {Config} from '@jest/types'; -import {Path, Options, SCMAdapter} from './types'; +import {SCMAdapter} from './types'; const findChangedFilesUsingCommand = async ( args: Array, - cwd: Path, -): Promise> => { + cwd: Config.Path, +): Promise> => { const result = await execa('git', args, {cwd}); return result.stdout @@ -24,14 +25,12 @@ const findChangedFilesUsingCommand = async ( }; const adapter: SCMAdapter = { - findChangedFiles: async ( - cwd: string, - options?: Options, - ): Promise> => { + findChangedFiles: async (cwd, options) => { const changedSince: string | undefined = options && (options.withAncestor ? 'HEAD^' : options.changedSince); - const includePaths: Array = (options && options.includePaths) || []; + const includePaths: Array = + (options && options.includePaths) || []; if (options && options.lastCommit) { return findChangedFilesUsingCommand( @@ -72,7 +71,7 @@ const adapter: SCMAdapter = { } }, - getRoot: async (cwd: string): Promise => { + getRoot: async cwd => { const options = ['rev-parse', '--show-toplevel']; try { diff --git a/packages/jest-changed-files/src/hg.ts b/packages/jest-changed-files/src/hg.ts index 96cb347a5882..89d4aea508b2 100644 --- a/packages/jest-changed-files/src/hg.ts +++ b/packages/jest-changed-files/src/hg.ts @@ -8,17 +8,16 @@ import path from 'path'; import execa from 'execa'; +import {Config} from '@jest/types'; -import {Path, Options, SCMAdapter} from './types'; +import {SCMAdapter} from './types'; const env = {...process.env, HGPLAIN: '1'}; const adapter: SCMAdapter = { - findChangedFiles: async ( - cwd: string, - options: Options, - ): Promise> => { - const includePaths: Array = (options && options.includePaths) || []; + findChangedFiles: async (cwd, options) => { + const includePaths: Array = + (options && options.includePaths) || []; const args = ['status', '-amnu']; if (options && options.withAncestor) { @@ -38,7 +37,7 @@ const adapter: SCMAdapter = { .map(changedPath => path.resolve(cwd, changedPath)); }, - getRoot: async (cwd: Path): Promise => { + getRoot: async cwd => { try { const result = await execa('hg', ['root'], {cwd, env}); diff --git a/packages/jest-changed-files/src/index.ts b/packages/jest-changed-files/src/index.ts index d3c160e683ee..b53f80db208f 100644 --- a/packages/jest-changed-files/src/index.ts +++ b/packages/jest-changed-files/src/index.ts @@ -7,11 +7,18 @@ */ import throat from 'throat'; +import {Config} from '@jest/types'; -import {Path, ChangedFilesPromise, Options, Repos} from './types'; +import {ChangedFilesPromise, Options, Repos, SCMAdapter} from './types'; import git from './git'; import hg from './hg'; +type RootPromise = ReturnType; + +function notEmpty(value: T | null | undefined): value is T { + return value != null; +} + // This is an arbitrary number. The main goal is to prevent projects with // many roots (50+) from spawning too many processes at once. const mutex = throat(5); @@ -20,7 +27,7 @@ const findGitRoot = (dir: string) => mutex(() => git.getRoot(dir)); const findHgRoot = (dir: string) => mutex(() => hg.getRoot(dir)); export const getChangedFilesForRoots = async ( - roots: Path[], + roots: Config.Path[], options: Options, ): ChangedFilesPromise => { const repos = await findRepos(roots); @@ -48,22 +55,22 @@ export const getChangedFilesForRoots = async ( return {changedFiles, repos}; }; -export const findRepos = async (roots: Path[]): Promise => { +export const findRepos = async (roots: Config.Path[]): Promise => { const gitRepos = await Promise.all( - roots.reduce[]>( + roots.reduce( (promises, root) => promises.concat(findGitRoot(root)), [], ), ); const hgRepos = await Promise.all( - roots.reduce[]>( + roots.reduce( (promises, root) => promises.concat(findHgRoot(root)), [], ), ); return { - git: new Set(gitRepos.filter(Boolean) as string[]), - hg: new Set(hgRepos.filter(Boolean) as string[]), + git: new Set(gitRepos.filter(notEmpty)), + hg: new Set(hgRepos.filter(notEmpty)), }; }; diff --git a/packages/jest-changed-files/src/types.ts b/packages/jest-changed-files/src/types.ts index 36bde410b70b..3d040a6c49dd 100644 --- a/packages/jest-changed-files/src/types.ts +++ b/packages/jest-changed-files/src/types.ts @@ -3,26 +3,28 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * */ -export type Path = string; +import {Config} from '@jest/types'; export type Options = { lastCommit?: boolean; withAncestor?: boolean; changedSince?: string; - includePaths?: Array; + includePaths?: Array; }; -export type ChangedFiles = Set; -export type Repos = {git: Set; hg: Set}; +type ChangedFiles = Set; +export type Repos = {git: ChangedFiles; hg: ChangedFiles}; export type ChangedFilesPromise = Promise<{ repos: Repos; changedFiles: ChangedFiles; }>; export type SCMAdapter = { - findChangedFiles: (cwd: Path, options: Options) => Promise>; - getRoot: (cwd: Path) => Promise; + findChangedFiles: ( + cwd: Config.Path, + options: Options, + ) => Promise>; + getRoot: (cwd: Config.Path) => Promise; }; diff --git a/packages/jest-changed-files/tsconfig.json b/packages/jest-changed-files/tsconfig.json index 7bb06bce6d20..25e34a0f4511 100644 --- a/packages/jest-changed-files/tsconfig.json +++ b/packages/jest-changed-files/tsconfig.json @@ -3,5 +3,8 @@ "compilerOptions": { "rootDir": "src", "outDir": "build" - } + }, + "references": [ + {"path": "../jest-types"} + ] } From cd43f6ed5dea250028f12ca1e385ca14dcc6dfe2 Mon Sep 17 00:00:00 2001 From: Andrew M Date: Sat, 23 Feb 2019 17:24:42 +0300 Subject: [PATCH 091/107] Migrate jest-phabricator to TypeScript (#7965) --- CHANGELOG.md | 1 + packages/jest-phabricator/package.json | 4 ++++ .../src/{index.js => index.ts} | 18 +++++------------- packages/jest-phabricator/tsconfig.json | 12 ++++++++++++ packages/jest-types/src/TestResult.ts | 6 +++--- 5 files changed, 25 insertions(+), 16 deletions(-) rename packages/jest-phabricator/src/{index.js => index.ts} (69%) create mode 100644 packages/jest-phabricator/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index aa078a389e3e..227365427b90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ - `[jest-resolve-dependencies]`: Migrate to TypeScript ([#7922](https://github.com/facebook/jest/pull/7922)) - `[expect]`: Migrate to TypeScript ([#7919](https://github.com/facebook/jest/pull/7919)) - `[jest-circus]`: Migrate to TypeScript ([#7916](https://github.com/facebook/jest/pull/7916)) +- `[jest-phabricator]`: Migrate to TypeScript ([#7965](https://github.com/facebook/jest/pull/7965)) ### Performance diff --git a/packages/jest-phabricator/package.json b/packages/jest-phabricator/package.json index b711d648af56..f1000201d15c 100644 --- a/packages/jest-phabricator/package.json +++ b/packages/jest-phabricator/package.json @@ -6,6 +6,10 @@ "url": "https://github.com/facebook/jest.git", "directory": "packages/jest-phabricator" }, + "types": "build/index.d.ts", + "dependencies": { + "@jest/types": "^24.1.0" + }, "engines": { "node": ">= 6" }, diff --git a/packages/jest-phabricator/src/index.js b/packages/jest-phabricator/src/index.ts similarity index 69% rename from packages/jest-phabricator/src/index.js rename to packages/jest-phabricator/src/index.ts index 5c02ee4ae1b3..0c1faa1cac99 100644 --- a/packages/jest-phabricator/src/index.js +++ b/packages/jest-phabricator/src/index.ts @@ -3,20 +3,11 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * @flow */ -import type { - AggregatedResult, - AggregatedResultWithoutCoverage, - CoverageMap, -} from 'types/TestResult'; +import {TestResult} from '@jest/types'; -type PhabricatorReport = AggregatedResultWithoutCoverage & { - coverageMap?: ?CoverageMap, -}; - -function summarize(coverageMap: CoverageMap) { +function summarize(coverageMap: TestResult.CoverageMap) { const summaries = Object.create(null); coverageMap.files().forEach(file => { @@ -41,8 +32,9 @@ function summarize(coverageMap: CoverageMap) { return summaries; } -module.exports = function(results: AggregatedResult): PhabricatorReport { - // $FlowFixMe: This should work, but it does not. +module.exports = function( + results: TestResult.AggregatedResult, +): TestResult.AggregatedResult { return { ...results, coverageMap: results.coverageMap && summarize(results.coverageMap), diff --git a/packages/jest-phabricator/tsconfig.json b/packages/jest-phabricator/tsconfig.json new file mode 100644 index 000000000000..7a7b7a460af1 --- /dev/null +++ b/packages/jest-phabricator/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [ + { + "path": "../jest-types" + } + ] +} diff --git a/packages/jest-types/src/TestResult.ts b/packages/jest-types/src/TestResult.ts index 841064692ff1..857751e6a9f0 100644 --- a/packages/jest-types/src/TestResult.ts +++ b/packages/jest-types/src/TestResult.ts @@ -52,10 +52,10 @@ export type CoverageSummary = { }; export type FileCoverage = { - getLineCoverage: () => Object; + getLineCoverage: () => {[line: string]: string}; getUncoveredLines: () => Array; - getBranchCoverageByLine: () => Object; - toJSON: () => Object; + getBranchCoverageByLine: () => {[line: string]: string}; + toJSON: () => {[line: string]: string}; merge: (other: Object) => undefined; computeSimpleTotals: (property: string) => FileCoverageTotal; computeBranchTotals: () => FileCoverageTotal; From afa572904529a2e13a375a82c849b6dd33ef42e3 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sat, 23 Feb 2019 15:43:42 +0100 Subject: [PATCH 092/107] chore: correct jest-phabricator export --- packages/jest-phabricator/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-phabricator/src/index.ts b/packages/jest-phabricator/src/index.ts index 0c1faa1cac99..7f509ebb3545 100644 --- a/packages/jest-phabricator/src/index.ts +++ b/packages/jest-phabricator/src/index.ts @@ -32,7 +32,7 @@ function summarize(coverageMap: TestResult.CoverageMap) { return summaries; } -module.exports = function( +export = function( results: TestResult.AggregatedResult, ): TestResult.AggregatedResult { return { From 3394b0d9b06c706b60a5f35361b2e33c6400b8a9 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sat, 23 Feb 2019 16:17:03 +0100 Subject: [PATCH 093/107] chore: remove custom coverage typings (#7967) --- .eslintrc.js | 2 +- packages/jest-phabricator/src/index.ts | 22 ++++---- packages/jest-types/package.json | 5 +- packages/jest-types/src/TestResult.ts | 70 ++------------------------ 4 files changed, 20 insertions(+), 79 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index db8aa1b063a8..347ca90d1ae8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -64,7 +64,7 @@ module.exports = { }, }, { - files: 'types/**/*', + files: ['types/**/*', 'packages/jest-types/**/*'], rules: { 'import/no-extraneous-dependencies': 0, }, diff --git a/packages/jest-phabricator/src/index.ts b/packages/jest-phabricator/src/index.ts index 7f509ebb3545..f4e6ffbbbe9a 100644 --- a/packages/jest-phabricator/src/index.ts +++ b/packages/jest-phabricator/src/index.ts @@ -7,7 +7,14 @@ import {TestResult} from '@jest/types'; -function summarize(coverageMap: TestResult.CoverageMap) { +type AggregatedResult = TestResult.AggregatedResult; +type CoverageMap = AggregatedResult['coverageMap']; + +function summarize(coverageMap: CoverageMap): CoverageMap { + if (!coverageMap) { + return coverageMap; + } + const summaries = Object.create(null); coverageMap.files().forEach(file => { @@ -15,9 +22,9 @@ function summarize(coverageMap: TestResult.CoverageMap) { const lineCoverage = coverageMap.fileCoverageFor(file).getLineCoverage(); Object.keys(lineCoverage).forEach(lineNumber => { + const number = parseInt(lineNumber, 10); // Line numbers start at one - const number = parseInt(lineNumber, 10) - 1; - covered[number] = lineCoverage[lineNumber] ? 'C' : 'U'; + covered[number - 1] = lineCoverage[number] ? 'C' : 'U'; }); for (let i = 0; i < covered.length; i++) { @@ -32,11 +39,6 @@ function summarize(coverageMap: TestResult.CoverageMap) { return summaries; } -export = function( - results: TestResult.AggregatedResult, -): TestResult.AggregatedResult { - return { - ...results, - coverageMap: results.coverageMap && summarize(results.coverageMap), - }; +export = function(results: AggregatedResult): AggregatedResult { + return {...results, coverageMap: summarize(results.coverageMap)}; }; diff --git a/packages/jest-types/package.json b/packages/jest-types/package.json index 86018bd9b822..7c7cd4c7538d 100644 --- a/packages/jest-types/package.json +++ b/packages/jest-types/package.json @@ -11,5 +11,8 @@ }, "license": "MIT", "main": "build/index.js", - "types": "build/index.d.ts" + "types": "build/index.d.ts", + "dependencies": { + "@types/istanbul-lib-coverage": "^1.1.0" + } } diff --git a/packages/jest-types/src/TestResult.ts b/packages/jest-types/src/TestResult.ts index 857751e6a9f0..c9434eae5a63 100644 --- a/packages/jest-types/src/TestResult.ts +++ b/packages/jest-types/src/TestResult.ts @@ -5,73 +5,9 @@ * LICENSE file in the root directory of this source tree. */ +import {CoverageMap, CoverageMapData} from 'istanbul-lib-coverage'; import {ConsoleBuffer} from './Console'; -export type RawFileCoverage = { - path: string; - s: { - [statementId: number]: number; - }; - b: { - [branchId: number]: number; - }; - f: { - [functionId: number]: number; - }; - l: { - [lineId: number]: number; - }; - fnMap: { - [functionId: number]: any; - }; - statementMap: { - [statementId: number]: any; - }; - branchMap: { - [branchId: number]: any; - }; - inputSourceMap?: Object; -}; - -export type RawCoverage = { - [filePath: string]: RawFileCoverage; -}; -type FileCoverageTotal = { - total: number; - covered: number; - skipped: number; - pct: number; -}; - -export type CoverageSummary = { - lines: FileCoverageTotal; - statements: FileCoverageTotal; - branches: FileCoverageTotal; - functions: FileCoverageTotal; - merge: (other: CoverageSummary) => undefined; -}; - -export type FileCoverage = { - getLineCoverage: () => {[line: string]: string}; - getUncoveredLines: () => Array; - getBranchCoverageByLine: () => {[line: string]: string}; - toJSON: () => {[line: string]: string}; - merge: (other: Object) => undefined; - computeSimpleTotals: (property: string) => FileCoverageTotal; - computeBranchTotals: () => FileCoverageTotal; - resetHits: () => undefined; - toSummary: () => CoverageSummary; -}; - -export type CoverageMap = { - merge: (data: Object) => undefined; - getCoverageSummary: () => FileCoverage; - data: RawCoverage; - addFileCoverage: (fileCoverage: RawFileCoverage) => undefined; - files: () => Array; - fileCoverageFor: (file: string) => FileCoverage; -}; - export type SerializableError = { code?: unknown; message: string; @@ -162,7 +98,7 @@ export type Suite = { export type TestResult = { console?: ConsoleBuffer | null; - coverage?: RawCoverage; + coverage?: CoverageMapData; displayName?: string | null; failureMessage?: string | null; leaks: boolean; @@ -226,7 +162,7 @@ export type FormattedTestResults = { export type CodeCoverageReporter = any; export type CodeCoverageFormatter = ( - coverage: RawCoverage | null | undefined, + coverage: CoverageMapData | null | undefined, reporter: CodeCoverageReporter, ) => Object | null | undefined; From 9467a736c3cfa14f65c56a21f483219ce626a8d8 Mon Sep 17 00:00:00 2001 From: Tim Seckinger Date: Sat, 23 Feb 2019 16:51:27 +0100 Subject: [PATCH 094/107] decentralize resolve types (#7966) --- .../moduleNameMapper.test.js.snap | 2 +- .../resolveNoFileExtensions.test.js.snap | 2 +- packages/jest-haste-map/src/index.ts | 1 + .../jest-resolve-dependencies/src/index.ts | 24 ++++++++++++------- packages/jest-resolve/src/index.ts | 14 ++++++++--- packages/jest-types/src/Resolve.ts | 17 ------------- packages/jest-types/src/index.ts | 2 -- 7 files changed, 30 insertions(+), 32 deletions(-) delete mode 100644 packages/jest-types/src/Resolve.ts diff --git a/e2e/__tests__/__snapshots__/moduleNameMapper.test.js.snap b/e2e/__tests__/__snapshots__/moduleNameMapper.test.js.snap index 687ca938186f..9609d706b96c 100644 --- a/e2e/__tests__/__snapshots__/moduleNameMapper.test.js.snap +++ b/e2e/__tests__/__snapshots__/moduleNameMapper.test.js.snap @@ -30,6 +30,6 @@ FAIL __tests__/index.js 12 | module.exports = () => 'test'; 13 | - at createNoMappedModuleFoundError (../../packages/jest-resolve/build/index.js:434:17) + at createNoMappedModuleFoundError (../../packages/jest-resolve/build/index.js:435:17) at Object.require (index.js:10:1) `; diff --git a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.js.snap b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.js.snap index 13dc33fad1c2..8bb20a9f7d29 100644 --- a/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.js.snap +++ b/e2e/__tests__/__snapshots__/resolveNoFileExtensions.test.js.snap @@ -33,6 +33,6 @@ FAIL __tests__/test.js | ^ 4 | - at Resolver.resolveModule (../../packages/jest-resolve/build/index.js:201:17) + at Resolver.resolveModule (../../packages/jest-resolve/build/index.js:202:17) at Object.require (index.js:3:18) `; diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index f809a46df037..fedc0f81a019 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -221,6 +221,7 @@ const getWhiteList = (list: Array | undefined): RegExp | null => { * Worker processes can directly access the cache through `HasteMap.read()`. * */ +/* eslint-disable-next-line no-redeclare */ class HasteMap extends EventEmitter { private _buildPromise: Promise | null; private _cachePath: Config.Path; diff --git a/packages/jest-resolve-dependencies/src/index.ts b/packages/jest-resolve-dependencies/src/index.ts index 583ab2f91f85..c0a081dffe0b 100644 --- a/packages/jest-resolve-dependencies/src/index.ts +++ b/packages/jest-resolve-dependencies/src/index.ts @@ -5,15 +5,23 @@ * LICENSE file in the root directory of this source tree. */ -import {Config, Resolve, Snapshot} from '@jest/types'; +import {Config, Snapshot} from '@jest/types'; import {FS as HasteFS} from 'jest-haste-map'; import Resolver from 'jest-resolve'; import {isSnapshotPath} from 'jest-snapshot'; +namespace DependencyResolver { + export type ResolvedModule = { + file: Config.Path; + dependencies: Config.Path[]; + }; +} + /** * DependencyResolver is used to resolve the direct dependencies of a module or * to retrieve a list of all transitive inverse dependencies. */ +/* eslint-disable-next-line no-redeclare */ class DependencyResolver { private _hasteFS: HasteFS; private _resolver: Resolver; @@ -31,7 +39,7 @@ class DependencyResolver { resolve( file: Config.Path, - options?: Resolve.ResolveModuleConfig, + options?: Resolver.ResolveModuleConfig, ): Array { const dependencies = this._hasteFS.getDependencies(file); if (!dependencies) { @@ -64,19 +72,19 @@ class DependencyResolver { resolveInverseModuleMap( paths: Set, filter: (file: Config.Path) => boolean, - options?: Resolve.ResolveModuleConfig, - ): Array { + options?: Resolver.ResolveModuleConfig, + ): Array { if (!paths.size) { return []; } const collectModules = ( related: Set, - moduleMap: Array, + moduleMap: Array, changed: Set, ) => { const visitedModules = new Set(); - const result: Array = []; + const result: Array = []; while (changed.size) { changed = new Set( moduleMap.reduce>((acc, module) => { @@ -116,7 +124,7 @@ class DependencyResolver { } } } - const modules: Array = []; + const modules: Array = []; for (const file of this._hasteFS.getAbsoluteFileIterator()) { modules.push({ dependencies: this.resolve(file, options), @@ -129,7 +137,7 @@ class DependencyResolver { resolveInverse( paths: Set, filter: (file: Config.Path) => boolean, - options?: Resolve.ResolveModuleConfig, + options?: Resolver.ResolveModuleConfig, ): Array { return this.resolveInverseModuleMap(paths, filter, options).map( module => module.file, diff --git a/packages/jest-resolve/src/index.ts b/packages/jest-resolve/src/index.ts index 666116b2db5d..c08f54cb9eab 100644 --- a/packages/jest-resolve/src/index.ts +++ b/packages/jest-resolve/src/index.ts @@ -6,7 +6,7 @@ */ import path from 'path'; -import {Config, Resolve} from '@jest/types'; +import {Config} from '@jest/types'; import {ModuleMap} from 'jest-haste-map'; import {sync as realpath} from 'realpath-native'; import chalk from 'chalk'; @@ -27,6 +27,13 @@ type FindNodeModuleConfig = { type BooleanObject = {[key: string]: boolean}; +namespace Resolver { + export type ResolveModuleConfig = { + skipNodeResolution?: boolean; + paths?: Config.Path[]; + }; +} + const NATIVE_PLATFORM = 'native'; // We might be inside a symlink. @@ -39,6 +46,7 @@ const nodePaths = process.env.NODE_PATH .map(p => path.resolve(resolvedCwd, p)) : null; +/* eslint-disable-next-line no-redeclare */ class Resolver { private readonly _options: ResolverConfig; private readonly _moduleMap: ModuleMap; @@ -97,7 +105,7 @@ class Resolver { resolveModuleFromDirIfExists( dirname: Config.Path, moduleName: string, - options?: Resolve.ResolveModuleConfig, + options?: Resolver.ResolveModuleConfig, ): Config.Path | null { const paths = (options && options.paths) || this._options.modulePaths; const moduleDirectory = this._options.moduleDirectories; @@ -180,7 +188,7 @@ class Resolver { resolveModule( from: Config.Path, moduleName: string, - options?: Resolve.ResolveModuleConfig, + options?: Resolver.ResolveModuleConfig, ): Config.Path { const dirname = path.dirname(from); const module = this.resolveModuleFromDirIfExists( diff --git a/packages/jest-types/src/Resolve.ts b/packages/jest-types/src/Resolve.ts deleted file mode 100644 index 61ce0329cb95..000000000000 --- a/packages/jest-types/src/Resolve.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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. - */ - -import {Path} from './Config'; - -export type ResolveModuleConfig = { - skipNodeResolution?: boolean; - paths?: Path[]; -}; -export type ResolvedModule = { - file: Path; - dependencies: Path[]; -}; diff --git a/packages/jest-types/src/index.ts b/packages/jest-types/src/index.ts index d845e25a2ff3..ed4c3acd0eac 100644 --- a/packages/jest-types/src/index.ts +++ b/packages/jest-types/src/index.ts @@ -10,7 +10,6 @@ import * as Console from './Console'; import * as Matchers from './Matchers'; import * as Mocks from './Mocks'; import * as PrettyFormat from './PrettyFormat'; -import * as Resolve from './Resolve'; import * as Snapshot from './Snapshot'; import * as SourceMaps from './SourceMaps'; import * as TestResult from './TestResult'; @@ -23,7 +22,6 @@ export { Matchers, Mocks, PrettyFormat, - Resolve, Snapshot, SourceMaps, TestResult, From 80523fd8a196adcf27c60b0b3805e399f3769b84 Mon Sep 17 00:00:00 2001 From: Tim Seckinger Date: Sun, 24 Feb 2019 18:39:09 +0100 Subject: [PATCH 095/107] decentralize jest-snapshot types (#7973) --- .../jest-resolve-dependencies/src/index.ts | 8 +++---- packages/jest-snapshot/src/index.ts | 13 +++++++--- .../jest-snapshot/src/snapshot_resolver.ts | 24 ++++++++++++------- packages/jest-types/src/Snapshot.ts | 14 ----------- packages/jest-types/src/index.ts | 2 -- 5 files changed, 29 insertions(+), 32 deletions(-) delete mode 100644 packages/jest-types/src/Snapshot.ts diff --git a/packages/jest-resolve-dependencies/src/index.ts b/packages/jest-resolve-dependencies/src/index.ts index c0a081dffe0b..71719bc090a4 100644 --- a/packages/jest-resolve-dependencies/src/index.ts +++ b/packages/jest-resolve-dependencies/src/index.ts @@ -5,10 +5,10 @@ * LICENSE file in the root directory of this source tree. */ -import {Config, Snapshot} from '@jest/types'; +import {Config} from '@jest/types'; import {FS as HasteFS} from 'jest-haste-map'; import Resolver from 'jest-resolve'; -import {isSnapshotPath} from 'jest-snapshot'; +import {isSnapshotPath, SnapshotResolver} from 'jest-snapshot'; namespace DependencyResolver { export type ResolvedModule = { @@ -25,12 +25,12 @@ namespace DependencyResolver { class DependencyResolver { private _hasteFS: HasteFS; private _resolver: Resolver; - private _snapshotResolver: Snapshot.SnapshotResolver; + private _snapshotResolver: SnapshotResolver; constructor( resolver: Resolver, hasteFS: HasteFS, - snapshotResolver: Snapshot.SnapshotResolver, + snapshotResolver: SnapshotResolver, ) { this._resolver = resolver; this._hasteFS = hasteFS; diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index 68a42e200c0a..0ed61078506c 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -6,7 +6,7 @@ */ import fs from 'fs'; -import {Config, Matchers, Snapshot} from '@jest/types'; +import {Config, Matchers} from '@jest/types'; import {FS as HasteFS} from 'jest-haste-map'; import diff from 'jest-diff'; @@ -14,6 +14,7 @@ import {EXPECTED_COLOR, matcherHint, RECEIVED_COLOR} from 'jest-matcher-utils'; import { buildSnapshotResolver, isSnapshotPath, + SnapshotResolver as JestSnapshotResolver, EXTENSION, } from './snapshot_resolver'; import SnapshotState from './State'; @@ -31,7 +32,7 @@ const fileExists = (filePath: Config.Path, hasteFS: HasteFS): boolean => const cleanup = ( hasteFS: HasteFS, update: Config.SnapshotUpdateState, - snapshotResolver: Snapshot.SnapshotResolver, + snapshotResolver: JestSnapshotResolver, ) => { const pattern = '\\.' + EXTENSION + '$'; const files = hasteFS.matchFiles(pattern); @@ -296,7 +297,7 @@ const _toThrowErrorMatchingSnapshot = ({ }); }; -export = { +const JestSnapshot = { EXTENSION, SnapshotState, addSerializer, @@ -310,3 +311,9 @@ export = { toThrowErrorMatchingSnapshot, utils, }; +/* eslint-disable-next-line no-redeclare */ +namespace JestSnapshot { + export type SnapshotResolver = JestSnapshotResolver; +} + +export = JestSnapshot; diff --git a/packages/jest-snapshot/src/snapshot_resolver.ts b/packages/jest-snapshot/src/snapshot_resolver.ts index 838345c30b23..824fb5307dfa 100644 --- a/packages/jest-snapshot/src/snapshot_resolver.ts +++ b/packages/jest-snapshot/src/snapshot_resolver.ts @@ -6,19 +6,25 @@ */ import path from 'path'; -import {Config, Snapshot} from '@jest/types'; +import {Config} from '@jest/types'; import chalk from 'chalk'; +export type SnapshotResolver = { + testPathForConsistencyCheck: string; + resolveSnapshotPath(testPath: Config.Path, extension?: string): Config.Path; + resolveTestPath(snapshotPath: Config.Path, extension?: string): Config.Path; +}; + export const EXTENSION = 'snap'; export const DOT_EXTENSION = '.' + EXTENSION; export const isSnapshotPath = (path: string): boolean => path.endsWith(DOT_EXTENSION); -const cache: Map = new Map(); +const cache: Map = new Map(); export const buildSnapshotResolver = ( config: Config.ProjectConfig, -): Snapshot.SnapshotResolver => { +): SnapshotResolver => { const key = config.rootDir; if (!cache.has(key)) { cache.set(key, createSnapshotResolver(config.snapshotResolver)); @@ -28,13 +34,13 @@ export const buildSnapshotResolver = ( function createSnapshotResolver( snapshotResolverPath?: Config.Path | null, -): Snapshot.SnapshotResolver { +): SnapshotResolver { return typeof snapshotResolverPath === 'string' ? createCustomSnapshotResolver(snapshotResolverPath) : createDefaultSnapshotResolver(); } -function createDefaultSnapshotResolver(): Snapshot.SnapshotResolver { +function createDefaultSnapshotResolver(): SnapshotResolver { return { resolveSnapshotPath: (testPath: Config.Path) => path.join( @@ -59,10 +65,10 @@ function createDefaultSnapshotResolver(): Snapshot.SnapshotResolver { function createCustomSnapshotResolver( snapshotResolverPath: Config.Path, -): Snapshot.SnapshotResolver { - const custom: Snapshot.SnapshotResolver = require(snapshotResolverPath); +): SnapshotResolver { + const custom: SnapshotResolver = require(snapshotResolverPath); - const keys: [keyof Snapshot.SnapshotResolver, string][] = [ + const keys: [keyof SnapshotResolver, string][] = [ ['resolveSnapshotPath', 'function'], ['resolveTestPath', 'function'], ['testPathForConsistencyCheck', 'string'], @@ -95,7 +101,7 @@ function mustImplement(propName: string, requiredType: string) { ); } -function verifyConsistentTransformations(custom: Snapshot.SnapshotResolver) { +function verifyConsistentTransformations(custom: SnapshotResolver) { const resolvedSnapshotPath = custom.resolveSnapshotPath( custom.testPathForConsistencyCheck, ); diff --git a/packages/jest-types/src/Snapshot.ts b/packages/jest-types/src/Snapshot.ts deleted file mode 100644 index c372b924d750..000000000000 --- a/packages/jest-types/src/Snapshot.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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. - */ - -import {Path} from './Config'; - -export type SnapshotResolver = { - testPathForConsistencyCheck: string; - resolveSnapshotPath(testPath: Path, extension?: string): Path; - resolveTestPath(snapshotPath: Path, extension?: string): Path; -}; diff --git a/packages/jest-types/src/index.ts b/packages/jest-types/src/index.ts index ed4c3acd0eac..102de09777a5 100644 --- a/packages/jest-types/src/index.ts +++ b/packages/jest-types/src/index.ts @@ -10,7 +10,6 @@ import * as Console from './Console'; import * as Matchers from './Matchers'; import * as Mocks from './Mocks'; import * as PrettyFormat from './PrettyFormat'; -import * as Snapshot from './Snapshot'; import * as SourceMaps from './SourceMaps'; import * as TestResult from './TestResult'; import * as Global from './Global'; @@ -22,7 +21,6 @@ export { Matchers, Mocks, PrettyFormat, - Snapshot, SourceMaps, TestResult, Global, From 8f7e6b465747aa0db32201d0798938013d95c209 Mon Sep 17 00:00:00 2001 From: Tim Seckinger Date: Sun, 24 Feb 2019 18:41:08 +0100 Subject: [PATCH 096/107] decentralize jest-mock types (#7971) --- CHANGELOG.md | 2 +- packages/jest-mock/src/index.ts | 63 ++++++++++++++++++++++---------- packages/jest-types/src/Mocks.ts | 31 ---------------- packages/jest-types/src/index.ts | 2 - 4 files changed, 45 insertions(+), 53 deletions(-) delete mode 100644 packages/jest-types/src/Mocks.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 227365427b90..3ae8ea7521dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,7 @@ - `[@jest/types]`: New package to handle shared types ([#7834](https://github.com/facebook/jest/pull/7834)) - `[jest-util]`: Migrate to TypeScript ([#7844](https://github.com/facebook/jest/pull/7844)) - `[jest-watcher]`: Migrate to TypeScript ([#7843](https://github.com/facebook/jest/pull/7843)) -- `[jest-mock]`: Migrate to TypeScript ([#7847](https://github.com/facebook/jest/pull/7847), [#7850](https://github.com/facebook/jest/pull/7850)) +- `[jest-mock]`: Migrate to TypeScript ([#7847](https://github.com/facebook/jest/pull/7847), [#7850](https://github.com/facebook/jest/pull/7850), [#7971](https://github.com/facebook/jest/pull/7971)) - `[jest-worker]`: Migrate to TypeScript ([#7853](https://github.com/facebook/jest/pull/7853)) - `[jest-haste-map]`: Migrate to TypeScript ([#7854](https://github.com/facebook/jest/pull/7854), [#7951](https://github.com/facebook/jest/pull/7951)) - `[docs]`: Fix image paths in SnapshotTesting.md for current and version 24 ([#7872](https://github.com/facebook/jest/pull/7872)) diff --git a/packages/jest-mock/src/index.ts b/packages/jest-mock/src/index.ts index 6a620ed27efe..a23a49d2fe36 100644 --- a/packages/jest-mock/src/index.ts +++ b/packages/jest-mock/src/index.ts @@ -5,10 +5,36 @@ * LICENSE file in the root directory of this source tree. */ -import {Mocks} from '@jest/types'; - type Global = NodeJS.Global; // | Window – add once TS improves typings; +namespace JestMock { + export type ModuleMocker = ModuleMockerClass; + export type MockFunctionMetadataType = + | 'object' + | 'array' + | 'regexp' + | 'function' + | 'constant' + | 'collection' + | 'null' + | 'undefined'; + + export type MockFunctionMetadata< + T, + Y extends unknown[], + Type = MockFunctionMetadataType + > = { + ref?: number; + members?: {[key: string]: MockFunctionMetadata}; + mockImpl?: (...args: Y) => T; + name?: string; + refID?: number; + type?: Type; + value?: T; + length?: number; + }; +} + /** * Possible types of a MockFunctionResult. * 'return': The call completed by returning normally. @@ -273,7 +299,7 @@ function getObjectType(value: unknown): string { return Object.prototype.toString.apply(value).slice(8, -1); } -function getType(ref?: unknown): Mocks.MockFunctionMetadataType | null { +function getType(ref?: unknown): JestMock.MockFunctionMetadataType | null { const typeName = getObjectType(ref); if ( typeName === 'Function' || @@ -449,19 +475,19 @@ class ModuleMockerClass { } private _makeComponent( - metadata: Mocks.MockFunctionMetadata, + metadata: JestMock.MockFunctionMetadata, restore?: () => void, ): Object; private _makeComponent( - metadata: Mocks.MockFunctionMetadata, + metadata: JestMock.MockFunctionMetadata, restore?: () => void, ): Array; private _makeComponent( - metadata: Mocks.MockFunctionMetadata, + metadata: JestMock.MockFunctionMetadata, restore?: () => void, ): RegExp; private _makeComponent( - metadata: Mocks.MockFunctionMetadata< + metadata: JestMock.MockFunctionMetadata< T, Y, 'constant' | 'collection' | 'null' | 'undefined' @@ -469,11 +495,11 @@ class ModuleMockerClass { restore?: () => void, ): T; private _makeComponent( - metadata: Mocks.MockFunctionMetadata, + metadata: JestMock.MockFunctionMetadata, restore?: () => void, ): Mock; private _makeComponent( - metadata: Mocks.MockFunctionMetadata, + metadata: JestMock.MockFunctionMetadata, restore?: () => void, ): Object | Array | RegExp | T | undefined | Mock { if (metadata.type === 'object') { @@ -719,7 +745,7 @@ class ModuleMockerClass { } private _createMockFunction( - metadata: Mocks.MockFunctionMetadata, + metadata: JestMock.MockFunctionMetadata, mockConstructor: Function, ): Function { let name = metadata.name; @@ -779,7 +805,7 @@ class ModuleMockerClass { } private _generateMock( - metadata: Mocks.MockFunctionMetadata, + metadata: JestMock.MockFunctionMetadata, callbacks: Array, refs: { [key: string]: @@ -829,7 +855,7 @@ class ModuleMockerClass { * getMetadata method of this module. */ generateFromMetadata( - _metadata: Mocks.MockFunctionMetadata, + _metadata: JestMock.MockFunctionMetadata, ): Mock { const callbacks: Function[] = []; const refs = {}; @@ -845,7 +871,7 @@ class ModuleMockerClass { getMetadata( component: T, _refs?: Map, - ): Mocks.MockFunctionMetadata | null { + ): JestMock.MockFunctionMetadata | null { const refs = _refs || new Map(); const ref = refs.get(component); if (ref != null) { @@ -857,7 +883,7 @@ class ModuleMockerClass { return null; } - const metadata: Mocks.MockFunctionMetadata = {type}; + const metadata: JestMock.MockFunctionMetadata = {type}; if ( type === 'constant' || type === 'collection' || @@ -880,7 +906,7 @@ class ModuleMockerClass { refs.set(component, metadata.refID); let members: { - [key: string]: Mocks.MockFunctionMetadata; + [key: string]: JestMock.MockFunctionMetadata; } | null = null; // Leave arrays alone if (type !== 'array') { @@ -1077,7 +1103,6 @@ class ModuleMockerClass { } } -// TODO: bring this type export back once done with TS migration -// export type ModuleMocker = ModuleMockerClass; - -export = new ModuleMockerClass(global); +/* eslint-disable-next-line no-redeclare */ +const JestMock = new ModuleMockerClass(global); +export = JestMock; diff --git a/packages/jest-types/src/Mocks.ts b/packages/jest-types/src/Mocks.ts deleted file mode 100644 index 36e28a49ca01..000000000000 --- a/packages/jest-types/src/Mocks.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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. - */ - -export type MockFunctionMetadataType = - | 'object' - | 'array' - | 'regexp' - | 'function' - | 'constant' - | 'collection' - | 'null' - | 'undefined'; - -export type MockFunctionMetadata< - T, - Y extends unknown[], - Type = MockFunctionMetadataType -> = { - ref?: number; - members?: {[key: string]: MockFunctionMetadata}; - mockImpl?: (...args: Y) => T; - name?: string; - refID?: number; - type?: Type; - value?: T; - length?: number; -}; diff --git a/packages/jest-types/src/index.ts b/packages/jest-types/src/index.ts index 102de09777a5..f233cb2eb733 100644 --- a/packages/jest-types/src/index.ts +++ b/packages/jest-types/src/index.ts @@ -8,7 +8,6 @@ import * as Config from './Config'; import * as Console from './Console'; import * as Matchers from './Matchers'; -import * as Mocks from './Mocks'; import * as PrettyFormat from './PrettyFormat'; import * as SourceMaps from './SourceMaps'; import * as TestResult from './TestResult'; @@ -19,7 +18,6 @@ export { Config, Console, Matchers, - Mocks, PrettyFormat, SourceMaps, TestResult, From ab838a62aae55795d0b8b3dec94529b48336239b Mon Sep 17 00:00:00 2001 From: Tim Seckinger Date: Sun, 24 Feb 2019 18:50:23 +0100 Subject: [PATCH 097/107] decentralize pretty-format types (#7972) --- CHANGELOG.md | 2 +- packages/jest-snapshot/src/mock_serializer.ts | 9 +- packages/jest-snapshot/src/plugins.ts | 7 +- packages/jest-types/src/PrettyFormat.ts | 117 ----------------- packages/jest-types/src/index.ts | 12 +- packages/pretty-format/src/index.ts | 74 ++++++----- packages/pretty-format/src/types.ts | 122 ++++++++++++++++-- 7 files changed, 162 insertions(+), 181 deletions(-) delete mode 100644 packages/jest-types/src/PrettyFormat.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ae8ea7521dd..3a098e0daece 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ ### Chore & Maintenance - `[*]`: Setup building, linting and testing of TypeScript ([#7808](https://github.com/facebook/jest/pull/7808), [#7855](https://github.com/facebook/jest/pull/7855), [#7951](https://github.com/facebook/jest/pull/7951)) -- `[pretty-format]`: Migrate to TypeScript ([#7809](https://github.com/facebook/jest/pull/7809)) +- `[pretty-format]`: Migrate to TypeScript ([#7809](https://github.com/facebook/jest/pull/7809), [#7809](https://github.com/facebook/jest/pull/7972)) - `[diff-sequences]`: Migrate to Typescript ([#7820](https://github.com/facebook/jest/pull/7820)) - `[jest-get-type]`: Migrate to TypeScript ([#7818](https://github.com/facebook/jest/pull/7818)) - `[jest-regex-util]`: Migrate to TypeScript ([#7822](https://github.com/facebook/jest/pull/7822)) diff --git a/packages/jest-snapshot/src/mock_serializer.ts b/packages/jest-snapshot/src/mock_serializer.ts index fbfaf7a1448c..c538e8468548 100644 --- a/packages/jest-snapshot/src/mock_serializer.ts +++ b/packages/jest-snapshot/src/mock_serializer.ts @@ -5,9 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -import {PrettyFormat} from '@jest/types'; +import {NewPlugin} from 'pretty-format'; -export const serialize: PrettyFormat.NewPlugin['serialize'] = ( +export const serialize: NewPlugin['serialize'] = ( val, config, indentation, @@ -42,9 +42,8 @@ export const serialize: PrettyFormat.NewPlugin['serialize'] = ( return '[MockFunction' + nameString + ']' + callsString; }; -export const test: PrettyFormat.NewPlugin['test'] = val => - val && !!val._isMockFunction; +export const test: NewPlugin['test'] = val => val && !!val._isMockFunction; -const plugin: PrettyFormat.NewPlugin = {serialize, test}; +const plugin: NewPlugin = {serialize, test}; export default plugin; diff --git a/packages/jest-snapshot/src/plugins.ts b/packages/jest-snapshot/src/plugins.ts index ebf50c0eb651..bd604c0c4286 100644 --- a/packages/jest-snapshot/src/plugins.ts +++ b/packages/jest-snapshot/src/plugins.ts @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import prettyFormat from 'pretty-format'; -import {PrettyFormat} from '@jest/types'; +import prettyFormat, {Plugin, Plugins} from 'pretty-format'; import jestMockSerializer from './mock_serializer'; @@ -19,7 +18,7 @@ const { AsymmetricMatcher, } = prettyFormat.plugins; -let PLUGINS: PrettyFormat.Plugins = [ +let PLUGINS: Plugins = [ ReactTestComponent, ReactElement, DOMElement, @@ -30,7 +29,7 @@ let PLUGINS: PrettyFormat.Plugins = [ ]; // Prepend to list so the last added is the first tested. -export const addSerializer = (plugin: PrettyFormat.Plugin) => { +export const addSerializer = (plugin: Plugin) => { PLUGINS = [plugin].concat(PLUGINS); }; diff --git a/packages/jest-types/src/PrettyFormat.ts b/packages/jest-types/src/PrettyFormat.ts deleted file mode 100644 index 89d06d5bd418..000000000000 --- a/packages/jest-types/src/PrettyFormat.ts +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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. - */ - -export type Colors = { - comment: {close: string; open: string}; - content: {close: string; open: string}; - prop: {close: string; open: string}; - tag: {close: string; open: string}; - value: {close: string; open: string}; -}; -type Indent = (arg0: string) => string; -export type Refs = Array; -type Print = (arg0: any) => string; - -export type Theme = { - comment: string; - content: string; - prop: string; - tag: string; - value: string; -}; - -type ThemeReceived = { - comment?: string; - content?: string; - prop?: string; - tag?: string; - value?: string; -}; - -export type Options = { - callToJSON: boolean; - escapeRegex: boolean; - escapeString: boolean; - highlight: boolean; - indent: number; - maxDepth: number; - min: boolean; - plugins: Plugins; - printFunctionName: boolean; - theme: Theme; -}; - -export type OptionsReceived = { - callToJSON?: boolean; - escapeRegex?: boolean; - escapeString?: boolean; - highlight?: boolean; - indent?: number; - maxDepth?: number; - min?: boolean; - plugins?: Plugins; - printFunctionName?: boolean; - theme?: ThemeReceived; -}; - -export type Config = { - callToJSON: boolean; - colors: Colors; - escapeRegex: boolean; - escapeString: boolean; - indent: string; - maxDepth: number; - min: boolean; - plugins: Plugins; - printFunctionName: boolean; - spacingInner: string; - spacingOuter: string; -}; - -export type Printer = ( - val: any, - config: Config, - indentation: string, - depth: number, - refs: Refs, - hasCalledToJSON?: boolean, -) => string; - -type Test = (arg0: any) => boolean; - -export type NewPlugin = { - serialize: ( - val: any, - config: Config, - indentation: string, - depth: number, - refs: Refs, - printer: Printer, - ) => string; - test: Test; -}; - -type PluginOptions = { - edgeSpacing: string; - min: boolean; - spacing: string; -}; - -type OldPlugin = { - print: ( - val: any, - print: Print, - indent: Indent, - options: PluginOptions, - colors: Colors, - ) => string; - test: Test; -}; - -export type Plugin = NewPlugin | OldPlugin; - -export type Plugins = Array; diff --git a/packages/jest-types/src/index.ts b/packages/jest-types/src/index.ts index f233cb2eb733..c99939515bae 100644 --- a/packages/jest-types/src/index.ts +++ b/packages/jest-types/src/index.ts @@ -8,19 +8,9 @@ import * as Config from './Config'; import * as Console from './Console'; import * as Matchers from './Matchers'; -import * as PrettyFormat from './PrettyFormat'; import * as SourceMaps from './SourceMaps'; import * as TestResult from './TestResult'; import * as Global from './Global'; import * as Environment from './Environment'; -export { - Config, - Console, - Matchers, - PrettyFormat, - SourceMaps, - TestResult, - Global, - Environment, -}; +export {Config, Console, Matchers, SourceMaps, TestResult, Global, Environment}; diff --git a/packages/pretty-format/src/index.ts b/packages/pretty-format/src/index.ts index 3a496046fdff..88455e20d7a7 100644 --- a/packages/pretty-format/src/index.ts +++ b/packages/pretty-format/src/index.ts @@ -6,17 +6,7 @@ */ import style from 'ansi-styles'; -import { - Colors, - Config, - Options, - OptionsReceived, - NewPlugin, - Plugin, - Plugins, - Refs, - Theme, -} from './types'; +import * as PrettyFormat from './types'; import { printIteratorEntries, @@ -179,10 +169,10 @@ function printBasicValue( */ function printComplexValue( val: any, - config: Config, + config: PrettyFormat.Config, indentation: string, depth: number, - refs: Refs, + refs: PrettyFormat.Refs, hasCalledToJSON?: boolean, ): string { if (refs.indexOf(val) !== -1) { @@ -261,17 +251,19 @@ function printComplexValue( '}'; } -function isNewPlugin(plugin: Plugin): plugin is NewPlugin { - return (plugin as NewPlugin).serialize != null; +function isNewPlugin( + plugin: PrettyFormat.Plugin, +): plugin is PrettyFormat.NewPlugin { + return (plugin as PrettyFormat.NewPlugin).serialize != null; } function printPlugin( - plugin: Plugin, + plugin: PrettyFormat.Plugin, val: any, - config: Config, + config: PrettyFormat.Config, indentation: string, depth: number, - refs: Refs, + refs: PrettyFormat.Refs, ): string { let printed; @@ -306,7 +298,7 @@ function printPlugin( return printed; } -function findPlugin(plugins: Plugins, val: any) { +function findPlugin(plugins: PrettyFormat.Plugins, val: any) { for (let p = 0; p < plugins.length; p++) { try { if (plugins[p].test(val)) { @@ -322,10 +314,10 @@ function findPlugin(plugins: Plugins, val: any) { function printer( val: any, - config: Config, + config: PrettyFormat.Config, indentation: string, depth: number, - refs: Refs, + refs: PrettyFormat.Refs, hasCalledToJSON?: boolean, ): string { const plugin = findPlugin(config.plugins, val); @@ -353,7 +345,7 @@ function printer( ); } -const DEFAULT_THEME: Theme = { +const DEFAULT_THEME: PrettyFormat.Theme = { comment: 'gray', content: 'reset', prop: 'yellow', @@ -363,7 +355,7 @@ const DEFAULT_THEME: Theme = { const DEFAULT_THEME_KEYS = Object.keys(DEFAULT_THEME); -const DEFAULT_OPTIONS: Options = { +const DEFAULT_OPTIONS: PrettyFormat.Options = { callToJSON: true, escapeRegex: false, escapeString: true, @@ -376,7 +368,7 @@ const DEFAULT_OPTIONS: Options = { theme: DEFAULT_THEME, }; -function validateOptions(options: OptionsReceived) { +function validateOptions(options: PrettyFormat.OptionsReceived) { Object.keys(options).forEach(key => { if (!DEFAULT_OPTIONS.hasOwnProperty(key)) { throw new Error(`pretty-format: Unknown option "${key}".`); @@ -402,7 +394,9 @@ function validateOptions(options: OptionsReceived) { } } -const getColorsHighlight = (options: OptionsReceived): Colors => +const getColorsHighlight = ( + options: PrettyFormat.OptionsReceived, +): PrettyFormat.Colors => DEFAULT_THEME_KEYS.reduce((colors, key) => { const value = options.theme && (options.theme as any)[key] !== undefined @@ -423,28 +417,30 @@ const getColorsHighlight = (options: OptionsReceived): Colors => return colors; }, Object.create(null)); -const getColorsEmpty = (): Colors => +const getColorsEmpty = (): PrettyFormat.Colors => DEFAULT_THEME_KEYS.reduce((colors, key) => { colors[key] = {close: '', open: ''}; return colors; }, Object.create(null)); -const getPrintFunctionName = (options?: OptionsReceived) => +const getPrintFunctionName = (options?: PrettyFormat.OptionsReceived) => options && options.printFunctionName !== undefined ? options.printFunctionName : DEFAULT_OPTIONS.printFunctionName; -const getEscapeRegex = (options?: OptionsReceived) => +const getEscapeRegex = (options?: PrettyFormat.OptionsReceived) => options && options.escapeRegex !== undefined ? options.escapeRegex : DEFAULT_OPTIONS.escapeRegex; -const getEscapeString = (options?: OptionsReceived) => +const getEscapeString = (options?: PrettyFormat.OptionsReceived) => options && options.escapeString !== undefined ? options.escapeString : DEFAULT_OPTIONS.escapeString; -const getConfig = (options?: OptionsReceived): Config => ({ +const getConfig = ( + options?: PrettyFormat.OptionsReceived, +): PrettyFormat.Config => ({ callToJSON: options && options.callToJSON !== undefined ? options.callToJSON @@ -486,7 +482,10 @@ function createIndent(indent: number): string { * @param val any potential JavaScript object * @param options Custom settings */ -function prettyFormat(val: any, options?: OptionsReceived): string { +function prettyFormat( + val: any, + options?: PrettyFormat.OptionsReceived, +): string { if (options) { validateOptions(options); if (options.plugins) { @@ -520,4 +519,17 @@ prettyFormat.plugins = { ReactTestComponent, }; +/* eslint-disable-next-line no-redeclare */ +namespace prettyFormat { + export type Colors = PrettyFormat.Colors; + export type Config = PrettyFormat.Config; + export type Options = PrettyFormat.Options; + export type OptionsReceived = PrettyFormat.OptionsReceived; + export type NewPlugin = PrettyFormat.NewPlugin; + export type Plugin = PrettyFormat.Plugin; + export type Plugins = PrettyFormat.Plugins; + export type Refs = PrettyFormat.Refs; + export type Theme = PrettyFormat.Theme; +} + export = prettyFormat; diff --git a/packages/pretty-format/src/types.ts b/packages/pretty-format/src/types.ts index 71f14783ed5f..89d06d5bd418 100644 --- a/packages/pretty-format/src/types.ts +++ b/packages/pretty-format/src/types.ts @@ -5,15 +5,113 @@ * LICENSE file in the root directory of this source tree. */ -import {PrettyFormat} from '@jest/types'; - -export type Colors = PrettyFormat.Colors; -export type Config = PrettyFormat.Config; -export type Options = PrettyFormat.Options; -export type OptionsReceived = PrettyFormat.OptionsReceived; -export type NewPlugin = PrettyFormat.NewPlugin; -export type Plugin = PrettyFormat.Plugin; -export type Plugins = PrettyFormat.Plugins; -export type Refs = PrettyFormat.Refs; -export type Theme = PrettyFormat.Theme; -export type Printer = PrettyFormat.Printer; +export type Colors = { + comment: {close: string; open: string}; + content: {close: string; open: string}; + prop: {close: string; open: string}; + tag: {close: string; open: string}; + value: {close: string; open: string}; +}; +type Indent = (arg0: string) => string; +export type Refs = Array; +type Print = (arg0: any) => string; + +export type Theme = { + comment: string; + content: string; + prop: string; + tag: string; + value: string; +}; + +type ThemeReceived = { + comment?: string; + content?: string; + prop?: string; + tag?: string; + value?: string; +}; + +export type Options = { + callToJSON: boolean; + escapeRegex: boolean; + escapeString: boolean; + highlight: boolean; + indent: number; + maxDepth: number; + min: boolean; + plugins: Plugins; + printFunctionName: boolean; + theme: Theme; +}; + +export type OptionsReceived = { + callToJSON?: boolean; + escapeRegex?: boolean; + escapeString?: boolean; + highlight?: boolean; + indent?: number; + maxDepth?: number; + min?: boolean; + plugins?: Plugins; + printFunctionName?: boolean; + theme?: ThemeReceived; +}; + +export type Config = { + callToJSON: boolean; + colors: Colors; + escapeRegex: boolean; + escapeString: boolean; + indent: string; + maxDepth: number; + min: boolean; + plugins: Plugins; + printFunctionName: boolean; + spacingInner: string; + spacingOuter: string; +}; + +export type Printer = ( + val: any, + config: Config, + indentation: string, + depth: number, + refs: Refs, + hasCalledToJSON?: boolean, +) => string; + +type Test = (arg0: any) => boolean; + +export type NewPlugin = { + serialize: ( + val: any, + config: Config, + indentation: string, + depth: number, + refs: Refs, + printer: Printer, + ) => string; + test: Test; +}; + +type PluginOptions = { + edgeSpacing: string; + min: boolean; + spacing: string; +}; + +type OldPlugin = { + print: ( + val: any, + print: Print, + indent: Indent, + options: PluginOptions, + colors: Colors, + ) => string; + test: Test; +}; + +export type Plugin = NewPlugin | OldPlugin; + +export type Plugins = Array; From 7f077743196544920c314a1e0207c17832cd554d Mon Sep 17 00:00:00 2001 From: Alcedo Nathaniel De Guzman Jr Date: Mon, 25 Feb 2019 17:59:14 +0800 Subject: [PATCH 098/107] Migrate jest-runner to typescript (#7968) --- CHANGELOG.md | 1 + packages/jest-runner/package.json | 3 + .../src/__tests__/testRunner.test.js | 1 - .../jest-runner/src/{index.js => index.ts} | 49 +++++----- .../src/{runTest.js => runTest.ts} | 87 +++++++++-------- .../src/{testWorker.js => testWorker.ts} | 37 ++++---- packages/jest-runner/src/types.ts | 94 +++++++++++++++++++ packages/jest-runner/tsconfig.json | 17 ++++ packages/jest-types/src/Global.ts | 2 +- packages/jest-worker/src/types.ts | 16 +--- 10 files changed, 212 insertions(+), 95 deletions(-) rename packages/jest-runner/src/{index.js => index.ts} (83%) rename packages/jest-runner/src/{runTest.js => runTest.ts} (78%) rename packages/jest-runner/src/{testWorker.js => testWorker.ts} (73%) create mode 100644 packages/jest-runner/src/types.ts create mode 100644 packages/jest-runner/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a098e0daece..144909b5c314 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ - `[expect]`: Migrate to TypeScript ([#7919](https://github.com/facebook/jest/pull/7919)) - `[jest-circus]`: Migrate to TypeScript ([#7916](https://github.com/facebook/jest/pull/7916)) - `[jest-phabricator]`: Migrate to TypeScript ([#7965](https://github.com/facebook/jest/pull/7965)) +- `[jest-runner]`: Migrate to TypeScript ([#7968](https://github.com/facebook/jest/pull/7968)) ### Performance diff --git a/packages/jest-runner/package.json b/packages/jest-runner/package.json index 98ecf80ca1e7..67cd2369cb63 100644 --- a/packages/jest-runner/package.json +++ b/packages/jest-runner/package.json @@ -8,7 +8,9 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { + "@jest/types": "^24.1.0", "chalk": "^2.4.2", "exit": "^0.1.2", "graceful-fs": "^4.1.15", @@ -18,6 +20,7 @@ "jest-jasmine2": "^24.1.0", "jest-leak-detector": "^24.0.0", "jest-message-util": "^24.0.0", + "jest-resolve": "^24.1.0", "jest-runtime": "^24.1.0", "jest-util": "^24.0.0", "jest-worker": "^24.0.0", diff --git a/packages/jest-runner/src/__tests__/testRunner.test.js b/packages/jest-runner/src/__tests__/testRunner.test.js index 9601969f012d..ca604b10c0ee 100644 --- a/packages/jest-runner/src/__tests__/testRunner.test.js +++ b/packages/jest-runner/src/__tests__/testRunner.test.js @@ -7,7 +7,6 @@ */ import {TestWatcher} from '@jest/core'; -// eslint-disable-next-line import/default import TestRunner from '../index'; let mockWorkerFarm; diff --git a/packages/jest-runner/src/index.js b/packages/jest-runner/src/index.ts similarity index 83% rename from packages/jest-runner/src/index.js rename to packages/jest-runner/src/index.ts index 538978903fd8..e50e94fcf8e3 100644 --- a/packages/jest-runner/src/index.js +++ b/packages/jest-runner/src/index.ts @@ -3,12 +3,15 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {GlobalConfig} from 'types/Config'; -import type { +import {Config, TestResult} from '@jest/types'; +import exit from 'exit'; +import throat from 'throat'; +import Worker from 'jest-worker'; +import runTest from './runTest'; +import {worker} from './testWorker'; +import { OnTestFailure, OnTestStart, OnTestSuccess, @@ -16,24 +19,20 @@ import type { TestRunnerContext, TestRunnerOptions, TestWatcher, -} from 'types/TestRunner'; - -import typeof {worker} from './testWorker'; - -import exit from 'exit'; -import runTest from './runTest'; -import throat from 'throat'; -import Worker from 'jest-worker'; + WatcherState, +} from './types'; const TEST_WORKER_PATH = require.resolve('./testWorker'); -type WorkerInterface = Worker & {worker: worker}; +interface WorkerInterface extends Worker { + worker: typeof worker; +} class TestRunner { - _globalConfig: GlobalConfig; - _context: TestRunnerContext; + private _globalConfig: Config.GlobalConfig; + private _context: TestRunnerContext; - constructor(globalConfig: GlobalConfig, context?: TestRunnerContext) { + constructor(globalConfig: Config.GlobalConfig, context?: TestRunnerContext) { this._globalConfig = globalConfig; this._context = context || {}; } @@ -57,7 +56,7 @@ class TestRunner { )); } - async _createInBandTestRun( + private async _createInBandTestRun( tests: Array, watcher: TestWatcher, onStart: OnTestStart, @@ -91,19 +90,19 @@ class TestRunner { ); } - async _createParallelTestRun( + private async _createParallelTestRun( tests: Array, watcher: TestWatcher, onStart: OnTestStart, onResult: OnTestSuccess, onFailure: OnTestFailure, ) { - const worker: WorkerInterface = new Worker(TEST_WORKER_PATH, { + const worker = new Worker(TEST_WORKER_PATH, { exposedMethods: ['worker'], forkOptions: {stdio: 'pipe'}, maxRetries: 3, numWorkers: this._globalConfig.maxWorkers, - }); + }) as WorkerInterface; if (worker.getStdout()) worker.getStdout().pipe(process.stdout); if (worker.getStderr()) worker.getStderr().pipe(process.stderr); @@ -112,7 +111,7 @@ class TestRunner { // Send test suites to workers continuously instead of all at once to track // the start time of individual tests. - const runTestInWorker = test => + const runTestInWorker = (test: Test) => mutex(async () => { if (watcher.isInterrupted()) { return Promise.reject(); @@ -131,7 +130,7 @@ class TestRunner { }); }); - const onError = async (err, test) => { + const onError = async (err: TestResult.SerializableError, test: Test) => { await onFailure(test, err); if (err.type === 'ProcessTerminatedError') { console.error( @@ -143,7 +142,7 @@ class TestRunner { }; const onInterrupt = new Promise((_, reject) => { - watcher.on('change', state => { + watcher.on('change', (state: WatcherState) => { if (state.interrupted) { reject(new CancelRun()); } @@ -164,10 +163,10 @@ class TestRunner { } class CancelRun extends Error { - constructor(message: ?string) { + constructor(message?: string) { super(message); this.name = 'CancelRun'; } } -module.exports = TestRunner; +export = TestRunner; diff --git a/packages/jest-runner/src/runTest.js b/packages/jest-runner/src/runTest.ts similarity index 78% rename from packages/jest-runner/src/runTest.js rename to packages/jest-runner/src/runTest.ts index 88fe237666cb..7b581863bb2a 100644 --- a/packages/jest-runner/src/runTest.js +++ b/packages/jest-runner/src/runTest.ts @@ -4,16 +4,16 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ -import type {EnvironmentClass} from 'types/Environment'; -import type {GlobalConfig, Path, ProjectConfig} from 'types/Config'; -import type {Resolver} from 'types/Resolve'; -import type {TestFramework, TestRunnerContext} from 'types/TestRunner'; -import type {TestResult} from 'types/TestResult'; -import type RuntimeClass from 'jest-runtime'; - +import { + Environment, + Config, + TestResult, + Console as ConsoleType, +} from '@jest/types'; +// @ts-ignore: not migrated to TS +import RuntimeClass from 'jest-runtime'; import fs from 'graceful-fs'; import { BufferedConsole, @@ -24,22 +24,31 @@ import { setGlobal, } from 'jest-util'; import LeakDetector from 'jest-leak-detector'; +import Resolver from 'jest-resolve'; +// @ts-ignore: not migrated to TS import {getTestEnvironment} from 'jest-config'; import * as docblock from 'jest-docblock'; import {formatExecError} from 'jest-message-util'; -import sourcemapSupport from 'source-map-support'; +import sourcemapSupport, { + Options as SourceMapOptions, +} from 'source-map-support'; import chalk from 'chalk'; +import {TestFramework, TestRunnerContext} from './types'; type RunTestInternalResult = { - leakDetector: ?LeakDetector, - result: TestResult, + leakDetector: LeakDetector | null; + result: TestResult.TestResult; }; function freezeConsole( + // @ts-ignore: Correct types when `jest-util` is ESM testConsole: BufferedConsole | Console | NullConsole, - config: ProjectConfig, + config: Config.ProjectConfig, ) { - testConsole._log = function fakeConsolePush(_type, message) { + testConsole._log = function fakeConsolePush( + _type: ConsoleType.LogType, + message: ConsoleType.LogMessage, + ) { const error = new ErrorWithStack( `${chalk.red( `${chalk.bold( @@ -73,11 +82,11 @@ function freezeConsole( // references to verify if there is a leak, which is not maintainable and error // prone. That's why "runTestInternal" CANNOT be inlined inside "runTest". async function runTestInternal( - path: Path, - globalConfig: GlobalConfig, - config: ProjectConfig, + path: Config.Path, + globalConfig: Config.GlobalConfig, + config: Config.ProjectConfig, resolver: Resolver, - context: ?TestRunnerContext, + context?: TestRunnerContext, ): Promise { const testSource = fs.readFileSync(path, 'utf8'); const parsedDocblock = docblock.parse(docblock.extract(testSource)); @@ -92,21 +101,22 @@ async function runTestInternal( }); } - /* $FlowFixMe */ - const TestEnvironment = (require(testEnvironment): EnvironmentClass); - const testFramework = ((process.env.JEST_CIRCUS === '1' - ? require('jest-circus/runner') // eslint-disable-line import/no-extraneous-dependencies - : /* $FlowFixMe */ - require(config.testRunner)): TestFramework); - const Runtime = ((config.moduleLoader - ? /* $FlowFixMe */ - require(config.moduleLoader) - : require('jest-runtime')): Class); + const TestEnvironment: Environment.EnvironmentClass = require(testEnvironment); + const testFramework: TestFramework = + process.env.JEST_CIRCUS === '1' + ? require('jest-circus/runner') // eslint-disable-line import/no-extraneous-dependencies + : require(config.testRunner); + const Runtime: RuntimeClass = config.moduleLoader + ? require(config.moduleLoader) + : require('jest-runtime'); - let runtime = undefined; + let runtime: RuntimeClass = undefined; const consoleOut = globalConfig.useStderr ? process.stderr : process.stdout; - const consoleFormatter = (type, message) => + const consoleFormatter = ( + type: ConsoleType.LogType, + message: ConsoleType.LogMessage, + ) => getConsoleOutput( config.cwd, !!globalConfig.verbose, @@ -150,7 +160,7 @@ async function runTestInternal( const start = Date.now(); - const sourcemapOptions = { + const sourcemapOptions: SourceMapOptions = { environment: 'node', handleUncaughtExceptions: false, retrieveSourceMap: source => { @@ -160,7 +170,7 @@ async function runTestInternal( if (sourceMapSource) { try { return { - map: JSON.parse(fs.readFileSync(sourceMapSource)), + map: JSON.parse(fs.readFileSync(sourceMapSource, 'utf8')), url: source, }; } catch (e) {} @@ -187,7 +197,7 @@ async function runTestInternal( ) { const realExit = environment.global.process.exit; - environment.global.process.exit = function exit(...args) { + environment.global.process.exit = function exit(...args: Array) { const error = new ErrorWithStack( `process.exit called with "${args.join(', ')}"`, exit, @@ -210,7 +220,7 @@ async function runTestInternal( try { await environment.setup(); - let result: TestResult; + let result: TestResult.TestResult; try { result = await testFramework( @@ -259,17 +269,18 @@ async function runTestInternal( } finally { await environment.teardown(); + // @ts-ignore: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/33351 sourcemapSupport.resetRetrieveHandlers(); } } export default async function runTest( - path: Path, - globalConfig: GlobalConfig, - config: ProjectConfig, + path: Config.Path, + globalConfig: Config.GlobalConfig, + config: Config.ProjectConfig, resolver: Resolver, - context: ?TestRunnerContext, -): Promise { + context?: TestRunnerContext, +): Promise { const {leakDetector, result} = await runTestInternal( path, globalConfig, diff --git a/packages/jest-runner/src/testWorker.js b/packages/jest-runner/src/testWorker.ts similarity index 73% rename from packages/jest-runner/src/testWorker.js rename to packages/jest-runner/src/testWorker.ts index 1651803861e6..51c8fbc3dd1c 100644 --- a/packages/jest-runner/src/testWorker.js +++ b/packages/jest-runner/src/testWorker.ts @@ -4,28 +4,24 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow */ -import type {GlobalConfig, Path, ProjectConfig} from 'types/Config'; -import type {SerializableError, TestResult} from 'types/TestResult'; -import type {SerializableModuleMap} from 'types/HasteMap'; -import type {ErrorWithCode} from 'types/Errors'; -import type {TestRunnerContext} from 'types/TestRunner'; - +import {Config, TestResult} from '@jest/types'; +import HasteMap, {SerializableModuleMap, ModuleMap} from 'jest-haste-map'; import exit from 'exit'; -import HasteMap from 'jest-haste-map'; import {separateMessageFromStack} from 'jest-message-util'; +// @ts-ignore: Not migrated to TS import Runtime from 'jest-runtime'; +import {ErrorWithCode, TestRunnerContext} from './types'; import runTest from './runTest'; -export type WorkerData = {| - config: ProjectConfig, - globalConfig: GlobalConfig, - path: Path, - serializableModuleMap: ?SerializableModuleMap, - context?: TestRunnerContext, -|}; +type WorkerData = { + config: Config.ProjectConfig; + globalConfig: Config.GlobalConfig; + path: Config.Path; + serializableModuleMap: SerializableModuleMap | null; + context?: TestRunnerContext; +}; // Make sure uncaught errors are logged before we exit. process.on('uncaughtException', err => { @@ -33,7 +29,9 @@ process.on('uncaughtException', err => { exit(1); }); -const formatError = (error: string | ErrorWithCode): SerializableError => { +const formatError = ( + error: string | ErrorWithCode, +): TestResult.SerializableError => { if (typeof error === 'string') { const {message, stack} = separateMessageFromStack(error); return { @@ -52,7 +50,10 @@ const formatError = (error: string | ErrorWithCode): SerializableError => { }; const resolvers = Object.create(null); -const getResolver = (config, moduleMap) => { +const getResolver = ( + config: Config.ProjectConfig, + moduleMap: ModuleMap | null, +) => { // In watch mode, the raw module map with all haste modules is passed from // the test runner to the watch command. This is because jest-haste-map's // watch mode does not persist the haste map on disk after every file change. @@ -77,7 +78,7 @@ export async function worker({ path, serializableModuleMap, context, -}: WorkerData): Promise { +}: WorkerData): Promise { try { const moduleMap = serializableModuleMap ? HasteMap.ModuleMap.fromJSON(serializableModuleMap) diff --git a/packages/jest-runner/src/types.ts b/packages/jest-runner/src/types.ts new file mode 100644 index 000000000000..5f1aeb7c557a --- /dev/null +++ b/packages/jest-runner/src/types.ts @@ -0,0 +1,94 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import {EventEmitter} from 'events'; +import {Environment, Config, TestResult} from '@jest/types'; +import {ModuleMap, FS as HasteFS} from 'jest-haste-map'; +import HasteResolver from 'jest-resolve'; +// @ts-ignore: not migrated to TS +import Runtime from 'jest-runtime'; + +export type ErrorWithCode = Error & {code?: string}; +export type Test = { + context: Context; + duration?: number; + path: Config.Path; +}; + +export type Context = { + config: Config.ProjectConfig; + hasteFS: HasteFS; + moduleMap: ModuleMap; + resolver: HasteResolver; +}; + +// TODO: Obtain this from @jest/reporters once its been migrated +type ReporterOnStartOptions = { + estimatedTime: number; + showStatus: boolean; +}; + +export type OnTestStart = (test: Test) => Promise; +export type OnTestFailure = ( + test: Test, + serializableError: TestResult.SerializableError, +) => Promise; +export type OnTestSuccess = ( + test: Test, + testResult: TestResult.TestResult, +) => Promise; + +export type Reporter = { + onTestResult: ( + test: Test, + testResult: TestResult.TestResult, + aggregatedResult: TestResult.AggregatedResult, + ) => Promise; + onRunStart: ( + results: TestResult.AggregatedResult, + options: ReporterOnStartOptions, + ) => Promise; + onTestStart: (test: Test) => Promise; + onRunComplete: ( + contexts: Set, + results: TestResult.AggregatedResult, + ) => Promise; + getLastError: () => Error; +}; + +export type TestFramework = ( + globalConfig: Config.GlobalConfig, + config: Config.ProjectConfig, + environment: Environment.Environment, + runtime: Runtime, + testPath: string, +) => Promise; + +export type TestRunnerOptions = { + serial: boolean; +}; + +export type TestRunnerContext = { + changedFiles?: Set; +}; + +export type TestRunData = Array<{ + context: Context; + matches: {allTests: number; tests: Array; total: number}; +}>; + +// TODO: Should live in `@jest/core` or `jest-watcher` +export type WatcherState = { + interrupted: boolean; +}; +export interface TestWatcher extends EventEmitter { + state: WatcherState; + new ({isWatchMode}: {isWatchMode: boolean}): TestWatcher; + setState(state: WatcherState): void; + isInterrupted(): boolean; + isWatchMode(): boolean; +} diff --git a/packages/jest-runner/tsconfig.json b/packages/jest-runner/tsconfig.json new file mode 100644 index 000000000000..59665ff7c74e --- /dev/null +++ b/packages/jest-runner/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [ + {"path": "../jest-docblock"}, + {"path": "../jest-haste-map"}, + {"path": "../jest-leak-detector"}, + {"path": "../jest-message-util"}, + {"path": "../jest-resolve"}, + {"path": "../jest-types"}, + {"path": "../jest-worker"}, + {"path": "../jest-util"} + ] +} diff --git a/packages/jest-types/src/Global.ts b/packages/jest-types/src/Global.ts index ed7739615e14..1a782eaaa386 100644 --- a/packages/jest-types/src/Global.ts +++ b/packages/jest-types/src/Global.ts @@ -48,7 +48,7 @@ export interface Describe extends DescribeBase { skip: ItBase; } -export interface Global { +export interface Global extends NodeJS.Global { it: It; test: ItConcurrent; fit: ItBase; diff --git a/packages/jest-worker/src/types.ts b/packages/jest-worker/src/types.ts index f0f4d86aecae..ce25219f525c 100644 --- a/packages/jest-worker/src/types.ts +++ b/packages/jest-worker/src/types.ts @@ -5,6 +5,9 @@ * LICENSE file in the root directory of this source tree. */ +import {EventEmitter} from 'events'; +import {ForkOptions} from 'child_process'; + // Because of the dynamic nature of a worker communication process, all messages // coming from any of the other processes cannot be typed. Thus, many types // include "unknown" as a TS type, which is (unfortunately) correct here. @@ -23,18 +26,7 @@ export type PARENT_MESSAGE_ERROR = // Option objects. -const EventEmitter = require('events'); - -export type ForkOptions = { - cwd?: string; - env?: NodeJS.ProcessEnv; - execPath?: string; - execArgv?: Array; - silent?: boolean; - stdio?: Array; - uid?: number; - gid?: number; -}; +export {ForkOptions}; export interface WorkerPoolInterface { getStderr(): NodeJS.ReadableStream; From 8d38dceaebc742d8eacedf9e00aebfe58cd108ee Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 25 Feb 2019 12:17:09 +0100 Subject: [PATCH 099/107] chore: migrate jest-runtime to TypeScript (#7964) --- CHANGELOG.md | 1 + packages/jest-circus/package.json | 1 + .../legacy-code-todo-rewrite/jestAdapter.ts | 9 +- packages/jest-circus/tsconfig.json | 12 +- packages/jest-environment/.npmignore | 3 + packages/jest-environment/package.json | 22 + packages/jest-environment/src/index.ts | 260 ++++++++++++ packages/jest-environment/tsconfig.json | 12 + packages/jest-resolve/src/index.ts | 4 +- packages/jest-resolve/src/types.ts | 6 +- packages/jest-runner/package.json | 1 + packages/jest-runner/src/runTest.ts | 15 +- packages/jest-runner/src/testWorker.ts | 1 - packages/jest-runner/src/types.ts | 6 +- packages/jest-runner/tsconfig.json | 2 + packages/jest-runtime/package.json | 6 +- .../jest-runtime/src/cli/{args.js => args.ts} | 9 +- .../src/cli/{index.js => index.ts} | 30 +- .../src/{helpers.js => helpers.ts} | 9 +- .../jest-runtime/src/{index.js => index.ts} | 379 +++++++++--------- packages/jest-runtime/src/types.ts | 17 + packages/jest-runtime/src/version.ts | 9 + packages/jest-runtime/tsconfig.json | 20 + .../jest-transform/src/ScriptTransformer.ts | 3 +- packages/jest-transform/src/index.ts | 2 +- .../jest-transform/src/shouldInstrument.ts | 4 +- packages/jest-transform/src/types.ts | 15 +- packages/jest-types/src/Config.ts | 94 ++++- packages/jest-types/src/Environment.ts | 44 -- packages/jest-types/src/Global.ts | 8 + packages/jest-types/src/index.ts | 3 +- 31 files changed, 705 insertions(+), 302 deletions(-) create mode 100644 packages/jest-environment/.npmignore create mode 100644 packages/jest-environment/package.json create mode 100644 packages/jest-environment/src/index.ts create mode 100644 packages/jest-environment/tsconfig.json rename packages/jest-runtime/src/cli/{args.js => args.ts} (87%) rename packages/jest-runtime/src/cli/{index.js => index.ts} (79%) rename packages/jest-runtime/src/{helpers.js => helpers.ts} (91%) rename packages/jest-runtime/src/{index.js => index.ts} (79%) create mode 100644 packages/jest-runtime/src/types.ts create mode 100644 packages/jest-runtime/src/version.ts create mode 100644 packages/jest-runtime/tsconfig.json delete mode 100644 packages/jest-types/src/Environment.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 144909b5c314..181243fc7757 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ - `[jest-circus]`: Migrate to TypeScript ([#7916](https://github.com/facebook/jest/pull/7916)) - `[jest-phabricator]`: Migrate to TypeScript ([#7965](https://github.com/facebook/jest/pull/7965)) - `[jest-runner]`: Migrate to TypeScript ([#7968](https://github.com/facebook/jest/pull/7968)) +- `[jest-runtime]`: Migrate to TypeScript ([#7964](https://github.com/facebook/jest/pull/7964)) ### Performance diff --git a/packages/jest-circus/package.json b/packages/jest-circus/package.json index 1986ebe211cf..d137fb0d5273 100644 --- a/packages/jest-circus/package.json +++ b/packages/jest-circus/package.json @@ -11,6 +11,7 @@ "types": "build/index.d.ts", "dependencies": { "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.1.0", "@jest/types": "^24.1.0", "@types/node": "*", "chalk": "^2.0.1", diff --git a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts index 84f32c39e2f4..7064ad335553 100644 --- a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts @@ -6,9 +6,10 @@ */ import path from 'path'; -import {Config, TestResult, Environment} from '@jest/types'; -// @ts-ignore TODO Remove ignore when jest-runtime is migrated to ts -import Runtime from 'jest-runtime'; // eslint-disable-line import/no-extraneous-dependencies +import {Config, TestResult} from '@jest/types'; +import {JestEnvironment} from '@jest/environment'; +// eslint-disable-next-line import/no-extraneous-dependencies +import Runtime from 'jest-runtime'; import {SnapshotState} from 'jest-snapshot'; const FRAMEWORK_INITIALIZER = require.resolve('./jestAdapterInit'); @@ -16,7 +17,7 @@ const FRAMEWORK_INITIALIZER = require.resolve('./jestAdapterInit'); const jestAdapter = async ( globalConfig: Config.GlobalConfig, config: Config.ProjectConfig, - environment: Environment.$JestEnvironment, + environment: JestEnvironment, runtime: Runtime, testPath: string, ): Promise => { diff --git a/packages/jest-circus/tsconfig.json b/packages/jest-circus/tsconfig.json index 3cb4125e6ac7..726c0ac6b1e5 100644 --- a/packages/jest-circus/tsconfig.json +++ b/packages/jest-circus/tsconfig.json @@ -4,5 +4,15 @@ "outDir": "build", "rootDir": "src" }, - "references": [{"path": "../jest-types"}, {"path": "../jest-snapshot"}, {"path": "../jest-matcher-utils"}, {"path": "../jest-message-util"}, {"path": "../jest-util"}, {"path": "../pretty-format"}, {"path": "../jest-diff"}] + "references": [ + {"path": "../jest-diff"}, + {"path": "../jest-environment"}, + {"path": "../jest-matcher-utils"}, + {"path": "../jest-message-util"}, + {"path": "../jest-runtime"}, + {"path": "../jest-snapshot"}, + {"path": "../jest-types"}, + {"path": "../jest-util"}, + {"path": "../pretty-format"} + ] } diff --git a/packages/jest-environment/.npmignore b/packages/jest-environment/.npmignore new file mode 100644 index 000000000000..85e48fe7b0a4 --- /dev/null +++ b/packages/jest-environment/.npmignore @@ -0,0 +1,3 @@ +**/__mocks__/** +**/__tests__/** +src diff --git a/packages/jest-environment/package.json b/packages/jest-environment/package.json new file mode 100644 index 000000000000..d1888425032d --- /dev/null +++ b/packages/jest-environment/package.json @@ -0,0 +1,22 @@ +{ + "name": "@jest/environment", + "version": "24.1.0", + "repository": { + "type": "git", + "url": "https://github.com/facebook/jest.git", + "directory": "packages/jest-environment" + }, + "license": "MIT", + "main": "build/index.js", + "types": "build/index.d.ts", + "dependencies": { + "@jest/transform": "^24.1.0", + "@jest/types": "^24.1.0", + "@types/node": "*", + "jest-mock": "^24.0.0" + }, + "engines": { + "node": ">= 6" + }, + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" +} diff --git a/packages/jest-environment/src/index.ts b/packages/jest-environment/src/index.ts new file mode 100644 index 000000000000..8f9d1e08502e --- /dev/null +++ b/packages/jest-environment/src/index.ts @@ -0,0 +1,260 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import {Script} from 'vm'; +import {Config, Global} from '@jest/types'; +import moduleMocker from 'jest-mock'; +import {ScriptTransformer} from '@jest/transform'; + +export type EnvironmentContext = { + console?: Console; + testPath?: Config.Path; +}; + +// TODO: type this better: https://nodejs.org/api/modules.html#modules_the_module_wrapper +type ModuleWrapper = (...args: Array) => unknown; + +export interface JestEnvironment { + new ( + config: Config.ProjectConfig, + context?: EnvironmentContext, + ): JestEnvironment; + runScript( + script: Script, + ): {[ScriptTransformer.EVAL_RESULT_VARIABLE]: ModuleWrapper} | null; + global: Global.Global; + // TODO: When `jest-util` is ESM, this can just be `fakeTimers: import('jest-util').FakeTimers` + fakeTimers: { + clearAllTimers(): void; + runAllImmediates(): void; + runAllTicks(): void; + runAllTimers(): void; + advanceTimersByTime(msToRun: number): void; + runOnlyPendingTimers(): void; + runWithRealTimers(callback: () => void): void; + getTimerCount(): number; + useFakeTimers(): void; + useRealTimers(): void; + }; + testFilePath: Config.Path; + moduleMocker: typeof moduleMocker; + setup(): Promise; + teardown(): Promise; +} + +export type Module = typeof module; + +export interface LocalModuleRequire extends NodeRequire { + requireActual(moduleName: string): unknown; + requireMock(moduleName: string): unknown; +} + +// TODO: Move to some separate package +export interface Jest { + /** + * Provides a way to add Jasmine-compatible matchers into your Jest context. + * + * @deprecated Use `expect.extend` instead + */ + addMatchers(matchers: Object): void; + /** + * Disables automatic mocking in the module loader. + */ + autoMockOff(): Jest; + /** + * Enables automatic mocking in the module loader. + */ + autoMockOn(): Jest; + /** + * Clears the mock.calls and mock.instances properties of all mocks. + * Equivalent to calling .mockClear() on every mocked function. + */ + clearAllMocks(): Jest; + /** + * Removes any pending timers from the timer system. If any timers have been + * scheduled, they will be cleared and will never have the opportunity to + * execute in the future. + */ + clearAllTimers(): void; + /** + * Indicates that the module system should never return a mocked version + * of the specified module, including all of the specified module's + * dependencies. + */ + deepUnmock(moduleName: string): Jest; + /** + * Disables automatic mocking in the module loader. + * + * After this method is called, all `require()`s will return the real + * versions of each module (rather than a mocked version). + */ + disableAutomock(): Jest; + /** + * When using `babel-jest`, calls to mock will automatically be hoisted to + * the top of the code block. Use this method if you want to explicitly avoid + * this behavior. + */ + doMock(moduleName: string, moduleFactory?: () => unknown): Jest; + /** + * Indicates that the module system should never return a mocked version + * of the specified module from require() (e.g. that it should always return + * the real module). + */ + dontMock(moduleName: string): Jest; + /** + * Enables automatic mocking in the module loader. + */ + enableAutomock(): Jest; + /** + * Creates a mock function. Optionally takes a mock implementation. + */ + fn: typeof moduleMocker.fn; + /** + * Given the name of a module, use the automatic mocking system to generate a + * mocked version of the module for you. + * + * This is useful when you want to create a manual mock that extends the + * automatic mock's behavior. + */ + genMockFromModule(moduleName: string): unknown; + /** + * Determines if the given function is a mocked function. + */ + isMockFunction(fn: Function): fn is ReturnType; + /** + * Mocks a module with an auto-mocked version when it is being required. + */ + mock( + moduleName: string, + moduleFactory?: () => unknown, + options?: {virtual?: boolean}, + ): Jest; + /** + * Returns the actual module instead of a mock, bypassing all checks on + * whether the module should receive a mock implementation or not. + */ + requireActual: (moduleName: string) => unknown; + /** + * Returns a mock module instead of the actual module, bypassing all checks + * on whether the module should be required normally or not. + */ + requireMock: (moduleName: string) => unknown; + /** + * Resets the state of all mocks. + * Equivalent to calling .mockReset() on every mocked function. + */ + resetAllMocks(): Jest; + /** + * Resets the module registry - the cache of all required modules. This is + * useful to isolate modules where local state might conflict between tests. + * + * @deprecated Use `jest.resetModules()` + */ + resetModuleRegistry(): Jest; + /** + * Resets the module registry - the cache of all required modules. This is + * useful to isolate modules where local state might conflict between tests. + */ + resetModules(): Jest; + /** + * Restores all mocks back to their original value. Equivalent to calling + * `.mockRestore` 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. + */ + restoreAllMocks(): Jest; + /** + * Runs failed tests n-times until they pass or until the max number of + * retries is exhausted. This only works with `jest-circus`! + */ + retryTimes(numRetries: number): Jest; + /** + * Exhausts tasks queued by setImmediate(). + */ + runAllImmediates(): void; + /** + * Exhausts the micro-task queue (usually interfaced in node via + * process.nextTick). + */ + runAllTicks(): void; + /** + * Exhausts the macro-task queue (i.e., all tasks queued by setTimeout() + * and setInterval()). + */ + runAllTimers(): void; + /** + * 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. + */ + runOnlyPendingTimers(): void; + /** + * Advances all timers by msToRun milliseconds. All pending "macro-tasks" + * that have been queued via setTimeout() or setInterval(), and would be + * executed within this timeframe will be executed. + */ + advanceTimersByTime(msToRun: number): void; + /** + * Executes only the macro task queue (i.e. all tasks queued by setTimeout() + * or setInterval() and setImmediate()). + * + * @deprecated Use `jest.advanceTimersByTime()` + */ + runTimersToTime(msToRun: number): void; + /** + * Returns the number of fake timers still left to run. + */ + getTimerCount(): number; + /** + * Explicitly supplies the mock object that the module system should return + * for the specified module. + * + * Note It is recommended to use `jest.mock()` instead. The `jest.mock` + * API's second argument is a module factory instead of the expected + * exported module object. + */ + setMock(moduleName: string, moduleExports: unknown): Jest; + /** + * 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. + */ + setTimeout(timeout: number): Jest; + /** + * Creates a mock function similar to `jest.fn` but also tracks calls to + * `object[methodName]`. + * + * Note: By default, jest.spyOn also calls the spied method. This is + * different behavior from most other test libraries. + */ + spyOn: typeof moduleMocker.spyOn; + /** + * Indicates that the module system should never return a mocked version of + * the specified module from require() (e.g. that it should always return the + * real module). + */ + unmock(moduleName: string): Jest; + /** + * Instructs Jest to use fake versions of the standard timer functions. + */ + useFakeTimers(): Jest; + /** + * Instructs Jest to use the real versions of the standard timer functions. + */ + useRealTimers(): Jest; + /** + * `jest.isolateModules(fn)` goes a step further than `jest.resetModules()` + * and creates a sandbox registry for the modules that are loaded inside + * the callback function. This is useful to isolate specific modules for + * every test so that local module state doesn't conflict between tests. + */ + isolateModules(fn: () => void): Jest; +} diff --git a/packages/jest-environment/tsconfig.json b/packages/jest-environment/tsconfig.json new file mode 100644 index 000000000000..8824863bd93b --- /dev/null +++ b/packages/jest-environment/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [ + {"path": "../jest-transform"}, + {"path": "../jest-types"}, + {"path": "../jest-util"} + ] +} diff --git a/packages/jest-resolve/src/index.ts b/packages/jest-resolve/src/index.ts index c08f54cb9eab..12ccc4fe0e22 100644 --- a/packages/jest-resolve/src/index.ts +++ b/packages/jest-resolve/src/index.ts @@ -21,7 +21,7 @@ type FindNodeModuleConfig = { extensions?: Array; moduleDirectory?: Array; paths?: Array; - resolver?: Config.Path; + resolver?: Config.Path | null; rootDir?: Config.Path; }; @@ -403,7 +403,7 @@ const createNoMappedModuleFoundError = ( updatedName: string, mappedModuleName: string, regex: RegExp, - resolver: Function | string, + resolver?: Function | string | null, ) => { const error = new Error( chalk.red(`${chalk.bold('Configuration error')}: diff --git a/packages/jest-resolve/src/types.ts b/packages/jest-resolve/src/types.ts index b765f0333107..1c65ac50af57 100644 --- a/packages/jest-resolve/src/types.ts +++ b/packages/jest-resolve/src/types.ts @@ -9,14 +9,14 @@ import {Config} from '@jest/types'; export type ResolverConfig = { browser?: boolean; - defaultPlatform?: string; + defaultPlatform?: string | null; extensions: Array; hasCoreModules: boolean; moduleDirectories: Array; - moduleNameMapper?: Array; + moduleNameMapper?: Array | null; modulePaths: Array; platforms?: Array; - resolver: Config.Path; + resolver?: Config.Path | null; rootDir: Config.Path; }; diff --git a/packages/jest-runner/package.json b/packages/jest-runner/package.json index 67cd2369cb63..bb54f544ac33 100644 --- a/packages/jest-runner/package.json +++ b/packages/jest-runner/package.json @@ -10,6 +10,7 @@ "main": "build/index.js", "types": "build/index.d.ts", "dependencies": { + "@jest/environment": "^24.1.0", "@jest/types": "^24.1.0", "chalk": "^2.4.2", "exit": "^0.1.2", diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 7b581863bb2a..94c759a5188f 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -6,13 +6,8 @@ * */ -import { - Environment, - Config, - TestResult, - Console as ConsoleType, -} from '@jest/types'; -// @ts-ignore: not migrated to TS +import {Config, TestResult, Console as ConsoleType} from '@jest/types'; +import {JestEnvironment} from '@jest/environment'; import RuntimeClass from 'jest-runtime'; import fs from 'graceful-fs'; import { @@ -101,16 +96,16 @@ async function runTestInternal( }); } - const TestEnvironment: Environment.EnvironmentClass = require(testEnvironment); + const TestEnvironment: JestEnvironment = require(testEnvironment); const testFramework: TestFramework = process.env.JEST_CIRCUS === '1' ? require('jest-circus/runner') // eslint-disable-line import/no-extraneous-dependencies : require(config.testRunner); - const Runtime: RuntimeClass = config.moduleLoader + const Runtime: typeof RuntimeClass = config.moduleLoader ? require(config.moduleLoader) : require('jest-runtime'); - let runtime: RuntimeClass = undefined; + let runtime: RuntimeClass | undefined = undefined; const consoleOut = globalConfig.useStderr ? process.stderr : process.stdout; const consoleFormatter = ( diff --git a/packages/jest-runner/src/testWorker.ts b/packages/jest-runner/src/testWorker.ts index 51c8fbc3dd1c..fed1e1e4a669 100644 --- a/packages/jest-runner/src/testWorker.ts +++ b/packages/jest-runner/src/testWorker.ts @@ -10,7 +10,6 @@ import {Config, TestResult} from '@jest/types'; import HasteMap, {SerializableModuleMap, ModuleMap} from 'jest-haste-map'; import exit from 'exit'; import {separateMessageFromStack} from 'jest-message-util'; -// @ts-ignore: Not migrated to TS import Runtime from 'jest-runtime'; import {ErrorWithCode, TestRunnerContext} from './types'; import runTest from './runTest'; diff --git a/packages/jest-runner/src/types.ts b/packages/jest-runner/src/types.ts index 5f1aeb7c557a..86f26d086fa2 100644 --- a/packages/jest-runner/src/types.ts +++ b/packages/jest-runner/src/types.ts @@ -6,10 +6,10 @@ */ import {EventEmitter} from 'events'; -import {Environment, Config, TestResult} from '@jest/types'; +import {Config, TestResult} from '@jest/types'; +import {JestEnvironment} from '@jest/environment'; import {ModuleMap, FS as HasteFS} from 'jest-haste-map'; import HasteResolver from 'jest-resolve'; -// @ts-ignore: not migrated to TS import Runtime from 'jest-runtime'; export type ErrorWithCode = Error & {code?: string}; @@ -63,7 +63,7 @@ export type Reporter = { export type TestFramework = ( globalConfig: Config.GlobalConfig, config: Config.ProjectConfig, - environment: Environment.Environment, + environment: JestEnvironment, runtime: Runtime, testPath: string, ) => Promise; diff --git a/packages/jest-runner/tsconfig.json b/packages/jest-runner/tsconfig.json index 59665ff7c74e..72bf161339bc 100644 --- a/packages/jest-runner/tsconfig.json +++ b/packages/jest-runner/tsconfig.json @@ -6,10 +6,12 @@ }, "references": [ {"path": "../jest-docblock"}, + {"path": "../jest-environment"}, {"path": "../jest-haste-map"}, {"path": "../jest-leak-detector"}, {"path": "../jest-message-util"}, {"path": "../jest-resolve"}, + {"path": "../jest-runtime"}, {"path": "../jest-types"}, {"path": "../jest-worker"}, {"path": "../jest-util"} diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index a4890ce50f79..f2f36459e08b 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -8,8 +8,12 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { + "@jest/environment": "^24.1.0", "@jest/transform": "^24.1.0", + "@jest/types": "^24.1.0", + "@types/yargs": "^12.0.2", "chalk": "^2.0.1", "exit": "^0.1.2", "glob": "^7.1.3", @@ -17,6 +21,7 @@ "jest-config": "^24.1.0", "jest-haste-map": "^24.0.0", "jest-message-util": "^24.0.0", + "jest-mock": "^24.0.0", "jest-regex-util": "^24.0.0", "jest-resolve": "^24.1.0", "jest-snapshot": "^24.1.0", @@ -33,7 +38,6 @@ "@types/graceful-fs": "^4.1.2", "@types/slash": "^2.0.0", "@types/strip-bom": "^3.0.0", - "@types/yargs": "^12.0.2", "jest-environment-node": "^24.0.0" }, "bin": { diff --git a/packages/jest-runtime/src/cli/args.js b/packages/jest-runtime/src/cli/args.ts similarity index 87% rename from packages/jest-runtime/src/cli/args.js rename to packages/jest-runtime/src/cli/args.ts index cce2e3cecda3..7af5ba6c24f5 100644 --- a/packages/jest-runtime/src/cli/args.js +++ b/packages/jest-runtime/src/cli/args.ts @@ -3,13 +3,16 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ +import {Options} from 'yargs'; + export const usage = 'Usage: $0 [--config=] '; -export const options = { +export const options: Record< + 'cache' | 'config' | 'debug' | 'version' | 'watchman', + Options +> = { cache: { default: true, description: diff --git a/packages/jest-runtime/src/cli/index.js b/packages/jest-runtime/src/cli/index.ts similarity index 79% rename from packages/jest-runtime/src/cli/index.js rename to packages/jest-runtime/src/cli/index.ts index 2662efe5407b..cb4527e312ec 100644 --- a/packages/jest-runtime/src/cli/index.js +++ b/packages/jest-runtime/src/cli/index.ts @@ -3,26 +3,25 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Argv} from 'types/Argv'; -import type {EnvironmentClass} from 'types/Environment'; - -import chalk from 'chalk'; import os from 'os'; import path from 'path'; +import chalk from 'chalk'; import {sync as realpath} from 'realpath-native'; import yargs from 'yargs'; +import {Config} from '@jest/types'; +import {JestEnvironment} from '@jest/environment'; import {Console, setGlobal} from 'jest-util'; +// @ts-ignore: Not migrated to TS import {validateCLIOptions} from 'jest-validate'; +// @ts-ignore: Not migrated to TS import {readConfig, deprecationEntries} from 'jest-config'; +import {VERSION} from '../version'; +import {Context} from '../types'; import * as args from './args'; -const VERSION = (require('../../package.json').version: string); - -export function run(cliArgv?: Argv, cliInfo?: Array) { +export function run(cliArgv?: Config.Argv, cliInfo?: Array) { const realFs = require('fs'); const fs = require('graceful-fs'); fs.gracefulify(realFs); @@ -74,23 +73,22 @@ export function run(cliArgv?: Argv, cliInfo?: Array) { }; // Break circular dependency - const Runtime = require('..'); + const Runtime: any = require('..'); - Runtime.createContext(config, { + (Runtime.createContext(config, { maxWorkers: Math.max(os.cpus().length - 1, 1), watchman: globalConfig.watchman, - }) + }) as Promise) .then(hasteMap => { - /* $FlowFixMe */ - const Environment = (require(config.testEnvironment): EnvironmentClass); + const Environment: JestEnvironment = require(config.testEnvironment); const environment = new Environment(config); setGlobal( environment.global, 'console', new Console(process.stdout, process.stderr), ); - environment.global.jestProjectConfig = config; - environment.global.jestGlobalConfig = globalConfig; + setGlobal(environment.global, 'jestProjectConfig', config); + setGlobal(environment.global, 'jestGlobalConfig', globalConfig); const runtime = new Runtime(config, environment, hasteMap.resolver); runtime.requireModule(filePath); diff --git a/packages/jest-runtime/src/helpers.js b/packages/jest-runtime/src/helpers.ts similarity index 91% rename from packages/jest-runtime/src/helpers.js rename to packages/jest-runtime/src/helpers.ts index 19265d836044..5b12d3aeb3aa 100644 --- a/packages/jest-runtime/src/helpers.js +++ b/packages/jest-runtime/src/helpers.ts @@ -1,16 +1,13 @@ // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. -// @flow - -import type {Path} from 'types/Config'; - import path from 'path'; import slash from 'slash'; import glob from 'glob'; +import {Config} from '@jest/types'; export const findSiblingsWithFileExtension = ( - moduleFileExtensions: Array, - from: Path, + moduleFileExtensions: Config.ProjectConfig['moduleFileExtensions'], + from: Config.Path, moduleName: string, ): string => { if (!path.isAbsolute(moduleName) && path.extname(moduleName) === '') { diff --git a/packages/jest-runtime/src/index.js b/packages/jest-runtime/src/index.ts similarity index 79% rename from packages/jest-runtime/src/index.js rename to packages/jest-runtime/src/index.ts index e6f393eef733..6c8849a792ee 100644 --- a/packages/jest-runtime/src/index.js +++ b/packages/jest-runtime/src/index.ts @@ -3,72 +3,61 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Console} from 'console'; -import type {Argv} from 'types/Argv'; -import type {Glob, Path, ProjectConfig} from 'types/Config'; -import type {Environment} from 'types/Environment'; -import type {Context} from 'types/Context'; -import type {Jest, LocalModuleRequire} from 'types/Jest'; -import type {ModuleMap} from 'jest-haste-map'; -import type {MockFunctionMetadata, ModuleMocker} from 'types/Mock'; -import type {SourceMapRegistry} from 'types/SourceMaps'; - import path from 'path'; -import HasteMap from 'jest-haste-map'; +import {Config, SourceMaps} from '@jest/types'; +import { + Jest, + JestEnvironment, + LocalModuleRequire, + Module, +} from '@jest/environment'; +import jestMock, {MockFunctionMetadata} from 'jest-mock'; +import HasteMap, {ModuleMap} from 'jest-haste-map'; import {formatStackTrace, separateMessageFromStack} from 'jest-message-util'; import Resolver from 'jest-resolve'; import {createDirectory, deepCyclicCopy} from 'jest-util'; import {escapePathForRegex} from 'jest-regex-util'; import Snapshot from 'jest-snapshot'; -import {ScriptTransformer, shouldInstrument} from '@jest/transform'; +import { + ScriptTransformer, + ShouldInstrumentOptions, + shouldInstrument, +} from '@jest/transform'; import fs from 'graceful-fs'; import stripBOM from 'strip-bom'; import {run as cliRun} from './cli'; import {options as cliOptions} from './cli/args'; import {findSiblingsWithFileExtension} from './helpers'; +import {Context} from './types'; + +type HasteMapOptions = { + console?: Console; + maxWorkers: number; + resetCache: boolean; + watch?: boolean; + watchman: boolean; +}; -type Module = {| - children: Array, - exports: any, - filename: string, - id: string, - loaded: boolean, - parent?: Module, - paths?: Array, - require?: (id: string) => any, -|}; - -type HasteMapOptions = {| - console?: Console, - maxWorkers: number, - resetCache: boolean, - watch?: boolean, - watchman: boolean, -|}; - -type InternalModuleOptions = {| - isInternalModule: boolean, -|}; - -type CoverageOptions = { - changedFiles: ?Set, - collectCoverage: boolean, - collectCoverageFrom: Array, - collectCoverageOnlyFrom: ?{[key: string]: boolean, __proto__: null}, +type InternalModuleOptions = { + isInternalModule: boolean; }; -type ModuleRegistry = {[key: string]: Module, __proto__: null}; +type InitialModule = Partial & + Pick; +type ModuleRegistry = {[key: string]: InitialModule | Module}; +type ResolveOptions = Parameters[1]; + +type BooleanObject = {[key: string]: boolean}; +type CacheFS = {[path: string]: string}; -type BooleanObject = {[key: string]: boolean, __proto__: null}; -type CacheFS = {[path: Path]: string, __proto__: null}; +const testTimeoutSymbol = Symbol.for('TEST_TIMEOUT_SYMBOL'); +const retryTimesSymbol = Symbol.for('RETRY_TIMES'); const NODE_MODULES = path.sep + 'node_modules' + path.sep; -const getModuleNameMapper = (config: ProjectConfig) => { +const getModuleNameMapper = (config: Config.ProjectConfig) => { if ( Array.isArray(config.moduleNameMapper) && config.moduleNameMapper.length @@ -84,45 +73,47 @@ const getModuleNameMapper = (config: ProjectConfig) => { const unmockRegExpCache = new WeakMap(); class Runtime { - static ScriptTransformer: Class; - - _cacheFS: CacheFS; - _config: ProjectConfig; - _coverageOptions: CoverageOptions; - _currentlyExecutingModulePath: string; - _environment: Environment; - _explicitShouldMock: BooleanObject; - _internalModuleRegistry: ModuleRegistry; - _isCurrentlyExecutingManualMock: ?string; - _mockFactories: {[key: string]: () => any, __proto__: null}; - _mockMetaDataCache: {[key: string]: MockFunctionMetadata, __proto__: null}; - _mockRegistry: {[key: string]: any, __proto__: null}; - _isolatedMockRegistry: ?{[key: string]: any, __proto__: null}; - _moduleMocker: ModuleMocker; - _isolatedModuleRegistry: ?ModuleRegistry; - _moduleRegistry: ModuleRegistry; - _needsCoverageMapped: Set; - _resolver: Resolver; - _shouldAutoMock: boolean; - _shouldMockModuleCache: BooleanObject; - _shouldUnmockTransitiveDependenciesCache: BooleanObject; - _sourceMapRegistry: SourceMapRegistry; - _scriptTransformer: ScriptTransformer; - _transitiveShouldMock: BooleanObject; - _unmockList: ?RegExp; - _virtualMocks: BooleanObject; + static ScriptTransformer: typeof ScriptTransformer; + + private _cacheFS: CacheFS; + private _config: Config.ProjectConfig; + private _coverageOptions: ShouldInstrumentOptions; + private _currentlyExecutingModulePath: string; + private _environment: JestEnvironment; + private _explicitShouldMock: BooleanObject; + private _internalModuleRegistry: ModuleRegistry; + private _isCurrentlyExecutingManualMock: string | null; + private _mockFactories: {[key: string]: () => unknown}; + private _mockMetaDataCache: { + [key: string]: MockFunctionMetadata; + }; + private _mockRegistry: {[key: string]: any}; + private _isolatedMockRegistry: {[key: string]: any} | null; + private _moduleMocker: typeof jestMock; + private _isolatedModuleRegistry: ModuleRegistry | null; + private _moduleRegistry: ModuleRegistry; + private _needsCoverageMapped: Set; + private _resolver: Resolver; + private _shouldAutoMock: boolean; + private _shouldMockModuleCache: BooleanObject; + private _shouldUnmockTransitiveDependenciesCache: BooleanObject; + private _sourceMapRegistry: SourceMaps.SourceMapRegistry; + private _scriptTransformer: ScriptTransformer; + private _transitiveShouldMock: BooleanObject; + private _unmockList: RegExp | undefined; + private _virtualMocks: BooleanObject; constructor( - config: ProjectConfig, - environment: Environment, + config: Config.ProjectConfig, + environment: JestEnvironment, resolver: Resolver, cacheFS?: CacheFS, - coverageOptions?: CoverageOptions, + coverageOptions?: ShouldInstrumentOptions, ) { this._cacheFS = cacheFS || Object.create(null); this._config = config; this._coverageOptions = coverageOptions || { - changedFiles: null, + changedFiles: undefined, collectCoverage: false, collectCoverageFrom: [], collectCoverageOnlyFrom: null, @@ -136,6 +127,7 @@ class Runtime { this._mockRegistry = Object.create(null); this._moduleMocker = this._environment.moduleMocker; this._isolatedModuleRegistry = null; + this._isolatedMockRegistry = null; this._moduleRegistry = Object.create(null); this._needsCoverageMapped = new Set(); this._resolver = resolver; @@ -178,30 +170,22 @@ class Runtime { } } + // TODO: Can this be `static shouldInstrument = shouldInstrument;`? static shouldInstrument( - filename: Path, - options: CoverageOptions, - config: ProjectConfig, + filename: Config.Path, + options: ShouldInstrumentOptions, + config: Config.ProjectConfig, ) { - return shouldInstrument( - filename, - { - changedFiles: options.changedFiles, - collectCoverage: options.collectCoverage, - collectCoverageFrom: options.collectCoverageFrom, - collectCoverageOnlyFrom: options.collectCoverageOnlyFrom, - }, - config, - ); + return shouldInstrument(filename, options, config); } static createContext( - config: ProjectConfig, + config: Config.ProjectConfig, options: { - console?: Console, - maxWorkers: number, - watch?: boolean, - watchman: boolean, + console?: Console; + maxWorkers: number; + watch?: boolean; + watchman: boolean; }, ): Promise { createDirectory(config.cacheDirectory); @@ -226,7 +210,7 @@ class Runtime { } static createHasteMap( - config: ProjectConfig, + config: Config.ProjectConfig, options?: HasteMapOptions, ): HasteMap { const ignorePatternParts = [ @@ -238,7 +222,7 @@ class Runtime { const ignorePattern = ignorePatternParts.length > 0 ? new RegExp(ignorePatternParts.join('|')) - : null; + : undefined; return new HasteMap({ cacheDirectory: config.cacheDirectory, @@ -262,7 +246,10 @@ class Runtime { }); } - static createResolver(config: ProjectConfig, moduleMap: ModuleMap): Resolver { + static createResolver( + config: Config.ProjectConfig, + moduleMap: ModuleMap, + ): Resolver { return new Resolver(moduleMap, { browser: config.browser, defaultPlatform: config.haste.defaultPlatform, @@ -277,7 +264,7 @@ class Runtime { }); } - static runCLI(args?: Argv, info?: Array) { + static runCLI(args?: Config.Argv, info?: Array) { return cliRun(args, info); } @@ -286,10 +273,10 @@ class Runtime { } requireModule( - from: Path, + from: Config.Path, moduleName?: string, - options: ?InternalModuleOptions, - isRequireActual: ?boolean, + options?: InternalModuleOptions, + isRequireActual?: boolean | null, ) { const moduleID = this._resolver.getModuleID( this._virtualMocks, @@ -338,7 +325,7 @@ class Runtime { // We must register the pre-allocated module object first so that any // circular dependencies that may arise while evaluating the module can // be satisfied. - const localModule: Module = { + const localModule: InitialModule = { children: [], exports: {}, filename: modulePath, @@ -351,7 +338,6 @@ class Runtime { stripBOM(fs.readFileSync(modulePath, 'utf8')), ); } else if (path.extname(modulePath) === '.node') { - // $FlowFixMe localModule.exports = require(modulePath); } else { // Only include the fromPath if a moduleName is given. Else treat as root. @@ -364,15 +350,15 @@ class Runtime { return moduleRegistry[modulePath].exports; } - requireInternalModule(from: Path, to?: string) { + requireInternalModule(from: Config.Path, to?: string) { return this.requireModule(from, to, {isInternalModule: true}); } - requireActual(from: Path, moduleName: string) { + requireActual(from: Config.Path, moduleName: string) { return this.requireModule(from, moduleName, undefined, true); } - requireMock(from: Path, moduleName: string) { + requireMock(from: Config.Path, moduleName: string) { const moduleID = this._resolver.getModuleID( this._virtualMocks, from, @@ -428,7 +414,7 @@ class Runtime { } } if (isManualMock) { - const localModule: Module = { + const localModule: InitialModule = { children: [], exports: {}, filename: modulePath, @@ -449,7 +435,7 @@ class Runtime { return mockRegistry[moduleID]; } - requireModuleOrMock(from: Path, moduleName: string) { + requireModuleOrMock(from: Config.Path, moduleName: string) { try { if (this._shouldMock(from, moduleName)) { return this.requireMock(from, moduleName); @@ -494,13 +480,14 @@ class Runtime { if (this._environment) { if (this._environment.global) { const envGlobal = this._environment.global; - Object.keys(envGlobal).forEach(key => { + (Object.keys(envGlobal) as Array).forEach(key => { const globalMock = envGlobal[key]; if ( - (typeof globalMock === 'object' && globalMock !== null) || - typeof globalMock === 'function' + ((typeof globalMock === 'object' && globalMock !== null) || + typeof globalMock === 'function') && + globalMock._isMockFunction === true ) { - globalMock._isMockFunction === true && globalMock.mockClear(); + globalMock.mockClear(); } }); } @@ -516,7 +503,9 @@ class Runtime { } getSourceMapInfo(coveredFiles: Set) { - return Object.keys(this._sourceMapRegistry).reduce((result, sourcePath) => { + return Object.keys(this._sourceMapRegistry).reduce<{ + [path: string]: string; + }>((result, sourcePath) => { if ( coveredFiles.has(sourcePath) && this._needsCoverageMapped.has(sourcePath) && @@ -528,15 +517,15 @@ class Runtime { }, {}); } - getSourceMaps(): SourceMapRegistry { + getSourceMaps(): SourceMaps.SourceMapRegistry { return this._sourceMapRegistry; } setMock( from: string, moduleName: string, - mockFactory: () => any, - options?: {virtual: boolean}, + mockFactory: () => unknown, + options?: {virtual?: boolean}, ) { if (options && options.virtual) { const mockPath = this._resolver.getModulePath(from, moduleName); @@ -563,14 +552,14 @@ class Runtime { this._moduleMocker.clearAllMocks(); } - _resolveModule(from: Path, to?: ?string) { + private _resolveModule(from: Config.Path, to?: string) { return to ? this._resolver.resolveModule(from, to) : from; } - _requireResolve( - from: Path, + private _requireResolve( + from: Config.Path, moduleName?: string, - {paths}: {paths?: Path[]} = {}, + options: ResolveOptions = {}, ) { if (moduleName == null) { throw new Error( @@ -578,6 +567,8 @@ class Runtime { ); } + const {paths} = options; + if (paths) { for (const p of paths) { const absolutePath = path.resolve(from, '..', p); @@ -610,7 +601,7 @@ class Runtime { } } - _requireResolvePaths(from: Path, moduleName?: string) { + private _requireResolvePaths(from: Config.Path, moduleName?: string) { if (moduleName == null) { throw new Error( 'The first argument to require.resolve.paths must be a string. Received null or undefined.', @@ -631,11 +622,11 @@ class Runtime { return this._resolver.getModulePaths(path.resolve(from, '..')); } - _execModule( - localModule: Module, - options: ?InternalModuleOptions, + private _execModule( + localModule: InitialModule, + options: InternalModuleOptions | undefined, moduleRegistry: ModuleRegistry, - from: ?Path, + from: Config.Path | null, ) { // If the environment was disposed, prevent this module from being executed. if (!this._environment.global) { @@ -652,18 +643,13 @@ class Runtime { const dirname = path.dirname(filename); localModule.children = []; - Object.defineProperty( - localModule, - 'parent', - // https://github.com/facebook/flow/issues/285#issuecomment-270810619 - ({ - enumerable: true, - get() { - const key = from || ''; - return moduleRegistry[key] || null; - }, - }: Object), - ); + Object.defineProperty(localModule, 'parent', { + enumerable: true, + get() { + const key = from || ''; + return moduleRegistry[key] || null; + }, + }); localModule.paths = this._resolver.getModulePaths(dirname); Object.defineProperty(localModule, 'require', { @@ -710,8 +696,7 @@ class Runtime { this._environment.global, // global object this._createJestObjectFor( filename, - // $FlowFixMe - (localModule.require: LocalModuleRequire), + localModule.require as LocalModuleRequire, ), // jest object ...extraGlobals.map(globalVariable => { if (this._environment.global[globalVariable]) { @@ -729,25 +714,24 @@ class Runtime { this._currentlyExecutingModulePath = lastExecutingModulePath; } - _requireCoreModule(moduleName: string) { + private _requireCoreModule(moduleName: string) { if (moduleName === 'process') { return this._environment.global.process; } - // $FlowFixMe return require(moduleName); } - _generateMock(from: Path, moduleName: string) { + private _generateMock(from: Config.Path, moduleName: string) { const modulePath = this._resolver.resolveStubModuleName(from, moduleName) || this._resolveModule(from, moduleName); if (!(modulePath in this._mockMetaDataCache)) { // This allows us to handle circular dependencies while generating an // automock - this._mockMetaDataCache[modulePath] = (this._moduleMocker.getMetadata( + this._mockMetaDataCache[modulePath] = this._moduleMocker.getMetadata( {}, - ): any); + ) as any; // In order to avoid it being possible for automocking to potentially // cause side-effects within the module environment, we need to execute @@ -778,7 +762,7 @@ class Runtime { ); } - _shouldMock(from: Path, moduleName: string) { + private _shouldMock(from: Config.Path, moduleName: string) { const mockPath = this._resolver.getModulePath(from, moduleName); if (mockPath in this._virtualMocks) { return true; @@ -845,45 +829,50 @@ class Runtime { return (this._shouldMockModuleCache[moduleID] = true); } - _createRequireImplementation( - from: Module, - options: ?InternalModuleOptions, + private _createRequireImplementation( + from: InitialModule, + options?: InternalModuleOptions, ): LocalModuleRequire { - const moduleRequire = - options && options.isInternalModule - ? (moduleName: string) => - this.requireInternalModule(from.filename, moduleName) - : this.requireModuleOrMock.bind(this, from.filename); + // TODO: somehow avoid having to type the arguments - they should come from `NodeRequire/LocalModuleRequire.resolve` + const resolve = (moduleName: string, options: ResolveOptions) => + this._requireResolve(from.filename, moduleName, options); + resolve.paths = (moduleName: string) => + this._requireResolvePaths(from.filename, moduleName); + + const moduleRequire = (options && options.isInternalModule + ? (moduleName: string) => + this.requireInternalModule(from.filename, moduleName) + : this.requireModuleOrMock.bind( + this, + from.filename, + )) as LocalModuleRequire; moduleRequire.cache = Object.create(null); moduleRequire.extensions = Object.create(null); moduleRequire.requireActual = this.requireActual.bind(this, from.filename); moduleRequire.requireMock = this.requireMock.bind(this, from.filename); - moduleRequire.resolve = (moduleName, options) => - this._requireResolve(from.filename, moduleName, options); - moduleRequire.resolve.paths = moduleName => - this._requireResolvePaths(from.filename, moduleName); - Object.defineProperty( - moduleRequire, - 'main', - ({ - enumerable: true, - get() { - let mainModule = from.parent; - while ( - mainModule && - mainModule.parent && - mainModule.id !== mainModule.parent.id - ) { - mainModule = mainModule.parent; - } - return mainModule; - }, - }: Object), - ); + moduleRequire.resolve = resolve; + + Object.defineProperty(moduleRequire, 'main', { + enumerable: true, + get() { + let mainModule = from.parent; + while ( + mainModule && + mainModule.parent && + mainModule.id !== mainModule.parent.id + ) { + mainModule = mainModule.parent; + } + return mainModule; + }, + }); return moduleRequire; } - _createJestObjectFor(from: Path, localRequire: LocalModuleRequire): Jest { + private _createJestObjectFor( + from: Config.Path, + localRequire: LocalModuleRequire, + ): Jest { const disableAutomock = () => { this._shouldAutoMock = false; return jestObject; @@ -911,11 +900,7 @@ class Runtime { this._transitiveShouldMock[moduleID] = false; return jestObject; }; - const mock = ( - moduleName: string, - mockFactory?: Object, - options?: {virtual: boolean}, - ) => { + const mock: Jest['mock'] = (moduleName, mockFactory, options) => { if (mockFactory !== undefined) { return setMockFactory(moduleName, mockFactory, options); } @@ -928,7 +913,11 @@ class Runtime { this._explicitShouldMock[moduleID] = true; return jestObject; }; - const setMockFactory = (moduleName, mockFactory, options) => { + const setMockFactory = ( + moduleName: string, + mockFactory: () => unknown, + options?: {virtual?: boolean}, + ) => { this.setMock(from, moduleName, mockFactory, options); return jestObject; }; @@ -964,16 +953,18 @@ class Runtime { const spyOn = this._moduleMocker.spyOn.bind(this._moduleMocker); const setTimeout = (timeout: number) => { - this._environment.global.jasmine - ? (this._environment.global.jasmine._DEFAULT_TIMEOUT_INTERVAL = timeout) - : (this._environment.global[ - Symbol.for('TEST_TIMEOUT_SYMBOL') - ] = timeout); + if (this._environment.global.jasmine) { + this._environment.global.jasmine._DEFAULT_TIMEOUT_INTERVAL = timeout; + } else { + // @ts-ignore: https://github.com/Microsoft/TypeScript/issues/24587 + this._environment.global[testTimeoutSymbol] = timeout; + } return jestObject; }; const retryTimes = (numTestRetries: number) => { - this._environment.global[Symbol.for('RETRY_TIMES')] = numTestRetries; + // @ts-ignore: https://github.com/Microsoft/TypeScript/issues/24587 + this._environment.global[retryTimesSymbol] = numTestRetries; return jestObject; }; @@ -988,7 +979,7 @@ class Runtime { return this._environment.fakeTimers; }; - const jestObject = { + const jestObject: Jest = { addMatchers: (matchers: Object) => this._environment.global.jasmine.addMatchers(matchers), advanceTimersByTime: (msToRun: number) => @@ -1022,7 +1013,7 @@ class Runtime { runOnlyPendingTimers: () => _getFakeTimers().runOnlyPendingTimers(), runTimersToTime: (msToRun: number) => _getFakeTimers().advanceTimersByTime(msToRun), - setMock: (moduleName: string, mock: Object) => + setMock: (moduleName: string, mock: unknown) => setMockFactory(moduleName, () => mock), setTimeout, spyOn, @@ -1034,8 +1025,8 @@ class Runtime { } _logFormattedReferenceError(errorMessage: string) { - const originalStack = new ReferenceError(errorMessage).stack - .split('\n') + const originalStack = new ReferenceError(errorMessage) + .stack!.split('\n') // Remove this file from the stack (jest-message-utils will keep one line) .filter(line => line.indexOf(__filename) === -1) .join('\n'); @@ -1051,4 +1042,4 @@ class Runtime { Runtime.ScriptTransformer = ScriptTransformer; -module.exports = Runtime; +export = Runtime; diff --git a/packages/jest-runtime/src/types.ts b/packages/jest-runtime/src/types.ts new file mode 100644 index 000000000000..954bcf8d6e71 --- /dev/null +++ b/packages/jest-runtime/src/types.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import {Config} from '@jest/types'; +import HasteResolver from 'jest-resolve'; +import {FS as HasteFS, ModuleMap} from 'jest-haste-map'; + +export type Context = { + config: Config.ProjectConfig; + hasteFS: HasteFS; + moduleMap: ModuleMap; + resolver: HasteResolver; +}; diff --git a/packages/jest-runtime/src/version.ts b/packages/jest-runtime/src/version.ts new file mode 100644 index 000000000000..6c68483e5054 --- /dev/null +++ b/packages/jest-runtime/src/version.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +// For some reason, doing `require`ing here works, while inside `cli` fails +export const VERSION: string = require('../package.json').version; diff --git a/packages/jest-runtime/tsconfig.json b/packages/jest-runtime/tsconfig.json new file mode 100644 index 000000000000..b73127d8e1fd --- /dev/null +++ b/packages/jest-runtime/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + // TODO: Missing `jest-config`, `jest-validate` and `jest-environment-node` + "references": [ + {"path": "../jest-environment"}, + {"path": "../jest-haste-map"}, + {"path": "../jest-message-util"}, + {"path": "../jest-mock"}, + {"path": "../jest-regex-util"}, + {"path": "../jest-resolve"}, + {"path": "../jest-snapshot"}, + {"path": "../jest-util"}, + {"path": "../jest-types"}, + {"path": "../pretty-format"} + ] +} diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index 3d92b9f94cee..2fbf03a78ecf 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -50,7 +50,7 @@ const projectCaches: WeakMap< const CACHE_VERSION = '1'; export default class ScriptTransformer { - static EVAL_RESULT_VARIABLE: string; + static EVAL_RESULT_VARIABLE: 'Object.'; private _cache: ProjectCache; private _config: Config.ProjectConfig; private _transformCache: Map; @@ -565,4 +565,5 @@ const wrap = (content: string, ...extras: Array) => { ); }; +// TODO: Can this be added to the static property? ScriptTransformer.EVAL_RESULT_VARIABLE = 'Object.'; diff --git a/packages/jest-transform/src/index.ts b/packages/jest-transform/src/index.ts index 6f10151c3051..4a757f5ad9fd 100644 --- a/packages/jest-transform/src/index.ts +++ b/packages/jest-transform/src/index.ts @@ -7,4 +7,4 @@ export {default as ScriptTransformer} from './ScriptTransformer'; export {default as shouldInstrument} from './shouldInstrument'; -export {Transformer} from './types'; +export {Transformer, ShouldInstrumentOptions} from './types'; diff --git a/packages/jest-transform/src/shouldInstrument.ts b/packages/jest-transform/src/shouldInstrument.ts index 2020e724ddb5..0aa7d2f7d74e 100644 --- a/packages/jest-transform/src/shouldInstrument.ts +++ b/packages/jest-transform/src/shouldInstrument.ts @@ -10,7 +10,7 @@ import {Config} from '@jest/types'; import {escapePathForRegex} from 'jest-regex-util'; import {replacePathSepForGlob} from 'jest-util'; import micromatch from 'micromatch'; -import {Options} from './types'; +import {ShouldInstrumentOptions} from './types'; const MOCKS_PATTERN = new RegExp( escapePathForRegex(path.sep + '__mocks__' + path.sep), @@ -18,7 +18,7 @@ const MOCKS_PATTERN = new RegExp( export default function shouldInstrument( filename: Config.Path, - options: Options, + options: ShouldInstrumentOptions, config: Config.ProjectConfig, ): boolean { if (!options.collectCoverage) { diff --git a/packages/jest-transform/src/types.ts b/packages/jest-transform/src/types.ts index 91df3f394de9..636cff4c27e2 100644 --- a/packages/jest-transform/src/types.ts +++ b/packages/jest-transform/src/types.ts @@ -9,18 +9,19 @@ import {Script} from 'vm'; import {RawSourceMap} from 'source-map'; import {Config} from '@jest/types'; -export type Options = Pick< +export type ShouldInstrumentOptions = Pick< Config.GlobalConfig, - | 'collectCoverage' - | 'collectCoverageFrom' - | 'collectCoverageOnlyFrom' - | 'extraGlobals' + 'collectCoverage' | 'collectCoverageFrom' | 'collectCoverageOnlyFrom' > & { changedFiles: Set | undefined; - isCoreModule?: boolean; - isInternalModule?: boolean; }; +export type Options = ShouldInstrumentOptions & + Pick & { + isCoreModule?: boolean; + isInternalModule?: boolean; + }; + // https://stackoverflow.com/a/48216010/1850276 type Omit = Pick>; diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index 85a5db4df75f..bdd9c64efc39 100644 --- a/packages/jest-types/src/Config.ts +++ b/packages/jest-types/src/Config.ts @@ -5,6 +5,8 @@ * LICENSE file in the root directory of this source tree. */ +import {Arguments} from 'yargs'; + export type Path = string; export type Glob = string; @@ -313,7 +315,7 @@ export type ProjectConfig = { detectOpenHandles: boolean; displayName: string | null | undefined; errorOnDeprecated: boolean; - extraGlobals: Array; + extraGlobals: Array; filter: Path | null | undefined; forceCoverageMatch: Array; globalSetup: string | null | undefined; @@ -355,3 +357,93 @@ export type ProjectConfig = { watchPathIgnorePatterns: Array; unmockedModulePathPatterns: Array | null | undefined; }; + +export type Argv = Arguments<{ + all: boolean; + automock: boolean; + bail: boolean | number; + browser: boolean; + cache: boolean; + cacheDirectory: string; + changedFilesWithAncestor: boolean; + changedSince: string; + ci: boolean; + clearCache: boolean; + clearMocks: boolean; + collectCoverage: boolean; + collectCoverageFrom: Array; + collectCoverageOnlyFrom: Array; + config: string; + coverage: boolean; + coverageDirectory: string; + coveragePathIgnorePatterns: Array; + coverageReporters: Array; + coverageThreshold: string; + debug: boolean; + env: string; + expand: boolean; + findRelatedTests: boolean; + forceExit: boolean; + globals: string; + globalSetup: string | null | undefined; + globalTeardown: string | null | undefined; + h: boolean; + haste: string; + help: boolean; + init: boolean; + json: boolean; + lastCommit: boolean; + logHeapUsage: boolean; + maxWorkers: number; + moduleDirectories: Array; + moduleFileExtensions: Array; + moduleLoader: string; + moduleNameMapper: string; + modulePathIgnorePatterns: Array; + modulePaths: Array; + name: string; + noSCM: boolean; + noStackTrace: boolean; + notify: boolean; + notifyMode: string; + onlyChanged: boolean; + outputFile: string; + preset: string | null | undefined; + projects: Array; + prettierPath: string | null | undefined; + replname: string | null | undefined; + resetMocks: boolean; + resetModules: boolean; + resolver: string | null | undefined; + restoreMocks: boolean; + rootDir: string; + roots: Array; + runInBand: boolean; + setupFiles: Array; + setupFilesAfterEnv: Array; + showConfig: boolean; + silent: boolean; + snapshotSerializers: Array; + testEnvironment: string; + testFailureExitCode: string | null | undefined; + testMatch: Array; + testNamePattern: string; + testPathIgnorePatterns: Array; + testPathPattern: Array; + testRegex: string | Array; + testResultsProcessor: string | null | undefined; + testRunner: string; + testURL: string; + timers: 'real' | 'fake'; + transform: string; + transformIgnorePatterns: Array; + unmockedModulePathPatterns: Array | null | undefined; + updateSnapshot: boolean; + useStderr: boolean; + verbose: boolean | null | undefined; + version: boolean; + watch: boolean; + watchAll: boolean; + watchman: boolean; + watchPathIgnorePatterns: Array; +}>; diff --git a/packages/jest-types/src/Environment.ts b/packages/jest-types/src/Environment.ts deleted file mode 100644 index 1af4493db851..000000000000 --- a/packages/jest-types/src/Environment.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. 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. - */ - -import {Script} from 'vm'; -import {ProjectConfig} from './Config'; -import {Global} from './Global'; - -// TODO Fix jest-mock and @jest/types has circular dependency -// import {ModuleMocker} from 'jest-mock'; -type ModuleMocker = any; - -export type EnvironmentContext = { - console?: Object; - testPath?: string; -}; - -export declare class $JestEnvironment { - constructor(config: ProjectConfig, context?: EnvironmentContext); - runScript(script: Script): any; - global: Global; - fakeTimers: { - clearAllTimers(): void; - runAllImmediates(): void; - runAllTicks(): void; - runAllTimers(): void; - advanceTimersByTime(msToRun: number): void; - runOnlyPendingTimers(): void; - runWithRealTimers(callback: any): void; - getTimerCount(): number; - useFakeTimers(): void; - useRealTimers(): void; - }; - testFilePath: string; - moduleMocker: ModuleMocker; - setup(): Promise; - teardown(): Promise; -} - -export type Environment = $JestEnvironment; -export type EnvironmentClass = typeof $JestEnvironment; diff --git a/packages/jest-types/src/Global.ts b/packages/jest-types/src/Global.ts index 1a782eaaa386..de0d500ca9cd 100644 --- a/packages/jest-types/src/Global.ts +++ b/packages/jest-types/src/Global.ts @@ -5,12 +5,17 @@ * LICENSE file in the root directory of this source tree. */ +import {CoverageMapData} from 'istanbul-lib-coverage'; + export type DoneFn = (reason?: string | Error) => void; export type TestName = string; export type TestFn = (done?: DoneFn) => Promise | void | undefined; export type BlockFn = () => void; export type BlockName = string; +// TODO: Get rid of this at some point +type JasmineType = {_DEFAULT_TIMEOUT_INTERVAL?: number; addMatchers: Function}; + // TODO Replace with actual type when `jest-each` is ready type Each = () => void; @@ -48,6 +53,7 @@ export interface Describe extends DescribeBase { skip: ItBase; } +// TODO: Maybe add `| Window` in the future? export interface Global extends NodeJS.Global { it: It; test: ItConcurrent; @@ -57,6 +63,8 @@ export interface Global extends NodeJS.Global { describe: Describe; xdescribe: DescribeBase; fdescribe: DescribeBase; + __coverage__: CoverageMapData; + jasmine: JasmineType; } declare global { diff --git a/packages/jest-types/src/index.ts b/packages/jest-types/src/index.ts index c99939515bae..458831633460 100644 --- a/packages/jest-types/src/index.ts +++ b/packages/jest-types/src/index.ts @@ -11,6 +11,5 @@ import * as Matchers from './Matchers'; import * as SourceMaps from './SourceMaps'; import * as TestResult from './TestResult'; import * as Global from './Global'; -import * as Environment from './Environment'; -export {Config, Console, Matchers, SourceMaps, TestResult, Global, Environment}; +export {Config, Console, Matchers, SourceMaps, TestResult, Global}; From 038a993e92483ec71ed9b7725e8af261a80b335c Mon Sep 17 00:00:00 2001 From: Hoseong Son Date: Mon, 25 Feb 2019 20:34:58 +0900 Subject: [PATCH 100/107] Remove console.log from the front page (#7977) --- CHANGELOG.md | 1 + website/static/landing.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 181243fc7757..0ff03985fa81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - `[jest-transform]` Normalize config and remove unecessary checks, convert `TestUtils.js` to TypeScript ([#7801](https://github.com/facebook/jest/pull/7801)) - `[jest-worker]` Fix `jest-worker` when using pre-allocated jobs ([#7934](https://github.com/facebook/jest/pull/7934)) - `[jest-changed-files]` Fix `getChangedFilesFromRoots` to not return parts of the commit messages as if they were files, when the commit messages contained multiple paragraphs ([#7961](https://github.com/facebook/jest/pull/7961)) +- `[static]` Remove console log '-' on the front page ### Chore & Maintenance diff --git a/website/static/landing.js b/website/static/landing.js index e2a28f0f3fc0..33a1a60c44f5 100644 --- a/website/static/landing.js +++ b/website/static/landing.js @@ -195,7 +195,6 @@ document.addEventListener('DOMContentLoaded', () => { // JS. Let's call it progressive enhancement, sure. function makeScreenshotsClickable() { document.querySelectorAll('.blockImage img').forEach(img => { - console.log('-'); img.style.cursor = 'pointer'; img.onclick = () => { document.location = img.src; From 6ed8875d364d1b1fafbb06c8e2b98fb03857202f Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 25 Feb 2019 13:13:55 +0100 Subject: [PATCH 101/107] chore: attempt to fix flaky test --- e2e/override-globals/__tests__/index.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/e2e/override-globals/__tests__/index.js b/e2e/override-globals/__tests__/index.js index c8752ea116d9..f758c69b5525 100644 --- a/e2e/override-globals/__tests__/index.js +++ b/e2e/override-globals/__tests__/index.js @@ -15,8 +15,16 @@ describe('parent', () => { }); describe('child', () => { - it('works well', () => { - expect(() => new Promise()).toThrow('Booo'); + it('works well', done => { + // A timeout to guarantee it doesn't finish after 0 ms + setTimeout(() => { + try { + expect(() => new Promise()).toThrow('Booo'); + done(); + } catch (e) { + done.fail(e); + } + }, 10); }); }); }); From 410d5c9c40ed60d21ef137eac8903fc271f3eed6 Mon Sep 17 00:00:00 2001 From: Noel Martin Llevares Date: Mon, 25 Feb 2019 23:39:45 +1100 Subject: [PATCH 102/107] (docs) Add missing numTodoTests in Result Object. (#7976) --- docs/Configuration.md | 1 + website/versioned_docs/version-24.0/Configuration.md | 1 + website/versioned_docs/version-24.1/Configuration.md | 1 + 3 files changed, 3 insertions(+) diff --git a/docs/Configuration.md b/docs/Configuration.md index 4a90737f9258..585a8a1b6e9d 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -941,6 +941,7 @@ This option allows the use of a custom results processor. This processor must be "numPassedTests": number, "numFailedTests": number, "numPendingTests": number, + "numTodoTests": number, "openHandles": Array, "testResults": [{ "numFailingTests": number, diff --git a/website/versioned_docs/version-24.0/Configuration.md b/website/versioned_docs/version-24.0/Configuration.md index 7f44e709df99..b7c2dc92c3ba 100644 --- a/website/versioned_docs/version-24.0/Configuration.md +++ b/website/versioned_docs/version-24.0/Configuration.md @@ -936,6 +936,7 @@ This option allows the use of a custom results processor. This processor must be "numPassedTests": number, "numFailedTests": number, "numPendingTests": number, + "numTodoTests": number, "openHandles": Array, "testResults": [{ "numFailingTests": number, diff --git a/website/versioned_docs/version-24.1/Configuration.md b/website/versioned_docs/version-24.1/Configuration.md index 4366def08088..e79fee27dd77 100644 --- a/website/versioned_docs/version-24.1/Configuration.md +++ b/website/versioned_docs/version-24.1/Configuration.md @@ -942,6 +942,7 @@ This option allows the use of a custom results processor. This processor must be "numPassedTests": number, "numFailedTests": number, "numPendingTests": number, + "numTodoTests": number, "openHandles": Array, "testResults": [{ "numFailingTests": number, From 12053a65c5a7f5b66f9177da0a4e92a8194a86a9 Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Tue, 26 Feb 2019 00:39:20 +0100 Subject: [PATCH 103/107] Fix asymmetric equal for Number (#7948) * Fix asymmetric equal for Number * Update CHANGELOG * Handle the case of a being a new Number * Use Object.is for Number equality * Add unit tests related to issues/7941 * Add extra units for equality * Remove unnecessary comment in eq --- CHANGELOG.md | 1 + .../__snapshots__/matchers.test.js.snap | 49 +++++++++++++++++++ .../expect/src/__tests__/matchers.test.js | 11 +++++ packages/expect/src/jasmineUtils.ts | 4 +- 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ff03985fa81..af04a394f8f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - `[jest-cli]` Refactor `-o` and `--coverage` combined ([#7611](https://github.com/facebook/jest/pull/7611)) - `[expect]` Fix custom async matcher stack trace ([#7652](https://github.com/facebook/jest/pull/7652)) - `[expect]` Fix `toStrictEqual` not considering arrays with objects having undefined values correctly ([#7938](https://github.com/facebook/jest/pull/7938)) +- `[expect]` Fix non-symmetric equal for Number ([#7948](https://github.com/facebook/jest/pull/7948)) - `[jest-changed-files]` Improve default file selection for Mercurial repos ([#7880](https://github.com/facebook/jest/pull/7880)) - `[jest-validate]` Fix validating async functions ([#7894](https://github.com/facebook/jest/issues/7894)) - `[jest-circus]` Fix bug with test.only ([#7888](https://github.com/facebook/jest/pull/7888)) diff --git a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap index 9a46e02d761a..19d8645061d3 100644 --- a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap @@ -1818,6 +1818,13 @@ Expected: \\"abc\\" Received: \\"abc\\"" `; +exports[`.toEqual() {pass: false} expect("abc").not.toEqual({"0": "a", "1": "b", "2": "c"}) 1`] = ` +"expect(received).not.toEqual(expected) + +Expected: {\\"0\\": \\"a\\", \\"1\\": \\"b\\", \\"2\\": \\"c\\"} +Received: \\"abc\\"" +`; + exports[`.toEqual() {pass: false} expect("abcd").not.toEqual(StringContaining "bc") 1`] = ` "expect(received).not.toEqual(expected) @@ -1931,6 +1938,13 @@ Expected: Any Received: [Function anonymous]" `; +exports[`.toEqual() {pass: false} expect({"0": "a", "1": "b", "2": "c"}).not.toEqual("abc") 1`] = ` +"expect(received).not.toEqual(expected) + +Expected: \\"abc\\" +Received: {\\"0\\": \\"a\\", \\"1\\": \\"b\\", \\"2\\": \\"c\\"}" +`; + exports[`.toEqual() {pass: false} expect({"a": 1, "b": [Function b], "c": true}).not.toEqual({"a": 1, "b": Any, "c": Anything}) 1`] = ` "expect(received).not.toEqual(expected) @@ -2028,6 +2042,20 @@ Expected: {} Received: {}" `; +exports[`.toEqual() {pass: false} expect({}).not.toEqual(0) 1`] = ` +"expect(received).not.toEqual(expected) + +Expected: 0 +Received: {}" +`; + +exports[`.toEqual() {pass: false} expect(0).not.toEqual({}) 1`] = ` +"expect(received).not.toEqual(expected) + +Expected: {} +Received: 0" +`; + exports[`.toEqual() {pass: false} expect(0).toEqual(-0) 1`] = ` "expect(received).toEqual(expected) @@ -2035,6 +2063,13 @@ Expected: -0 Received: 0" `; +exports[`.toEqual() {pass: false} expect(0).toEqual(5e-324) 1`] = ` +"expect(received).toEqual(expected) + +Expected: 5e-324 +Received: 0" +`; + exports[`.toEqual() {pass: false} expect(1).not.toEqual(1) 1`] = ` "expect(received).not.toEqual(expected) @@ -2056,6 +2091,13 @@ Expected: ArrayContaining [1, 2] Received: 1" `; +exports[`.toEqual() {pass: false} expect(5e-324).toEqual(0) 1`] = ` +"expect(received).toEqual(expected) + +Expected: 0 +Received: 5e-324" +`; + exports[`.toEqual() {pass: false} expect(Immutable.List [1, 2]).not.toEqual(Immutable.List [1, 2]) 1`] = ` "expect(received).not.toEqual(expected) @@ -2416,6 +2458,13 @@ Expected: Map {2 => [\\"two\\"], 1 => [\\"one\\"]} Received: Map {1 => [\\"one\\"], 2 => [\\"two\\"]}" `; +exports[`.toEqual() {pass: false} expect(NaN).not.toEqual(NaN) 1`] = ` +"expect(received).not.toEqual(expected) + +Expected: NaN +Received: NaN" +`; + exports[`.toEqual() {pass: false} expect(Set {[1], [2], [3], [3]}).not.toEqual(Set {[3], [3], [2], [1]}) 1`] = ` "expect(received).not.toEqual(expected) diff --git a/packages/expect/src/__tests__/matchers.test.js b/packages/expect/src/__tests__/matchers.test.js index 70ec53f66fa1..4be1e90b0ac8 100644 --- a/packages/expect/src/__tests__/matchers.test.js +++ b/packages/expect/src/__tests__/matchers.test.js @@ -344,6 +344,8 @@ describe('.toEqual()', () => { [true, false], [1, 2], [0, -0], + [0, Number.MIN_VALUE], // issues/7941 + [Number.MIN_VALUE, 0], [{a: 5}, {b: 6}], ['banana', 'apple'], [null, undefined], @@ -436,7 +438,16 @@ describe('.toEqual()', () => { [ [true, true], [1, 1], + [NaN, NaN], + // eslint-disable-next-line no-new-wrappers + [0, new Number(0)], + // eslint-disable-next-line no-new-wrappers + [new Number(0), 0], ['abc', 'abc'], + // eslint-disable-next-line no-new-wrappers + [new String('abc'), 'abc'], + // eslint-disable-next-line no-new-wrappers + ['abc', new String('abc')], [[1], [1]], [[1, 2], [1, 2]], [Immutable.List([1]), Immutable.List([1])], diff --git a/packages/expect/src/jasmineUtils.ts b/packages/expect/src/jasmineUtils.ts index cb8b6deb9e16..d87bd502daec 100644 --- a/packages/expect/src/jasmineUtils.ts +++ b/packages/expect/src/jasmineUtils.ts @@ -106,9 +106,7 @@ function eq( // $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 - // other numeric values. - return a != +a ? b != +b : a === 0 ? 1 / a == 1 / b : a == +b; + return Object.is(Number(a), Number(b)); case '[object Date]': case '[object Boolean]': // Coerce dates and booleans to numeric primitive values. Dates are compared by their From 21a4547182b2716e93867505f0b35ab31d78ddd5 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 26 Feb 2019 09:38:54 +0100 Subject: [PATCH 104/107] chore: extract FakeTimers into a separate package (#7987) --- CHANGELOG.md | 1 + packages/jest-environment-jsdom/package.json | 1 + packages/jest-environment-jsdom/src/index.js | 3 +- packages/jest-environment-node/package.json | 1 + packages/jest-environment-node/src/index.js | 3 +- packages/jest-environment/package.json | 1 + packages/jest-environment/src/index.ts | 29 +++++++------------ packages/jest-environment/tsconfig.json | 1 + packages/jest-fake-timers/.npmignore | 3 ++ packages/jest-fake-timers/package.json | 22 ++++++++++++++ .../jestFakeTimers.test.ts.snap} | 0 .../src/__tests__/jestFakeTimers.test.ts} | 6 ++-- packages/jest-fake-timers/src/index.ts | 8 +++++ .../src/jestFakeTimers.ts} | 22 +++++++------- packages/jest-fake-timers/tsconfig.json | 11 +++++++ packages/jest-util/package.json | 3 +- packages/jest-util/src/index.ts | 3 +- packages/jest-util/tsconfig.json | 5 ++-- 18 files changed, 83 insertions(+), 40 deletions(-) create mode 100644 packages/jest-fake-timers/.npmignore create mode 100644 packages/jest-fake-timers/package.json rename packages/{jest-util/src/__tests__/__snapshots__/fakeTimers.test.ts.snap => jest-fake-timers/src/__tests__/__snapshots__/jestFakeTimers.test.ts.snap} (100%) rename packages/{jest-util/src/__tests__/fakeTimers.test.ts => jest-fake-timers/src/__tests__/jestFakeTimers.test.ts} (99%) create mode 100644 packages/jest-fake-timers/src/index.ts rename packages/{jest-util/src/FakeTimers.ts => jest-fake-timers/src/jestFakeTimers.ts} (97%) create mode 100644 packages/jest-fake-timers/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index af04a394f8f3..c28550f79bee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ - `[jest-phabricator]`: Migrate to TypeScript ([#7965](https://github.com/facebook/jest/pull/7965)) - `[jest-runner]`: Migrate to TypeScript ([#7968](https://github.com/facebook/jest/pull/7968)) - `[jest-runtime]`: Migrate to TypeScript ([#7964](https://github.com/facebook/jest/pull/7964)) +- `[@jest/fake-timers]`: Extract FakeTimers class from `jest-util` into a new separate package ([#7987](https://github.com/facebook/jest/pull/7987)) ### Performance diff --git a/packages/jest-environment-jsdom/package.json b/packages/jest-environment-jsdom/package.json index 0fb34d1b04f9..65799a5f7909 100644 --- a/packages/jest-environment-jsdom/package.json +++ b/packages/jest-environment-jsdom/package.json @@ -9,6 +9,7 @@ "license": "MIT", "main": "build/index.js", "dependencies": { + "@jest/fake-timers": "^24.1.0", "jest-mock": "^24.0.0", "jest-util": "^24.0.0", "jsdom": "^11.5.1" diff --git a/packages/jest-environment-jsdom/src/index.js b/packages/jest-environment-jsdom/src/index.js index 52eed2ff069c..56f417ba8188 100644 --- a/packages/jest-environment-jsdom/src/index.js +++ b/packages/jest-environment-jsdom/src/index.js @@ -12,7 +12,8 @@ import type {EnvironmentContext} from 'types/Environment'; import type {Global} from 'types/Global'; import type {ModuleMocker} from 'jest-mock'; -import {FakeTimers, installCommonGlobals} from 'jest-util'; +import {JestFakeTimers as FakeTimers} from '@jest/fake-timers'; +import {installCommonGlobals} from 'jest-util'; import mock from 'jest-mock'; import {JSDOM, VirtualConsole} from 'jsdom'; diff --git a/packages/jest-environment-node/package.json b/packages/jest-environment-node/package.json index 0914ca74f5e8..374695a24a69 100644 --- a/packages/jest-environment-node/package.json +++ b/packages/jest-environment-node/package.json @@ -9,6 +9,7 @@ "license": "MIT", "main": "build/index.js", "dependencies": { + "@jest/fake-timers": "^24.1.0", "jest-mock": "^24.0.0", "jest-util": "^24.0.0" }, diff --git a/packages/jest-environment-node/src/index.js b/packages/jest-environment-node/src/index.js index f09b3f7c30a1..1ba6d72de0af 100644 --- a/packages/jest-environment-node/src/index.js +++ b/packages/jest-environment-node/src/index.js @@ -13,7 +13,8 @@ import type {Global} from 'types/Global'; import type {ModuleMocker} from 'jest-mock'; import vm from 'vm'; -import {FakeTimers, installCommonGlobals} from 'jest-util'; +import {JestFakeTimers as FakeTimers} from '@jest/fake-timers'; +import {installCommonGlobals} from 'jest-util'; import mock from 'jest-mock'; type Timer = {| diff --git a/packages/jest-environment/package.json b/packages/jest-environment/package.json index d1888425032d..d9595ec1d00b 100644 --- a/packages/jest-environment/package.json +++ b/packages/jest-environment/package.json @@ -10,6 +10,7 @@ "main": "build/index.js", "types": "build/index.d.ts", "dependencies": { + "@jest/fake-timers": "^24.1.0", "@jest/transform": "^24.1.0", "@jest/types": "^24.1.0", "@types/node": "*", diff --git a/packages/jest-environment/src/index.ts b/packages/jest-environment/src/index.ts index 8f9d1e08502e..d8a6aed05f4e 100644 --- a/packages/jest-environment/src/index.ts +++ b/packages/jest-environment/src/index.ts @@ -7,8 +7,12 @@ import {Script} from 'vm'; import {Config, Global} from '@jest/types'; -import moduleMocker from 'jest-mock'; +import jestMock, {ModuleMocker} from 'jest-mock'; import {ScriptTransformer} from '@jest/transform'; +import {JestFakeTimers as FakeTimers} from '@jest/fake-timers'; + +type JestMockFn = typeof jestMock.fn; +type JestMockSpyOn = typeof jestMock.spyOn; export type EnvironmentContext = { console?: Console; @@ -27,21 +31,10 @@ export interface JestEnvironment { script: Script, ): {[ScriptTransformer.EVAL_RESULT_VARIABLE]: ModuleWrapper} | null; global: Global.Global; - // TODO: When `jest-util` is ESM, this can just be `fakeTimers: import('jest-util').FakeTimers` - fakeTimers: { - clearAllTimers(): void; - runAllImmediates(): void; - runAllTicks(): void; - runAllTimers(): void; - advanceTimersByTime(msToRun: number): void; - runOnlyPendingTimers(): void; - runWithRealTimers(callback: () => void): void; - getTimerCount(): number; - useFakeTimers(): void; - useRealTimers(): void; - }; + // TODO: This is nullable, and TS doesn't understand we deal with it in `jest-runtime`. Should be fixed + fakeTimers: FakeTimers; testFilePath: Config.Path; - moduleMocker: typeof moduleMocker; + moduleMocker: ModuleMocker; setup(): Promise; teardown(): Promise; } @@ -112,7 +105,7 @@ export interface Jest { /** * Creates a mock function. Optionally takes a mock implementation. */ - fn: typeof moduleMocker.fn; + fn: JestMockFn; /** * Given the name of a module, use the automatic mocking system to generate a * mocked version of the module for you. @@ -124,7 +117,7 @@ export interface Jest { /** * Determines if the given function is a mocked function. */ - isMockFunction(fn: Function): fn is ReturnType; + isMockFunction(fn: Function): fn is ReturnType; /** * Mocks a module with an auto-mocked version when it is being required. */ @@ -235,7 +228,7 @@ export interface Jest { * Note: By default, jest.spyOn also calls the spied method. This is * different behavior from most other test libraries. */ - spyOn: typeof moduleMocker.spyOn; + spyOn: JestMockSpyOn; /** * Indicates that the module system should never return a mocked version of * the specified module from require() (e.g. that it should always return the diff --git a/packages/jest-environment/tsconfig.json b/packages/jest-environment/tsconfig.json index 8824863bd93b..cfce4b39485b 100644 --- a/packages/jest-environment/tsconfig.json +++ b/packages/jest-environment/tsconfig.json @@ -5,6 +5,7 @@ "outDir": "build" }, "references": [ + {"path": "../jest-fake-timers"}, {"path": "../jest-transform"}, {"path": "../jest-types"}, {"path": "../jest-util"} diff --git a/packages/jest-fake-timers/.npmignore b/packages/jest-fake-timers/.npmignore new file mode 100644 index 000000000000..85e48fe7b0a4 --- /dev/null +++ b/packages/jest-fake-timers/.npmignore @@ -0,0 +1,3 @@ +**/__mocks__/** +**/__tests__/** +src diff --git a/packages/jest-fake-timers/package.json b/packages/jest-fake-timers/package.json new file mode 100644 index 000000000000..988be66bb72f --- /dev/null +++ b/packages/jest-fake-timers/package.json @@ -0,0 +1,22 @@ +{ + "name": "@jest/fake-timers", + "version": "24.1.0", + "repository": { + "type": "git", + "url": "https://github.com/facebook/jest.git", + "directory": "packages/jest-fake-timers" + }, + "license": "MIT", + "main": "build/index.js", + "types": "build/index.d.ts", + "dependencies": { + "@jest/types": "^24.1.0", + "@types/node": "*", + "jest-message-util": "^24.0.0", + "jest-mock": "^24.0.0" + }, + "engines": { + "node": ">= 6" + }, + "gitHead": "b16789230fd45056a7f2fa199bae06c7a1780deb" +} diff --git a/packages/jest-util/src/__tests__/__snapshots__/fakeTimers.test.ts.snap b/packages/jest-fake-timers/src/__tests__/__snapshots__/jestFakeTimers.test.ts.snap similarity index 100% rename from packages/jest-util/src/__tests__/__snapshots__/fakeTimers.test.ts.snap rename to packages/jest-fake-timers/src/__tests__/__snapshots__/jestFakeTimers.test.ts.snap diff --git a/packages/jest-util/src/__tests__/fakeTimers.test.ts b/packages/jest-fake-timers/src/__tests__/jestFakeTimers.test.ts similarity index 99% rename from packages/jest-util/src/__tests__/fakeTimers.test.ts rename to packages/jest-fake-timers/src/__tests__/jestFakeTimers.test.ts index 7d53b0c91e51..ac0d7ad149d1 100644 --- a/packages/jest-util/src/__tests__/fakeTimers.test.ts +++ b/packages/jest-fake-timers/src/__tests__/jestFakeTimers.test.ts @@ -7,9 +7,7 @@ import vm from 'vm'; import mock from 'jest-mock'; -import FakeTimers from '../FakeTimers'; -// TODO: import this type directly from jest-mock once TS migration is done -type ModuleMocker = typeof mock; +import FakeTimers from '../jestFakeTimers'; const timerConfig = { idToRef: (id: number) => id, @@ -22,7 +20,7 @@ const config = { }; describe('FakeTimers', () => { - let moduleMocker: ModuleMocker; + let moduleMocker: mock.ModuleMocker; beforeEach(() => { const global = vm.runInNewContext('this'); diff --git a/packages/jest-fake-timers/src/index.ts b/packages/jest-fake-timers/src/index.ts new file mode 100644 index 000000000000..fe0d39d5b62a --- /dev/null +++ b/packages/jest-fake-timers/src/index.ts @@ -0,0 +1,8 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +export {default as JestFakeTimers} from './jestFakeTimers'; diff --git a/packages/jest-util/src/FakeTimers.ts b/packages/jest-fake-timers/src/jestFakeTimers.ts similarity index 97% rename from packages/jest-util/src/FakeTimers.ts rename to packages/jest-fake-timers/src/jestFakeTimers.ts index aea4916981b5..9dca4a0ac1d2 100644 --- a/packages/jest-util/src/FakeTimers.ts +++ b/packages/jest-fake-timers/src/jestFakeTimers.ts @@ -5,18 +5,10 @@ * LICENSE file in the root directory of this source tree. */ -import mock from 'jest-mock'; +import {ModuleMocker} from 'jest-mock'; import {formatStackTrace, StackTraceConfig} from 'jest-message-util'; -import setGlobal from './setGlobal'; -type ModuleMocker = typeof mock; - -/** - * We don't know the type of arguments for a callback ahead of time which is why - * we are disabling the flowtype/no-weak-types rule here. - */ - -type Callback = (...args: any) => void; +type Callback = (...args: Array) => void; type TimerID = string; @@ -50,6 +42,16 @@ type TimerConfig = { const MS_IN_A_YEAR = 31536000000; +// TODO: Copied from `jest-util` to avoid cyclic dependency. Import from `jest-util` in the next major +const setGlobal = ( + globalToMutate: NodeJS.Global | Window, + key: string, + value: unknown, +) => { + // @ts-ignore: no index + globalToMutate[key] = value; +}; + export default class FakeTimers { private _cancelledImmediates!: {[key: string]: boolean}; private _cancelledTicks!: {[key: string]: boolean}; diff --git a/packages/jest-fake-timers/tsconfig.json b/packages/jest-fake-timers/tsconfig.json new file mode 100644 index 000000000000..335b1e9c373a --- /dev/null +++ b/packages/jest-fake-timers/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [ + {"path": "../jest-message-util"}, + {"path": "../jest-mock"} + ] +} diff --git a/packages/jest-util/package.json b/packages/jest-util/package.json index 13e9e9f6c706..4fceefa7a5da 100644 --- a/packages/jest-util/package.json +++ b/packages/jest-util/package.json @@ -10,14 +10,13 @@ "main": "build/index.js", "types": "build/index.d.ts", "dependencies": { + "@jest/fake-timers": "^24.1.0", "@jest/types": "^24.1.0", "@types/node": "*", "callsites": "^3.0.0", "chalk": "^2.0.1", "graceful-fs": "^4.1.15", "is-ci": "^2.0.0", - "jest-message-util": "^24.0.0", - "jest-mock": "^24.0.0", "mkdirp": "^0.5.1", "readable-stream": "^3.1.1", "slash": "^2.0.0", diff --git a/packages/jest-util/src/index.ts b/packages/jest-util/src/index.ts index 66c38c2b63e0..a7291e0ae7e1 100644 --- a/packages/jest-util/src/index.ts +++ b/packages/jest-util/src/index.ts @@ -5,12 +5,13 @@ * LICENSE file in the root directory of this source tree. */ +// TODO: Remove this export in the next major +import {JestFakeTimers as FakeTimers} from '@jest/fake-timers'; import BufferedConsole from './BufferedConsole'; import clearLine from './clearLine'; import CustomConsole from './CustomConsole'; import createDirectory from './createDirectory'; import ErrorWithStack from './ErrorWithStack'; -import FakeTimers from './FakeTimers'; import formatTestResults from './formatTestResults'; import getFailedSnapshotTests from './getFailedSnapshotTests'; import getConsoleOutput from './getConsoleOutput'; diff --git a/packages/jest-util/tsconfig.json b/packages/jest-util/tsconfig.json index c8a0f5ac5aa8..876baa6cf5c8 100644 --- a/packages/jest-util/tsconfig.json +++ b/packages/jest-util/tsconfig.json @@ -5,8 +5,7 @@ "outDir": "build" }, "references": [ - {"path": "../jest-types"}, - {"path": "../jest-message-util"}, - {"path": "../jest-mock"} + {"path": "../jest-fake-timers"}, + {"path": "../jest-types"} ] } From 4730c9dc50eee822d6db663033317c5a422f7372 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 26 Feb 2019 11:05:17 +0100 Subject: [PATCH 105/107] chore: cleanup JestEnvironment types (#7988) --- CHANGELOG.md | 2 +- .../src/legacy-code-todo-rewrite/jestAdapter.ts | 6 ++++-- packages/jest-environment/src/index.ts | 16 ++++++---------- packages/jest-runner/src/runTest.ts | 2 +- packages/jest-runtime/src/cli/index.ts | 2 +- packages/jest-runtime/src/index.ts | 16 +++++++++------- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c28550f79bee..e6e9e2d3890b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,7 +59,7 @@ - `[jest-circus]`: Migrate to TypeScript ([#7916](https://github.com/facebook/jest/pull/7916)) - `[jest-phabricator]`: Migrate to TypeScript ([#7965](https://github.com/facebook/jest/pull/7965)) - `[jest-runner]`: Migrate to TypeScript ([#7968](https://github.com/facebook/jest/pull/7968)) -- `[jest-runtime]`: Migrate to TypeScript ([#7964](https://github.com/facebook/jest/pull/7964)) +- `[jest-runtime]`: Migrate to TypeScript ([#7964](https://github.com/facebook/jest/pull/7964), [#7988](https://github.com/facebook/jest/pull/7988)) - `[@jest/fake-timers]`: Extract FakeTimers class from `jest-util` into a new separate package ([#7987](https://github.com/facebook/jest/pull/7987)) ### Performance diff --git a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts index 7064ad335553..7ad289942726 100644 --- a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts @@ -47,7 +47,8 @@ const jestAdapter = async ( }); if (config.timers === 'fake') { - environment.fakeTimers.useFakeTimers(); + // during setup, this cannot be null (and it's fine to explode if it is) + environment.fakeTimers!.useFakeTimers(); } globals.beforeEach(() => { @@ -63,7 +64,8 @@ const jestAdapter = async ( runtime.resetAllMocks(); if (config.timers === 'fake') { - environment.fakeTimers.useFakeTimers(); + // during setup, this cannot be null (and it's fine to explode if it is) + environment.fakeTimers!.useFakeTimers(); } } diff --git a/packages/jest-environment/src/index.ts b/packages/jest-environment/src/index.ts index d8a6aed05f4e..a29270083abb 100644 --- a/packages/jest-environment/src/index.ts +++ b/packages/jest-environment/src/index.ts @@ -22,19 +22,15 @@ export type EnvironmentContext = { // TODO: type this better: https://nodejs.org/api/modules.html#modules_the_module_wrapper type ModuleWrapper = (...args: Array) => unknown; -export interface JestEnvironment { - new ( - config: Config.ProjectConfig, - context?: EnvironmentContext, - ): JestEnvironment; +export declare class JestEnvironment { + constructor(config: Config.ProjectConfig); + constructor(config: Config.ProjectConfig, context: EnvironmentContext); + global: Global.Global; + fakeTimers: FakeTimers | null; + moduleMocker: ModuleMocker | null; runScript( script: Script, ): {[ScriptTransformer.EVAL_RESULT_VARIABLE]: ModuleWrapper} | null; - global: Global.Global; - // TODO: This is nullable, and TS doesn't understand we deal with it in `jest-runtime`. Should be fixed - fakeTimers: FakeTimers; - testFilePath: Config.Path; - moduleMocker: ModuleMocker; setup(): Promise; teardown(): Promise; } diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 94c759a5188f..22e7cd57465b 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -96,7 +96,7 @@ async function runTestInternal( }); } - const TestEnvironment: JestEnvironment = require(testEnvironment); + const TestEnvironment: typeof JestEnvironment = require(testEnvironment); const testFramework: TestFramework = process.env.JEST_CIRCUS === '1' ? require('jest-circus/runner') // eslint-disable-line import/no-extraneous-dependencies diff --git a/packages/jest-runtime/src/cli/index.ts b/packages/jest-runtime/src/cli/index.ts index cb4527e312ec..5a6284388da5 100644 --- a/packages/jest-runtime/src/cli/index.ts +++ b/packages/jest-runtime/src/cli/index.ts @@ -80,7 +80,7 @@ export function run(cliArgv?: Config.Argv, cliInfo?: Array) { watchman: globalConfig.watchman, }) as Promise) .then(hasteMap => { - const Environment: JestEnvironment = require(config.testEnvironment); + const Environment: typeof JestEnvironment = require(config.testEnvironment); const environment = new Environment(config); setGlobal( environment.global, diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 6c8849a792ee..694738227f3a 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -125,7 +125,8 @@ class Runtime { this._isCurrentlyExecutingManualMock = null; this._mockFactories = Object.create(null); this._mockRegistry = Object.create(null); - this._moduleMocker = this._environment.moduleMocker; + // during setup, this cannot be null (and it's fine to explode if it is) + this._moduleMocker = this._environment.moduleMocker!; this._isolatedModuleRegistry = null; this._isolatedMockRegistry = null; this._moduleRegistry = Object.create(null); @@ -170,7 +171,7 @@ class Runtime { } } - // TODO: Can this be `static shouldInstrument = shouldInstrument;`? + // TODO: Make this `static shouldInstrument = shouldInstrument;` after https://github.com/facebook/jest/issues/7846 static shouldInstrument( filename: Config.Path, options: ShouldInstrumentOptions, @@ -934,11 +935,11 @@ class Runtime { return jestObject; }; const useFakeTimers = () => { - this._environment.fakeTimers.useFakeTimers(); + _getFakeTimers().useFakeTimers(); return jestObject; }; const useRealTimers = () => { - this._environment.fakeTimers.useRealTimers(); + _getFakeTimers().useRealTimers(); return jestObject; }; const resetModules = () => { @@ -968,7 +969,7 @@ class Runtime { return jestObject; }; - const _getFakeTimers = () => { + const _getFakeTimers = (): NonNullable => { if (!this._environment.fakeTimers) { this._logFormattedReferenceError( 'You are trying to access a property or method of the Jest environment after it has been torn down.', @@ -976,7 +977,8 @@ class Runtime { process.exitCode = 1; } - return this._environment.fakeTimers; + // We've logged a user message above, so it doesn't matter if we return `null` here + return this._environment.fakeTimers!; }; const jestObject: Jest = { @@ -1024,7 +1026,7 @@ class Runtime { return jestObject; } - _logFormattedReferenceError(errorMessage: string) { + private _logFormattedReferenceError(errorMessage: string) { const originalStack = new ReferenceError(errorMessage) .stack!.split('\n') // Remove this file from the stack (jest-message-utils will keep one line) From e5a7739eaf47f81568a84f49837ffada15c0011a Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Tue, 26 Feb 2019 12:09:21 +0100 Subject: [PATCH 106/107] chore: migrate jest-config to TypeScript (#7944) --- packages/jest-config/package.json | 2 + .../src/{Defaults.js => Defaults.ts} | 11 +- .../src/{Deprecated.js => Deprecated.ts} | 14 +- .../src/{Descriptions.js => Descriptions.ts} | 10 +- ...nErrors.js => ReporterValidationErrors.ts} | 12 +- .../src/{ValidConfig.js => ValidConfig.ts} | 15 +- .../{Defaults.test.js => Defaults.test.ts} | 0 ...xWorkers.test.js => getMaxWorkers.test.ts} | 3 + ...{readConfig.test.js => readConfig.test.ts} | 1 + ...eadConfigs.test.js => readConfigs.test.ts} | 1 + ...Path.test.js => resolveConfigPath.test.ts} | 2 - ...etFromArgv.test.js => setFromArgv.test.ts} | 17 +- ...attern.test.js => validatePattern.test.ts} | 0 .../src/{constants.js => constants.ts} | 2 - ...CacheDirectory.js => getCacheDirectory.ts} | 7 +- .../{getMaxWorkers.js => getMaxWorkers.ts} | 17 +- .../jest-config/src/{index.js => index.ts} | 63 ++-- .../src/{normalize.js => normalize.ts} | 347 ++++++++++-------- ...tDir.js => readConfigFileAndSetRootDir.ts} | 9 +- ...olveConfigPath.js => resolveConfigPath.ts} | 22 +- .../src/{setFromArgv.js => setFromArgv.ts} | 13 +- .../jest-config/src/{utils.js => utils.ts} | 71 ++-- ...{validatePattern.js => validatePattern.ts} | 4 +- packages/jest-config/tsconfig.json | 17 + packages/jest-runner/src/runTest.ts | 8 +- packages/jest-runner/tsconfig.json | 1 + packages/jest-runtime/src/cli/index.ts | 3 +- packages/jest-runtime/tsconfig.json | 3 +- 28 files changed, 377 insertions(+), 298 deletions(-) rename packages/jest-config/src/{Defaults.js => Defaults.ts} (94%) rename packages/jest-config/src/{Deprecated.js => Deprecated.ts} (86%) rename packages/jest-config/src/{Descriptions.js => Descriptions.ts} (97%) rename packages/jest-config/src/{ReporterValidationErrors.js => ReporterValidationErrors.ts} (91%) rename packages/jest-config/src/{ValidConfig.js => ValidConfig.ts} (93%) rename packages/jest-config/src/__tests__/{Defaults.test.js => Defaults.test.ts} (100%) rename packages/jest-config/src/__tests__/{getMaxWorkers.test.js => getMaxWorkers.test.ts} (91%) rename packages/jest-config/src/__tests__/{readConfig.test.js => readConfig.test.ts} (95%) rename packages/jest-config/src/__tests__/{readConfigs.test.js => readConfigs.test.ts} (94%) rename packages/jest-config/src/__tests__/{resolveConfigPath.test.js => resolveConfigPath.test.ts} (99%) rename packages/jest-config/src/__tests__/{setFromArgv.test.js => setFromArgv.test.ts} (82%) rename packages/jest-config/src/__tests__/{validatePattern.test.js => validatePattern.test.ts} (100%) rename packages/jest-config/src/{constants.js => constants.ts} (97%) rename packages/jest-config/src/{getCacheDirectory.js => getCacheDirectory.ts} (91%) rename packages/jest-config/src/{getMaxWorkers.js => getMaxWorkers.ts} (60%) rename packages/jest-config/src/{index.js => index.ts} (92%) rename packages/jest-config/src/{normalize.js => normalize.ts} (75%) rename packages/jest-config/src/{readConfigFileAndSetRootDir.js => readConfigFileAndSetRootDir.ts} (90%) rename packages/jest-config/src/{resolveConfigPath.js => resolveConfigPath.ts} (87%) rename packages/jest-config/src/{setFromArgv.js => setFromArgv.ts} (86%) rename packages/jest-config/src/{utils.js => utils.ts} (77%) rename packages/jest-config/src/{validatePattern.js => validatePattern.ts} (84%) create mode 100644 packages/jest-config/tsconfig.json diff --git a/packages/jest-config/package.json b/packages/jest-config/package.json index fb97cfa3117a..a3886c4bcaf2 100644 --- a/packages/jest-config/package.json +++ b/packages/jest-config/package.json @@ -8,8 +8,10 @@ }, "license": "MIT", "main": "build/index.js", + "types": "build/index.d.ts", "dependencies": { "@babel/core": "^7.1.0", + "@jest/types": "^24.1.0", "babel-jest": "^24.1.0", "chalk": "^2.0.1", "glob": "^7.1.1", diff --git a/packages/jest-config/src/Defaults.js b/packages/jest-config/src/Defaults.ts similarity index 94% rename from packages/jest-config/src/Defaults.js rename to packages/jest-config/src/Defaults.ts index 3ef1d484ba24..b25ff9c149c0 100644 --- a/packages/jest-config/src/Defaults.js +++ b/packages/jest-config/src/Defaults.ts @@ -3,19 +3,16 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {DefaultOptions} from 'types/Config'; - +import {Config} from '@jest/types'; import {replacePathSepForRegex} from 'jest-regex-util'; import {NODE_MODULES} from './constants'; import getCacheDirectory from './getCacheDirectory'; const NODE_MODULES_REGEXP = replacePathSepForRegex(NODE_MODULES); -export default ({ +const defaultOptions: Config.DefaultOptions = { automock: false, bail: 0, browser: false, @@ -83,4 +80,6 @@ export default ({ watch: false, watchPathIgnorePatterns: [], watchman: true, -}: DefaultOptions); +}; + +export default defaultOptions; diff --git a/packages/jest-config/src/Deprecated.js b/packages/jest-config/src/Deprecated.ts similarity index 86% rename from packages/jest-config/src/Deprecated.js rename to packages/jest-config/src/Deprecated.ts index cc14996e5ad6..8ad752842d2a 100644 --- a/packages/jest-config/src/Deprecated.js +++ b/packages/jest-config/src/Deprecated.ts @@ -3,14 +3,12 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import chalk from 'chalk'; import prettyFormat from 'pretty-format'; -const format = (value: mixed) => prettyFormat(value, {min: true}); +const format = (value: unknown) => prettyFormat(value, {min: true}); export default { mapCoverage: () => ` Option ${chalk.bold( @@ -20,7 +18,7 @@ export default { Please update your configuration.`, preprocessorIgnorePatterns: (options: { - preprocessorIgnorePatterns: Array, + preprocessorIgnorePatterns: Array; }) => ` Option ${chalk.bold( '"preprocessorIgnorePatterns"', )} was replaced by ${chalk.bold( @@ -37,7 +35,7 @@ export default { Please update your configuration.`, scriptPreprocessor: (options: { - scriptPreprocessor: string, + scriptPreprocessor: string; }) => ` Option ${chalk.bold( '"scriptPreprocessor"', )} was replaced by ${chalk.bold( @@ -53,8 +51,8 @@ export default { Please update your configuration.`, - setupTestFrameworkScriptFile: (options: { - setupTestFrameworkScriptFile: Array, + setupTestFrameworkScriptFile: (_options: { + setupTestFrameworkScriptFile: Array; }) => ` Option ${chalk.bold( '"setupTestFrameworkScriptFile"', )} was replaced by configuration ${chalk.bold( @@ -64,7 +62,7 @@ export default { Please update your configuration.`, testPathDirs: (options: { - testPathDirs: Array, + testPathDirs: Array; }) => ` Option ${chalk.bold('"testPathDirs"')} was replaced by ${chalk.bold( '"roots"', )}. diff --git a/packages/jest-config/src/Descriptions.js b/packages/jest-config/src/Descriptions.ts similarity index 97% rename from packages/jest-config/src/Descriptions.js rename to packages/jest-config/src/Descriptions.ts index 44c4e4d6c116..e9088b6849e3 100644 --- a/packages/jest-config/src/Descriptions.js +++ b/packages/jest-config/src/Descriptions.ts @@ -3,11 +3,11 @@ * * 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 ({ +import {Config} from '@jest/types'; + +const descriptions: {[key in keyof Config.InitialOptions]: string} = { automock: 'All imported modules in your tests should be mocked automatically', bail: 'Stop running tests after `n` failures', browser: 'Respect "browser" field in package.json when resolving modules', @@ -91,4 +91,6 @@ export default ({ watchPathIgnorePatterns: 'An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode', watchman: 'Whether to use watchman for file crawling', -}: {[string]: string}); +}; + +export default descriptions; diff --git a/packages/jest-config/src/ReporterValidationErrors.js b/packages/jest-config/src/ReporterValidationErrors.ts similarity index 91% rename from packages/jest-config/src/ReporterValidationErrors.js rename to packages/jest-config/src/ReporterValidationErrors.ts index 144b5f4ea950..0ded7dc3ef29 100644 --- a/packages/jest-config/src/ReporterValidationErrors.js +++ b/packages/jest-config/src/ReporterValidationErrors.ts @@ -6,8 +6,8 @@ * @flow */ -import type {ReporterConfig} from 'types/Config'; - +import {Config} from '@jest/types'; +// @ts-ignore: Not migrated to TS import {ValidationError} from 'jest-validate'; import chalk from 'chalk'; import getType from 'jest-get-type'; @@ -26,7 +26,7 @@ const ERROR = `${BULLET}Reporter Validation Error`; */ export function createReporterError( reporterIndex: number, - reporterValue: Array | string, + reporterValue: Array | string, ): ValidationError { const errorMessage = ` Reporter at index ${reporterIndex} must be of type:\n` + @@ -38,7 +38,7 @@ export function createReporterError( } export function createArrayReporterError( - arrayReporter: ReporterConfig, + arrayReporter: Config.ReporterConfig, reporterIndex: number, valueIndex: number, value: string | Object, @@ -63,7 +63,7 @@ export function createArrayReporterError( } export function validateReporters( - reporterConfig: Array, + reporterConfig: Array, ): boolean { return reporterConfig.every((reporter, index) => { if (Array.isArray(reporter)) { @@ -77,7 +77,7 @@ export function validateReporters( } function validateArrayReporter( - arrayReporter: ReporterConfig, + arrayReporter: Config.ReporterConfig, reporterIndex: number, ) { const [path, options] = arrayReporter; diff --git a/packages/jest-config/src/ValidConfig.js b/packages/jest-config/src/ValidConfig.ts similarity index 93% rename from packages/jest-config/src/ValidConfig.js rename to packages/jest-config/src/ValidConfig.ts index 883ebb0b8d56..eb1019e57ec9 100644 --- a/packages/jest-config/src/ValidConfig.js +++ b/packages/jest-config/src/ValidConfig.ts @@ -3,21 +3,19 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {InitialOptions} from 'types/Config'; - +import {Config} from '@jest/types'; import {replacePathSepForRegex} from 'jest-regex-util'; +// @ts-ignore: Not migrated to TS import {multipleValidOptions} from 'jest-validate'; import {NODE_MODULES} from './constants'; const NODE_MODULES_REGEXP = replacePathSepForRegex(NODE_MODULES); -export default ({ +const initialOptions: Config.InitialOptions = { automock: false, - bail: (multipleValidOptions(false, 0): any), + bail: multipleValidOptions(false, 0), browser: false, cache: true, cacheDirectory: '/tmp/user/jest', @@ -40,6 +38,7 @@ export default ({ statements: 100, }, }, + // @ts-ignore: Missing from initial options... https://github.com/facebook/jest/pull/7923 cwd: '/root', dependencyExtractor: '/dependencyExtractor.js', displayName: 'project-name', @@ -135,4 +134,6 @@ export default ({ ], ], watchman: true, -}: InitialOptions); +}; + +export default initialOptions; diff --git a/packages/jest-config/src/__tests__/Defaults.test.js b/packages/jest-config/src/__tests__/Defaults.test.ts similarity index 100% rename from packages/jest-config/src/__tests__/Defaults.test.js rename to packages/jest-config/src/__tests__/Defaults.test.ts diff --git a/packages/jest-config/src/__tests__/getMaxWorkers.test.js b/packages/jest-config/src/__tests__/getMaxWorkers.test.ts similarity index 91% rename from packages/jest-config/src/__tests__/getMaxWorkers.test.js rename to packages/jest-config/src/__tests__/getMaxWorkers.test.ts index 5ce2cf846dd1..aab3290c5357 100644 --- a/packages/jest-config/src/__tests__/getMaxWorkers.test.js +++ b/packages/jest-config/src/__tests__/getMaxWorkers.test.ts @@ -38,16 +38,19 @@ describe('getMaxWorkers', () => { describe('% based', () => { it('50% = 2 workers', () => { const argv = {maxWorkers: '50%'}; + // @ts-ignore: need to fix the typing expect(getMaxWorkers(argv)).toBe(2); }); it('< 0 workers should become 1', () => { const argv = {maxWorkers: '1%'}; + // @ts-ignore: need to fix the typing expect(getMaxWorkers(argv)).toBe(1); }); it("0% shouldn't break", () => { const argv = {maxWorkers: '0%'}; + // @ts-ignore: need to fix the typing expect(getMaxWorkers(argv)).toBe(1); }); }); diff --git a/packages/jest-config/src/__tests__/readConfig.test.js b/packages/jest-config/src/__tests__/readConfig.test.ts similarity index 95% rename from packages/jest-config/src/__tests__/readConfig.test.js rename to packages/jest-config/src/__tests__/readConfig.test.ts index f3914aa8b89c..58ff3551430b 100644 --- a/packages/jest-config/src/__tests__/readConfig.test.js +++ b/packages/jest-config/src/__tests__/readConfig.test.ts @@ -5,6 +5,7 @@ import {readConfig} from '../index'; test('readConfig() throws when an object is passed without a file path', () => { expect(() => { readConfig( + // @ts-ignore null /* argv */, {} /* packageRootOrConfig */, false /* skipArgvConfigOption */, diff --git a/packages/jest-config/src/__tests__/readConfigs.test.js b/packages/jest-config/src/__tests__/readConfigs.test.ts similarity index 94% rename from packages/jest-config/src/__tests__/readConfigs.test.js rename to packages/jest-config/src/__tests__/readConfigs.test.ts index 0e114e97b3c7..e08204487c07 100644 --- a/packages/jest-config/src/__tests__/readConfigs.test.js +++ b/packages/jest-config/src/__tests__/readConfigs.test.ts @@ -4,6 +4,7 @@ import {readConfigs} from '../index'; test('readConfigs() throws when called without project paths', () => { expect(() => { + // @ts-ignore readConfigs(null /* argv */, [] /* projectPaths */); }).toThrowError('jest: No configuration found for any project.'); }); diff --git a/packages/jest-config/src/__tests__/resolveConfigPath.test.js b/packages/jest-config/src/__tests__/resolveConfigPath.test.ts similarity index 99% rename from packages/jest-config/src/__tests__/resolveConfigPath.test.js rename to packages/jest-config/src/__tests__/resolveConfigPath.test.ts index 5000d5e58ee8..713c38def193 100644 --- a/packages/jest-config/src/__tests__/resolveConfigPath.test.js +++ b/packages/jest-config/src/__tests__/resolveConfigPath.test.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import os from 'os'; diff --git a/packages/jest-config/src/__tests__/setFromArgv.test.js b/packages/jest-config/src/__tests__/setFromArgv.test.ts similarity index 82% rename from packages/jest-config/src/__tests__/setFromArgv.test.js rename to packages/jest-config/src/__tests__/setFromArgv.test.ts index 182ab5bb570a..8216b0e2c35c 100644 --- a/packages/jest-config/src/__tests__/setFromArgv.test.js +++ b/packages/jest-config/src/__tests__/setFromArgv.test.ts @@ -6,16 +6,17 @@ * */ +import {Config} from '@jest/types'; import setFromArgv from '../setFromArgv'; test('maps special values to valid options', () => { - const options = {}; + const options = {} as Config.InitialOptions; const argv = { coverage: true, env: 'node', json: true, watchAll: true, - }; + } as Config.Argv; expect(setFromArgv(options, argv)).toMatchObject({ collectCoverage: true, @@ -27,12 +28,12 @@ test('maps special values to valid options', () => { }); test('maps regular values to themselves', () => { - const options = {}; + const options = {} as Config.InitialOptions; const argv = { collectCoverageOnlyFrom: ['a', 'b'], coverageDirectory: 'covDir', watchman: true, - }; + } as Config.Argv; expect(setFromArgv(options, argv)).toMatchObject({ collectCoverageOnlyFrom: ['a', 'b'], @@ -42,11 +43,11 @@ test('maps regular values to themselves', () => { }); test('works with string objects', () => { - const options = {}; + const options = {} as Config.InitialOptions; const argv = { moduleNameMapper: '{"types/(.*)": "/src/types/$1"}', transform: '{"*.js": "/transformer"}', - }; + } as Config.Argv; expect(setFromArgv(options, argv)).toMatchObject({ moduleNameMapper: { 'types/(.*)': '/src/types/$1', @@ -58,10 +59,10 @@ test('works with string objects', () => { }); test('explicit flags override those from --config', () => { - const options = {}; + const options = {} as Config.InitialOptions; const argv = { config: '{"watch": false}', watch: true, - }; + } as Config.Argv; expect(setFromArgv(options, argv)).toMatchObject({watch: true}); }); diff --git a/packages/jest-config/src/__tests__/validatePattern.test.js b/packages/jest-config/src/__tests__/validatePattern.test.ts similarity index 100% rename from packages/jest-config/src/__tests__/validatePattern.test.js rename to packages/jest-config/src/__tests__/validatePattern.test.ts diff --git a/packages/jest-config/src/constants.js b/packages/jest-config/src/constants.ts similarity index 97% rename from packages/jest-config/src/constants.js rename to packages/jest-config/src/constants.ts index 3877e91b3177..90f055b87320 100644 --- a/packages/jest-config/src/constants.js +++ b/packages/jest-config/src/constants.ts @@ -3,8 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ import path from 'path'; diff --git a/packages/jest-config/src/getCacheDirectory.js b/packages/jest-config/src/getCacheDirectory.ts similarity index 91% rename from packages/jest-config/src/getCacheDirectory.js rename to packages/jest-config/src/getCacheDirectory.ts index 587d9c21fdb6..d9a4ca266460 100644 --- a/packages/jest-config/src/getCacheDirectory.js +++ b/packages/jest-config/src/getCacheDirectory.ts @@ -3,13 +3,10 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -const path = require('path'); -const os = require('os'); - +import path from 'path'; +import os from 'os'; import {sync as realpath} from 'realpath-native'; const getCacheDirectory = () => { diff --git a/packages/jest-config/src/getMaxWorkers.js b/packages/jest-config/src/getMaxWorkers.ts similarity index 60% rename from packages/jest-config/src/getMaxWorkers.js rename to packages/jest-config/src/getMaxWorkers.ts index b8419dfcbc7a..29e350b7a367 100644 --- a/packages/jest-config/src/getMaxWorkers.js +++ b/packages/jest-config/src/getMaxWorkers.ts @@ -3,23 +3,24 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Argv} from 'types/Argv'; - import os from 'os'; +import {Config} from '@jest/types'; -export default function getMaxWorkers(argv: Argv): number { +export default function getMaxWorkers( + argv: Partial>, +): number { if (argv.runInBand) { return 1; } else if (argv.maxWorkers) { - const parsed = parseInt(argv.maxWorkers, 10); + // TODO: How to type this properly? Should probably use `coerce` from `yargs` + const maxWorkers = (argv.maxWorkers as unknown) as number | string; + const parsed = parseInt(maxWorkers as string, 10); if ( - typeof argv.maxWorkers === 'string' && - argv.maxWorkers.trim().endsWith('%') && + typeof maxWorkers === 'string' && + maxWorkers.trim().endsWith('%') && parsed > 0 && parsed <= 100 ) { diff --git a/packages/jest-config/src/index.js b/packages/jest-config/src/index.ts similarity index 92% rename from packages/jest-config/src/index.js rename to packages/jest-config/src/index.ts index 81d6970908c5..45bebcafcdac 100644 --- a/packages/jest-config/src/index.js +++ b/packages/jest-config/src/index.ts @@ -3,26 +3,16 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Argv} from 'types/Argv'; -import type { - GlobalConfig, - InitialOptions, - Path, - ProjectConfig, -} from 'types/Config'; - -import chalk from 'chalk'; import fs from 'fs'; import path from 'path'; +import {Config} from '@jest/types'; +import chalk from 'chalk'; import {isJSONString, replaceRootDirInPath} from './utils'; import normalize from './normalize'; import resolveConfigPath from './resolveConfigPath'; import readConfigFileAndSetRootDir from './readConfigFileAndSetRootDir'; - export {getTestEnvironment, isJSONString} from './utils'; export {default as normalize} from './normalize'; export {default as deprecationEntries} from './Deprecated'; @@ -30,22 +20,24 @@ export {replaceRootDirInPath} from './utils'; export {default as defaults} from './Defaults'; export {default as descriptions} from './Descriptions'; +type ReadConfig = { + configPath: Config.Path | null | undefined; + globalConfig: Config.GlobalConfig; + hasDeprecationWarnings: boolean; + projectConfig: Config.ProjectConfig; +}; + export function readConfig( - argv: Argv, - packageRootOrConfig: Path | InitialOptions, + argv: Config.Argv, + packageRootOrConfig: Config.Path | Config.InitialOptions, // Whether it needs to look into `--config` arg passed to CLI. // It only used to read initial config. If the initial config contains // `project` property, we don't want to read `--config` value and rather // read individual configs for every project. skipArgvConfigOption?: boolean, - parentConfigPath: ?Path, - projectIndex?: number = Infinity, -): { - configPath: ?Path, - globalConfig: GlobalConfig, - hasDeprecationWarnings: boolean, - projectConfig: ProjectConfig, -} { + parentConfigPath?: Config.Path | null, + projectIndex: number = Infinity, +): ReadConfig { let rawOptions; let configPath = null; @@ -104,8 +96,11 @@ export function readConfig( } const groupOptions = ( - options: Object, -): {globalConfig: GlobalConfig, projectConfig: ProjectConfig} => ({ + options: Config.ProjectConfig & Config.GlobalConfig, +): { + globalConfig: Config.GlobalConfig; + projectConfig: Config.ProjectConfig; +} => ({ globalConfig: Object.freeze({ bail: options.bail, changedFilesWithAncestor: options.changedFilesWithAncestor, @@ -218,11 +213,15 @@ const groupOptions = ( }), }); -const ensureNoDuplicateConfigs = (parsedConfigs, projects) => { +const ensureNoDuplicateConfigs = ( + parsedConfigs: Array, + projects: Config.GlobalConfig['projects'], +) => { const configPathMap = new Map(); for (const config of parsedConfigs) { const {configPath} = config; + if (configPathMap.has(configPath)) { const message = `Whoops! Two projects resolved to the same config path: ${chalk.bold( String(configPath), @@ -256,18 +255,18 @@ This usually means that your ${chalk.bold( // If no projects are specified, process.cwd() will be used as the default // (and only) project. export function readConfigs( - argv: Argv, - projectPaths: Array, + argv: Config.Argv, + projectPaths: Array, ): { - globalConfig: GlobalConfig, - configs: Array, - hasDeprecationWarnings: boolean, + globalConfig: Config.GlobalConfig; + configs: Array; + hasDeprecationWarnings: boolean; } { let globalConfig; let hasDeprecationWarnings; - let configs: Array = []; + let configs: Array = []; let projects = projectPaths; - let configPath: ?Path; + let configPath: Config.Path | null | undefined; if (projectPaths.length === 1) { const parsedConfig = readConfig(argv, projects[0]); diff --git a/packages/jest-config/src/normalize.js b/packages/jest-config/src/normalize.ts similarity index 75% rename from packages/jest-config/src/normalize.js rename to packages/jest-config/src/normalize.ts index f938d520e03d..ed8940cc3d57 100644 --- a/packages/jest-config/src/normalize.js +++ b/packages/jest-config/src/normalize.ts @@ -3,32 +3,22 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Argv} from 'types/Argv'; -import type { - InitialOptions, - DefaultOptions, - ReporterConfig, - GlobalConfig, - Path, - ProjectConfig, -} from 'types/Config'; - import crypto from 'crypto'; -import glob from 'glob'; import path from 'path'; +import glob from 'glob'; +import {Config} from '@jest/types'; +// @ts-ignore: Not migrated to TS import {ValidationError, validate} from 'jest-validate'; -import validatePattern from './validatePattern'; import {clearLine, replacePathSepForGlob} from 'jest-util'; import chalk from 'chalk'; -import getMaxWorkers from './getMaxWorkers'; import micromatch from 'micromatch'; import {sync as realpath} from 'realpath-native'; import Resolver from 'jest-resolve'; import {replacePathSepForRegex} from 'jest-regex-util'; +import validatePattern from './validatePattern'; +import getMaxWorkers from './getMaxWorkers'; import { BULLET, DOCUMENTATION_NOTE, @@ -46,18 +36,22 @@ import DEFAULT_CONFIG from './Defaults'; import DEPRECATED_CONFIG from './Deprecated'; import setFromArgv from './setFromArgv'; import VALID_CONFIG from './ValidConfig'; - const ERROR = `${BULLET}Validation Error`; const PRESET_EXTENSIONS = ['.json', '.js']; const PRESET_NAME = 'jest-preset'; -const createConfigError = message => +type AllOptions = Config.ProjectConfig & Config.GlobalConfig; + +const createConfigError = (message: string) => new ValidationError(ERROR, message, DOCUMENTATION_NOTE); const mergeOptionWithPreset = ( - options: InitialOptions, - preset: InitialOptions, - optionName: string, + options: Config.InitialOptions, + preset: Config.InitialOptions, + optionName: keyof Pick< + Config.InitialOptions, + 'moduleNameMapper' | 'transform' + >, ) => { if (options[optionName] && preset[optionName]) { options[optionName] = { @@ -69,10 +63,10 @@ const mergeOptionWithPreset = ( }; const setupPreset = ( - options: InitialOptions, + options: Config.InitialOptions, optionsPreset: string, -): InitialOptions => { - let preset; +): Config.InitialOptions => { + let preset: Config.InitialOptions; const presetPath = replaceRootDirInPath(options.rootDir, optionsPreset); const presetModule = Resolver.findNodeModule( presetPath.startsWith('.') @@ -92,8 +86,8 @@ const setupPreset = ( } } catch (e) {} - // $FlowFixMe - preset = (require(presetModule): InitialOptions); + // @ts-ignore: `presetModule` can be null? + preset = require(presetModule); } catch (error) { if (error instanceof SyntaxError || error instanceof TypeError) { throw createConfigError( @@ -132,7 +126,7 @@ const setupPreset = ( return {...preset, ...options}; }; -const setupBabelJest = (options: InitialOptions) => { +const setupBabelJest = (options: Config.InitialOptions) => { const transform = options.transform; let babelJest; if (transform) { @@ -175,12 +169,16 @@ const setupBabelJest = (options: InitialOptions) => { }; const normalizeCollectCoverageOnlyFrom = ( - options: InitialOptions, - key: string, + options: Config.InitialOptions & + Required>, + key: keyof Pick, ) => { - const collectCoverageOnlyFrom = Array.isArray(options[key]) - ? options[key] // passed from argv - : Object.keys(options[key]); // passed from options + const initialCollectCoverageFrom = options[key]; + const collectCoverageOnlyFrom: Config.Glob[] = Array.isArray( + initialCollectCoverageFrom, + ) + ? initialCollectCoverageFrom // passed from argv + : Object.keys(initialCollectCoverageFrom); // passed from options return collectCoverageOnlyFrom.reduce((map, filePath) => { filePath = path.resolve( options.rootDir, @@ -191,22 +189,27 @@ const normalizeCollectCoverageOnlyFrom = ( }, Object.create(null)); }; -const normalizeCollectCoverageFrom = (options: InitialOptions, key: string) => { - let value; - if (!options[key]) { +const normalizeCollectCoverageFrom = ( + options: Config.InitialOptions & + Required>, + key: keyof Pick, +) => { + const initialCollectCoverageFrom = options[key]; + let value: Config.Glob[] | undefined; + if (!initialCollectCoverageFrom) { value = []; } - if (!Array.isArray(options[key])) { + if (!Array.isArray(initialCollectCoverageFrom)) { try { - value = JSON.parse(options[key]); + value = JSON.parse(initialCollectCoverageFrom); } catch (e) {} if (options[key] && !Array.isArray(value)) { - value = [options[key]]; + value = [initialCollectCoverageFrom]; } } else { - value = options[key]; + value = initialCollectCoverageFrom; } if (value) { @@ -219,8 +222,16 @@ const normalizeCollectCoverageFrom = (options: InitialOptions, key: string) => { }; const normalizeUnmockedModulePathPatterns = ( - options: InitialOptions, - key: string, + options: Config.InitialOptions, + key: keyof Pick< + Config.InitialOptions, + | 'coveragePathIgnorePatterns' + | 'modulePathIgnorePatterns' + | 'testPathIgnorePatterns' + | 'transformIgnorePatterns' + | 'watchPathIgnorePatterns' + | 'unmockedModulePathPatterns' + >, ) => // _replaceRootDirTags is specifically well-suited for substituting // in paths (it deals with properly interpreting relative path @@ -228,11 +239,13 @@ const normalizeUnmockedModulePathPatterns = ( // // For patterns, direct global substitution is far more ideal, so we // special case substitutions for patterns here. - options[key].map(pattern => + options[key]!.map(pattern => replacePathSepForRegex(pattern.replace(//g, options.rootDir)), ); -const normalizePreprocessor = (options: InitialOptions): InitialOptions => { +const normalizePreprocessor = ( + options: Config.InitialOptions, +): Config.InitialOptions => { if (options.scriptPreprocessor && options.transform) { throw createConfigError( ` Options: ${chalk.bold('scriptPreprocessor')} and ${chalk.bold( @@ -269,10 +282,10 @@ const normalizePreprocessor = (options: InitialOptions): InitialOptions => { }; const normalizeMissingOptions = ( - options: InitialOptions, - configPath: ?Path, + options: Config.InitialOptions, + configPath: Config.Path | null | undefined, projectIndex: number, -): InitialOptions => { +): Config.InitialOptions => { if (!options.name) { options.name = crypto .createHash('md5') @@ -290,7 +303,9 @@ const normalizeMissingOptions = ( return options; }; -const normalizeRootDir = (options: InitialOptions): InitialOptions => { +const normalizeRootDir = ( + options: Config.InitialOptions, +): Config.InitialOptions => { // Assert that there *is* a rootDir if (!options.hasOwnProperty('rootDir')) { throw createConfigError( @@ -309,7 +324,7 @@ const normalizeRootDir = (options: InitialOptions): InitialOptions => { return options; }; -const normalizeReporters = (options: InitialOptions, basedir) => { +const normalizeReporters = (options: Config.InitialOptions) => { const reporters = options.reporters; if (!reporters || !Array.isArray(reporters)) { return options; @@ -317,7 +332,7 @@ const normalizeReporters = (options: InitialOptions, basedir) => { validateReporters(reporters); options.reporters = reporters.map(reporterConfig => { - const normalizedReporterConfig: ReporterConfig = + const normalizedReporterConfig: Config.ReporterConfig = typeof reporterConfig === 'string' ? // if reporter config is a string, we wrap it in an array // and pass an empty object for options argument, to normalize @@ -348,7 +363,7 @@ const normalizeReporters = (options: InitialOptions, basedir) => { return options; }; -const buildTestPathPattern = (argv: Argv): string => { +const buildTestPathPattern = (argv: Config.Argv): string => { const patterns = []; if (argv._) { @@ -386,11 +401,14 @@ const showTestPathPatternError = (testPathPattern: string) => { }; export default function normalize( - options: InitialOptions, - argv: Argv, - configPath: ?Path, - projectIndex?: number = Infinity, -) { + options: Config.InitialOptions, + argv: Config.Argv, + configPath?: Config.Path | null, + projectIndex: number = Infinity, +): { + hasDeprecationWarnings: boolean; + options: AllOptions; +} { const {hasDeprecationWarnings} = validate(options, { comment: DOCUMENTATION_NOTE, deprecatedConfig: DEPRECATED_CONFIG, @@ -429,14 +447,12 @@ export default function normalize( options.setupTestFrameworkScriptFile && options.setupFilesAfterEnv.length > 0 ) { - throw createConfigError( - ` Options: ${chalk.bold( - 'setupTestFrameworkScriptFile', - )} and ${chalk.bold('setupFilesAfterEnv')} cannot be used together. + throw createConfigError(` Options: ${chalk.bold( + 'setupTestFrameworkScriptFile', + )} and ${chalk.bold('setupFilesAfterEnv')} cannot be used together. Please change your configuration to only use ${chalk.bold( 'setupFilesAfterEnv', - )}.`, - ); + )}.`); } if (options.setupTestFrameworkScriptFile) { @@ -452,6 +468,7 @@ export default function normalize( options.roots = options.testPathDirs; delete options.testPathDirs; } + if (!options.roots) { options.roots = [options.rootDir]; } @@ -465,10 +482,10 @@ export default function normalize( } setupBabelJest(options); - - const newOptions: $Shape = { + // TODO: Type this properly + const newOptions = ({ ...DEFAULT_CONFIG, - }; + } as unknown) as AllOptions; try { // try to resolve windows short paths, ignoring errors (permission errors, mostly) @@ -485,51 +502,68 @@ export default function normalize( }); } - Object.keys(options).reduce((newOptions, key: $Keys) => { + const optionKeys = Object.keys(options) as Array; + + optionKeys.reduce((newOptions, key: keyof Config.InitialOptions) => { // The resolver has been resolved separately; skip it if (key === 'resolver') { return newOptions; } + + // This is cheating, because it claims that all keys of InitialOptions are Required. + // We only really know it's Required for oldOptions[key], not for oldOptions.someOtherKey, + // so oldOptions[key] is the only way it should be used. + const oldOptions = options as Config.InitialOptions & + Required>; let value; switch (key) { case 'collectCoverageOnlyFrom': - value = normalizeCollectCoverageOnlyFrom(options, key); + value = normalizeCollectCoverageOnlyFrom(oldOptions, key); break; case 'setupFiles': case 'setupFilesAfterEnv': case 'snapshotSerializers': - value = - options[key] && - options[key].map(filePath => - resolve(newOptions.resolver, { - filePath, - key, - rootDir: options.rootDir, - }), - ); + { + const option = oldOptions[key]; + value = + option && + option.map(filePath => + resolve(newOptions.resolver, { + filePath, + key, + rootDir: options.rootDir, + }), + ); + } break; case 'modulePaths': case 'roots': - value = - options[key] && - options[key].map(filePath => - path.resolve( - options.rootDir, - replaceRootDirInPath(options.rootDir, filePath), - ), - ); + { + const option = oldOptions[key]; + value = + option && + option.map(filePath => + path.resolve( + options.rootDir, + replaceRootDirInPath(options.rootDir, filePath), + ), + ); + } break; case 'collectCoverageFrom': - value = normalizeCollectCoverageFrom(options, key); + value = normalizeCollectCoverageFrom(oldOptions, key); break; case 'cacheDirectory': case 'coverageDirectory': - value = - options[key] && - path.resolve( - options.rootDir, - replaceRootDirInPath(options.rootDir, options[key]), - ); + { + const option = oldOptions[key]; + value = + option && + path.resolve( + options.rootDir, + replaceRootDirInPath(options.rootDir, option), + ); + } break; case 'dependencyExtractor': case 'globalSetup': @@ -539,37 +573,48 @@ export default function normalize( case 'testResultsProcessor': case 'testRunner': case 'filter': - value = - options[key] && - resolve(newOptions.resolver, { - filePath: options[key], - key, - rootDir: options.rootDir, - }); + { + const option = oldOptions[key]; + value = + option && + resolve(newOptions.resolver, { + filePath: option, + key, + rootDir: options.rootDir, + }); + } break; case 'runner': - value = - options[key] && - getRunner(newOptions.resolver, { - filePath: options[key], - rootDir: options.rootDir, - }); + { + const option = oldOptions[key]; + value = + option && + getRunner(newOptions.resolver, { + filePath: option, + 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 - // set it to null and throw an error lazily when it is used. - value = - options[key] && - resolve(newOptions.resolver, { - filePath: options[key], - key, - optional: options[key] === DEFAULT_CONFIG[key], - rootDir: options.rootDir, - }); + { + // We only want this to throw if "prettierPath" is explicitly passed + // from config or CLI, and the requested path isn't found. Otherwise we + // set it to null and throw an error lazily when it is used. + + const option = oldOptions[key]; + + value = + option && + resolve(newOptions.resolver, { + filePath: option, + key, + optional: option === DEFAULT_CONFIG[key], + rootDir: options.rootDir, + }); + } break; case 'moduleNameMapper': - const moduleNameMapper = options[key]; + const moduleNameMapper = oldOptions[key]; value = moduleNameMapper && Object.keys(moduleNameMapper).map(regex => { @@ -578,7 +623,7 @@ export default function normalize( }); break; case 'transform': - const transform = options[key]; + const transform = oldOptions[key]; value = transform && Object.keys(transform).map(regex => [ @@ -596,12 +641,12 @@ export default function normalize( case 'transformIgnorePatterns': case 'watchPathIgnorePatterns': case 'unmockedModulePathPatterns': - value = normalizeUnmockedModulePathPatterns(options, key); + value = normalizeUnmockedModulePathPatterns(oldOptions, key); break; case 'haste': - value = {...options[key]}; + value = {...oldOptions[key]}; if (value.hasteImplModulePath != null) { - value.hasteImplModulePath = resolve(newOptions.resolver, { + const resolvedHasteImpl = resolve(newOptions.resolver, { filePath: replaceRootDirInPath( options.rootDir, value.hasteImplModulePath, @@ -609,10 +654,12 @@ export default function normalize( key: 'haste.hasteImplModulePath', rootDir: options.rootDir, }); + + value.hasteImplModulePath = resolvedHasteImpl || undefined; } break; case 'projects': - value = (options[key] || []) + value = (oldOptions[key] || []) .map(project => typeof project === 'string' ? _replaceRootDirTags(options.rootDir, project) @@ -632,7 +679,7 @@ export default function normalize( { const replacedRootDirTags = _replaceRootDirTags( escapeGlobCharacters(options.rootDir), - options[key], + oldOptions[key], ); if (replacedRootDirTags) { @@ -645,12 +692,17 @@ export default function normalize( } break; case 'testRegex': - value = options[key] - ? [].concat(options[key]).map(replacePathSepForRegex) - : []; + { + const option = oldOptions[key]; + value = option + ? (Array.isArray(option) ? option : [option]).map( + replacePathSepForRegex, + ) + : []; + } break; case 'moduleFileExtensions': { - value = options[key]; + value = oldOptions[key]; if ( Array.isArray(value) && // If it's the wrong type, it can throw at a later time @@ -680,16 +732,17 @@ export default function normalize( break; } case 'bail': { - if (typeof options[key] === 'boolean') { - value = options[key] ? 1 : 0; - } else if (typeof options[key] === 'string') { + const bail = oldOptions[key]; + if (typeof bail === 'boolean') { + value = bail ? 1 : 0; + } else if (typeof bail === 'string') { value = 1; // If Jest is invoked as `jest --bail someTestPattern` then need to // move the pattern from the `bail` configuration and into `argv._` // to be processed as an extra parameter - argv._.push(options[key]); + argv._.push(bail); } else { - value = options[key]; + value = oldOptions[key]; } break; } @@ -746,10 +799,10 @@ export default function normalize( case 'watch': case 'watchAll': case 'watchman': - value = options[key]; + value = oldOptions[key]; break; case 'watchPlugins': - value = (options[key] || []).map(watchPlugin => { + value = (oldOptions[key] || []).map(watchPlugin => { if (typeof watchPlugin === 'string') { return { config: {}, @@ -770,7 +823,7 @@ export default function normalize( }); break; } - // $FlowFixMe - automock is missing in GlobalConfig, so what + // @ts-ignore: automock is missing in GlobalConfig, so what newOptions[key] = value; return newOptions; }, newOptions); @@ -779,16 +832,17 @@ export default function normalize( newOptions.testPathPattern = buildTestPathPattern(argv); newOptions.json = argv.json; - newOptions.testFailureExitCode = parseInt(newOptions.testFailureExitCode, 10); + newOptions.testFailureExitCode = parseInt( + (newOptions.testFailureExitCode as unknown) as string, + 10, + ); - for (const key of [ - 'lastCommit', - 'changedFilesWithAncestor', - 'changedSince', - ]) { - if (newOptions[key]) { - newOptions.onlyChanged = true; - } + if ( + newOptions.lastCommit || + newOptions.changedFilesWithAncestor || + newOptions.changedSince + ) { + newOptions.onlyChanged = true; } if (argv.all) { @@ -806,17 +860,20 @@ export default function normalize( ? 'all' : 'new'; - newOptions.maxConcurrency = parseInt(newOptions.maxConcurrency, 10); + newOptions.maxConcurrency = parseInt( + (newOptions.maxConcurrency as unknown) as string, + 10, + ); newOptions.maxWorkers = getMaxWorkers(argv); - if (newOptions.testRegex.length && options.testMatch) { + if (newOptions.testRegex!.length && options.testMatch) { throw createConfigError( ` Configuration options ${chalk.bold('testMatch')} and` + ` ${chalk.bold('testRegex')} cannot be used together.`, ); } - if (newOptions.testRegex.length && !options.testMatch) { + if (newOptions.testRegex!.length && !options.testMatch) { // Prevent the default testMatch conflicting with any explicitly // configured `testRegex` value newOptions.testMatch = []; @@ -849,7 +906,7 @@ export default function normalize( if ( !micromatch.some( replacePathSepForGlob(path.relative(options.rootDir, filename)), - newOptions.collectCoverageFrom, + newOptions.collectCoverageFrom!, ) ) { return patterns; diff --git a/packages/jest-config/src/readConfigFileAndSetRootDir.js b/packages/jest-config/src/readConfigFileAndSetRootDir.ts similarity index 90% rename from packages/jest-config/src/readConfigFileAndSetRootDir.js rename to packages/jest-config/src/readConfigFileAndSetRootDir.ts index a4a23f5eb02c..497a238a9281 100644 --- a/packages/jest-config/src/readConfigFileAndSetRootDir.js +++ b/packages/jest-config/src/readConfigFileAndSetRootDir.ts @@ -3,26 +3,23 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {InitialOptions, Path} from 'types/Config'; - import path from 'path'; import fs from 'fs'; +import {Config} from '@jest/types'; +// @ts-ignore: vendored import jsonlint from './vendor/jsonlint'; import {PACKAGE_JSON} from './constants'; // Read the configuration and set its `rootDir` // 1. If it's a `package.json` file, we look into its "jest" property // 2. For any other file, we just require it. -export default (configPath: Path): InitialOptions => { +export default (configPath: Config.Path): Config.InitialOptions => { const isJSON = configPath.endsWith('.json'); let configObject; try { - // $FlowFixMe dynamic require configObject = require(configPath); } catch (error) { if (isJSON) { diff --git a/packages/jest-config/src/resolveConfigPath.js b/packages/jest-config/src/resolveConfigPath.ts similarity index 87% rename from packages/jest-config/src/resolveConfigPath.js rename to packages/jest-config/src/resolveConfigPath.ts index a36dd758a807..d85cfb39e20c 100644 --- a/packages/jest-config/src/resolveConfigPath.js +++ b/packages/jest-config/src/resolveConfigPath.ts @@ -3,20 +3,17 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Path} from 'types/Config'; - import path from 'path'; import fs from 'fs'; +import {Config} from '@jest/types'; import {JEST_CONFIG, PACKAGE_JSON} from './constants'; -const isFile = filePath => +const isFile = (filePath: Config.Path) => fs.existsSync(filePath) && !fs.lstatSync(filePath).isDirectory(); -export default (pathToResolve: Path, cwd: Path): Path => { +export default (pathToResolve: Config.Path, cwd: Config.Path): Config.Path => { if (!path.isAbsolute(cwd)) { throw new Error(`"cwd" must be an absolute path. cwd: ${cwd}`); } @@ -50,10 +47,10 @@ export default (pathToResolve: Path, cwd: Path): Path => { }; const resolveConfigPathByTraversing = ( - pathToResolve: Path, - initialPath: Path, - cwd: Path, -) => { + pathToResolve: Config.Path, + initialPath: Config.Path, + cwd: Config.Path, +): Config.Path => { const jestConfig = path.resolve(pathToResolve, JEST_CONFIG); if (isFile(jestConfig)) { return jestConfig; @@ -78,7 +75,10 @@ const resolveConfigPathByTraversing = ( ); }; -const makeResolutionErrorMessage = (initialPath: Path, cwd: Path) => +const makeResolutionErrorMessage = ( + initialPath: Config.Path, + cwd: Config.Path, +) => 'Could not find a config file based on provided values:\n' + `path: "${initialPath}"\n` + `cwd: "${cwd}"\n` + diff --git a/packages/jest-config/src/setFromArgv.js b/packages/jest-config/src/setFromArgv.ts similarity index 86% rename from packages/jest-config/src/setFromArgv.js rename to packages/jest-config/src/setFromArgv.ts index 2cde2ee37d33..f1ca92ed20be 100644 --- a/packages/jest-config/src/setFromArgv.js +++ b/packages/jest-config/src/setFromArgv.ts @@ -3,24 +3,21 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {InitialOptions} from 'types/Config'; -import type {Argv} from 'types/Argv'; +import {Config} from '@jest/types'; const specialArgs = ['_', '$0', 'h', 'help', 'config']; import {isJSONString} from './utils'; export default function setFromArgv( - options: InitialOptions, - argv: Argv, -): InitialOptions { + options: Config.InitialOptions, + argv: Config.Argv, +): Config.InitialOptions { // $FlowFixMe: Seems like flow doesn't approve of string values const argvToOptions = Object.keys(argv) .filter(key => argv[key] !== undefined && specialArgs.indexOf(key) === -1) - .reduce((options: {[key: string]: mixed}, key) => { + .reduce((options: {[key: string]: unknown}, key) => { switch (key) { case 'coverage': options.collectCoverage = argv[key]; diff --git a/packages/jest-config/src/utils.js b/packages/jest-config/src/utils.ts similarity index 77% rename from packages/jest-config/src/utils.js rename to packages/jest-config/src/utils.ts index 168c07657cb3..5a93c832969d 100644 --- a/packages/jest-config/src/utils.js +++ b/packages/jest-config/src/utils.ts @@ -3,23 +3,21 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * - * @flow */ -import type {Path, Glob} from 'types/Config'; - import path from 'path'; +import {Config} from '@jest/types'; +// @ts-ignore: Not migrated to TS import {ValidationError} from 'jest-validate'; import Resolver from 'jest-resolve'; import chalk from 'chalk'; -type ResolveOptions = {| - rootDir: string, - key: string, - filePath: Path, - optional?: boolean, -|}; +type ResolveOptions = { + rootDir: Config.Path; + key: string; + filePath: Config.Path; + optional?: boolean; +}; export const BULLET: string = chalk.bold('\u25cf '); export const DOCUMENTATION_NOTE = ` ${chalk.bold( @@ -32,14 +30,14 @@ const createValidationError = (message: string) => new ValidationError(`${BULLET}Validation Error`, message, DOCUMENTATION_NOTE); export const resolve = ( - resolver: ?string, + resolver: string | null | undefined, {key, filePath, rootDir, optional}: ResolveOptions, ) => { const module = Resolver.findNodeModule( replaceRootDirInPath(rootDir, filePath), { basedir: rootDir, - resolver, + resolver: resolver || undefined, }, ); @@ -55,12 +53,12 @@ export const resolve = ( return module; }; -export const escapeGlobCharacters = (path: Path): Glob => +export const escapeGlobCharacters = (path: Config.Path): Config.Glob => path.replace(/([()*{}\[\]!?\\])/g, '\\$1'); export const replaceRootDirInPath = ( - rootDir: string, - filePath: Path, + rootDir: Config.Path, + filePath: Config.Path, ): string => { if (!/^/.test(filePath)) { return filePath; @@ -72,9 +70,13 @@ export const replaceRootDirInPath = ( ); }; -const _replaceRootDirInObject = (rootDir: string, config: any): Object => { +// TODO: Type as returning same type as input +const _replaceRootDirInObject = ( + rootDir: Config.Path, + config: any, +): {[key: string]: unknown} => { if (config !== null) { - const newConfig = {}; + const newConfig: {[key: string]: unknown} = {}; for (const configKey in config) { newConfig[configKey] = configKey === 'rootDir' @@ -86,7 +88,8 @@ const _replaceRootDirInObject = (rootDir: string, config: any): Object => { return config; }; -export const _replaceRootDirTags = (rootDir: string, config: any) => { +// TODO: Type as returning same type as input +export const _replaceRootDirTags = (rootDir: Config.Path, config: any): any => { switch (typeof config) { case 'object': if (Array.isArray(config)) { @@ -103,7 +106,7 @@ export const _replaceRootDirTags = (rootDir: string, config: any) => { }; export const resolveWithPrefix = ( - resolver: ?string, + resolver: string | undefined | null, { filePath, humanOptionName, @@ -111,17 +114,17 @@ export const resolveWithPrefix = ( prefix, rootDir, }: { - filePath: string, - humanOptionName: string, - optionName: string, - prefix: string, - rootDir: string, + filePath: string; + humanOptionName: string; + optionName: string; + prefix: string; + rootDir: Config.Path; }, ) => { const fileName = replaceRootDirInPath(rootDir, filePath); let module = Resolver.findNodeModule(`${prefix}${fileName}`, { basedir: rootDir, - resolver, + resolver: resolver || undefined, }); if (module) { return module; @@ -133,7 +136,7 @@ export const resolveWithPrefix = ( module = Resolver.findNodeModule(fileName, { basedir: rootDir, - resolver, + resolver: resolver || undefined, }); if (module) { return module; @@ -164,10 +167,10 @@ export const getTestEnvironment = ({ rootDir, testEnvironment: filePath, }: { - rootDir: string, - testEnvironment: string, + rootDir: Config.Path; + testEnvironment: string; }) => - resolveWithPrefix(null, { + resolveWithPrefix(undefined, { filePath, humanOptionName: 'Test environment', optionName: 'testEnvironment', @@ -184,8 +187,8 @@ export const getTestEnvironment = ({ * 1. looks for relative to Jest. */ export const getWatchPlugin = ( - resolver: ?string, - {filePath, rootDir}: {filePath: string, rootDir: string}, + resolver: string | undefined | null, + {filePath, rootDir}: {filePath: string; rootDir: Config.Path}, ) => resolveWithPrefix(resolver, { filePath, @@ -204,8 +207,8 @@ export const getWatchPlugin = ( * 1. looks for relative to Jest. */ export const getRunner = ( - resolver: ?string, - {filePath, rootDir}: {filePath: string, rootDir: string}, + resolver: string | undefined | null, + {filePath, rootDir}: {filePath: string; rootDir: Config.Path}, ) => resolveWithPrefix(resolver, { filePath, @@ -215,7 +218,7 @@ export const getRunner = ( rootDir, }); -export const isJSONString = (text: ?string) => +export const isJSONString = (text?: string) => text && typeof text === 'string' && text.startsWith('{') && diff --git a/packages/jest-config/src/validatePattern.js b/packages/jest-config/src/validatePattern.ts similarity index 84% rename from packages/jest-config/src/validatePattern.js rename to packages/jest-config/src/validatePattern.ts index 483277ad9d6a..476a0b271f46 100644 --- a/packages/jest-config/src/validatePattern.js +++ b/packages/jest-config/src/validatePattern.ts @@ -3,11 +3,9 @@ * * 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 validatePattern(pattern: string) { +export default function validatePattern(pattern?: string) { if (pattern) { try { // eslint-disable-next-line no-new diff --git a/packages/jest-config/tsconfig.json b/packages/jest-config/tsconfig.json new file mode 100644 index 000000000000..50965f82bc07 --- /dev/null +++ b/packages/jest-config/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + // TODO: This is missing `jest-validate`, in addition to `jest-environment-jsdom`, `jest-environment-node` and + // `jest-jasmine2`, but those are just `require.resolve`d, so no real use for their types + "references": [ + {"path": "../jest-get-type"}, + {"path": "../jest-regex-util"}, + {"path": "../jest-resolve"}, + {"path": "../jest-types"}, + {"path": "../jest-util"}, + {"path": "../pretty-format"} + ] +} diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 22e7cd57465b..2bda5aa71b3c 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -20,7 +20,6 @@ import { } from 'jest-util'; import LeakDetector from 'jest-leak-detector'; import Resolver from 'jest-resolve'; -// @ts-ignore: not migrated to TS import {getTestEnvironment} from 'jest-config'; import * as docblock from 'jest-docblock'; import {formatExecError} from 'jest-message-util'; @@ -90,6 +89,13 @@ async function runTestInternal( let testEnvironment = config.testEnvironment; if (customEnvironment) { + if (Array.isArray(customEnvironment)) { + throw new Error( + `You can only define a single test environment through docblocks, got "${customEnvironment.join( + ', ', + )}"`, + ); + } testEnvironment = getTestEnvironment({ ...config, testEnvironment: customEnvironment, diff --git a/packages/jest-runner/tsconfig.json b/packages/jest-runner/tsconfig.json index 72bf161339bc..2b89d723edb6 100644 --- a/packages/jest-runner/tsconfig.json +++ b/packages/jest-runner/tsconfig.json @@ -5,6 +5,7 @@ "outDir": "build" }, "references": [ + {"path": "../jest-config"}, {"path": "../jest-docblock"}, {"path": "../jest-environment"}, {"path": "../jest-haste-map"}, diff --git a/packages/jest-runtime/src/cli/index.ts b/packages/jest-runtime/src/cli/index.ts index 5a6284388da5..70a125076fb4 100644 --- a/packages/jest-runtime/src/cli/index.ts +++ b/packages/jest-runtime/src/cli/index.ts @@ -15,7 +15,6 @@ import {JestEnvironment} from '@jest/environment'; import {Console, setGlobal} from 'jest-util'; // @ts-ignore: Not migrated to TS import {validateCLIOptions} from 'jest-validate'; -// @ts-ignore: Not migrated to TS import {readConfig, deprecationEntries} from 'jest-config'; import {VERSION} from '../version'; import {Context} from '../types'; @@ -63,6 +62,8 @@ export function run(cliArgv?: Config.Argv, cliInfo?: Array) { const info = cliInfo ? ', ' + cliInfo.join(', ') : ''; console.log(`Using Jest Runtime v${VERSION}${info}`); } + // TODO: Figure this out + // @ts-ignore: this might not have the correct arguments const options = readConfig(argv, root); const globalConfig = options.globalConfig; // Always disable automocking in scripts. diff --git a/packages/jest-runtime/tsconfig.json b/packages/jest-runtime/tsconfig.json index b73127d8e1fd..1b373a713a15 100644 --- a/packages/jest-runtime/tsconfig.json +++ b/packages/jest-runtime/tsconfig.json @@ -4,8 +4,9 @@ "rootDir": "src", "outDir": "build" }, - // TODO: Missing `jest-config`, `jest-validate` and `jest-environment-node` + // TODO: Missing `jest-validate` and `jest-environment-node` "references": [ + {"path": "../jest-config"}, {"path": "../jest-environment"}, {"path": "../jest-haste-map"}, {"path": "../jest-message-util"}, From 6dbd4df4137fae20e253a517de8c174912a42182 Mon Sep 17 00:00:00 2001 From: tjallingt Date: Tue, 26 Feb 2019 16:17:57 +0100 Subject: [PATCH 107/107] Improve instructions for enabling typescript support (#7989) --- docs/GettingStarted.md | 20 +++++++++++++++++-- .../version-24.0/GettingStarted.md | 20 +++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 57d64640527d..2da889c273a8 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -151,6 +151,22 @@ Jest can be used in projects that use [webpack](https://webpack.github.io/) to m ### Using TypeScript -Jest supports TypeScript out of the box, via Babel. +Jest supports TypeScript, via Babel. First make sure you followed the instructions on [using Babel](#using-babel) above. Next install the `@babel/preset-typescript` via `yarn`: -However, there are some caveats to using Typescript with Babel, see http://artsy.github.io/blog/2017/11/27/Babel-7-and-TypeScript/. Another caveat is that Jest will not typecheck your tests. If you want that, you can use [ts-jest](https://github.com/kulshekhar/ts-jest). +```bash +yarn add --dev @babel/preset-typescript +``` + +Then add `@babel/preset-typescript` to the list of presets in your `babel.config.js`. + +```javascript +// babel.config.js +module.exports = { + presets: [ + ['@babel/preset-env', {targets: {node: 'current'}}], + '@babel/preset-typescript', + ], +}; +``` + +Note that there are some caveats to using Typescript with Babel, see http://artsy.github.io/blog/2017/11/27/Babel-7-and-TypeScript/. Another caveat is that Jest will not typecheck your tests. If you want that, you can use [ts-jest](https://github.com/kulshekhar/ts-jest). diff --git a/website/versioned_docs/version-24.0/GettingStarted.md b/website/versioned_docs/version-24.0/GettingStarted.md index ae1d57d5a7f4..637d74655e12 100644 --- a/website/versioned_docs/version-24.0/GettingStarted.md +++ b/website/versioned_docs/version-24.0/GettingStarted.md @@ -152,6 +152,22 @@ Jest can be used in projects that use [webpack](https://webpack.github.io/) to m ### Using TypeScript -Jest supports TypeScript out of the box, via Babel. +Jest supports TypeScript, via Babel. First make sure you followed the instructions on [using Babel](#using-babel) above. Next install the `@babel/preset-typescript` via `yarn`: -However, there are some caveats to using Typescript with Babel, see http://artsy.github.io/blog/2017/11/27/Babel-7-and-TypeScript/. Another caveat is that Jest will not typecheck your tests. If you want that, you can use [ts-jest](https://github.com/kulshekhar/ts-jest). +```bash +yarn add --dev @babel/preset-typescript +``` + +Then add `@babel/preset-typescript` to the list of presets in your `babel.config.js`. + +```javascript +// babel.config.js +module.exports = { + presets: [ + ['@babel/preset-env', {targets: {node: 'current'}}], + '@babel/preset-typescript', + ], +}; +``` + +Note that there are some caveats to using Typescript with Babel, see http://artsy.github.io/blog/2017/11/27/Babel-7-and-TypeScript/. Another caveat is that Jest will not typecheck your tests. If you want that, you can use [ts-jest](https://github.com/kulshekhar/ts-jest).