From 4506693cc16a9cbabd77266a28a66b02d13747a0 Mon Sep 17 00:00:00 2001 From: James Ide Date: Mon, 11 May 2015 04:17:03 -0700 Subject: [PATCH 1/2] [io.js] Update jsdom and get jest working on io.js This gets jest working on io.js 1.8.x and 2.0.x. io.js is reconciling with the Node Foundation so users who are still on Node are on the upgrade path that converges with io.js anyway. What this means is the jest version probably should be bumped to 0.5.0, where 0.4.x is a maintenance branch for legacy Node (typically with semver, a major version bump is required but 0.x.x releases are considered a free-for-all). This diff is near the minimal amount of work to bring jest up to date and pass all of its tests. - Upgraded npm dependencies. The major update is jsdom from 0.10.3 to 5.4.3. This lets us drop the indirect contextify dependency. - Removed harmonize, since io.js uses modern V8 and enables stable ES6 features by default - Removed the `--harmony` flag from the TestRunner for the same reason (trivia: i added that line once upon a time, let me delete it) - Replaced jsdom's `run` with io.js's built-in `vm` module - Added support for curried arguments in the fake `nextTick` implementation, which io.js itself relies on - Updated the HasteModuleLoader to handle Node built-in modules before `node_modules` -- this fixes an issue I was getting with graceful-fs trying to require smalloc. - Patched coffeescript to use Array.isArray instead of `instanceof Array`. Working on getting that merged upstream. Also mocked out JSDomEnvironment under __mocks__ instead of doing it in each test. Each test used to implement its own JSDomEnvironment. This diff removes the copy-pasted code and introduces a shared mock under __mocks__. Test Plan: ``` nvm install iojs-1 rm -fr node_modules && npm install nvm install iojs-2 rm -fr node_modules && npm install ``` --- .travis.yml | 3 +- bin/jest.js | 3 -- package.json | 35 +++++++++-------- src/HasteModuleLoader/HasteModuleLoader.js | 21 ++++------ .../HasteModuleLoader-NODE_PATH-test.js | 15 ++------ ...asteModuleLoader-genMockFromModule-test.js | 16 ++------ .../HasteModuleLoader-hasDependency-test.js | 13 ++----- .../HasteModuleLoader-requireMock-test.js | 16 ++------ .../HasteModuleLoader-requireModule-test.js | 17 ++------- ...teModuleLoader-requireModuleOrMock-test.js | 16 ++------ src/JSDomEnvironment.js | 38 +++++++++---------- src/TestRunner.js | 1 - src/__mocks__/JSDomEnvironment.js | 30 +++++++++++++++ src/jasmineTestRunner/JasmineReporter.js | 8 +--- src/jasmineTestRunner/jasmineTestRunner.js | 6 --- src/lib/FakeTimers.js | 14 +++++-- src/lib/utils.js | 14 +++++++ 17 files changed, 123 insertions(+), 143 deletions(-) create mode 100644 src/__mocks__/JSDomEnvironment.js diff --git a/.travis.yml b/.travis.yml index 9144899aff6b..e8536f5a4d7c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: node_js node_js: -- "0.10" +- "iojs-v1" +- "iojs-v2" sudo: false diff --git a/bin/jest.js b/bin/jest.js index 8d0bb3964fa6..d2cc37a75e37 100755 --- a/bin/jest.js +++ b/bin/jest.js @@ -10,7 +10,6 @@ 'use strict'; var fs = require('fs'); -var harmonize = require('harmonize'); var optimist = require('optimist'); var path = require('path'); @@ -36,8 +35,6 @@ function _wrapDesc(desc) { }, ['']).join(indent); } -harmonize(); - var argv = optimist .usage('Usage: $0 [--config=] [TestPathRegExp]') .options({ diff --git a/package.json b/package.json index 3ffd7eb8cbd4..be43a79db52b 100644 --- a/package.json +++ b/package.json @@ -4,32 +4,31 @@ "version": "0.4.13", "main": "src/jest.js", "dependencies": { - "coffee-script": "^1.8.0", - "cover": "~0.2.8", - "diff": "~1.0.4", - "graceful-fs": "^2.0.3", - "harmonize": "1.4.2", - "istanbul": "^0.3.2", - "jasmine-only": "0.1.0", - "jasmine-pit": "~2.0.0", - "jsdom": "~0.10.3", + "coffee-script": "jashkenas/coffeescript", + "cover": "^0.2.9", + "diff": "^1.4.0", + "graceful-fs": "^3.0.8", + "istanbul": "^0.3.15", + "jasmine-only": "^0.1.1", + "jasmine-pit": "^2.0.2", + "jsdom": "^5.4.3", + "lodash.template": "^3.6.1", "node-haste": "^1.2.8", - "node-worker-pool": "~2.4.2", - "optimist": "~0.6.0", - "bluebird": "~2.9.30", - "resolve": "~0.6.1", - "through": "^2.3.4", - "lodash.template": "^3.0.0" + "node-worker-pool": "^2.4.4", + "optimist": "^0.6.1", + "bluebird": "^2.9.30", + "resolve": "^1.1.6", + "through": "^2.3.7" }, "devDependencies": { - "jshint": "~2.5.0", - "node-find-files": "0.0.2" + "jshint": "^2.8.0", + "node-find-files": "0.0.4" }, "bin": { "jest": "./bin/jest.js" }, "engines": { - "node": "0.8.x || 0.10.x" + "node": ">= 1" }, "repository": { "type": "git", diff --git a/src/HasteModuleLoader/HasteModuleLoader.js b/src/HasteModuleLoader/HasteModuleLoader.js index 3489b8c30149..596479912c99 100644 --- a/src/HasteModuleLoader/HasteModuleLoader.js +++ b/src/HasteModuleLoader/HasteModuleLoader.js @@ -132,15 +132,6 @@ function Loader(config, environment, resourceMap) { } if (_configUnmockListRegExpCache === null) { - // Node must have been run with --harmony in order for WeakMap to be - // available prior to version 0.12 - if (typeof WeakMap !== 'function') { - throw new Error( - 'Please run node with the --harmony flag! jest requires WeakMap ' + - 'which is only available with the --harmony flag in node < v0.12' - ); - } - _configUnmockListRegExpCache = new WeakMap(); } @@ -860,14 +851,14 @@ Loader.prototype.requireModule = function(currPath, moduleName, modulePath = manualMockResource.path; } - if (!modulePath) { - modulePath = this._moduleNameToPath(currPath, moduleName); - } - if (NODE_CORE_MODULES[moduleName]) { return require(moduleName); } + if (!modulePath) { + modulePath = this._moduleNameToPath(currPath, moduleName); + } + // Always natively require the jasmine runner. if (modulePath.indexOf(VENDOR_PATH) === 0) { return require(modulePath); @@ -1035,6 +1026,10 @@ Loader.prototype.resetModuleRegistry = function() { this._environment.fakeTimers.runAllTicks(); }.bind(this), + runAllImmediates: function() { + this._environment.fakeTimers.runAllImmediates(); + }.bind(this), + runAllTimers: function() { this._environment.fakeTimers.runAllTimers(); }.bind(this), diff --git a/src/HasteModuleLoader/__tests__/HasteModuleLoader-NODE_PATH-test.js b/src/HasteModuleLoader/__tests__/HasteModuleLoader-NODE_PATH-test.js index 5d4abeb636ae..27b9badc2c31 100644 --- a/src/HasteModuleLoader/__tests__/HasteModuleLoader-NODE_PATH-test.js +++ b/src/HasteModuleLoader/__tests__/HasteModuleLoader-NODE_PATH-test.js @@ -8,6 +8,7 @@ 'use strict'; jest.autoMockOff(); +jest.mock('../../JSDomEnvironment'); var path = require('path'); var Promise = require('bluebird'); @@ -15,7 +16,7 @@ var utils = require('../../lib/utils'); describe('HasteModuleLoader', function() { var HasteModuleLoader; - var mockEnvironment; + var JSDomEnvironment; var resourceMap; var CONFIG = utils.normalizeConfig({ @@ -30,6 +31,7 @@ describe('HasteModuleLoader', function() { return buildLoader(); }); } else { + var mockEnvironment = new JSDomEnvironment(CONFIG); return Promise.resolve( new HasteModuleLoader(CONFIG, mockEnvironment, resourceMap) ); @@ -39,16 +41,7 @@ describe('HasteModuleLoader', function() { function initHasteModuleLoader(nodePath) { process.env.NODE_PATH = nodePath; HasteModuleLoader = require('../HasteModuleLoader'); - mockEnvironment = { - global: { - console: {}, - mockClearTimers: jest.genMockFn() - }, - runSourceText: jest.genMockFn().mockImplementation(function(codeStr) { - /* jshint evil:true */ - return (new Function('return ' + codeStr))(); - }) - }; + JSDomEnvironment = require('../../JSDomEnvironment'); } pit('uses NODE_PATH to find modules', function() { diff --git a/src/HasteModuleLoader/__tests__/HasteModuleLoader-genMockFromModule-test.js b/src/HasteModuleLoader/__tests__/HasteModuleLoader-genMockFromModule-test.js index ea8719e146a3..a7298f3142f1 100644 --- a/src/HasteModuleLoader/__tests__/HasteModuleLoader-genMockFromModule-test.js +++ b/src/HasteModuleLoader/__tests__/HasteModuleLoader-genMockFromModule-test.js @@ -8,6 +8,7 @@ 'use strict'; jest.autoMockOff(); +jest.mock('../../JSDomEnvironment'); var path = require('path'); var Promise = require('bluebird'); @@ -15,7 +16,7 @@ var utils = require('../../lib/utils'); describe('nodeHasteModuleLoader', function() { var HasteModuleLoader; - var mockEnvironment; + var JSDomEnvironment; var resourceMap; var CONFIG = utils.normalizeConfig({ @@ -30,6 +31,7 @@ describe('nodeHasteModuleLoader', function() { return buildLoader(); }); } else { + var mockEnvironment = new JSDomEnvironment(CONFIG); return Promise.resolve( new HasteModuleLoader(CONFIG, mockEnvironment, resourceMap) ); @@ -38,17 +40,7 @@ describe('nodeHasteModuleLoader', function() { beforeEach(function() { HasteModuleLoader = require('../HasteModuleLoader'); - - mockEnvironment = { - global: { - console: {}, - mockClearTimers: jest.genMockFn() - }, - runSourceText: jest.genMockFn().mockImplementation(function(codeStr) { - /* jshint evil:true */ - return (new Function('return ' + codeStr))(); - }) - }; + JSDomEnvironment = require('../../JSDomEnvironment'); }); describe('genMockFromModule', function() { diff --git a/src/HasteModuleLoader/__tests__/HasteModuleLoader-hasDependency-test.js b/src/HasteModuleLoader/__tests__/HasteModuleLoader-hasDependency-test.js index 627a463ff61d..5bacca00bb46 100644 --- a/src/HasteModuleLoader/__tests__/HasteModuleLoader-hasDependency-test.js +++ b/src/HasteModuleLoader/__tests__/HasteModuleLoader-hasDependency-test.js @@ -8,6 +8,7 @@ 'use strict'; jest.autoMockOff(); +jest.mock('../../JSDomEnvironment'); describe('nodeHasteModuleLoader', function() { var HasteModuleLoader; @@ -42,18 +43,10 @@ describe('nodeHasteModuleLoader', function() { } beforeEach(function() { + var JSDomEnvironment = require('../../JSDomEnvironment'); HasteModuleLoader = require('../HasteModuleLoader'); - mockEnvironment = { - global: { - console: {}, - mockClearTimers: jest.genMockFn() - }, - runSourceText: jest.genMockFn().mockImplementation(function(codeStr) { - /* jshint evil: true */ - return (new Function('return ' + codeStr))(); - }) - }; + mockEnvironment = new JSDomEnvironment(mockConfig); resources = {}; }); diff --git a/src/HasteModuleLoader/__tests__/HasteModuleLoader-requireMock-test.js b/src/HasteModuleLoader/__tests__/HasteModuleLoader-requireMock-test.js index b3b658e8d3f0..a2220b88b135 100644 --- a/src/HasteModuleLoader/__tests__/HasteModuleLoader-requireMock-test.js +++ b/src/HasteModuleLoader/__tests__/HasteModuleLoader-requireMock-test.js @@ -8,6 +8,7 @@ 'use strict'; jest.autoMockOff(); +jest.mock('../../JSDomEnvironment'); var path = require('path'); var Promise = require('bluebird'); @@ -15,7 +16,7 @@ var utils = require('../../lib/utils'); describe('HasteModuleLoader', function() { var HasteModuleLoader; - var mockEnvironment; + var JSDomEnvironment; var resourceMap; var CONFIG = utils.normalizeConfig({ @@ -30,6 +31,7 @@ describe('HasteModuleLoader', function() { return buildLoader(); }); } else { + var mockEnvironment = new JSDomEnvironment(CONFIG); return Promise.resolve( new HasteModuleLoader(CONFIG, mockEnvironment, resourceMap) ); @@ -38,17 +40,7 @@ describe('HasteModuleLoader', function() { beforeEach(function() { HasteModuleLoader = require('../HasteModuleLoader'); - - mockEnvironment = { - global: { - console: {}, - mockClearTimers: jest.genMockFn() - }, - runSourceText: jest.genMockFn().mockImplementation(function(codeStr) { - /* jshint evil:true */ - return (new Function('return ' + codeStr))(); - }) - }; + JSDomEnvironment = require('../../JSDomEnvironment'); }); describe('requireMock', function() { diff --git a/src/HasteModuleLoader/__tests__/HasteModuleLoader-requireModule-test.js b/src/HasteModuleLoader/__tests__/HasteModuleLoader-requireModule-test.js index c1ef374b72bf..33fbfc7c461f 100644 --- a/src/HasteModuleLoader/__tests__/HasteModuleLoader-requireModule-test.js +++ b/src/HasteModuleLoader/__tests__/HasteModuleLoader-requireModule-test.js @@ -8,6 +8,7 @@ 'use strict'; jest.autoMockOff(); +jest.mock('../../JSDomEnvironment'); var path = require('path'); var Promise = require('bluebird'); @@ -15,7 +16,7 @@ var utils = require('../../lib/utils'); describe('HasteModuleLoader', function() { var HasteModuleLoader; - var mockEnvironment; + var JSDomEnvironment; var resourceMap; var CONFIG = utils.normalizeConfig({ @@ -30,6 +31,7 @@ describe('HasteModuleLoader', function() { return buildLoader(); }); } else { + var mockEnvironment = new JSDomEnvironment(CONFIG); return Promise.resolve( new HasteModuleLoader(CONFIG, mockEnvironment, resourceMap) ); @@ -38,18 +40,7 @@ describe('HasteModuleLoader', function() { beforeEach(function() { HasteModuleLoader = require('../HasteModuleLoader'); - - mockEnvironment = { - global: { - console: {}, - mockClearTimers: jest.genMockFn(), - JSON: JSON - }, - runSourceText: jest.genMockFn().mockImplementation(function(codeStr) { - /* jshint evil:true */ - return (new Function('return ' + codeStr))(); - }) - }; + JSDomEnvironment = require('../../JSDomEnvironment'); }); describe('requireModule', function() { diff --git a/src/HasteModuleLoader/__tests__/HasteModuleLoader-requireModuleOrMock-test.js b/src/HasteModuleLoader/__tests__/HasteModuleLoader-requireModuleOrMock-test.js index 1477cf0d1fcb..257dc805bd87 100644 --- a/src/HasteModuleLoader/__tests__/HasteModuleLoader-requireModuleOrMock-test.js +++ b/src/HasteModuleLoader/__tests__/HasteModuleLoader-requireModuleOrMock-test.js @@ -8,6 +8,7 @@ 'use strict'; jest.autoMockOff(); +jest.mock('../../JSDomEnvironment'); var path = require('path'); var Promise = require('bluebird'); @@ -15,7 +16,7 @@ var utils = require('../../lib/utils'); describe('HasteModuleLoader', function() { var HasteModuleLoader; - var mockEnvironment; + var JSDomEnvironment; var resourceMap; var CONFIG = utils.normalizeConfig({ @@ -30,6 +31,7 @@ describe('HasteModuleLoader', function() { return buildLoader(); }); } else { + var mockEnvironment = new JSDomEnvironment(CONFIG); return Promise.resolve( new HasteModuleLoader(CONFIG, mockEnvironment, resourceMap) ); @@ -38,17 +40,7 @@ describe('HasteModuleLoader', function() { beforeEach(function() { HasteModuleLoader = require('../HasteModuleLoader'); - - mockEnvironment = { - global: { - console: {}, - mockClearTimers: jest.genMockFn() - }, - runSourceText: jest.genMockFn().mockImplementation(function(codeStr) { - /* jshint evil:true */ - return (new Function('return ' + codeStr))(); - }) - }; + JSDomEnvironment = require('../../JSDomEnvironment'); }); describe('requireModuleOrMock', function() { diff --git a/src/JSDomEnvironment.js b/src/JSDomEnvironment.js index 30be9ea1e88b..17e4aaad1c51 100644 --- a/src/JSDomEnvironment.js +++ b/src/JSDomEnvironment.js @@ -8,19 +8,8 @@ 'use strict'; var FakeTimers = require('./lib/FakeTimers'); - -function _deepCopy(obj) { - var newObj = {}; - var value; - for (var key in obj) { - value = obj[key]; - if (typeof value === 'object' && value !== null) { - value = _deepCopy(value); - } - newObj[key] = value; - } - return newObj; -} +var utils = require('./lib/utils'); +var vm = require('vm'); function JSDomEnvironment(config) { // We lazily require jsdom because it takes a good ~.5s to load. @@ -29,7 +18,9 @@ function JSDomEnvironment(config) { // use it (depending on the context -- such as TestRunner.js when operating as // a workerpool parent), this is the best way to ensure we only spend time // require()ing this when necessary. - this.global = require('./lib/jsdom-compat').jsdom().parentWindow; + var jsdom = require('./lib/jsdom-compat'); + this.document = jsdom.jsdom(); + this.global = this.document.defaultView; // Node's error-message stack size is limited at 10, but it's pretty useful to // see more than that when a test fails. @@ -83,13 +74,8 @@ function JSDomEnvironment(config) { this.global.Image = function Image() {}; } - // Pass through the node `process` global. - // TODO: Consider locking this down somehow so tests can't do crazy stuff to - // worker processes... - this.global.process = process; - // Apply any user-specified global vars - var globalValues = _deepCopy(config.globals); + var globalValues = utils.deepCopy(config.globals); for (var customGlobalKey in globalValues) { // Always deep-copy objects so isolated test environments can't share memory this.global[customGlobalKey] = globalValues[customGlobalKey]; @@ -107,8 +93,18 @@ JSDomEnvironment.prototype.dispose = function() { //this.global.close(); }; +/** + * Evaluates the given source text as if it were in a file with the given name + * and returns the result. + */ JSDomEnvironment.prototype.runSourceText = function(sourceText, fileName) { - return this.global.run(sourceText, fileName); + // TODO: Stop using the private API and instead configure jsdom with a + // resource loader and insert