From 351f961ff2ac95f8ea2f06e7cefd14f3d6f442da Mon Sep 17 00:00:00 2001 From: Tom Mrazauskas Date: Mon, 16 May 2022 20:15:40 +0300 Subject: [PATCH 1/3] feat: support slowTestThreshold --- src/__tests__/__snapshots__/fail.test.ts.snap | 58 ------- src/__tests__/__snapshots__/pass.test.ts.snap | 42 ----- .../__snapshots__/runTest.test.ts.snap | 158 ++++++++++++++++++ src/__tests__/fail.test.ts | 27 --- src/__tests__/pass.test.ts | 16 -- src/__tests__/runTest.test.ts | 134 +++++++++++++++ src/fail.js | 46 ----- src/pass.js | 34 ---- src/run.js | 85 +++++++--- src/toTestResult.js | 79 --------- 10 files changed, 356 insertions(+), 323 deletions(-) delete mode 100644 src/__tests__/__snapshots__/fail.test.ts.snap delete mode 100644 src/__tests__/__snapshots__/pass.test.ts.snap create mode 100644 src/__tests__/__snapshots__/runTest.test.ts.snap delete mode 100644 src/__tests__/fail.test.ts delete mode 100644 src/__tests__/pass.test.ts create mode 100644 src/__tests__/runTest.test.ts delete mode 100644 src/fail.js delete mode 100644 src/pass.js delete mode 100644 src/toTestResult.js diff --git a/src/__tests__/__snapshots__/fail.test.ts.snap b/src/__tests__/__snapshots__/fail.test.ts.snap deleted file mode 100644 index 5cdaafc..0000000 --- a/src/__tests__/__snapshots__/fail.test.ts.snap +++ /dev/null @@ -1,58 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`fails 1`] = ` -Object { - "failureMessage": " ● tsd typecheck - - Argument of type 'number' is not assignable to parameter of type 'string'. - - > 5 | expectType(concat(1, 2)); - | ^ - - at e2e/__fixtures__/js-failing/index.test.ts:5:1", - "leaks": false, - "numFailingTests": 1, - "numPassingTests": 3, - "numPendingTests": 0, - "numTodoTests": 0, - "openHandles": Array [], - "perfStats": Object { - "end": 1200, - "runtime": 1200, - "slow": false, - "start": 0, - }, - "skipped": false, - "snapshot": Object { - "added": 0, - "fileDeleted": false, - "matched": 0, - "unchecked": 0, - "uncheckedKeys": Array [], - "unmatched": 0, - "updated": 0, - }, - "testFilePath": "/path/to/e2e/__fixtures__/js-failing/index.test.ts", - "testResults": Array [ - Object { - "ancestorTitles": Array [], - "duration": 1200, - "failureDetails": Array [], - "failureMessages": Array [ - " ● tsd typecheck - - Argument of type 'number' is not assignable to parameter of type 'string'. - - > 5 | expectType(concat(1, 2)); - | ^ - - at e2e/__fixtures__/js-failing/index.test.ts:5:1", - ], - "fullName": "/path/to/e2e/__fixtures__/js-failing/index.test.ts", - "numPassingAsserts": 1, - "status": "failed", - "title": "tsd typecheck", - }, - ], -} -`; diff --git a/src/__tests__/__snapshots__/pass.test.ts.snap b/src/__tests__/__snapshots__/pass.test.ts.snap deleted file mode 100644 index 6ee3e14..0000000 --- a/src/__tests__/__snapshots__/pass.test.ts.snap +++ /dev/null @@ -1,42 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`pass 1`] = ` -Object { - "failureMessage": null, - "leaks": false, - "numFailingTests": 0, - "numPassingTests": 5, - "numPendingTests": 0, - "numTodoTests": 0, - "openHandles": Array [], - "perfStats": Object { - "end": 1200, - "runtime": 1200, - "slow": false, - "start": 0, - }, - "skipped": false, - "snapshot": Object { - "added": 0, - "fileDeleted": false, - "matched": 0, - "unchecked": 0, - "uncheckedKeys": Array [], - "unmatched": 0, - "updated": 0, - }, - "testFilePath": "/path/to/e2e/__fixtures__/js-passing/index.test.ts", - "testResults": Array [ - Object { - "ancestorTitles": Array [], - "duration": 1200, - "failureDetails": Array [], - "failureMessages": Array [], - "fullName": "/path/to/e2e/__fixtures__/js-passing/index.test.ts", - "numPassingAsserts": 0, - "status": "passed", - "title": "tsd typecheck", - }, - ], -} -`; diff --git a/src/__tests__/__snapshots__/runTest.test.ts.snap b/src/__tests__/__snapshots__/runTest.test.ts.snap new file mode 100644 index 0000000..ff43213 --- /dev/null +++ b/src/__tests__/__snapshots__/runTest.test.ts.snap @@ -0,0 +1,158 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`runs test with all failing results 1`] = ` +Object { + "failureMessage": "", + "leaks": false, + "numFailingTests": 2, + "numPassingTests": 0, + "numPendingTests": 0, + "numTodoTests": 0, + "openHandles": Array [], + "perfStats": Object { + "end": 2800, + "runtime": 1800, + "slow": false, + "start": 1000, + }, + "skipped": false, + "snapshot": Object { + "added": 0, + "fileDeleted": false, + "matched": 0, + "unchecked": 0, + "uncheckedKeys": Array [], + "unmatched": 0, + "updated": 0, + }, + "testFilePath": "/path/to/some/failing.test.ts", + "testResults": Array [ + Object { + "ancestorTitles": Array [], + "duration": 1800, + "failureDetails": Array [], + "failureMessages": Array [ + "", + ], + "fullName": "tsd typecheck", + "numPassingAsserts": 0, + "status": "failed", + "title": "tsd typecheck", + }, + ], +} +`; + +exports[`runs test with all passing results 1`] = ` +Object { + "failureMessage": null, + "leaks": false, + "numFailingTests": 0, + "numPassingTests": 2, + "numPendingTests": 0, + "numTodoTests": 0, + "openHandles": Array [], + "perfStats": Object { + "end": 2800, + "runtime": 1800, + "slow": false, + "start": 1000, + }, + "skipped": false, + "snapshot": Object { + "added": 0, + "fileDeleted": false, + "matched": 0, + "unchecked": 0, + "uncheckedKeys": Array [], + "unmatched": 0, + "updated": 0, + }, + "testFilePath": "/path/to/some/passing.test.ts", + "testResults": Array [ + Object { + "ancestorTitles": Array [], + "duration": 1800, + "failureDetails": Array [], + "failureMessages": Array [], + "fullName": "tsd typecheck", + "numPassingAsserts": 1, + "status": "passed", + "title": "tsd typecheck", + }, + ], +} +`; + +exports[`runs test with empty results 1`] = ` +Object { + "failureMessage": null, + "leaks": false, + "numFailingTests": 0, + "numPassingTests": 0, + "numPendingTests": 0, + "numTodoTests": 0, + "openHandles": Array [], + "perfStats": Object { + "end": 2800, + "runtime": 1800, + "slow": false, + "start": 1000, + }, + "skipped": false, + "snapshot": Object { + "added": 0, + "fileDeleted": false, + "matched": 0, + "unchecked": 0, + "uncheckedKeys": Array [], + "unmatched": 0, + "updated": 0, + }, + "testFilePath": "/path/to/some/empty.test.ts", + "testResults": Array [], +} +`; + +exports[`runs test with some failing results 1`] = ` +Object { + "failureMessage": "", + "leaks": false, + "numFailingTests": 2, + "numPassingTests": 2, + "numPendingTests": 0, + "numTodoTests": 0, + "openHandles": Array [], + "perfStats": Object { + "end": 2800, + "runtime": 1800, + "slow": false, + "start": 1000, + }, + "skipped": false, + "snapshot": Object { + "added": 0, + "fileDeleted": false, + "matched": 0, + "unchecked": 0, + "uncheckedKeys": Array [], + "unmatched": 0, + "updated": 0, + }, + "testFilePath": "/path/to/some/failing.test.ts", + "testResults": Array [ + Object { + "ancestorTitles": Array [], + "duration": 1800, + "failureDetails": Array [], + "failureMessages": Array [ + "", + ], + "fullName": "tsd typecheck", + "numPassingAsserts": 0, + "status": "failed", + "title": "tsd typecheck", + }, + ], +} +`; diff --git a/src/__tests__/fail.test.ts b/src/__tests__/fail.test.ts deleted file mode 100644 index f011715..0000000 --- a/src/__tests__/fail.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { expect, test } from '@jest/globals'; -import { fail } from '../fail'; - -const errorMessage = ` ● tsd typecheck - - Argument of type 'number' is not assignable to parameter of type 'string'. - - > 5 | expectType(concat(1, 2)); - | ^ - - at e2e/__fixtures__/js-failing/index.test.ts:5:1`; - -test('fails', () => { - const testResult = fail({ - start: 0, - end: 1200, - test: { - path: '/path/to/e2e/__fixtures__/js-failing/index.test.ts', - title: 'tsd typecheck', - }, - errorMessage, - numFailed: 1, - numPassed: 3, - }); - - expect(testResult).toMatchSnapshot(); -}); diff --git a/src/__tests__/pass.test.ts b/src/__tests__/pass.test.ts deleted file mode 100644 index 335fe31..0000000 --- a/src/__tests__/pass.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { expect, test } from '@jest/globals'; -import { pass } from '../pass'; - -test('pass', () => { - const testResult = pass({ - start: 0, - end: 1200, - test: { - path: '/path/to/e2e/__fixtures__/js-passing/index.test.ts', - title: 'tsd typecheck', - }, - numPassed: 5, - }); - - expect(testResult).toMatchSnapshot(); -}); diff --git a/src/__tests__/runTest.test.ts b/src/__tests__/runTest.test.ts new file mode 100644 index 0000000..bc9159a --- /dev/null +++ b/src/__tests__/runTest.test.ts @@ -0,0 +1,134 @@ +import { + afterEach, + beforeEach, + describe, + expect, + jest, + test, +} from '@jest/globals'; +import type { RunTestOptions } from 'create-jest-runner'; +import tsdLite, { type TsdResult } from 'tsd-lite'; +import type ts from 'typescript'; +import { formatTsdResults } from '../formatter'; +import runTest from '../run'; + +jest.mock('tsd-lite'); +jest.mock('../formatter.js'); + +beforeEach(() => { + jest.mocked(formatTsdResults).mockReturnValue(''); + jest.spyOn(Date, 'now').mockReturnValueOnce(1000).mockReturnValueOnce(2800); +}); + +afterEach(() => { + jest.resetAllMocks(); +}); + +const runOptions = { config: { slowTestThreshold: 5 } } as RunTestOptions; +const slowRunOptions = { config: { slowTestThreshold: 1 } } as RunTestOptions; + +const tsdResults: Array = [ + { + file: {} as ts.SourceFile, + messageText: + "Argument of type 'string' is not assignable to parameter of type 'number'.", + start: 64, + }, + { + file: {} as ts.SourceFile, + messageText: + "Argument of type 'number' is not assignable to parameter of type 'string'.", + start: 106, + }, +]; + +describe('runs test', () => { + test('with all failing results', () => { + jest + .mocked(tsdLite) + .mockReturnValueOnce({ assertionsCount: 2, tsdResults }); + + const testResult = runTest({ + ...runOptions, + testPath: '/path/to/some/failing.test.ts', + }); + + expect(tsdLite).toBeCalledTimes(1); + expect(tsdLite).toBeCalledWith('/path/to/some/failing.test.ts'); + + expect(formatTsdResults).toBeCalledTimes(1); + expect(formatTsdResults).toBeCalledWith(tsdResults); + + expect(testResult).toMatchSnapshot(); + }); + + test('with some failing results', () => { + jest + .mocked(tsdLite) + .mockReturnValueOnce({ assertionsCount: 4, tsdResults }); + + const testResult = runTest({ + ...runOptions, + testPath: '/path/to/some/failing.test.ts', + }); + + expect(tsdLite).toBeCalledTimes(1); + expect(tsdLite).toBeCalledWith('/path/to/some/failing.test.ts'); + + expect(formatTsdResults).toBeCalledTimes(1); + expect(formatTsdResults).toBeCalledWith(tsdResults); + + expect(testResult).toMatchSnapshot(); + }); + + test('with slow results', () => { + jest + .mocked(tsdLite) + .mockReturnValueOnce({ assertionsCount: 2, tsdResults: [] }); + + const testResult = runTest({ + ...slowRunOptions, + testPath: '/path/to/some/slow.test.ts', + }); + + expect(testResult).toMatchObject({ + perfStats: { slow: true }, + }); + }); + + test('with all passing results', () => { + jest + .mocked(tsdLite) + .mockReturnValueOnce({ assertionsCount: 2, tsdResults: [] }); + + const testResult = runTest({ + ...runOptions, + testPath: '/path/to/some/passing.test.ts', + }); + + expect(tsdLite).toBeCalledTimes(1); + expect(tsdLite).toBeCalledWith('/path/to/some/passing.test.ts'); + + expect(formatTsdResults).not.toBeCalled(); + + expect(testResult).toMatchSnapshot(); + }); + + test('with empty results', () => { + jest + .mocked(tsdLite) + .mockReturnValueOnce({ assertionsCount: 0, tsdResults: [] }); + + const testResult = runTest({ + ...runOptions, + testPath: '/path/to/some/empty.test.ts', + }); + + expect(tsdLite).toBeCalledTimes(1); + expect(tsdLite).toBeCalledWith('/path/to/some/empty.test.ts'); + + expect(formatTsdResults).not.toBeCalled(); + + expect(testResult).toMatchSnapshot(); + }); +}); diff --git a/src/fail.js b/src/fail.js deleted file mode 100644 index 9383be8..0000000 --- a/src/fail.js +++ /dev/null @@ -1,46 +0,0 @@ -const { toTestResult } = require('./toTestResult'); - -/** - * @typedef {object} Test - * @property {string} path - * @property {string} title - */ - -/** - * @typedef {object} Options - * @property {number} start - * @property {number} end - * @property {Test} test - * @property {number} numFailed - * @property {number} numPassed - * @property {string=} errorMessage - */ - -/** - * @param {Options} options - */ -module.exports.fail = ({ - start, - end, - test, - errorMessage, - numFailed, - numPassed, -}) => { - const stats = { - failures: numFailed, - passes: numPassed, - pending: 0, - todo: 0, - end, - start, - }; - - return toTestResult({ - stats, - errorMessage, - skipped: false, - tests: [{ duration: end - start, ...test, errorMessage }], - jestTestPath: test.path, - }); -}; diff --git a/src/pass.js b/src/pass.js deleted file mode 100644 index 01e63e6..0000000 --- a/src/pass.js +++ /dev/null @@ -1,34 +0,0 @@ -const { toTestResult } = require('./toTestResult'); - -/** - * @typedef {object} Test - * @property {string} path - * @property {string} title - */ - -/** - * @typedef {object} Options - * @property {number} start - * @property {number} end - * @property {Test} test - * @property {number} numPassed - */ - -/** - * @param {Options} options - */ -module.exports.pass = ({ start, end, test, numPassed }) => { - return toTestResult({ - stats: { - failures: 0, - passes: numPassed, - pending: 0, - todo: 0, - start, - end, - }, - skipped: false, - tests: [{ duration: end - start, ...test }], - jestTestPath: test.path, - }); -}; diff --git a/src/run.js b/src/run.js index 37f628d..82eb6c0 100644 --- a/src/run.js +++ b/src/run.js @@ -1,38 +1,81 @@ const tsdLite = require('tsd-lite'); const { formatTsdResults } = require('./formatter'); -const { fail } = require('./fail'); -const { pass } = require('./pass'); + +/** @typedef {import('@jest/test-result').TestCaseResult} TestCaseResult */ const TEST_TITLE = 'tsd typecheck'; +/** + * @param {'failed' | 'passed'} status + * @param {number} duration + * @param {string=} errorMessage + * @returns {TestCaseResult} + */ +function toJestResult(status, duration, errorMessage) { + return { + ancestorTitles: [], + failureDetails: [], + failureMessages: errorMessage ? [errorMessage] : [], + fullName: TEST_TITLE, + numPassingAsserts: status === 'passed' ? 1 : 0, + status, + title: TEST_TITLE, + duration, + }; +} + /** @type {import('create-jest-runner').RunTest} */ -module.exports = ({ testPath }) => { +module.exports = ({ testPath, config }) => { const start = Date.now(); const { assertionsCount, tsdResults } = tsdLite.default(testPath); const end = Date.now(); - const numFailed = tsdResults.length; - const numPassed = assertionsCount - numFailed; + const runtime = end - start; + const slow = runtime / 1000 > config.slowTestThreshold; - if (tsdResults.length > 0) { - const errorMessage = formatTsdResults(tsdResults); + const numFailingTests = tsdResults.length; + const numPassingTests = assertionsCount - numFailingTests; - return fail({ - start, - end, - test: { path: testPath, title: TEST_TITLE }, - numFailed, - numPassed, - errorMessage, - }); + /** @type {string | null} */ + let failureMessage = null; + + /** @type {TestCaseResult | null} */ + let testResult = null; + + if (numFailingTests) { + failureMessage = formatTsdResults(tsdResults); + testResult = toJestResult('failed', runtime, failureMessage); + } else if (numPassingTests) { + testResult = toJestResult('passed', runtime); } - return pass({ - start, - end, - test: { path: testPath, title: TEST_TITLE }, - numPassed, - }); + return { + failureMessage, + leaks: false, + numFailingTests, + numPassingTests, + numPendingTests: 0, + numTodoTests: 0, + openHandles: [], + perfStats: { + end, + runtime, + slow, + start, + }, + skipped: false, + snapshot: { + added: 0, + fileDeleted: false, + matched: 0, + unchecked: 0, + uncheckedKeys: [], + unmatched: 0, + updated: 0, + }, + testFilePath: testPath, + testResults: testResult ? [testResult] : [], + }; }; diff --git a/src/toTestResult.js b/src/toTestResult.js deleted file mode 100644 index 3f629e4..0000000 --- a/src/toTestResult.js +++ /dev/null @@ -1,79 +0,0 @@ -/** @typedef {import('@jest/test-result').TestResult} TestResult */ - -/** - * @typedef {object} Stats - * @property {number} failures - * @property {number} passes - * @property {number} pending - * @property {number} todo - * @property {number} start - * @property {number} end - */ - -/** - * @typedef {object} Test - * @property {number} duration - * @property {string} path - * @property {string=} errorMessage - * @property {string=} title - */ - -/** - * @typedef {object} Options - * @property {Stats} stats - * @property {boolean} skipped - * @property {string=} errorMessage - * @property {Array} tests - * @property {string} jestTestPath - */ - -/** - * @param {Options} options - * @returns {TestResult} - */ -module.exports.toTestResult = ({ - stats, - skipped, - errorMessage, - tests, - jestTestPath, -}) => { - return { - failureMessage: errorMessage || null, - leaks: false, - numFailingTests: stats.failures, - numPassingTests: stats.passes, - numPendingTests: stats.pending, - numTodoTests: stats.todo, - openHandles: [], - perfStats: { - end: stats.end, - start: stats.start, - runtime: stats.end - stats.start, - slow: false, - }, - skipped, - snapshot: { - added: 0, - fileDeleted: false, - matched: 0, - unchecked: 0, - uncheckedKeys: [], - unmatched: 0, - updated: 0, - }, - testFilePath: jestTestPath, - testResults: tests.map(test => { - return { - ancestorTitles: [], - duration: test.duration, - failureDetails: [], - failureMessages: test.errorMessage ? [test.errorMessage] : [], - fullName: test.path, - numPassingAsserts: test.errorMessage ? 1 : 0, - status: test.errorMessage ? 'failed' : 'passed', - title: test.title || '', - }; - }), - }; -}; From 4366d7749cf68e94e46695ffc9d0e8def7dc9630 Mon Sep 17 00:00:00 2001 From: Tom Mrazauskas Date: Mon, 16 May 2022 20:36:03 +0300 Subject: [PATCH 2/3] just clearAllMocks --- src/__tests__/runTest.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/__tests__/runTest.test.ts b/src/__tests__/runTest.test.ts index bc9159a..58eff84 100644 --- a/src/__tests__/runTest.test.ts +++ b/src/__tests__/runTest.test.ts @@ -15,13 +15,14 @@ import runTest from '../run'; jest.mock('tsd-lite'); jest.mock('../formatter.js'); +jest.mocked(formatTsdResults).mockReturnValue(''); + beforeEach(() => { - jest.mocked(formatTsdResults).mockReturnValue(''); jest.spyOn(Date, 'now').mockReturnValueOnce(1000).mockReturnValueOnce(2800); }); afterEach(() => { - jest.resetAllMocks(); + jest.clearAllMocks(); }); const runOptions = { config: { slowTestThreshold: 5 } } as RunTestOptions; From f5bb0e76ceda0cf0007ba39ded4fb5d2cc78c2d4 Mon Sep 17 00:00:00 2001 From: Tom Mrazauskas Date: Mon, 16 May 2022 20:49:35 +0300 Subject: [PATCH 3/3] use undefined --- src/run.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/run.js b/src/run.js index 82eb6c0..97a6f47 100644 --- a/src/run.js +++ b/src/run.js @@ -41,8 +41,8 @@ module.exports = ({ testPath, config }) => { /** @type {string | null} */ let failureMessage = null; - /** @type {TestCaseResult | null} */ - let testResult = null; + /** @type {TestCaseResult | undefined} */ + let testResult = undefined; if (numFailingTests) { failureMessage = formatTsdResults(tsdResults);