diff --git a/.babelrc b/.babelrc new file mode 100644 index 000000000000..5a6a8f03c43a --- /dev/null +++ b/.babelrc @@ -0,0 +1,7 @@ +{ + "plugins": [ + "syntax-trailing-function-commas", + "transform-flow-strip-types" + ], + "retainLines": true +} diff --git a/examples/react/package.json b/examples/react/package.json index 833c43c17b2d..2628bf6d9407 100644 --- a/examples/react/package.json +++ b/examples/react/package.json @@ -1,7 +1,7 @@ { "dependencies": { - "react": "~0.14.0", - "react-dom": "~0.14.0" + "react": "~15.2.0", + "react-dom": "~15.2.0" }, "devDependencies": { "babel-jest": "*", diff --git a/integration_tests/__tests__/snapshot-test.js b/integration_tests/__tests__/snapshot-test.js index 9d85ae364ffc..03a24f1dca15 100644 --- a/integration_tests/__tests__/snapshot-test.js +++ b/integration_tests/__tests__/snapshot-test.js @@ -26,7 +26,7 @@ const originalTestPath = path.resolve( __dirname, '../snapshot/__tests__/snapshot-test.js' ); -const originalTestContent = fs.readFileSync(originalTestPath, 'utf-8'); +const originalTestContent = fs.readFileSync(originalTestPath, 'utf8'); const copyOfTestPath = originalTestPath.replace('.js', '_copy.js'); const snapshotEscapeDir = @@ -49,7 +49,7 @@ const fileExists = filePath => { const getSnapshotOfCopy = () => { const exports = Object.create(null); // eslint-disable-next-line no-eval - eval(fs.readFileSync(snapshotOfCopy, 'utf-8')); + eval(fs.readFileSync(snapshotOfCopy, 'utf8')); return exports; }; diff --git a/package.json b/package.json index 81c886b59730..c7299682dfc5 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "clean": "rm -rf ./packages/*/node_modules; npm run build-clean", "lint": "eslint .", "postinstall": "node ./scripts/postinstall.js && node ./scripts/build.js", + "publish": "npm run build-clean && npm run build && lerna publish", "t": "node ./scripts/test.js", "test": "npm run typecheck && npm run lint && npm run build && npm run t", "typecheck": "flow check", diff --git a/packages/babel-jest/.npmignore b/packages/babel-jest/.npmignore new file mode 100644 index 000000000000..85e48fe7b0a4 --- /dev/null +++ b/packages/babel-jest/.npmignore @@ -0,0 +1,3 @@ +**/__mocks__/** +**/__tests__/** +src diff --git a/packages/babel-plugin-jest-hoist/.babelrc b/packages/babel-plugin-jest-hoist/.babelrc index f18db8331b36..0c9c840a4ff1 100644 --- a/packages/babel-plugin-jest-hoist/.babelrc +++ b/packages/babel-plugin-jest-hoist/.babelrc @@ -1,3 +1,3 @@ { - "presets": ["es2015", {plugins: ["./"]}] + "presets": ["es2015", {"plugins": ["./"]}] } diff --git a/packages/babel-plugin-jest-hoist/.npmignore b/packages/babel-plugin-jest-hoist/.npmignore index 50d0c59cfb8c..85e48fe7b0a4 100644 --- a/packages/babel-plugin-jest-hoist/.npmignore +++ b/packages/babel-plugin-jest-hoist/.npmignore @@ -1,2 +1,3 @@ -__tests__/ -__test_modules__/ +**/__mocks__/** +**/__tests__/** +src diff --git a/packages/babel-plugin-jest-hoist/package.json b/packages/babel-plugin-jest-hoist/package.json index 82fa31531b47..f403b7b8d52f 100644 --- a/packages/babel-plugin-jest-hoist/package.json +++ b/packages/babel-plugin-jest-hoist/package.json @@ -12,7 +12,7 @@ "react": "^0.14.0" }, "jest": { - "rootDir": "./build", + "rootDir": "./src", "scriptPreprocessor": "../../babel-jest", "testEnvironment": "node" }, diff --git a/packages/babel-plugin-jest-hoist/src/index.js b/packages/babel-plugin-jest-hoist/src/index.js index f7e1c54809a7..b06b89a0ee24 100644 --- a/packages/babel-plugin-jest-hoist/src/index.js +++ b/packages/babel-plugin-jest-hoist/src/index.js @@ -14,10 +14,12 @@ function invariant(condition, message) { } } -// We allow `jest`, `require`, all default Node.js globals and all ES2015 -// built-ins to be used inside of a `jest.mock` factory. +// We allow `jest`, `expect`, `require`, all default Node.js globals and all +// ES2015 built-ins to be used inside of a `jest.mock` factory. +// We also allow variables prefixed with `mock` as an escape-hatch. const WHITELISTED_IDENTIFIERS = { jest: true, + expect: true, require: true, Infinity: true, NaN: true, @@ -103,12 +105,16 @@ FUNCTIONS.mock = args => { if (!found) { invariant( - scope.hasGlobal(name) && WHITELISTED_IDENTIFIERS[name], - 'The second argument of `jest.mock()` is not allowed to ' + - 'reference any outside variables.\n' + + (scope.hasGlobal(name) && WHITELISTED_IDENTIFIERS[name]) || + /^mock/.test(name), + 'The module factory of `jest.mock()` is not allowed to ' + + 'reference any out-of-scope variables.\n' + 'Invalid variable access: ' + name + '\n' + 'Whitelisted objects: ' + - Object.keys(WHITELISTED_IDENTIFIERS).join(', ') + '.', + Object.keys(WHITELISTED_IDENTIFIERS).join(', ') + '.\n' + + 'Note: This is a precaution to guard against uninitialized mock ' + + 'variables. If it is ensured that the mock is required lazily, ' + + 'variable names prefixed with `mock` are permitted.', ); } } diff --git a/packages/babel-preset-jest/.npmignore b/packages/babel-preset-jest/.npmignore new file mode 100644 index 000000000000..85e48fe7b0a4 --- /dev/null +++ b/packages/babel-preset-jest/.npmignore @@ -0,0 +1,3 @@ +**/__mocks__/** +**/__tests__/** +src diff --git a/packages/jest-changed-files/.npmignore b/packages/jest-changed-files/.npmignore index 47a78158e488..85e48fe7b0a4 100644 --- a/packages/jest-changed-files/.npmignore +++ b/packages/jest-changed-files/.npmignore @@ -1,3 +1,3 @@ -__mocks__/ -__tests__/ +**/__mocks__/** +**/__tests__/** src diff --git a/packages/jest-changed-files/package.json b/packages/jest-changed-files/package.json index a30d9b12f358..13ffce636e7e 100644 --- a/packages/jest-changed-files/package.json +++ b/packages/jest-changed-files/package.json @@ -11,7 +11,8 @@ "rimraf": "^2.5.2" }, "jest": { - "rootDir": "./build", + "rootDir": "./src", + "scriptPreprocessor": "../../babel-jest", "testEnvironment": "node" }, "scripts": { diff --git a/packages/jest-cli/.npmignore b/packages/jest-cli/.npmignore index e31125afcece..85e48fe7b0a4 100644 --- a/packages/jest-cli/.npmignore +++ b/packages/jest-cli/.npmignore @@ -1,3 +1,3 @@ -__tests__ -__mocks__ +**/__mocks__/** +**/__tests__/** src diff --git a/packages/jest-cli/package.json b/packages/jest-cli/package.json index 9c52730622de..54f6d82e3c5b 100644 --- a/packages/jest-cli/package.json +++ b/packages/jest-cli/package.json @@ -18,7 +18,6 @@ "jest-util": "^13.2.2", "jest-snapshot": "^13.2.3", "json-stable-stringify": "^1.0.0", - "lodash.template": "^4.2.4", "sane": "^1.2.0", "which": "^1.1.1", "worker-farm": "^1.3.1", @@ -48,8 +47,9 @@ "test": "./bin/jest.js" }, "jest": { - "rootDir": "./build", - "testEnvironment": "jest-environment-node" + "rootDir": "./src", + "scriptPreprocessor": "../../babel-jest", + "testEnvironment": "node" }, "bugs": { "url": "https://github.com/facebook/jest/issues" diff --git a/packages/jest-cli/src/CoverageCollector.js b/packages/jest-cli/src/CoverageCollector.js deleted file mode 100644 index ceabb3ada254..000000000000 --- a/packages/jest-cli/src/CoverageCollector.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * 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. - */ -'use strict'; - -const CoverageInstrumentor = require('cover/instrument').Instrumentor; -const fs = require('graceful-fs'); -const path = require('path'); - -const COVERAGE_TEMPLATE_PATH = path.join(__dirname, 'coverage.template'); - -let _memoizedCoverageTemplate = null; - -function _getCoverageTemplate() { - if (_memoizedCoverageTemplate === null) { - _memoizedCoverageTemplate = require('lodash.template')( - fs.readFileSync(COVERAGE_TEMPLATE_PATH, 'utf8'), - ); - } - return _memoizedCoverageTemplate; -} - -class CoverageCollector { - - constructor(sourceText) { - this._coverageDataStore = {}; - this._instrumentedSourceText = null; - this._instrumentor = new CoverageInstrumentor(); - this._origSourceText = sourceText; - } - - getCoverageDataStore() { - return this._coverageDataStore; - } - - getInstrumentedSource(storageVarName) { - if (this._instrumentedSourceText === null) { - this._instrumentedSourceText = _getCoverageTemplate()({ - instrumented: this._instrumentor, - coverageStorageVar: storageVarName, - source: this._instrumentor.instrument(this._origSourceText), - }); - } - return this._instrumentedSourceText; - } - - extractRuntimeCoverageInfo() { - const instrumentationInfo = this._instrumentor.objectify(); - const coverageInfo = { - coveredSpans: [], - uncoveredSpans: [], - sourceText: this._origSourceText, - }; - - let nodeIndex; - - // Find all covered spans - for (nodeIndex in this._coverageDataStore.nodes) { - coverageInfo.coveredSpans.push(instrumentationInfo.nodes[nodeIndex].loc); - } - - // Find all definitely uncovered spans - for (nodeIndex in instrumentationInfo.nodes) { - if (!this._coverageDataStore.nodes.hasOwnProperty(nodeIndex)) { - coverageInfo.uncoveredSpans.push( - instrumentationInfo.nodes[nodeIndex].loc, - ); - } - } - - return coverageInfo; - } - -} - -module.exports = CoverageCollector; diff --git a/packages/jest-config/.npmignore b/packages/jest-config/.npmignore index e31125afcece..85e48fe7b0a4 100644 --- a/packages/jest-config/.npmignore +++ b/packages/jest-config/.npmignore @@ -1,3 +1,3 @@ -__tests__ -__mocks__ +**/__mocks__/** +**/__tests__/** src diff --git a/packages/jest-config/package.json b/packages/jest-config/package.json index 352a396d3c13..2be8b48435c8 100644 --- a/packages/jest-config/package.json +++ b/packages/jest-config/package.json @@ -22,6 +22,7 @@ }, "jest": { "rootDir": "./build", + "scriptPreprocessor": "../../babel-jest", "testEnvironment": "jest-environment-node" }, "scripts": { diff --git a/packages/jest-config/src/IstanbulCollector.js b/packages/jest-config/src/IstanbulCollector.js index 5e880239957e..af29c730db8f 100644 --- a/packages/jest-config/src/IstanbulCollector.js +++ b/packages/jest-config/src/IstanbulCollector.js @@ -4,28 +4,36 @@ * 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 */ 'use strict'; +import type {Path} from 'types/Config'; + const istanbul = require('istanbul'); class IstanbulCollector { - constructor(sourceText, filename) { - const instr = new istanbul.Instrumenter(); - this._coverageDataStore = {}; - this._instrumentor = instr; - this._origSourceText = sourceText; - this._instrumentedSourceText = instr.instrumentSync(sourceText, filename); + _coverageDataStore: Object; + + constructor() { + this._coverageDataStore = Object.create(null); } getCoverageDataStore() { return this._coverageDataStore; } - getInstrumentedSource(storageVarName) { - return this._instrumentedSourceText + storageVarName + '.coverState=' + - this._instrumentor.currentState.trackerVar + ';'; + getInstrumentedSource( + sourceText: string, + filename: Path, + storageVarName: string, + ) { + const instrumentor = new istanbul.Instrumenter(); + const source = instrumentor.instrumentSync(sourceText, filename); + const tracker = instrumentor.currentState.trackerVar; + return source + storageVarName + '.coverState=' + tracker + ';'; } extractRuntimeCoverageInfo() { diff --git a/packages/jest-config/src/reporters/IstanbulTestReporter.js b/packages/jest-config/src/reporters/IstanbulTestReporter.js index 0143a2d741a5..f34b0a86ba74 100644 --- a/packages/jest-config/src/reporters/IstanbulTestReporter.js +++ b/packages/jest-config/src/reporters/IstanbulTestReporter.js @@ -9,19 +9,31 @@ */ 'use strict'; +import type {Config} from 'types/Config'; +import type {AggregatedResult, TestResult} from 'types/TestResult'; +import type {Process} from 'types/Process'; + const DefaultTestReporter = require('./DefaultTestReporter'); + const chalk = require('chalk'); const istanbul = require('istanbul'); -const collector = new istanbul.Collector(); -const testCollectors = Object.create(null); -const reporter = new istanbul.Reporter(); const FAIL_COLOR = chalk.bold.red; -import type {Config} from 'types/Config'; -import type {AggregatedResult, TestResult} from 'types/TestResult'; - class IstanbulTestReporter extends DefaultTestReporter { + + _collector: istanbul.Collector; + _reporter: istanbul.Reporter; + _testCollectors: Object; + + constructor(customProcess: Process) { + super(customProcess); + + this._collector = new istanbul.Collector(); + this._testCollectors = Object.create(null); + this._reporter = new istanbul.Reporter(); + } + onTestResult( config: Config, testResult: TestResult, @@ -30,27 +42,29 @@ class IstanbulTestReporter extends DefaultTestReporter { super.onTestResult(config, testResult, aggregatedResults); if (config.collectCoverage && testResult.coverage) { - collector.add(testResult.coverage); - if (!testCollectors[testResult.testFilePath]) { - testCollectors[testResult.testFilePath] = new istanbul.Collector(); + const testFilePath = testResult.testFilePath; + this._collector.add(testResult.coverage); + if (!this._testCollectors[testFilePath]) { + this._testCollectors[testFilePath] = new istanbul.Collector(); } - testCollectors[testResult.testFilePath].add(testResult.coverage); + this._testCollectors[testFilePath].add(testResult.coverage); } } onRunComplete(config: Config, aggregatedResults: AggregatedResult) { aggregatedResults.success = super.onRunComplete(config, aggregatedResults); + const reporter = this._reporter; if (config.collectCoverage) { try { if (config.coverageDirectory) { reporter.dir = config.coverageDirectory; } reporter.addAll(config.coverageReporters); - reporter.write(collector, true, () => {}); + reporter.write(this._collector, true, () => {}); } catch (e) {} if (config.coverageThreshold) { - const rawCoverage = collector.getFinalCoverage(); + const rawCoverage = this._collector.getFinalCoverage(); const globalResults = istanbul.utils.summarizeCoverage(rawCoverage); function check(name, thresholds, actuals) { @@ -97,16 +111,16 @@ class IstanbulTestReporter extends DefaultTestReporter { return aggregatedResults.success; } - static getReporter() { - return reporter; + getReporter() { + return this._reporter; } - static getCollector() { - return collector; + getCollector() { + return this._collector; } - static getTestCollectors() { - return testCollectors; + getTestCollectors() { + return this._testCollectors; } } diff --git a/packages/jest-environment-jsdom/.npmignore b/packages/jest-environment-jsdom/.npmignore index e31125afcece..85e48fe7b0a4 100644 --- a/packages/jest-environment-jsdom/.npmignore +++ b/packages/jest-environment-jsdom/.npmignore @@ -1,3 +1,3 @@ -__tests__ -__mocks__ +**/__mocks__/** +**/__tests__/** src diff --git a/packages/jest-environment-jsdom/package.json b/packages/jest-environment-jsdom/package.json index a40152785999..637b6c3dc7f8 100644 --- a/packages/jest-environment-jsdom/package.json +++ b/packages/jest-environment-jsdom/package.json @@ -8,7 +8,7 @@ "license": "BSD-3-Clause", "main": "build/index.js", "dependencies": { - "jsdom": "^9.2.1", + "jsdom": "^9.4.0", "jest-util": "^13.2.2" } } diff --git a/packages/jest-environment-jsdom/src/index.js b/packages/jest-environment-jsdom/src/index.js index 07c221022416..2e7d1ed49649 100644 --- a/packages/jest-environment-jsdom/src/index.js +++ b/packages/jest-environment-jsdom/src/index.js @@ -10,6 +10,7 @@ import type {Config} from 'types/Config'; import type {Global} from 'types/Global'; +import type {Script} from 'vm'; const FakeTimers = require('jest-util').FakeTimers; const installCommonGlobals = require('jest-util').installCommonGlobals; @@ -45,9 +46,9 @@ class JSDOMEnvironment { this.fakeTimers = null; } - runSourceText(sourceText: string, filename: string): ?any { + runScript(script: Script): ?any { if (this.global) { - return this.global.eval(sourceText + '\n//# sourceURL=' + filename); + return require('jsdom').evalVMScript(this.global, script); } return null; } diff --git a/packages/jest-environment-node/.npmignore b/packages/jest-environment-node/.npmignore index e31125afcece..85e48fe7b0a4 100644 --- a/packages/jest-environment-node/.npmignore +++ b/packages/jest-environment-node/.npmignore @@ -1,3 +1,3 @@ -__tests__ -__mocks__ +**/__mocks__/** +**/__tests__/** src diff --git a/packages/jest-environment-node/package.json b/packages/jest-environment-node/package.json index edb08db03dbb..66bba13e8175 100644 --- a/packages/jest-environment-node/package.json +++ b/packages/jest-environment-node/package.json @@ -11,7 +11,8 @@ "jest-util": "^13.2.2" }, "jest": { - "rootDir": "./build", + "rootDir": "./src", + "scriptPreprocessor": "../../babel-jest", "testEnvironment": "node" }, "scripts": { diff --git a/packages/jest-environment-node/src/index.js b/packages/jest-environment-node/src/index.js index 36560a7e9bb6..863a6dd1922c 100644 --- a/packages/jest-environment-node/src/index.js +++ b/packages/jest-environment-node/src/index.js @@ -11,6 +11,7 @@ import type {Config} from 'types/Config'; import type {Global} from 'types/Global'; +import type {Script} from 'vm'; const FakeTimers = require('jest-util').FakeTimers; const installCommonGlobals = require('jest-util').installCommonGlobals; @@ -46,12 +47,9 @@ class NodeEnvironment { this.fakeTimers = null; } - runSourceText(sourceText: string, filename: string): ?any { + runScript(script: Script): ?any { if (this.global) { - return vm.runInContext(sourceText, this.global, { - filename, - displayErrors: false, - }); + return script.runInContext(this.global); } return null; } diff --git a/packages/jest-haste-map/.npmignore b/packages/jest-haste-map/.npmignore index e31125afcece..85e48fe7b0a4 100644 --- a/packages/jest-haste-map/.npmignore +++ b/packages/jest-haste-map/.npmignore @@ -1,3 +1,3 @@ -__tests__ -__mocks__ +**/__mocks__/** +**/__tests__/** src diff --git a/packages/jest-haste-map/package.json b/packages/jest-haste-map/package.json index cd281d40ad42..24419392b956 100644 --- a/packages/jest-haste-map/package.json +++ b/packages/jest-haste-map/package.json @@ -13,7 +13,8 @@ "worker-farm": "^1.3.1" }, "jest": { - "rootDir": "./build", + "rootDir": "./src", + "scriptPreprocessor": "../../babel-jest", "testEnvironment": "node" }, "scripts": { diff --git a/packages/jest-haste-map/src/__tests__/index-test.js b/packages/jest-haste-map/src/__tests__/index-test.js index 57363478e81d..3f018b98d24d 100644 --- a/packages/jest-haste-map/src/__tests__/index-test.js +++ b/packages/jest-haste-map/src/__tests__/index-test.js @@ -36,7 +36,7 @@ jest.mock('../crawlers/watchman', () => jest.fn((roots, extensions, ignore, data) => { data.clocks = mockClocks; - const list = changedFiles || mockFs; + const list = mockChangedFiles || mockFs; for (const file in list) { if (new RegExp(roots.join('|')).test(file) && !ignore(file)) { if (list[file]) { @@ -52,18 +52,18 @@ jest.mock('../crawlers/watchman', () => ); const cacheFilePath = '/cache-file'; -let H; -let HasteMap; -let changedFiles; let consoleWarn; let defaultConfig; let fs; +let H; +let HasteMap; +let mockChangedFiles; let mockClocks; let mockFs; let object; let readFileSync; -let writeFileSync; let workerFarmMock; +let writeFileSync; describe('HasteMap', () => { @@ -108,18 +108,18 @@ describe('HasteMap', () => { '/vegetables': 'c:fake-clock:2', }); - changedFiles = null; + mockChangedFiles = null; fs = require('graceful-fs'); readFileSync = fs.readFileSync; writeFileSync = fs.writeFileSync; fs.readFileSync = jest.fn((path, options) => { - expect(options).toBe('utf-8'); + expect(options).toBe('utf8'); // A file change can be triggered by writing into the - // changedFiles object. - if (changedFiles && (path in changedFiles)) { - return changedFiles[path]; + // mockChangedFiles object. + if (mockChangedFiles && (path in mockChangedFiles)) { + return mockChangedFiles[path]; } if (mockFs[path]) { @@ -129,7 +129,7 @@ describe('HasteMap', () => { throw new Error(`Cannot read path '${path}'.`); }); fs.writeFileSync = jest.fn((path, data, options) => { - expect(options).toBe('utf-8'); + expect(options).toBe('utf8'); mockFs[path] = data; }); @@ -354,7 +354,7 @@ describe('HasteMap', () => { fs.readFileSync.mockClear(); // Explicitly mock that no files have changed. - changedFiles = Object.create(null); + mockChangedFiles = Object.create(null); // Watchman would give us different clocks. mockClocks = object({ @@ -364,7 +364,7 @@ describe('HasteMap', () => { return new HasteMap(defaultConfig).build().then(data => { expect(fs.readFileSync.mock.calls.length).toBe(1); - expect(fs.readFileSync).toBeCalledWith(cacheFilePath, 'utf-8'); + expect(fs.readFileSync).toBeCalledWith(cacheFilePath, 'utf8'); expect(data.clocks).toEqual(mockClocks); expect(data.files).toEqual(initialData.files); @@ -378,7 +378,7 @@ describe('HasteMap', () => { fs.readFileSync.mockClear(); // Let's assume one JS file has changed. - changedFiles = object({ + mockChangedFiles = object({ '/fruits/banana.js': [ '/**', ' * @providesModule Kiwi', // Identity crisis. @@ -396,8 +396,8 @@ describe('HasteMap', () => { return new HasteMap(defaultConfig).build().then(data => { expect(fs.readFileSync.mock.calls.length).toBe(2); - expect(fs.readFileSync).toBeCalledWith(cacheFilePath, 'utf-8'); - expect(fs.readFileSync).toBeCalledWith('/fruits/banana.js', 'utf-8'); + expect(fs.readFileSync).toBeCalledWith(cacheFilePath, 'utf8'); + expect(fs.readFileSync).toBeCalledWith('/fruits/banana.js', 'utf8'); expect(data.clocks).toEqual(mockClocks); @@ -421,7 +421,7 @@ describe('HasteMap', () => { // Let's assume one JS file was removed. delete mockFs['/fruits/banana.js']; - changedFiles = object({ + mockChangedFiles = object({ '/fruits/banana.js': null, }); diff --git a/packages/jest-haste-map/src/__tests__/worker-test.js b/packages/jest-haste-map/src/__tests__/worker-test.js index 740f546f8af1..13e1b1838582 100644 --- a/packages/jest-haste-map/src/__tests__/worker-test.js +++ b/packages/jest-haste-map/src/__tests__/worker-test.js @@ -58,7 +58,7 @@ describe('worker', () => { fs = require('graceful-fs'); readFileSync = fs.readFileSync; fs.readFileSync = jest.fn((path, options) => { - expect(options).toBe('utf-8'); + expect(options).toBe('utf8'); if (mockFs[path]) { return mockFs[path]; diff --git a/packages/jest-haste-map/src/index.js b/packages/jest-haste-map/src/index.js index 5357fc55c28e..36c3c46b3bfb 100644 --- a/packages/jest-haste-map/src/index.js +++ b/packages/jest-haste-map/src/index.js @@ -265,7 +265,7 @@ class HasteMap { * 1. read data from the cache or create an empty structure. */ read(): HasteMapObject { - return this._parse(fs.readFileSync(this._cachePath, 'utf-8')); + return this._parse(fs.readFileSync(this._cachePath, 'utf8')); } /** @@ -368,7 +368,7 @@ class HasteMap { * 4. serialize the new `HasteMap` in a cache file. */ _persist(hasteMap: HasteMapObject): HasteMapObject { - fs.writeFileSync(this._cachePath, JSON.stringify(hasteMap), 'utf-8'); + fs.writeFileSync(this._cachePath, JSON.stringify(hasteMap), 'utf8'); return hasteMap; } diff --git a/packages/jest-haste-map/src/worker.js b/packages/jest-haste-map/src/worker.js index 7f2b7a2801e7..84afbdb1db0f 100644 --- a/packages/jest-haste-map/src/worker.js +++ b/packages/jest-haste-map/src/worker.js @@ -44,7 +44,7 @@ const formatError = (error: string|Error): Error => { module.exports = (data: WorkerMessage, callback: WorkerCallback): void => { try { const filePath = data.filePath; - const content = fs.readFileSync(filePath, 'utf-8'); + const content = fs.readFileSync(filePath, 'utf8'); let module; let id; let dependencies; diff --git a/packages/jest-jasmine1/.npmignore b/packages/jest-jasmine1/.npmignore index e31125afcece..85e48fe7b0a4 100644 --- a/packages/jest-jasmine1/.npmignore +++ b/packages/jest-jasmine1/.npmignore @@ -1,3 +1,3 @@ -__tests__ -__mocks__ +**/__mocks__/** +**/__tests__/** src diff --git a/packages/jest-jasmine1/package.json b/packages/jest-jasmine1/package.json index fe029a740747..5bf47b973ba6 100644 --- a/packages/jest-jasmine1/package.json +++ b/packages/jest-jasmine1/package.json @@ -12,7 +12,8 @@ "jest-util": "^13.2.2" }, "jest": { - "rootDir": "./build", + "rootDir": "./src", + "scriptPreprocessor": "../../babel-jest", "testEnvironment": "node" }, "scripts": { diff --git a/packages/jest-jasmine1/src/index.js b/packages/jest-jasmine1/src/index.js index 6a1c5e9567bf..4d6fbb24083d 100644 --- a/packages/jest-jasmine1/src/index.js +++ b/packages/jest-jasmine1/src/index.js @@ -7,15 +7,27 @@ */ 'use strict'; +const JasmineReporter = require('./reporter'); + const fs = require('graceful-fs'); const jasminePit = require('./jasmine-pit'); -const JasmineReporter = require('./reporter'); +const vm = require('vm'); const JASMINE_PATH = require.resolve('../vendor/jasmine-1.3.0'); const JASMINE_ONLY_PATH = require.resolve('./jasmine-only.js'); -const jasmineFileContent = fs.readFileSync(JASMINE_PATH, 'utf8'); -const jasmineOnlyContent = fs.readFileSync(JASMINE_ONLY_PATH, 'utf8'); +const jasmineScript = new vm.Script(fs.readFileSync(JASMINE_PATH, 'utf8'), { + displayErrors: true, + filename: JASMINE_PATH, +}); + +const jasmineOnlyScript = new vm.Script( + fs.readFileSync(JASMINE_ONLY_PATH, 'utf8'), + { + displayErrors: true, + filename: JASMINE_ONLY_PATH, + }, +); function isSpyLike(test) { return test.calls !== undefined; @@ -100,9 +112,9 @@ function jasmine1(config, environment, moduleLoader, testPath) { // To account for this conflict, we set up jasmine in an environment with real // timers (instead of mock timers). environment.fakeTimers.runWithRealTimers(() => { - environment.runSourceText(jasmineFileContent, JASMINE_PATH); + environment.runScript(jasmineScript); jasminePit.install(environment.global); - environment.runSourceText(jasmineOnlyContent); + environment.runScript(jasmineOnlyScript); const _comparedObjects = new WeakMap(); environment.global.jasmine.Env.prototype.compareObjects_ = diff --git a/packages/jest-jasmine2/.npmignore b/packages/jest-jasmine2/.npmignore index e31125afcece..85e48fe7b0a4 100644 --- a/packages/jest-jasmine2/.npmignore +++ b/packages/jest-jasmine2/.npmignore @@ -1,3 +1,3 @@ -__tests__ -__mocks__ +**/__mocks__/** +**/__tests__/** src diff --git a/packages/jest-jasmine2/package.json b/packages/jest-jasmine2/package.json index ba4eeb313a44..18cdcf6813e0 100644 --- a/packages/jest-jasmine2/package.json +++ b/packages/jest-jasmine2/package.json @@ -14,7 +14,8 @@ "jest-util": "^13.2.2" }, "jest": { - "rootDir": "./build", + "rootDir": "./src", + "scriptPreprocessor": "../../babel-jest", "testEnvironment": "node" }, "scripts": { diff --git a/packages/jest-jasmine2/src/index.js b/packages/jest-jasmine2/src/index.js index 5318b96aeb86..d5b2b9e949d4 100644 --- a/packages/jest-jasmine2/src/index.js +++ b/packages/jest-jasmine2/src/index.js @@ -14,17 +14,22 @@ import type {Environment} from 'types/Environment'; import type {TestResult} from 'types/TestResult'; import type Runtime from '../../jest-runtime/src'; +const JasmineReporter = require('./reporter'); + const fs = require('graceful-fs'); const jasminePit = require('./jasmine-pit'); const snapshot = require('jest-snapshot'); -const JasmineReporter = require('./reporter'); +const vm = require('vm'); const CALL_PRINT_LIMIT = 3; const LAST_CALL_PRINT_LIMIT = 1; const JASMINE_PATH = require.resolve('../vendor/jasmine-2.4.1.js'); const JASMINE_CHECK_PATH = require.resolve('./jasmine-check'); -const jasmineFileContent = - fs.readFileSync(require.resolve(JASMINE_PATH), 'utf8'); + +const jasmineScript = new vm.Script(fs.readFileSync(JASMINE_PATH, 'utf8'), { + displayErrors: true, + filename: JASMINE_PATH, +}); function isSpyLike(test) { return test.calls && test.calls.all !== undefined; @@ -62,7 +67,7 @@ function jasmine2( // To account for this conflict, we set up jasmine in an environment with real // timers (instead of mock timers). environment.fakeTimers.runWithRealTimers(() => { - environment.runSourceText(jasmineFileContent, JASMINE_PATH); + environment.runScript(jasmineScript); const requireJasmine = environment.global.jasmineRequire; jasmine = requireJasmine.core(requireJasmine); diff --git a/packages/jest-mock/.npmignore b/packages/jest-mock/.npmignore index e31125afcece..85e48fe7b0a4 100644 --- a/packages/jest-mock/.npmignore +++ b/packages/jest-mock/.npmignore @@ -1,3 +1,3 @@ -__tests__ -__mocks__ +**/__mocks__/** +**/__tests__/** src diff --git a/packages/jest-mock/package.json b/packages/jest-mock/package.json index d54381051133..534effb33808 100644 --- a/packages/jest-mock/package.json +++ b/packages/jest-mock/package.json @@ -8,7 +8,8 @@ "license": "BSD-3-Clause", "main": "build/index.js", "jest": { - "rootDir": "./build", + "rootDir": "./src", + "scriptPreprocessor": "../../babel-jest", "testEnvironment": "node" }, "scripts": { diff --git a/packages/jest-repl/.npmignore b/packages/jest-repl/.npmignore index 47a78158e488..85e48fe7b0a4 100644 --- a/packages/jest-repl/.npmignore +++ b/packages/jest-repl/.npmignore @@ -1,3 +1,3 @@ -__mocks__/ -__tests__/ +**/__mocks__/** +**/__tests__/** src diff --git a/packages/jest-repl/package.json b/packages/jest-repl/package.json index 17a3b518cb5a..a4648af94ec8 100644 --- a/packages/jest-repl/package.json +++ b/packages/jest-repl/package.json @@ -18,11 +18,9 @@ }, "jest": { "automock": false, - "rootDir": "./build", - "testEnvironment": "jest-environment-node", - "testPathIgnorePatterns": [ - "/__tests__/[^/]*/.+" - ] + "rootDir": "./src", + "scriptPreprocessor": "../../babel-jest", + "testEnvironment": "node" }, "scripts": { "test": "../../packages/jest-cli/bin/jest.js" diff --git a/packages/jest-resolve/.npmignore b/packages/jest-resolve/.npmignore index e31125afcece..85e48fe7b0a4 100644 --- a/packages/jest-resolve/.npmignore +++ b/packages/jest-resolve/.npmignore @@ -1,3 +1,3 @@ -__tests__ -__mocks__ +**/__mocks__/** +**/__tests__/** src diff --git a/packages/jest-resolve/package.json b/packages/jest-resolve/package.json index a2f284d813be..36c744abd470 100644 --- a/packages/jest-resolve/package.json +++ b/packages/jest-resolve/package.json @@ -13,7 +13,8 @@ "resolve": "^1.1.6" }, "jest": { - "rootDir": "./build", + "rootDir": "./src", + "scriptPreprocessor": "../../babel-jest", "testEnvironment": "node" }, "scripts": { diff --git a/packages/jest-runtime/.npmignore b/packages/jest-runtime/.npmignore index 47a78158e488..85e48fe7b0a4 100644 --- a/packages/jest-runtime/.npmignore +++ b/packages/jest-runtime/.npmignore @@ -1,3 +1,3 @@ -__mocks__/ -__tests__/ +**/__mocks__/** +**/__tests__/** src diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index 6ff1ea8d117f..a57cb136ee95 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -10,7 +10,6 @@ "dependencies": { "graceful-fs": "^4.1.3", "jest-config": "^13.2.3", - "jest-environment-jsdom": "^13.2.2", "jest-haste-map": "^13.2.2", "jest-mock": "^13.2.2", "jest-resolve": "^13.2.2", @@ -23,11 +22,14 @@ "jest-runtime": "./bin/jest-runtime.js" }, "devDependencies": { - "jest-config": "^13.2.3" + "jest-config": "^13.2.3", + "jest-environment-node": "^13.2.2" }, "jest": { "automock": false, - "rootDir": "./build", + "rootDir": "./src", + "scriptPreprocessor": "../../babel-jest", + "setupTestFrameworkScriptFile": "/__mocks__/env.js", "testEnvironment": "jest-environment-node", "testPathIgnorePatterns": [ "/__tests__/[^/]*/.+" diff --git a/packages/jest-runtime/src/__mocks__/createRuntime.js b/packages/jest-runtime/src/__mocks__/createRuntime.js index efdb26137aae..47afec42f2c2 100644 --- a/packages/jest-runtime/src/__mocks__/createRuntime.js +++ b/packages/jest-runtime/src/__mocks__/createRuntime.js @@ -9,7 +9,7 @@ const path = require('path'); module.exports = function createRuntime(filename, config) { - const JSDOMEnvironment = require('jest-environment-jsdom'); + const NodeEnvironment = require('jest-environment-node'); const Runtime = require('../'); const createHasteMap = require('jest-haste-map').create; @@ -21,7 +21,7 @@ module.exports = function createRuntime(filename, config) { rootDir: path.resolve(path.dirname(filename), 'test_root'), }, config)); - const environment = new JSDOMEnvironment(config); + const environment = new NodeEnvironment(config); return createHasteMap(config, {resetCache: false, maxWorkers: 1}) .build() .then(moduleMap => { diff --git a/packages/jest-runtime/src/__mocks__/env.js b/packages/jest-runtime/src/__mocks__/env.js new file mode 100644 index 000000000000..4d6c4ae94253 --- /dev/null +++ b/packages/jest-runtime/src/__mocks__/env.js @@ -0,0 +1,10 @@ +/** + * 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. + */ +'use strict'; + +jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000; diff --git a/packages/jest-runtime/src/__mocks__/jest-environment-jsdom.js b/packages/jest-runtime/src/__mocks__/jest-environment-jsdom.js deleted file mode 100644 index 7cf8b67e2632..000000000000 --- a/packages/jest-runtime/src/__mocks__/jest-environment-jsdom.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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. - */ -'use strict'; - -const vm = require.requireActual('vm'); - -const JSDOMEnvironment = jest.genMockFromModule('jest-environment-jsdom'); - -JSDOMEnvironment.mockImplementation(function(config) { - this.global = { - console: {}, - mockClearTimers: jest.genMockFn(), - JSON, - }; - - const globalValues = Object.assign({}, config.globals); - for (const customGlobalKey in globalValues) { - this.global[customGlobalKey] = globalValues[customGlobalKey]; - } -}); - -JSDOMEnvironment.prototype.runSourceText.mockImplementation( - function(sourceText, filename) { - return vm.runInNewContext(sourceText, this.global, { - filename, - displayErrors: false, - }); - }, -); - -module.exports = JSDOMEnvironment; diff --git a/packages/jest-runtime/src/__tests__/Resolve-browser-test.js b/packages/jest-runtime/src/__tests__/Resolve-browser-test.js index 6e0c9d1ba684..04bba0275fbb 100644 --- a/packages/jest-runtime/src/__tests__/Resolve-browser-test.js +++ b/packages/jest-runtime/src/__tests__/Resolve-browser-test.js @@ -9,12 +9,6 @@ */ 'use strict'; -jest.disableAutomock(); -jest.mock( - 'jest-environment-jsdom', - () => require('../__mocks__/jest-environment-jsdom'), -); - let createRuntime; describe('resolve', () => { diff --git a/packages/jest-runtime/src/__tests__/Runtime-NODE_PATH-test.js b/packages/jest-runtime/src/__tests__/Runtime-NODE_PATH-test.js index 448dcd4e649d..7b7b4031a8c5 100644 --- a/packages/jest-runtime/src/__tests__/Runtime-NODE_PATH-test.js +++ b/packages/jest-runtime/src/__tests__/Runtime-NODE_PATH-test.js @@ -9,12 +9,6 @@ */ 'use strict'; -jest.disableAutomock(); -jest.mock( - 'jest-environment-jsdom', - () => require('../__mocks__/jest-environment-jsdom'), -); - const path = require('path'); const cwd = process.cwd(); diff --git a/packages/jest-runtime/src/__tests__/Runtime-cli-test.js b/packages/jest-runtime/src/__tests__/Runtime-cli-test.js index 6db9df9eea90..b2c8e3678673 100644 --- a/packages/jest-runtime/src/__tests__/Runtime-cli-test.js +++ b/packages/jest-runtime/src/__tests__/Runtime-cli-test.js @@ -14,8 +14,6 @@ const path = require('path'); const JEST_RUNTIME = path.resolve(__dirname, '../../bin/jest-runtime.js'); -jest.disableAutomock(); - describe('Runtime', () => { describe('cli', () => { it('fails with no path', () => { diff --git a/packages/jest-runtime/src/__tests__/Runtime-currentTestPath-test.js b/packages/jest-runtime/src/__tests__/Runtime-currentTestPath-test.js index efb6411d3660..b3a51f05eee7 100644 --- a/packages/jest-runtime/src/__tests__/Runtime-currentTestPath-test.js +++ b/packages/jest-runtime/src/__tests__/Runtime-currentTestPath-test.js @@ -9,8 +9,6 @@ */ 'use strict'; -jest.disableAutomock(); - describe('Runtime', () => { describe('currentTestPath', () => { it('makes the current test path available', () => { diff --git a/packages/jest-runtime/src/__tests__/Runtime-jsdom-env-test.js b/packages/jest-runtime/src/__tests__/Runtime-env-error-test.js similarity index 96% rename from packages/jest-runtime/src/__tests__/Runtime-jsdom-env-test.js rename to packages/jest-runtime/src/__tests__/Runtime-env-error-test.js index efab916d496d..26c998b22e00 100644 --- a/packages/jest-runtime/src/__tests__/Runtime-jsdom-env-test.js +++ b/packages/jest-runtime/src/__tests__/Runtime-env-error-test.js @@ -9,10 +9,6 @@ */ 'use strict'; -jest.disableAutomock(); - -jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000; - let createRuntime; describe('Runtime', () => { diff --git a/packages/jest-runtime/src/__tests__/Runtime-genMockFromModule-test.js b/packages/jest-runtime/src/__tests__/Runtime-genMockFromModule-test.js index 91260ff0e252..5bd7e9f5ddbf 100644 --- a/packages/jest-runtime/src/__tests__/Runtime-genMockFromModule-test.js +++ b/packages/jest-runtime/src/__tests__/Runtime-genMockFromModule-test.js @@ -9,12 +9,6 @@ */ 'use strict'; -jest.disableAutomock(); -jest.mock( - 'jest-environment-jsdom', - () => require('../__mocks__/jest-environment-jsdom'), -); - let createRuntime; describe('Runtime', () => { diff --git a/packages/jest-runtime/src/__tests__/Runtime-getTestEnvData-test.js b/packages/jest-runtime/src/__tests__/Runtime-getTestEnvData-test.js index 5d26b9317c58..cee91885a5be 100644 --- a/packages/jest-runtime/src/__tests__/Runtime-getTestEnvData-test.js +++ b/packages/jest-runtime/src/__tests__/Runtime-getTestEnvData-test.js @@ -9,12 +9,6 @@ */ 'use strict'; -jest.disableAutomock(); -jest.mock( - 'jest-environment-jsdom', - () => require('../__mocks__/jest-environment-jsdom'), -); - const config = { testEnvData: { someTestData: 42, diff --git a/packages/jest-runtime/src/__tests__/Runtime-jest-fn.js b/packages/jest-runtime/src/__tests__/Runtime-jest-fn.js index f62fd37ccce2..38807b9706d5 100644 --- a/packages/jest-runtime/src/__tests__/Runtime-jest-fn.js +++ b/packages/jest-runtime/src/__tests__/Runtime-jest-fn.js @@ -9,12 +9,6 @@ */ 'use strict'; -jest.disableAutomock(); -jest.mock( - 'jest-environment-jsdom', - () => require('../__mocks__/jest-environment-jsdom'), -); - let createRuntime; describe('Runtime', () => { diff --git a/packages/jest-runtime/src/__tests__/Runtime-mock-test.js b/packages/jest-runtime/src/__tests__/Runtime-mock-test.js index 30cb710f8e67..ffd54a819252 100644 --- a/packages/jest-runtime/src/__tests__/Runtime-mock-test.js +++ b/packages/jest-runtime/src/__tests__/Runtime-mock-test.js @@ -9,12 +9,6 @@ */ 'use strict'; -jest.disableAutomock(); -jest.mock( - 'jest-environment-jsdom', - () => require('../__mocks__/jest-environment-jsdom'), -); - let createRuntime; describe('Runtime', () => { diff --git a/packages/jest-runtime/src/__tests__/Runtime-moduleDirectories-test.js b/packages/jest-runtime/src/__tests__/Runtime-moduleDirectories-test.js index 6eab7d286a9a..567a9082c4c2 100644 --- a/packages/jest-runtime/src/__tests__/Runtime-moduleDirectories-test.js +++ b/packages/jest-runtime/src/__tests__/Runtime-moduleDirectories-test.js @@ -9,12 +9,6 @@ */ 'use strict'; -jest.disableAutomock(); -jest.mock( - 'jest-environment-jsdom', - () => require('../__mocks__/jest-environment-jsdom'), -); - const path = require('path'); const moduleDirectories = ['module_dir']; diff --git a/packages/jest-runtime/src/__tests__/Runtime-requireMock-test.js b/packages/jest-runtime/src/__tests__/Runtime-requireMock-test.js index f9cb59100a0d..04a109dcc089 100644 --- a/packages/jest-runtime/src/__tests__/Runtime-requireMock-test.js +++ b/packages/jest-runtime/src/__tests__/Runtime-requireMock-test.js @@ -9,12 +9,6 @@ */ 'use strict'; -jest.disableAutomock(); -jest.mock( - 'jest-environment-jsdom', - () => require('../__mocks__/jest-environment-jsdom'), -); - let createRuntime; describe('Runtime', () => { diff --git a/packages/jest-runtime/src/__tests__/Runtime-requireModule-test.js b/packages/jest-runtime/src/__tests__/Runtime-requireModule-test.js index d8f7a9b90d35..0c71be84acd8 100644 --- a/packages/jest-runtime/src/__tests__/Runtime-requireModule-test.js +++ b/packages/jest-runtime/src/__tests__/Runtime-requireModule-test.js @@ -9,12 +9,6 @@ */ 'use strict'; -jest.disableAutomock(); -jest.mock( - 'jest-environment-jsdom', - () => require('../__mocks__/jest-environment-jsdom'), -); - const path = require('path'); let createRuntime; diff --git a/packages/jest-runtime/src/__tests__/Runtime-requireModuleOrMock-test.js b/packages/jest-runtime/src/__tests__/Runtime-requireModuleOrMock-test.js index 97c59e1fb7a8..33a2ede9c1b0 100644 --- a/packages/jest-runtime/src/__tests__/Runtime-requireModuleOrMock-test.js +++ b/packages/jest-runtime/src/__tests__/Runtime-requireModuleOrMock-test.js @@ -9,12 +9,6 @@ */ 'use strict'; -jest.disableAutomock(); -jest.mock( - 'jest-environment-jsdom', - () => require('../__mocks__/jest-environment-jsdom'), -); - let createRuntime; describe('Runtime', () => { diff --git a/packages/jest-runtime/src/__tests__/__snapshots__/transform-test.js.snap b/packages/jest-runtime/src/__tests__/__snapshots__/transform-test.js.snap new file mode 100644 index 000000000000..b5d5b862c7e2 --- /dev/null +++ b/packages/jest-runtime/src/__tests__/__snapshots__/transform-test.js.snap @@ -0,0 +1,29 @@ +exports[`transform transforms a file properly 0`] = ` +"({"Object.":function(module,exports,require,__dirname,__filename,global,jest,$JEST){module.exports = "banana"; +}});" +`; + +exports[`transform transforms a file properly 1`] = ` +"({"Object.":function(module,exports,require,__dirname,__filename,global,jest,$JEST){instrumented kiwi +}});" +`; + +exports[`transform uses the supplied preprocessor 0`] = ` +"({\"Object.\":function(module,exports,require,__dirname,__filename,global,jest,$JEST){ +Script: module.exports = \"banana\"; +Path: /fruits/banana.js +Config: { + \"cache\": true, + \"cacheDirectory\": \"/cache/\", + \"preprocessorIgnorePatterns\": [ + \"/node_modules/\" + ], + \"scriptPreprocessor\": \"test-preprocessor\" +} +}});" +`; + +exports[`transform uses the supplied preprocessor 1`] = ` +"({"Object.":function(module,exports,require,__dirname,__filename,global,jest,$JEST){module.exports = "react" +}});" +`; diff --git a/packages/jest-runtime/src/__tests__/test_root/haste-modules/FooRenderUtil.js b/packages/jest-runtime/src/__tests__/test_root/haste-modules/FooRenderUtil.js index b27fcc46ee92..4ebabc194de8 100644 --- a/packages/jest-runtime/src/__tests__/test_root/haste-modules/FooRenderUtil.js +++ b/packages/jest-runtime/src/__tests__/test_root/haste-modules/FooRenderUtil.js @@ -11,11 +11,11 @@ 'use strict'; module.exports = { - getHeaderHeight(): number { + getHeaderHeight() { return 5; }, - getBodyHeight(): number { + getBodyHeight() { return 5; }, }; diff --git a/packages/jest-runtime/src/__tests__/transform-test.js b/packages/jest-runtime/src/__tests__/transform-test.js new file mode 100644 index 000000000000..d48125fb3d3b --- /dev/null +++ b/packages/jest-runtime/src/__tests__/transform-test.js @@ -0,0 +1,164 @@ +/** + * 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. + * + * @emails oncall+jsinfra + */ +'use strict'; + +jest + .mock('graceful-fs') + .mock('jest-resolve') + .mock('jest-util') + .mock('vm'); + +jest.mock( + 'test-preprocessor', + () => { + const stableStringify = require('json-stable-stringify'); + return { + getCacheKey: jest.fn((content, filename, configStr) => 'ab'), + process: (content, filename, config) => { + return ( + `\nScript: ${content}\n` + + `Path: ${filename}\n` + + `Config: ${stableStringify(config, {space: 2})}` + ); + }, + }; + }, + {virtual: true}, +); + +const getCachePath = (fs, config) => { + for (const path in mockFs) { + if (path.startsWith(config.cacheDirectory)) { + return path; + } + } + return null; +}; + +let config; +let fs; +let mockFs; +let object; +let transform; +let vm; + +describe('transform', () => { + + const reset = () => { + object = data => Object.assign(Object.create(null), data); + + vm = require('vm'); + + mockFs = object({ + '/fruits/banana.js': [ + 'module.exports = "banana";', + ].join('\n'), + '/fruits/kiwi.js': [ + 'module.exports = () => "kiwi"', + ].join('\n'), + '/node_modules/react.js': [ + 'module.exports = "react"', + ].join('\n'), + }); + + fs = require('graceful-fs'); + fs.readFileSync = jest.fn((path, options) => { + expect(options).toBe('utf8'); + + if (mockFs[path]) { + return mockFs[path]; + } + + throw new Error(`Cannot read path '${path}'.`); + }); + fs.writeFileSync = jest.fn((path, data, options) => { + expect(options).toBe('utf8'); + mockFs[path] = data; + }); + + fs.unlinkSync = jest.fn(); + fs.statSync = jest.fn(path => ({ + isFile: () => !!mockFs[path], + mtime: {getTime: () => 42}, + })); + + require('jest-resolve').fileExists = jest.fn(path => !!mockFs[path]); + + config = { + cache: true, + cacheDirectory: '/cache/', + preprocessorIgnorePatterns: ['/node_modules/'], + }; + + transform = require('../transform'); + }; + + beforeEach(reset); + + it('transforms a file properly', () => { + const response = transform('/fruits/banana.js', config); + + expect(response instanceof vm.Script); + expect(vm.Script.mock.calls[0][0]).toMatchSnapshot(); + + // no-cache case + expect(fs.readFileSync.mock.calls.length).toBe(1); + expect(fs.readFileSync).toBeCalledWith('/fruits/banana.js', 'utf8'); + + // in-memory cache + const response2 = transform('/fruits/banana.js', config); + expect(response2).toBe(response); + + transform('/fruits/kiwi.js', config, { + instrument: () => 'instrumented kiwi', + }); + expect(vm.Script.mock.calls[1][0]).toMatchSnapshot(); + }); + + it('uses the supplied preprocessor', () => { + config = Object.assign(config, { + scriptPreprocessor: 'test-preprocessor', + }); + + transform('/fruits/banana.js', config); + + expect(require('test-preprocessor').getCacheKey).toBeCalled(); + + expect(vm.Script.mock.calls[0][0]).toMatchSnapshot(); + + transform('/node_modules/react.js', config); + expect(vm.Script.mock.calls[1][0]).toMatchSnapshot(); + }); + + it('reads values from the cache', () => { + const transformConfig = Object.assign(config, { + scriptPreprocessor: 'test-preprocessor', + }); + transform('/fruits/banana.js', transformConfig); + + const cachePath = getCachePath(mockFs, config); + expect(fs.writeFileSync).toBeCalled(); + expect(fs.writeFileSync.mock.calls[0][0]).toBe(cachePath); + + // Cache the state in `mockFsCopy` + const mockFsCopy = mockFs; + jest.resetModuleRegistry(); + reset(); + + // Restore the cached fs + mockFs = mockFsCopy; + transform('/fruits/banana.js', transformConfig); + + expect(fs.readFileSync.mock.calls.length).toBe(2); + expect(fs.readFileSync).toBeCalledWith('/fruits/banana.js', 'utf8'); + expect(fs.readFileSync).toBeCalledWith(cachePath, 'utf8'); + expect(fs.writeFileSync).not.toBeCalled(); + }); +}); diff --git a/packages/jest-runtime/src/index.js b/packages/jest-runtime/src/index.js index c273e86b64fc..00c39034812c 100644 --- a/packages/jest-runtime/src/index.js +++ b/packages/jest-runtime/src/index.js @@ -14,13 +14,14 @@ import type {Config, Path} from 'types/Config'; import type {Environment} from 'types/Environment'; import type {HasteResolverContext} from 'types/Runtime'; import type Resolver from '../../jest-resolve/src'; +import type {Script} from 'vm'; const createHasteMap = require('jest-haste-map').create; const createResolver = require('jest-resolve').create; const fs = require('graceful-fs'); const moduleMocker = require('jest-mock'); const path = require('path'); -const transform = require('./lib/transform'); +const transform = require('./transform'); const utils = require('jest-util'); type Module = { @@ -327,38 +328,34 @@ class Runtime { return to ? this._resolver.resolveModule(from, to) : from; } - _execModule(localModule: Module) { - // If the environment was disposed, prevent this module from - // being executed. - if (!this._environment.global) { - return; - } - - const filename = localModule.filename; - const collectors = this._coverageCollectors; + _shouldCollectCoverage(filename: Path) { const collectOnlyFrom = this._config.collectCoverageOnlyFrom; const shouldCollectCoverage = ( (this._config.collectCoverage && !collectOnlyFrom) || (collectOnlyFrom && collectOnlyFrom[filename]) ); - let moduleContent = transform(filename, this._config); - let collectorStore; - if ( + return ( shouldCollectCoverage && !this._coverageRegex.test(filename) && !(this._mocksPattern && this._mocksPattern.test(filename)) && !this._testRegex.test(filename) - ) { - if (!collectors[filename]) { - collectors[filename] = new this._CoverageCollector( - moduleContent, - filename, - ); - } - const collector = collectors[filename]; - collectorStore = collector.getCoverageDataStore(); - moduleContent = collector.getInstrumentedSource('$JEST_COVERAGE_DATA'); + ); + } + + _execModule(localModule: Module) { + // If the environment was disposed, prevent this module from being executed. + if (!this._environment.global) { + return; + } + + const filename = localModule.filename; + const shouldCollectCoverage = this._shouldCollectCoverage(filename); + const collectors = this._coverageCollectors; + if (shouldCollectCoverage && !collectors[filename]) { + collectors[filename] = new this._CoverageCollector(); } + const collectorStore = + shouldCollectCoverage && collectors[filename].getCoverageDataStore(); const lastExecutingModulePath = this._currentlyExecutingModulePath; this._currentlyExecutingModulePath = filename; @@ -371,8 +368,17 @@ class Runtime { localModule.paths = this._resolver.getModulePaths(dirname); localModule.require = this._createRequireImplementation(filename); - const wrapperFunc = this._runSourceText(moduleContent, filename); - wrapperFunc.call( + const script = transform(filename, this._config, { + instrument: shouldCollectCoverage && ( + source => collectors[filename].getInstrumentedSource( + source, + filename, + '$JEST', + ) + ), + }); + const wrapper = this._runScript(script, filename); + wrapper.call( localModule.exports, // module context localModule, // module object localModule.exports, // module exports @@ -388,16 +394,14 @@ class Runtime { this._currentlyExecutingModulePath = lastExecutingModulePath; } - _runSourceText(moduleContent: string, filename: string) { - /* eslint-disable max-len */ - const config = this._config; - const relative = filePath => path.relative(config.rootDir, filePath); - const env = this._environment; - const evalResultVariable = 'Object.'; - const wrapper = '({ "' + evalResultVariable + '": function(module, exports, require, __dirname, __filename, global, jest, $JEST_COVERAGE_DATA) {' + moduleContent + '\n}});'; + _runScript(script: Script, filename: string) { try { - return env.runSourceText(wrapper, filename)[evalResultVariable]; + return this._environment.runScript(script)[ + transform.EVAL_RESULT_VARIABLE + ]; } catch (e) { + const config = this._config; + const relative = filePath => path.relative(config.rootDir, filePath); if (e.constructor.name === 'SyntaxError') { const hasPreprocessor = config.scriptPreprocessor; const preprocessorInfo = hasPreprocessor @@ -407,18 +411,18 @@ class Runtime { ? `Make sure your '.babelrc' is set up correctly, ` + `for example it should include the 'es2015' preset.\n` : ''; + /* eslint-disable max-len */ throw new SyntaxError( `${e.message} in file '${relative(filename)}'.\n\n` + `Make sure your preprocessor is set up correctly and ensure ` + `your 'preprocessorIgnorePatterns' configuration is correct: http://facebook.github.io/jest/docs/api.html#preprocessorignorepatterns-array-string\n` + 'If you are currently setting up Jest or modifying your preprocessor, try `jest --no-cache`.\n' + - `Preprocessor: ${preprocessorInfo}.\n${babelInfo}` + - `Jest tried to the execute the following ${hasPreprocessor ? 'preprocessed ' : ''}code:\n${moduleContent}\n`, + `Preprocessor: ${preprocessorInfo}.\n${babelInfo}`, ); + /* eslint-enable max-len */ } throw e; } - /* eslint-enable max-len */ } _generateMock(from: Path, moduleName: string) { diff --git a/packages/jest-runtime/src/lib/transform.js b/packages/jest-runtime/src/transform.js similarity index 50% rename from packages/jest-runtime/src/lib/transform.js rename to packages/jest-runtime/src/transform.js index 9f8e761aca2b..d2fb1dbe6dca 100644 --- a/packages/jest-runtime/src/lib/transform.js +++ b/packages/jest-runtime/src/transform.js @@ -11,20 +11,29 @@ import type {Config, Path} from 'types/Config'; +const Resolver = require('jest-resolve'); + const createDirectory = require('jest-util').createDirectory; const crypto = require('crypto'); const fs = require('graceful-fs'); const path = require('path'); const stableStringify = require('json-stable-stringify'); - -const cache = new Map(); -const configToJsonMap = new Map(); -const preprocessorRegExpCache = new WeakMap(); +const vm = require('vm'); export type Processor = { - process: (sourceText: string, sourcePath: string) => string, + process: (sourceText: string, sourcePath: Path) => string, +}; + +type TransformOptions = { + instrument: (source: string) => string, }; +const EVAL_RESULT_VARIABLE = 'Object.'; + +const cache = new Map(); +const configToJsonMap = new Map(); +const ignoreCache = new WeakMap(); + const removeFile = (path: Path) => { try { fs.unlinkSync(path); @@ -42,16 +51,14 @@ const getCacheKey = ( // cached output instead of all config options. configToJsonMap.set(config, stableStringify({ cacheDirectory: config.cacheDirectory, + collectCoverage: config.collectCoverage, haste: config.haste, mocksPattern: config.mocksPattern, moduleFileExtensions: config.moduleFileExtensions, moduleNameMapper: config.moduleNameMapper, - modulePathIgnorePatterns: config.modulePathIgnorePatterns, rootDir: config.rootDir, - testDirectoryName: config.testDirectoryName, - testFileExtensions: config.testFileExtensions, testPathDirs: config.testPathDirs, - testPathIgnorePatterns: config.testPathIgnorePatterns, + testRegex: config.testRegex, })); } const configStr = configToJsonMap.get(config); @@ -65,18 +72,22 @@ const getCacheKey = ( } }; -const writeCacheFile = (cachePath: string, fileData: string) => { +const writeCacheFile = (cachePath: Path, fileData: string) => { try { - fs.writeFileSync(cachePath, fileData); + fs.writeFileSync(cachePath, fileData, 'utf8'); } catch (e) { - e.message = 'jest: failed to cache preprocess results in: ' + cachePath; + e.message = 'jest: failed to cache transform results in: ' + cachePath; removeFile(cachePath); throw e; } }; -const readCacheFile = (filePath: string, cachePath: string): ?string => { - if (!fs.existsSync(cachePath)) { +/* eslint-disable max-len */ +const wrap = content => '({"' + EVAL_RESULT_VARIABLE + '":function(module,exports,require,__dirname,__filename,global,jest,$JEST){' + content + '\n}});'; +/* eslint-enable max-len */ + +const readCacheFile = (filePath: Path, cachePath: Path): ?string => { + if (!Resolver.fileExists(cachePath)) { return null; } @@ -84,7 +95,7 @@ const readCacheFile = (filePath: string, cachePath: string): ?string => { try { fileData = fs.readFileSync(cachePath, 'utf8'); } catch (e) { - e.message = 'jest: failed to read preprocess cache file: ' + cachePath; + e.message = 'jest: failed to read cache file: ' + cachePath; removeFile(cachePath); throw e; } @@ -97,32 +108,40 @@ const readCacheFile = (filePath: string, cachePath: string): ?string => { return fileData; }; -module.exports = (filePath: Path, config: Config): string => { - const mtime = fs.statSync(filePath).mtime; - const mapCacheKey = filePath + '_' + mtime.getTime(); +module.exports = ( + filename: Path, + config: Config, + options: ?TransformOptions, +): vm.Script => { + const mtime = fs.statSync(filename).mtime; + const mapCacheKey = filename + '_' + mtime.getTime(); if (cache.has(mapCacheKey)) { - return cache.get(mapCacheKey) || ''; + const content = cache.get(mapCacheKey); + if (content) { + return content; + } } - let fileData = fs.readFileSync(filePath, 'utf8'); + let content = fs.readFileSync(filename, 'utf8'); // If the file data starts with a shebang remove it. Leaves the empty line // to keep stack trace line numbers correct. - if (fileData.startsWith('#!')) { - fileData = fileData.replace(/^#!.*/, ''); + if (content.startsWith('#!')) { + content = content.replace(/^#!.*/, ''); } - if (!preprocessorRegExpCache.has(config)) { - preprocessorRegExpCache.set( + if (!ignoreCache.has(config)) { + ignoreCache.set( config, new RegExp(config.preprocessorIgnorePatterns.join('|')), ); } - const regex = preprocessorRegExpCache.get(config); + if ( config.scriptPreprocessor && ( - !config.preprocessorIgnorePatterns.length || !regex.test(filePath) + !config.preprocessorIgnorePatterns.length || + !ignoreCache.get(config).test(filename) ) ) { // $FlowFixMe @@ -133,29 +152,41 @@ module.exports = (filePath: Path, config: Config): string => { ); } - if (config.cache === true) { - const baseCacheDir = path.join(config.cacheDirectory, 'preprocess-cache'); - const cacheKey = getCacheKey(preprocessor, fileData, filePath, config); - // Create sub folders based on the cacheKey to avoid creating one - // directory with many files. - const cacheDir = path.join(baseCacheDir, cacheKey[0] + cacheKey[1]); - const cachePath = path.join( - cacheDir, - path.basename(filePath, path.extname(filePath)) + '_' + cacheKey, - ); - createDirectory(cacheDir); - const cachedData = readCacheFile(filePath, cachePath); - if (cachedData) { - fileData = cachedData; - } else { - fileData = preprocessor.process(fileData, filePath, config); - writeCacheFile(cachePath, fileData); - } + const baseCacheDir = path.join(config.cacheDirectory, 'preprocess-cache'); + const cacheKey = getCacheKey(preprocessor, content, filename, config); + // Create sub folders based on the cacheKey to avoid creating one + // directory with many files. + const cacheDir = path.join(baseCacheDir, cacheKey[0] + cacheKey[1]); + const cachePath = path.join( + cacheDir, + path.basename(filename, path.extname(filename)) + '_' + cacheKey, + ); + createDirectory(cacheDir); + + const cacheData = config.cache ? readCacheFile(filename, cachePath) : null; + if (cacheData) { + content = cacheData; } else { - fileData = preprocessor.process(fileData, filePath, config); + content = preprocessor.process(content, filename, config); + if (options && options.instrument) { + content = options.instrument(content); + } + content = wrap(content); + writeCacheFile(cachePath, content); } + } else if (options && options.instrument) { + content = wrap(options.instrument(content)); + } else { + content = wrap(content); } - cache.set(mapCacheKey, fileData); - return fileData; + const script = new vm.Script(content, { + displayErrors: true, + filename, + }); + + cache.set(mapCacheKey, script); + return script; }; + +module.exports.EVAL_RESULT_VARIABLE = EVAL_RESULT_VARIABLE; diff --git a/packages/jest-snapshot/.npmignore b/packages/jest-snapshot/.npmignore index e31125afcece..85e48fe7b0a4 100644 --- a/packages/jest-snapshot/.npmignore +++ b/packages/jest-snapshot/.npmignore @@ -1,3 +1,3 @@ -__tests__ -__mocks__ +**/__mocks__/** +**/__tests__/** src diff --git a/packages/jest-snapshot/package.json b/packages/jest-snapshot/package.json index 8e733f7354b8..84ae85aa64c4 100644 --- a/packages/jest-snapshot/package.json +++ b/packages/jest-snapshot/package.json @@ -11,11 +11,12 @@ "jest-util": "^13.2.2", "pretty-format": "^3.4.3" }, - "scripts": { - "test": "../jest-cli/bin/jest.js" - }, "jest": { - "rootDir": "./build", + "rootDir": "./src", + "scriptPreprocessor": "../../babel-jest", "testEnvironment": "node" + }, + "scripts": { + "test": "../jest-cli/bin/jest.js" } } diff --git a/packages/jest-util/.npmignore b/packages/jest-util/.npmignore index e31125afcece..85e48fe7b0a4 100644 --- a/packages/jest-util/.npmignore +++ b/packages/jest-util/.npmignore @@ -1,3 +1,3 @@ -__tests__ -__mocks__ +**/__mocks__/** +**/__tests__/** src diff --git a/packages/jest-util/package.json b/packages/jest-util/package.json index 0a770758c5b1..3af06cd52988 100644 --- a/packages/jest-util/package.json +++ b/packages/jest-util/package.json @@ -18,7 +18,8 @@ "jsdom": "^9.2.1" }, "jest": { - "rootDir": "./build", + "rootDir": "./src", + "scriptPreprocessor": "../../babel-jest", "testEnvironment": "node" }, "scripts": { diff --git a/packages/jest/.npmignore b/packages/jest/.npmignore new file mode 100644 index 000000000000..85e48fe7b0a4 --- /dev/null +++ b/packages/jest/.npmignore @@ -0,0 +1,3 @@ +**/__mocks__/** +**/__tests__/** +src diff --git a/scripts/build.js b/scripts/build.js index a15f26a1c463..6c802544157e 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -33,6 +33,12 @@ const JS_FILES_PATTERN = '**/*.js'; const IGNORE_PATTERN = ''; const PACKAGES_DIR = path.resolve(__dirname, '../packages'); +const babelOptions = JSON.parse(fs.readFileSync( + path.resolve(__dirname, '..', '.babelrc'), + 'utf8' +)); +babelOptions.babelrc = false; + function buildPackage(p) { const srcDir = path.resolve(p, SRC_DIR); const pattern = path.resolve(srcDir, '**/*'); @@ -65,14 +71,7 @@ function buildFile(file) { '\n' ); } else { - const transformed = babel.transformFileSync(file, { - plugins: [ - 'syntax-trailing-function-commas', - 'transform-flow-strip-types', - ], - retainLines: true, - babelrc: false, - }).code; + const transformed = babel.transformFileSync(file, babelOptions).code; spawnSync('mkdir', ['-p', path.dirname(destPath)]); fs.writeFileSync(destPath, transformed); process.stdout.write( diff --git a/scripts/test.js b/scripts/test.js index fee34308638b..8fe9120e0206 100644 --- a/scripts/test.js +++ b/scripts/test.js @@ -28,7 +28,9 @@ const rimraf = require('rimraf'); const EXAMPLES_DIR = path.resolve(__dirname, '../examples'); const INTEGRATION_TESTS_DIR = path.resolve(__dirname, '../integration_tests'); const JEST_CLI_PATH = path.resolve(__dirname, '../packages/jest-cli'); +const VERSION = require('../lerna').version; +const packages = getPackages(); const examples = fs.readdirSync(EXAMPLES_DIR) .map(file => path.resolve(EXAMPLES_DIR, file)) @@ -43,20 +45,30 @@ function runExampleTests(exampleDirectory) { console.log(chalk.bold(chalk.cyan('Testing example: ') + exampleDirectory)); runCommands('npm update', exampleDirectory); - rimraf.sync(path.resolve(exampleDirectory, './node_modules/jest-cli')); - mkdirp.sync(path.resolve(exampleDirectory, './node_modules/jest-cli')); - mkdirp.sync(path.resolve(exampleDirectory, './node_modules/.bin')); - - // Using `npm link jest-cli` can create problems with module resolution, - // so instead of this we'll create an `index.js` file that will export the - // local `jest-cli` package. - fs.writeFileSync( - path.resolve(exampleDirectory, './node_modules/jest-cli/index.js'), - `module.exports = require('${JEST_CLI_PATH}');\n`, // link to the local jest - 'utf8' - ); + packages.forEach(pkg => { + const name = path.basename(pkg); + const directory = path.resolve(exampleDirectory, 'node_modules', name); + + if (fs.existsSync(directory)) { + rimraf.sync(directory); + mkdirp.sync(directory); + // Using `npm link jest-*` can create problems with module resolution, + // so instead of this we'll create a proxy module. + fs.writeFileSync( + path.resolve(directory, 'index.js'), + `module.exports = require('${pkg}');\n`, + 'utf8' + ); + fs.writeFileSync( + path.resolve(directory, 'package.json'), + `{"name": "${name}", "version": "${VERSION}"}\n`, + 'utf8' + ); + } + }); // overwrite the jest link and point it to the local jest-cli + mkdirp.sync(path.resolve(exampleDirectory, './node_modules/.bin')); runCommands( `ln -sf ${JEST_CLI_PATH}/bin/jest.js ./node_modules/.bin/jest`, exampleDirectory @@ -65,8 +77,7 @@ function runExampleTests(exampleDirectory) { runCommands('npm test', exampleDirectory); } - -getPackages().forEach(runPackageTests); +packages.forEach(runPackageTests); if (packagesOnly) { return; diff --git a/scripts/watch.js b/scripts/watch.js index be648a6def1e..06594708f6aa 100644 --- a/scripts/watch.js +++ b/scripts/watch.js @@ -22,6 +22,12 @@ const BUILD_CMD = `node ${path.resolve(__dirname, './build.js')}`; let filesToBuild = new Map(); +const exists = filename => { + try { + return fs.statSync(filename).isFile(); + } catch (e) {} + return false; +}; const rebuild = filename => filesToBuild.set(filename, true); getPackages().forEach(p => { @@ -29,9 +35,25 @@ getPackages().forEach(p => { try { fs.accessSync(srcDir, fs.F_OK); fs.watch(path.resolve(p, 'src'), {recursive: true}, (event, filename) => { - if (event === 'change' || event === 'rename') { + const filePath = path.resolve(srcDir, filename); + + if ( + (event === 'change' || event === 'rename') && + exists(filePath) + ) { console.log(chalk.green('->'), `${event}: ${filename}`); - rebuild(path.resolve(srcDir, filename)); + rebuild(filePath); + } else { + const buildFile = path.resolve(srcDir, '..', 'build', filename); + try { + fs.unlinkSync(buildFile); + process.stdout.write( + chalk.red(' \u2022 ') + + path.relative(path.resolve(srcDir, '..', '..'), buildFile) + + ' (deleted)' + + '\n' + ); + } catch (e) {} } }); } catch (e) { diff --git a/types/Config.js b/types/Config.js index de01da28e707..740fd2728d61 100644 --- a/types/Config.js +++ b/types/Config.js @@ -55,6 +55,7 @@ export type Config = BaseConfig & { cache: boolean, collectCoverageOnlyFrom: {[key: string]: Path}, colors: boolean, + collectCoverage: boolean, coverageThreshold: { global: { [key: string]: number, diff --git a/types/Environment.js b/types/Environment.js index e0dbf3d4e26c..fc8ac500dc8b 100644 --- a/types/Environment.js +++ b/types/Environment.js @@ -11,11 +11,12 @@ import type {Config} from './Config'; import type {Global} from './Global'; +import type {Script} from 'vm'; export type Environment = { constructor(config: Config): void; dispose(): void; - runSourceText(sourceText: string, filename: string): any; + runScript(script: Script): any; global: Global; fakeTimers: { clearAllTimers(): void;