/** * 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 { JestEnvironment } from '@jest/environment'; import { getCallsite } from '@jest/source-map'; import { TestResult } from '@jest/test-result'; import { Config, Global } from '@jest/types'; import { installErrorOnPrivate } from 'jest-jasmine2/build/errorOnPrivate'; import Spec from 'jest-jasmine2/build/jasmine/Spec'; import jasmineAsyncInstall from 'jest-jasmine2/build/jasmineAsyncInstall'; import each from 'jest-jasmine2/build/each'; import JasmineReporter from 'jest-jasmine2/build/reporter'; import { Jasmine } from 'jest-jasmine2/build/types'; import Runtime from 'jest-runtime'; import { default as supportCodeLibraryBuilder } from 'cucumber/lib/support_code_library_builder'; import addSnapshotData from './utils/addSnapshotData'; import * as paths from './utils/paths'; import { JasmineFactory, JestSetupGlobals, Test } from './types'; export default async function testRunner( globalConfig: Config.GlobalConfig, projectConfig: Config.ProjectConfig, environment: JestEnvironment & Jasmine, runtime: Runtime, testPath: string, test: Test ): Promise { const reporter = new JasmineReporter(globalConfig, projectConfig, testPath); const jasmineFactory = runtime.requireInternalModule(paths.JASMINE_PATH); 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 (projectConfig.testLocationInResults === true) { const originalIt = environment.global.it; environment.global.it = ((...args) => { const stack = getCallsite(1, runtime.getSourceMaps()); const it = originalIt(...args); // @ts-ignore it.result.__callsite = stack; return it; }) as Global.Global['it']; const originalXit = environment.global.xit; environment.global.xit = ((...args) => { const stack = getCallsite(1, runtime.getSourceMaps()); const xit = originalXit(...args); // @ts-ignore xit.result.__callsite = stack; return xit; }) as Global.Global['xit']; const originalFit = environment.global.fit; environment.global.fit = ((...args) => { const stack = getCallsite(1, runtime.getSourceMaps()); const fit = originalFit(...args); // @ts-ignore fit.result.__callsite = stack; return fit; }) as Global.Global['fit']; } jasmineAsyncInstall(globalConfig, environment.global); each(environment); 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; environment.global.restoreMocks = projectConfig.restoreMocks; if (projectConfig.timers === 'fake' || projectConfig.timers === 'legacy') { environment.fakeTimers!.useFakeTimers(); } else if (projectConfig.timers === 'modern') { environment.fakeTimersModern!.useFakeTimers(); } env.beforeEach(() => { if (projectConfig.resetModules) { runtime.resetModules(); } if (projectConfig.clearMocks) { runtime.clearAllMocks(); } if (projectConfig.resetMocks) { runtime.resetAllMocks(); if (projectConfig.timers === 'fake' || projectConfig.timers === 'legacy') { environment.fakeTimers!.useFakeTimers(); } } if (projectConfig.restoreMocks) { runtime.restoreAllMocks(); } }); env.addReporter(reporter); runtime.requireInternalModule<{ default: (config: { expand: boolean; }) => void; }>(paths.JEST_EXPECT_PATH).default({ expand: globalConfig.expand }); if (globalConfig.errorOnDeprecated) { 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 = runtime.requireInternalModule(paths.SETUP_GLOBAL_PATHS).default({ config: projectConfig, globalConfig, localRequire: runtime.requireModule.bind(runtime), testPath }); if (globalConfig.enabledTestsMap) { env.specFilter = (spec: Spec) => { const suiteMap = globalConfig.enabledTestsMap && globalConfig.enabledTestsMap[spec.result.testPath]; return (suiteMap && suiteMap[spec.result.fullName]) || false; }; } else if (globalConfig.testNamePattern) { const testNameRegex = new RegExp(globalConfig.testNamePattern, 'i'); env.specFilter = (spec: Spec) => testNameRegex.test(spec.getFullName()); } environment.global.cwd = projectConfig.cwd; environment.global.featurePath = test.tmpPath || test.path; environment.global.moduleFileExtensions = projectConfig.moduleFileExtensions.filter((ext) => ext !== 'feature'); environment.global.setupFilesAfterEnv = projectConfig.setupFilesAfterEnv; environment.global.testTimeout = globalConfig.testTimeout || jasmine.testTimeout || jasmine._DEFAULT_TIMEOUT_INTERVAL; supportCodeLibraryBuilder.reset(projectConfig.cwd); environment.global.supportCodeLibraryBuilder = supportCodeLibraryBuilder; // mock the cucumber path so that steps get registered to existing supportCodeLibraryBuilder runtime.setMock(paths.CUCUMBER_PATH, 'cucumber', () => supportCodeLibraryBuilder.methods); for (const path of projectConfig.setupFilesAfterEnv) { // TODO: remove ? in Jest 26 const esm = runtime.unstable_shouldLoadAsEsm?.(path); if (esm) { await runtime.unstable_importModule(path); } else { runtime.requireModule(path.replace(projectConfig.cwd, '.')); } } // finalize the cucumber builder supportCodeLibraryBuilder.finalize(); // TODO: remove ? in Jest 26 const esm = runtime.unstable_shouldLoadAsEsm?.(paths.CUCUMBER_JEST_PATH); if (esm) { await runtime.unstable_importModule(paths.CUCUMBER_JEST_PATH); } else { runtime.requireModule(paths.CUCUMBER_JEST_PATH); } await env.execute(); environment.global.log = null; environment.global.CucumberWorld = null; runtime.setMock(paths.CUCUMBER_PATH, 'cucumber', null); supportCodeLibraryBuilder.reset(projectConfig.cwd); runtime.resetAllMocks(); const results = await reporter.getResults(); return addSnapshotData(results, snapshotState); }