UNPKG

jest-jasmine2

Version:
701 lines (658 loc) 26.2 kB
/*! * /** * * Copyright (c) Meta Platforms, Inc. and affiliates. * * * * This source code is licensed under the MIT license found in the * * LICENSE file in the root directory of this source tree. * * / */ /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ "./src/each.ts": /***/ ((__unused_webpack_module, exports) => { Object.defineProperty(exports, "__esModule", ({ value: true })); exports["default"] = each; var _jestEach = require("jest-each"); /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ function each(environment) { environment.global.it.each = (0, _jestEach.bind)(environment.global.it); environment.global.fit.each = (0, _jestEach.bind)(environment.global.fit); environment.global.xit.each = (0, _jestEach.bind)(environment.global.xit); environment.global.describe.each = (0, _jestEach.bind)(environment.global.describe, false); environment.global.xdescribe.each = (0, _jestEach.bind)(environment.global.xdescribe, false); environment.global.fdescribe.each = (0, _jestEach.bind)(environment.global.fdescribe, false); environment.global.it.concurrent.each = (0, _jestEach.bind)(environment.global.it.concurrent, false); environment.global.it.concurrent.only.each = (0, _jestEach.bind)(environment.global.it.concurrent.only, false); environment.global.it.concurrent.skip.each = (0, _jestEach.bind)(environment.global.it.concurrent.skip, false); } /***/ }), /***/ "./src/errorOnPrivate.ts": /***/ ((__unused_webpack_module, exports) => { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.installErrorOnPrivate = installErrorOnPrivate; var _jestUtil = require("jest-util"); /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ // prettier-ignore const disabledGlobals = { fail: 'Illegal usage of global `fail`, prefer throwing an error, or the `done.fail` callback.', pending: 'Illegal usage of global `pending`, prefer explicitly skipping a test using `test.skip`', spyOn: 'Illegal usage of global `spyOn`, prefer `jest.spyOn`.', spyOnProperty: 'Illegal usage of global `spyOnProperty`, prefer `jest.spyOn`.' }; // prettier-ignore const disabledJasmineMethods = { addMatchers: 'Illegal usage of `jasmine.addMatchers`, prefer `expect.extends`.', any: 'Illegal usage of `jasmine.any`, prefer `expect.any`.', anything: 'Illegal usage of `jasmine.anything`, prefer `expect.anything`.', arrayContaining: 'Illegal usage of `jasmine.arrayContaining`, prefer `expect.arrayContaining`.', createSpy: 'Illegal usage of `jasmine.createSpy`, prefer `jest.fn`.', objectContaining: 'Illegal usage of `jasmine.objectContaining`, prefer `expect.objectContaining`.', stringMatching: 'Illegal usage of `jasmine.stringMatching`, prefer `expect.stringMatching`.' }; function installErrorOnPrivate(global) { const jasmine = global.jasmine; for (const functionName of Object.keys(disabledGlobals)) { global[functionName] = () => { throwAtFunction(disabledGlobals[functionName], global[functionName]); }; } for (const methodName of Object.keys(disabledJasmineMethods)) { // @ts-expect-error - void unallowd, but it throws 🤷 jasmine[methodName] = () => { throwAtFunction(disabledJasmineMethods[methodName], jasmine[methodName]); }; } function set() { throwAtFunction('Illegal usage of `jasmine.DEFAULT_TIMEOUT_INTERVAL`, prefer `jest.setTimeout`.', set); } const original = jasmine.DEFAULT_TIMEOUT_INTERVAL; Object.defineProperty(jasmine, 'DEFAULT_TIMEOUT_INTERVAL', { configurable: true, enumerable: true, get: () => original, set }); } function throwAtFunction(message, fn) { throw new _jestUtil.ErrorWithStack(message, fn); } /***/ }), /***/ "./src/isError.ts": /***/ ((__unused_webpack_module, exports) => { Object.defineProperty(exports, "__esModule", ({ value: true })); exports["default"] = isError; var _prettyFormat = require("pretty-format"); /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ function isError( // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types potentialError) { // duck-type Error, see #2549 const isError = potentialError !== null && typeof potentialError === 'object' && typeof potentialError.message === 'string' && typeof potentialError.name === 'string'; const message = isError ? null : `Failed: ${(0, _prettyFormat.format)(potentialError, { maxDepth: 3 })}`; return { isError, message }; } /***/ }), /***/ "./src/jasmineAsyncInstall.ts": /***/ ((__unused_webpack_module, exports, __webpack_require__) => { Object.defineProperty(exports, "__esModule", ({ value: true })); exports["default"] = jasmineAsyncInstall; var _co = _interopRequireDefault(require("co")); var _isGeneratorFn = _interopRequireDefault(require("is-generator-fn")); var _pLimit = _interopRequireDefault(require("p-limit")); var _jestUtil = require("jest-util"); var _isError = _interopRequireDefault(__webpack_require__("./src/isError.ts")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol; var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol; var Promise = globalThis[Symbol.for('jest-native-promise')] || globalThis.Promise; /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ /** * This module adds ability to test async promise code with jasmine by * returning a promise from `it/test` and `before/afterEach/All` blocks. */ // eslint-disable-next-line @typescript-eslint/no-empty-function const doneFnNoop = () => {}; // eslint-disable-next-line @typescript-eslint/no-empty-function doneFnNoop.fail = () => {}; function promisifyLifeCycleFunction(originalFn, env) { return function (fn, timeout) { if (!fn) { // @ts-expect-error: missing fn arg is handled by originalFn return originalFn.call(env); } if (typeof fn !== 'function') { // Pass non-functions to Jest, which throws a nice error. return originalFn.call(env, fn, timeout); } const hasDoneCallback = fn.length > 0; if (hasDoneCallback) { // Give the function a name so it can be detected in call stacks, but // otherwise Jasmine will handle it. const asyncJestLifecycleWithCallback = function (...args) { // @ts-expect-error: Support possible extra args at runtime return fn.apply(this, args); }; return originalFn.call(env, asyncJestLifecycleWithCallback, timeout); } // eslint-disable-next-line unicorn/error-message const extraError = new Error(); // Without this line v8 stores references to all closures // in the stack in the Error object. This line stringifies the stack // property to allow garbage-collecting objects on the stack // https://crbug.com/v8/7142 const originalExtraErrorStack = extraError.stack; extraError.stack = originalExtraErrorStack; // We make *all* functions async and run `done` right away if they // didn't return a promise. const asyncJestLifecycle = function (done) { const wrappedFn = (0, _isGeneratorFn.default)(fn) ? _co.default.wrap(fn) : fn; const returnValue = wrappedFn.call({}, doneFnNoop); if ((0, _jestUtil.isPromise)(returnValue)) { returnValue.then(done.bind(null, null), error => { const { isError: checkIsError, message } = (0, _isError.default)(error); if (message) { extraError.message = message; extraError.stack = originalExtraErrorStack && originalExtraErrorStack.replace('Error: ', `Error: ${message}`); } done.fail(checkIsError ? error : extraError); }); } else { done(); } }; return originalFn.call(env, asyncJestLifecycle, timeout); }; } // Similar to promisifyLifeCycleFunction but throws an error // when the return value is neither a Promise nor `undefined` function promisifyIt(originalFn, env, jasmine) { return function (specName, fn, timeout) { if (!fn) { // @ts-expect-error: missing fn arg is handled by originalFn const spec = originalFn.call(env, specName); spec.pend('not implemented'); return spec; } if (typeof fn !== 'function') { // Pass non-functions to Jest, which throws a nice error. return originalFn.call(env, specName, fn, timeout); } const hasDoneCallback = fn.length > 0; if (hasDoneCallback) { // Give the function a name so it can be detected in call stacks, but // otherwise Jasmine will handle it. const asyncJestTestWithCallback = function (...args) { // @ts-expect-error: Support possible extra args at runtime return fn.apply(this, args); }; return originalFn.call(env, specName, asyncJestTestWithCallback, timeout); } // eslint-disable-next-line unicorn/error-message const extraError = new Error(); // Without this line v8 stores references to all closures // in the stack in the Error object. This line stringifies the stack // property to allow garbage-collecting objects on the stack // https://crbug.com/v8/7142 const originalExtraErrorStack = extraError.stack; extraError.stack = originalExtraErrorStack; const asyncJestTest = function (done) { const wrappedFn = (0, _isGeneratorFn.default)(fn) ? _co.default.wrap(fn) : fn; const returnValue = wrappedFn.call({}, doneFnNoop); if ((0, _jestUtil.isPromise)(returnValue)) { returnValue.then(done.bind(null, null), error => { const { isError: checkIsError, message } = (0, _isError.default)(error); if (message) { extraError.message = message; extraError.stack = originalExtraErrorStack && originalExtraErrorStack.replace('Error: ', `Error: ${message}`); } if (jasmine.Spec.isPendingSpecException(error)) { env.pending(message); done(); } else { done.fail(checkIsError ? error : extraError); } }); } else if (returnValue === undefined) { done(); } else { done.fail(new Error('Jest: `it` and `test` must return either a Promise or undefined.')); } }; return originalFn.call(env, specName, asyncJestTest, timeout); }; } function makeConcurrent(originalFn, env, mutex) { const concurrentFn = function (specName, fn, timeout) { let promise = Promise.resolve(); const spec = originalFn.call(env, specName, () => promise, timeout); if (env != null && !env.specFilter(spec)) { return spec; } try { promise = mutex(() => { const promise = fn(); if ((0, _jestUtil.isPromise)(promise)) { return promise; } throw new Error(`Jest: concurrent test "${spec.getFullName()}" must return a Promise.`); }); } catch (error) { promise = Promise.reject(error); } // Avoid triggering the uncaught promise rejection handler in case the test errors before // being awaited on. // eslint-disable-next-line @typescript-eslint/no-empty-function promise.catch(() => {}); return spec; }; // eslint-disable-next-line unicorn/consistent-function-scoping const failing = () => { throw new Error('Jest: `failing` tests are only supported in `jest-circus`.'); }; failing.each = () => { throw new Error('Jest: `failing` tests are only supported in `jest-circus`.'); }; // each is bound after the function is made concurrent, so for now it is made noop // eslint-disable-next-line @typescript-eslint/no-empty-function,unicorn/consistent-function-scoping concurrentFn.each = () => () => {}; concurrentFn.failing = failing; return concurrentFn; } function jasmineAsyncInstall(globalConfig, global) { const jasmine = global.jasmine; const mutex = (0, _pLimit.default)(globalConfig.maxConcurrency); const env = jasmine.getEnv(); env.it = promisifyIt(env.it, env, jasmine); env.fit = promisifyIt(env.fit, env, jasmine); global.it.concurrent = (env => { const concurrent = makeConcurrent(env.it, env, mutex); concurrent.only = makeConcurrent(env.fit, env, mutex); concurrent.skip = makeConcurrent(env.xit, env, mutex); return concurrent; })(env); global.fit.concurrent = makeConcurrent(env.fit, env, mutex); env.afterAll = promisifyLifeCycleFunction(env.afterAll, env); env.afterEach = promisifyLifeCycleFunction(env.afterEach, env); env.beforeAll = promisifyLifeCycleFunction(env.beforeAll, env); env.beforeEach = promisifyLifeCycleFunction(env.beforeEach, env); } /***/ }), /***/ "./src/reporter.ts": /***/ ((__unused_webpack_module, exports) => { Object.defineProperty(exports, "__esModule", ({ value: true })); exports["default"] = void 0; var _testResult = require("@jest/test-result"); var _jestMessageUtil = require("jest-message-util"); var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol; var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol; var jestNow = globalThis[Symbol.for('jest-native-now')] || globalThis.Date.now; var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol; var Promise = globalThis[Symbol.for('jest-native-promise')] || globalThis.Promise; /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ class Jasmine2Reporter { _testResults; _globalConfig; _config; _currentSuites; _resolve; _resultsPromise; _startTimes; _testPath; constructor(globalConfig, config, testPath) { this._globalConfig = globalConfig; this._config = config; this._testPath = testPath; this._testResults = []; this._currentSuites = []; this._resolve = null; this._resultsPromise = new Promise(resolve => this._resolve = resolve); this._startTimes = new Map(); } // eslint-disable-next-line @typescript-eslint/no-empty-function jasmineStarted(_runDetails) {} specStarted(spec) { this._startTimes.set(spec.id, jestNow()); } specDone(result) { this._testResults.push(this._extractSpecResults(result, [...this._currentSuites])); } suiteStarted(suite) { this._currentSuites.push(suite.description); } suiteDone(_result) { this._currentSuites.pop(); } jasmineDone(_runDetails) { let numFailingTests = 0; let numPassingTests = 0; let numPendingTests = 0; let numTodoTests = 0; const testResults = this._testResults; for (const testResult of testResults) { if (testResult.status === 'failed') { numFailingTests++; } else if (testResult.status === 'pending') { numPendingTests++; } else if (testResult.status === 'todo') { numTodoTests++; } else { numPassingTests++; } } const testResult = { ...(0, _testResult.createEmptyTestResult)(), console: null, failureMessage: (0, _jestMessageUtil.formatResultsErrors)(testResults, this._config, this._globalConfig, this._testPath), numFailingTests, numPassingTests, numPendingTests, numTodoTests, snapshot: { added: 0, fileDeleted: false, matched: 0, unchecked: 0, unmatched: 0, updated: 0 }, testFilePath: this._testPath, testResults }; this._resolve(testResult); } getResults() { return this._resultsPromise; } _addMissingMessageToStack(stack, message) { // Some errors (e.g. Angular injection error) don't prepend error.message // to stack, instead the first line of the stack is just plain 'Error' const ERROR_REGEX = /^Error:?\s*\n/; if (stack && message && !stack.includes(message)) { return message + stack.replace(ERROR_REGEX, '\n'); } return stack; } _extractSpecResults(specResult, ancestorTitles) { const status = specResult.status === 'disabled' ? 'pending' : specResult.status; const start = this._startTimes.get(specResult.id); const duration = start && !['pending', 'skipped'].includes(status) ? jestNow() - start : null; const location = specResult.__callsite ? { column: specResult.__callsite.getColumnNumber(), line: specResult.__callsite.getLineNumber() } : null; const results = { ancestorTitles, duration, failureDetails: [], failureMessages: [], fullName: specResult.fullName, location, numPassingAsserts: 0, // Jasmine2 only returns an array of failed asserts. status, title: specResult.description }; for (const failed of specResult.failedExpectations) { const message = !failed.matcherName && typeof failed.stack === 'string' ? this._addMissingMessageToStack(failed.stack, failed.message) : failed.message || ''; results.failureMessages.push(message); results.failureDetails.push(failed); } return results; } } exports["default"] = Jasmine2Reporter; /***/ }) /******/ }); /************************************************************************/ /******/ // The module cache /******/ var __webpack_module_cache__ = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ var cachedModule = __webpack_module_cache__[moduleId]; /******/ if (cachedModule !== undefined) { /******/ return cachedModule.exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { /******/ // no module.id needed /******/ // no module.loaded needed /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ var __webpack_exports__ = {}; // This entry needs to be wrapped in an IIFE because it uses a non-standard name for the exports (exports). (() => { var exports = __webpack_exports__; Object.defineProperty(exports, "__esModule", ({ value: true })); exports["default"] = jasmine2; var path = _interopRequireWildcard(require("path")); var _sourceMap = require("@jest/source-map"); var _jestUtil = require("jest-util"); var _each = _interopRequireDefault(__webpack_require__("./src/each.ts")); var _errorOnPrivate = __webpack_require__("./src/errorOnPrivate.ts"); var _jasmineAsyncInstall = _interopRequireDefault(__webpack_require__("./src/jasmineAsyncInstall.ts")); var _reporter = _interopRequireDefault(__webpack_require__("./src/reporter.ts")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ const JASMINE = require.resolve('./jasmine/jasmineLight'); const jestEachBuildDir = path.dirname(require.resolve('jest-each')); async function jasmine2(globalConfig, config, environment, runtime, testPath) { const reporter = new _reporter.default(globalConfig, config, testPath); const jasmineFactory = runtime.requireInternalModule(JASMINE); const jasmine = jasmineFactory.create({ process, testPath, testTimeout: globalConfig.testTimeout }); const env = jasmine.getEnv(); const jasmineInterface = jasmineFactory._interface(jasmine, env); Object.assign(environment.global, jasmineInterface); env.addReporter(jasmineInterface.jsApiReporter); // TODO: Remove config option if V8 exposes some way of getting location of caller // in a future version if (config.testLocationInResults === true) { function wrapIt(original) { const wrapped = (testName, fn, timeout) => { const sourcemaps = runtime.getSourceMaps(); let stack = (0, _sourceMap.getCallsite)(1, sourcemaps); const it = original(testName, fn, timeout); if (stack.getFileName()?.startsWith(jestEachBuildDir)) { stack = (0, _sourceMap.getCallsite)(2, sourcemaps); } // @ts-expect-error: `it` is `void` for some reason it.result.__callsite = stack; return it; }; return wrapped; } environment.global.it = wrapIt(environment.global.it); environment.global.xit = wrapIt(environment.global.xit); environment.global.fit = wrapIt(environment.global.fit); } (0, _jasmineAsyncInstall.default)(globalConfig, environment.global); (0, _each.default)(environment); const failing = () => { throw new _jestUtil.ErrorWithStack('Jest: `failing` tests are only supported in `jest-circus`.', failing); }; failing.each = () => { throw new _jestUtil.ErrorWithStack('Jest: `failing` tests are only supported in `jest-circus`.', failing.each); }; environment.global.it.failing = failing; environment.global.fit.failing = failing; environment.global.xit.failing = failing; environment.global.test = environment.global.it; environment.global.it.only = environment.global.fit; environment.global.it.todo = env.todo; environment.global.it.skip = environment.global.xit; environment.global.xtest = environment.global.xit; environment.global.describe.skip = environment.global.xdescribe; environment.global.describe.only = environment.global.fdescribe; if (config.fakeTimers.enableGlobally) { if (config.fakeTimers.legacyFakeTimers) { environment.fakeTimers.useFakeTimers(); } else { environment.fakeTimersModern.useFakeTimers(); } } env.beforeEach(() => { if (config.resetModules) { runtime.resetModules(); } if (config.clearMocks) { runtime.clearAllMocks(); } if (config.resetMocks) { runtime.resetAllMocks(); if (config.fakeTimers.enableGlobally && config.fakeTimers.legacyFakeTimers) { environment.fakeTimers.useFakeTimers(); } } if (config.restoreMocks) { runtime.restoreAllMocks(); } }); env.addReporter(reporter); runtime.requireInternalModule(require.resolve('./jestExpect.js')).default({ expand: globalConfig.expand }); if (globalConfig.errorOnDeprecated) { (0, _errorOnPrivate.installErrorOnPrivate)(environment.global); } else { Object.defineProperty(jasmine, 'DEFAULT_TIMEOUT_INTERVAL', { configurable: true, enumerable: true, get() { return this._DEFAULT_TIMEOUT_INTERVAL; }, set(value) { this._DEFAULT_TIMEOUT_INTERVAL = value; } }); } const snapshotState = await runtime.requireInternalModule(require.resolve('./setup_jest_globals.js')).default({ config, globalConfig, localRequire: runtime.requireModule.bind(runtime), testPath }); for (const path of config.setupFilesAfterEnv) { const esm = runtime.unstable_shouldLoadAsEsm(path); if (esm) { await runtime.unstable_importModule(path); } else { const setupFile = runtime.requireModule(path); if (typeof setupFile === 'function') { await setupFile(); } } } if (globalConfig.testNamePattern) { const testNameRegex = new RegExp(globalConfig.testNamePattern, 'i'); env.specFilter = spec => testNameRegex.test(spec.getFullName()); } const esm = runtime.unstable_shouldLoadAsEsm(testPath); if (esm) { await runtime.unstable_importModule(testPath); } else { runtime.requireModule(testPath); } await env.execute(); const results = await reporter.getResults(); return addSnapshotData(results, snapshotState); } const addSnapshotData = (results, snapshotState) => { for (const { fullName, status } of results.testResults) { if (status === 'pending' || status === 'failed') { // if test is skipped or failed, we don't want to mark // its snapshots as obsolete. snapshotState.markSnapshotsAsCheckedForTest(fullName); } } const uncheckedCount = snapshotState.getUncheckedCount(); const uncheckedKeys = snapshotState.getUncheckedKeys(); if (uncheckedCount) { snapshotState.removeUncheckedKeys(); } const status = snapshotState.save(); results.snapshot.fileDeleted = status.deleted; results.snapshot.added = snapshotState.added; results.snapshot.matched = snapshotState.matched; results.snapshot.unmatched = snapshotState.unmatched; results.snapshot.updated = snapshotState.updated; results.snapshot.unchecked = status.deleted ? 0 : uncheckedCount; // Copy the array to prevent memory leaks results.snapshot.uncheckedKeys = [...uncheckedKeys]; return results; }; })(); module.exports = __webpack_exports__; /******/ })() ;