Skip to content

Commit

Permalink
Pass docblock pragmas to TestEnvironment constructor. (#8320)
Browse files Browse the repository at this point in the history
* Pass docblock pragmas to test environment constructor.

* Add test and documentation.

* Update Configuration.md

* Update CHANGELOG.md

* Update runTest.ts

* Move docblock pragmas test into own files.
  • Loading branch information
scotthovestadt committed Apr 13, 2019
1 parent 538cef4 commit 1f280d8
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -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

Expand Down
12 changes: 10 additions & 2 deletions docs/Configuration.md
Expand Up @@ -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:
Expand All @@ -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() {
Expand Down
2 changes: 1 addition & 1 deletion e2e/__tests__/testEnvironment.test.ts
Expand Up @@ -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);
});
14 changes: 14 additions & 0 deletions 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;
4 changes: 2 additions & 2 deletions e2e/test-environment/EsmDefaultEnvironment.js
Expand Up @@ -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';
}
}
Expand Down
10 changes: 10 additions & 0 deletions 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
});
14 changes: 8 additions & 6 deletions packages/jest-environment/src/index.ts
Expand Up @@ -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<string>};
testPath: Config.Path;
}>;

// TODO: type this better: https://nodejs.org/api/modules.html#modules_the_module_wrapper
type ModuleWrapper = (...args: Array<unknown>) => 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<unknown> | null;
moduleMocker: ModuleMocker | null;
Expand Down
5 changes: 3 additions & 2 deletions packages/jest-runner/src/runTest.ts
Expand Up @@ -85,8 +85,8 @@ async function runTestInternal(
context?: TestRunnerContext,
): Promise<RunTestInternalResult> {
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;

Expand Down Expand Up @@ -144,6 +144,7 @@ async function runTestInternal(

const environment = new TestEnvironment(config, {
console: testConsole,
docblockPragmas,
testPath: path,
});
const leakDetector = config.detectLeaks
Expand Down

0 comments on commit 1f280d8

Please sign in to comment.