Skip to content

Commit

Permalink
[WIP] jest-framework
Browse files Browse the repository at this point in the history
[WIP] jest-framework

[WIP] jest-framework

[WIP] Integrating with Jest

[WIP] jest-framework

[WIP] jest-framework

[WIP] jest-framework
  • Loading branch information
aaronabramov committed Jun 1, 2017
1 parent 5449f7f commit 657f967
Show file tree
Hide file tree
Showing 17 changed files with 1,024 additions and 317 deletions.
16 changes: 16 additions & 0 deletions packages/jest-framework/package.json
@@ -0,0 +1,16 @@
{
"name": "jest-framework",
"version": "20.0.3",
"repository": {
"type": "git",
"url": "https://github.com/facebook/jest.git"
},
"license": "BSD-3-Clause",
"main": "build/index.js",
"dependencies": {
"jest-snapshot": "^20.0.3"
},
"devDependencies": {
"jest-runtime": "^20.0.3"
}
}
90 changes: 90 additions & 0 deletions packages/jest-framework/src/eventHandler.js
@@ -0,0 +1,90 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/

import type {EventHandler} from '../types';

const {
makeDescribe,
getTestDuration,
makeTest,
isSharedHook,
} = require('./utils');

// To pass this value from Runtime object to state we need to use global[sym]
const TEST_TIMEOUT_SYMBOL = Symbol.for('TEST_TIMEOUT_SYMBOL');

const handler: EventHandler = (event, state): void => {
switch (event.name) {
case 'start_describe_definition': {
const {blockName, mode} = event;
const {currentDescribeBlock} = state;
const describeBlock = makeDescribe(blockName, currentDescribeBlock, mode);
currentDescribeBlock.children.push(describeBlock);
state.currentDescribeBlock = describeBlock;
break;
}
case 'finish_describe_definition': {
const {currentDescribeBlock} = state;
if (!currentDescribeBlock) {
throw new Error(
`currentDescribeBlock has to be there since we're finishing its definition`,
);
}
if (currentDescribeBlock.parent) {
state.currentDescribeBlock = currentDescribeBlock.parent;
}
break;
}
case 'add_hook': {
const {currentDescribeBlock} = state;
const {fn, hookType: type} = event;
currentDescribeBlock.hooks.push({fn, type});
break;
}
case 'add_test': {
const {currentDescribeBlock} = state;
const {fn, mode, testName: name} = event;
const test = makeTest(fn, mode, name, currentDescribeBlock);
test.mode === 'only' && (state.hasFocusedTests = true);
currentDescribeBlock.tests.push(test);
break;
}
case 'hook_start': {
isSharedHook(event.hook) &&
state.sharedHooksThatHaveBeenExecuted.add(event.hook);
break;
}
case 'test_start': {
event.test.startedAt = Date.now();
break;
}
case 'test_skip': {
event.test.status = 'skip';
break;
}
case 'test_failure': {
event.test.status = 'fail';
event.test.duration = getTestDuration(event.test);
event.test.errors.push(event.error);
break;
}
case 'test_success': {
event.test.status = 'pass';
event.test.duration = getTestDuration(event.test);
break;
}
case 'run_start': {
state.testTimeout = global[TEST_TIMEOUT_SYMBOL] || state.testTimeout;
break;
}
}
};

module.exports = handler;
58 changes: 58 additions & 0 deletions packages/jest-framework/src/index.js
@@ -0,0 +1,58 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/

import type {
BlockFn,
HookFn,
HookType,
TestFn,
BlockMode,
BlockName,
TestName,
} from '../types';
const {dispatch} = require('./state');

const describe = (blockName: BlockName, blockFn: BlockFn) =>
_dispatchDescribe(blockFn, blockName);
describe.only = (blockName: BlockName, blockFn: BlockFn) =>
_dispatchDescribe(blockFn, blockName, 'only');
describe.skip = (blockName: BlockName, blockFn: BlockFn) =>
_dispatchDescribe(blockFn, blockName, 'skip');

const _dispatchDescribe = (blockFn, blockName, mode?: BlockMode) => {
dispatch({blockName, mode, name: 'start_describe_definition'});
blockFn();
dispatch({name: 'finish_describe_definition'});
};

const _addHook = (fn: HookFn, hookType: HookType) =>
dispatch({fn, hookType, name: 'add_hook'});
const beforeEach = (fn: HookFn) => _addHook(fn, 'beforeEach');
const beforeAll = (fn: HookFn) => _addHook(fn, 'beforeAll');
const afterEach = (fn: HookFn) => _addHook(fn, 'afterEach');
const afterAll = (fn: HookFn) => _addHook(fn, 'afterAll');

