diff --git a/CHANGELOG.md b/CHANGELOG.md index e23216d3f0a3..30ed31b65ec4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features - `[expect]` Improve report when matcher fails, part 15 ([#8281](https://github.com/facebook/jest/pull/8281)) +- `[jest-runner]` Pass docblock pragmas to TestEnvironment constructor ([#8320](https://github.com/facebook/jest/pull/8320)) ### Fixes diff --git a/docs/Configuration.md b/docs/Configuration.md index b5c33f13455a..01cd50f533e9 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -861,6 +861,8 @@ test('use jsdom in this test file', () => { 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. +Any docblock pragmas in test files will be passed to the environment constructor and can be used for per-test configuration. If the pragma does not have a value, it will be present in the object with it's value set to an empty string. If the pragma is not present, it will not be present in the object. + _Note: TestEnvironment is sandboxed. Each test suite will trigger setup/teardown in their own TestEnvironment._ Example: @@ -870,15 +872,21 @@ Example: const NodeEnvironment = require('jest-environment-node'); class CustomEnvironment extends NodeEnvironment { - constructor(config, context) { + constructor(config, {testPath, docblockPragmas}) { super(config, context); - this.testPath = context.testPath; + this.testPath = testPath; + this.docblockPragmas = docblockPragmas; } async setup() { await super.setup(); await someSetupTasks(this.testPath); this.global.someGlobalObject = createGlobalObject(); + + // Will trigger if docblock contains @my-custom-pragma my-pragma-value + if (this.docblockPragmas['my-custom-pragma'] === 'my-pragma-value') { + // ... + } } async teardown() { diff --git a/e2e/__tests__/testEnvironment.test.ts b/e2e/__tests__/testEnvironment.test.ts index 141fed8eb310..6783ba921969 100644 --- a/e2e/__tests__/testEnvironment.test.ts +++ b/e2e/__tests__/testEnvironment.test.ts @@ -15,5 +15,5 @@ it('respects testEnvironment docblock', () => { const {json: result} = runWithJson('test-environment'); expect(result.success).toBe(true); - expect(result.numTotalTests).toBe(2); + expect(result.numTotalTests).toBe(3); }); diff --git a/e2e/test-environment/DocblockPragmasEnvironment.js b/e2e/test-environment/DocblockPragmasEnvironment.js new file mode 100644 index 000000000000..6868bc3d4093 --- /dev/null +++ b/e2e/test-environment/DocblockPragmasEnvironment.js @@ -0,0 +1,14 @@ +// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + +'use strict'; + +const JSDOMEnvironment = require('jest-environment-jsdom'); + +class TestEnvironment extends JSDOMEnvironment { + constructor(config, context) { + super(config, context); + this.global.myCustomPragma = context.docblockPragmas['my-custom-pragma']; + } +} + +module.exports = TestEnvironment; diff --git a/e2e/test-environment/EsmDefaultEnvironment.js b/e2e/test-environment/EsmDefaultEnvironment.js index a15261466885..6c3e9ad5b769 100644 --- a/e2e/test-environment/EsmDefaultEnvironment.js +++ b/e2e/test-environment/EsmDefaultEnvironment.js @@ -11,8 +11,8 @@ exports.__esModule = true; const NodeEnvironment = require('jest-environment-node'); class Env extends NodeEnvironment { - constructor(...args) { - super(...args); + constructor(config, options) { + super(config, options); this.global.property = 'value'; } } diff --git a/e2e/test-environment/__tests__/docblockPragmas.test.js b/e2e/test-environment/__tests__/docblockPragmas.test.js new file mode 100644 index 000000000000..213e5c22a273 --- /dev/null +++ b/e2e/test-environment/__tests__/docblockPragmas.test.js @@ -0,0 +1,10 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * @jest-environment ./DocblockPragmasEnvironment.js + * @my-custom-pragma pragma-value + */ + +test('docblock pragmas', () => { + expect(myCustomPragma).toEqual('pragma-value'); // eslint-disable-line no-undef +}); diff --git a/packages/jest-environment/src/index.ts b/packages/jest-environment/src/index.ts index f1b262c6ce98..00fb37b8fab8 100644 --- a/packages/jest-environment/src/index.ts +++ b/packages/jest-environment/src/index.ts @@ -14,17 +14,19 @@ import {JestFakeTimers as FakeTimers} from '@jest/fake-timers'; type JestMockFn = typeof jestMock.fn; type JestMockSpyOn = typeof jestMock.spyOn; -export type EnvironmentContext = { - console?: Console; - testPath?: Config.Path; -}; +// In Jest 25, remove `Partial` since it's incorrect. The properties are always +// passed, or not. The context itself is optional, not properties within it. +export type EnvironmentContext = Partial<{ + console: Console; + docblockPragmas: {[key: string]: string | Array}; + testPath: Config.Path; +}>; // TODO: type this better: https://nodejs.org/api/modules.html#modules_the_module_wrapper type ModuleWrapper = (...args: Array) => unknown; export declare class JestEnvironment { - constructor(config: Config.ProjectConfig); - constructor(config: Config.ProjectConfig, context: EnvironmentContext); + constructor(config: Config.ProjectConfig, context?: EnvironmentContext); global: Global.Global; fakeTimers: FakeTimers | null; moduleMocker: ModuleMocker | null; diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index dadef559f159..860105dc99e1 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -85,8 +85,8 @@ async function runTestInternal( context?: TestRunnerContext, ): Promise { const testSource = fs.readFileSync(path, 'utf8'); - const parsedDocblock = docblock.parse(docblock.extract(testSource)); - const customEnvironment = parsedDocblock['jest-environment']; + const docblockPragmas = docblock.parse(docblock.extract(testSource)); + const customEnvironment = docblockPragmas['jest-environment']; let testEnvironment = config.testEnvironment; @@ -144,6 +144,7 @@ async function runTestInternal( const environment = new TestEnvironment(config, { console: testConsole, + docblockPragmas, testPath: path, }); const leakDetector = config.detectLeaks