const test = (testName: TestName, fn?: TestFn) =>
dispatch({fn, name: 'add_test', testName});
const it = test;
test.skip = (testName: TestName, fn?: TestFn) =>
dispatch({fn, mode: 'skip', name: 'add_test', testName});
test.only = (testName: TestName, fn: TestFn) =>
dispatch({fn, mode: 'only', name: 'add_test', testName});

module.exports = {
afterAll,
afterEach,
beforeAll,
beforeEach,
describe,
it,
test,
};
@@ -0,0 +1,174 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/

import type {TestResult, Status} from 'types/TestResult';
import type {GlobalConfig, Path, ProjectConfig} from 'types/Config';
import type {Event, Test} from '../../types';

const {getState, setState} = require('jest-matchers');
const {formatResultsErrors} = require('jest-message-util');
const {SnapshotState, addSerializer} = require('jest-snapshot');
const {addEventHandler, TOP_DESCRIBE_BLOCK_NAME} = require('../state');
const {getTestID} = require('../utils');
const run = require('../run');
const globals = require('../index');

const initialize = ({
config,
globalConfig,
localRequire,
testPath,
}: {
config: ProjectConfig,
globalConfig: GlobalConfig,
localRequire: Path => any,
testPath: Path,
}) => {
Object.assign(global, globals);

addEventHandler(eventHandler);

// Jest tests snapshotSerializers in order preceding built-in serializers.
// Therefore, add in reverse because the last added is the first tested.
config.snapshotSerializers.concat().reverse().forEach(path => {
addSerializer(localRequire(path));
});

const {expand, updateSnapshot} = globalConfig;
const snapshotState = new SnapshotState(testPath, {expand, updateSnapshot});
setState({snapshotState, testPath});

// Return it back to the outer scope (test runner outside the VM).
return {globals, snapshotState};
};

const runAndTransformResultsToJestFormat = async ({
config,
globalConfig,
testPath,
}: {
config: ProjectConfig,
globalConfig: GlobalConfig,
testPath: string,
}): Promise<TestResult> => {
const result = await run();

let numFailingTests = 0;
let numPassingTests = 0;
let numPendingTests = 0;

for (const testResult of result) {
switch (testResult.status) {
case 'fail':
numFailingTests += 1;
break;
case 'pass':
numPassingTests += 1;
break;
case 'skip':
numPendingTests += 1;
break;
}
}

const assertionResults = result.map(testResult => {
let status: Status;
switch (testResult.status) {
case 'fail':
status = 'failed';
break;
case 'pass':
status = 'passed';
break;
case 'skip':
status = 'skipped';
break;
}

const ancestorTitles = testResult.testPath.filter(
name => name !== TOP_DESCRIBE_BLOCK_NAME,
);
const title = ancestorTitles.pop();

// $FlowFixMe Types are slightly incompatible and need to be refactored
return {
ancestorTitles,
duration: testResult.duration,
failureMessages: testResult.errors,
fullName: ancestorTitles.concat(title).join(' '),
numPassingAsserts: 0,
status,
title: testResult.testPath[testResult.testPath.length - 1],
};
});

const failureMessage = formatResultsErrors(
assertionResults,
config,
globalConfig,
testPath,
);

return {
console: null,
failureMessage,
numFailingTests,
numPassingTests,
numPendingTests,
perfStats: {
// populated outside
end: 0,
start: 0,
},
skipped: false,
snapshot: {
added: 0,
fileDeleted: false,
matched: 0,
unchecked: 0,
unmatched: 0,
updated: 0,
},
sourceMaps: {},
testFilePath: testPath,
testResults: assertionResults,
};
};

const eventHandler = (event: Event) => {
switch (event.name) {
case 'test_start': {
setState({currentTestName: getTestID(event.test)});
break;
}
case 'test_success':
case 'test_failure': {
_addSuppressedErrors(event.test);
break;
}
}
};

// Get suppressed errors from ``jest-matchers`` that weren't throw during
// test execution and add them to the test result, potentially failing
// a passing test.
const _addSuppressedErrors = (test: Test) => {
const {suppressedErrors} = getState();
setState({suppressedErrors: []});
if (suppressedErrors.length) {
test.status = 'fail';
test.errors = test.errors.concat(suppressedErrors);
}
};

module.exports = {
initialize,
runAndTransformResultsToJestFormat,
};

0 comments on commit 657f967

Please sign in to comment.