diff --git a/.eslintignore b/.eslintignore index 25fbf5a1..0bf2048d 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ node_modules/ coverage/ +test/fixtures/enzyme-example-mocha/ diff --git a/README.md b/README.md index cee78720..26ba77d9 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,16 @@ You can set `COV_EXCLUDES` env to add dir ignore coverage. $ COV_EXCLUDES="app/plugins/c*,app/autocreate/**" egg-bin cov ``` +### auto require `test/.setup.js` + +If `test/.setup.js` file exists, it will be auto require on `test` and `cov` command. + +```js +test + ├── .setup.js + └── foo.test.js +``` + ## Custom egg-bin for your team You maybe need a custom egg-bin to implement more custom features diff --git a/lib/cov_command.js b/lib/cov_command.js index 34ce27f4..f953f9e3 100644 --- a/lib/cov_command.js +++ b/lib/cov_command.js @@ -57,7 +57,7 @@ class CovCommand extends Command { } getCovArgs(args) { - let covArgs = [ + const covArgs = [ 'cover', '--report', 'none', '--print', 'none', @@ -67,21 +67,11 @@ class CovCommand extends Command { covArgs.push('-x'); covArgs.push(exclude); } - covArgs = covArgs.concat([ - require.resolve('mocha/bin/_mocha'), - '--', - '--timeout', process.env.TEST_TIMEOUT || '200000', - '--require', require.resolve('thunk-mocha'), - ]).concat(this.helper.getTestFiles()).concat(args); - - if (args.indexOf('intelli-espower-loader') !== -1) { - console.warn('[egg-bin] don\'t need to manually require `intelli-espower-loader` anymore'); - } else { - covArgs.push('--require'); - covArgs.push(require.resolve('intelli-espower-loader')); - } - - return covArgs; + const mochaFile = require.resolve('mocha/bin/_mocha'); + const testArgs = this.helper.formatTestArgs(args); + return covArgs.concat([ + mochaFile, '--', + ]).concat(testArgs); } getReportArgs(coverageDir) { diff --git a/lib/helper.js b/lib/helper.js index c2d1f62a..c5e4d06e 100644 --- a/lib/helper.js +++ b/lib/helper.js @@ -1,5 +1,6 @@ 'use strict'; +const fs = require('fs'); const path = require('path'); const glob = require('glob'); const detect = require('detect-port'); @@ -7,16 +8,52 @@ const detect = require('detect-port'); exports.defaultPort = 7001; exports.serverBin = path.join(__dirname, 'start-cluster'); -exports.getTestFiles = function() { +exports.getTestFiles = () => { const files = process.env.TESTS || 'test/**/*.test.js'; const base = process.cwd(); return glob.sync(files, { cwd: base, - }).map(function(file) { + }).map(file => { return path.join(base, file); }); }; +exports.getTestSetupFile = () => { + const setupFile = path.join(process.cwd(), 'test/.setup.js'); + if (fs.existsSync(setupFile)) { + return setupFile; + } + return null; +}; + +exports.formatTestArgs = args => { + const newArgs = [ + '--timeout', process.env.TEST_TIMEOUT || '30000', + '--require', require.resolve('thunk-mocha'), + ]; + if (process.env.TEST_REPORTER) { + newArgs.push('--reporter'); + newArgs.push(process.env.TEST_REPORTER); + } + + if (args.indexOf('intelli-espower-loader') !== -1) { + console.warn('[egg-bin] don\'t need to manually require `intelli-espower-loader` anymore'); + } else { + // should be require before args + newArgs.push('--require'); + newArgs.push(require.resolve('intelli-espower-loader')); + } + + // auto require setup file + const setupFile = exports.getTestSetupFile(); + if (setupFile) { + newArgs.push('--require'); + newArgs.push(setupFile); + } + + return newArgs.concat(exports.getTestFiles()).concat(args); +}; + // TODO: add egg-dependencies // const checkDeps = require('egg-dependencies'); exports.checkDeps = function* () { diff --git a/lib/test_command.js b/lib/test_command.js index 02fc2b63..89b226d7 100644 --- a/lib/test_command.js +++ b/lib/test_command.js @@ -1,32 +1,19 @@ 'use strict'; -const mochaFile = require.resolve('mocha/bin/_mocha'); const Command = require('./command'); class TestCommand extends Command { * run(_, args) { - args = [ - mochaFile, - '--reporter', process.env.TEST_REPORTER || 'spec', - '--timeout', process.env.TEST_TIMEOUT || '30000', - '--require', require.resolve('thunk-mocha'), - ].concat(this.helper.getTestFiles()).concat(args); - - if (args.indexOf('intelli-espower-loader') !== -1) { - console.warn('[egg-bin] don\'t need to manually require `intelli-espower-loader` anymore'); - } else { - args.push('--require'); - args.push(require.resolve('intelli-espower-loader')); - } - - process.env.NODE_ENV = 'test'; + yield this.helper.checkDeps(); + const newArgs = this.helper.formatTestArgs(args); const opt = { - env: process.env, + env: Object.assign({}, process.env, { + NODE_ENV: 'test', + }), }; - - yield this.helper.checkDeps(); - yield this.helper.forkNode(mochaFile, args, opt); + const mochaFile = require.resolve('mocha/bin/_mocha'); + yield this.helper.forkNode(mochaFile, newArgs, opt); } help() { diff --git a/package.json b/package.json index 7035115b..60003632 100644 --- a/package.json +++ b/package.json @@ -26,11 +26,19 @@ }, "devDependencies": { "autod": "^2.7.1", + "babel": "^6.3.26", + "babel-preset-airbnb": "^1.0.1", + "babel-register": "^6.4.3", "coffee": "^3.3.0", "egg-ci": "^1.1.0", + "enzyme": "^2.0.0", "eslint": "^3.12.2", "eslint-config-egg": "^3.2.0", - "mm": "^2.0.0" + "jsdom": "^8.0.1", + "mm": "^2.0.0", + "react": "^0.14.7", + "react-addons-test-utils": "^0.14.7", + "react-dom": "^0.14.7" }, "repository": { "type": "git", diff --git a/test/egg-test.test.js b/test/egg-test.test.js index 55e0da7f..868dea41 100644 --- a/test/egg-test.test.js +++ b/test/egg-test.test.js @@ -72,17 +72,28 @@ describe('egg-bin test', () => { .end(done); }); - it('should warn when require intelli-espower-loader', done => { + it('should warn when require intelli-espower-loader', () => { mm(process.env, 'TESTS', 'test/power-assert-fail.js'); - coffee.fork(eggBin, [ 'cov', '-r', 'intelli-espower-loader' ], { cwd }) - .coverage(false) + return coffee.fork(eggBin, [ 'cov', '-r', 'intelli-espower-loader' ], { cwd }) + .coverage(false) + // .debug() + .expect('stderr', /manually require `intelli-espower-loader`/) + .expect('stdout', /1\) should fail/) + .expect('stdout', /assert\(1 === 2\)/) + .expect('stdout', /1 failing/) + .expect('code', 1) + .end(); + }); + + it('should auto require test/.setup.js', () => { + // example: https://github.com/lelandrichardson/enzyme-example-mocha + return coffee.fork(eggBin, [ 'test' ], { + cwd: path.join(__dirname, 'fixtures/enzyme-example-mocha'), + }) // .debug() - .expect('stderr', /manually require `intelli-espower-loader`/) - .expect('stdout', /1\) should fail/) - .expect('stdout', /assert\(1 === 2\)/) - .expect('stdout', /1 failing/) - .expect('code', 1) - .end(done); + .expect('stdout', /3 passing/) + .expect('code', 0) + .end(); }); it.skip('should check node dependencies fail', done => { diff --git a/test/fixtures/enzyme-example-mocha/.babelrc b/test/fixtures/enzyme-example-mocha/.babelrc new file mode 100644 index 00000000..ba8e7fd1 --- /dev/null +++ b/test/fixtures/enzyme-example-mocha/.babelrc @@ -0,0 +1,3 @@ +{ + presets: ["airbnb"] +} diff --git a/test/fixtures/enzyme-example-mocha/.gitignore b/test/fixtures/enzyme-example-mocha/.gitignore new file mode 100644 index 00000000..e920c167 --- /dev/null +++ b/test/fixtures/enzyme-example-mocha/.gitignore @@ -0,0 +1,33 @@ +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +node_modules + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history diff --git a/test/fixtures/enzyme-example-mocha/LICENSE b/test/fixtures/enzyme-example-mocha/LICENSE new file mode 100644 index 00000000..0c373221 --- /dev/null +++ b/test/fixtures/enzyme-example-mocha/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Leland Richardson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/test/fixtures/enzyme-example-mocha/README.md b/test/fixtures/enzyme-example-mocha/README.md new file mode 100644 index 00000000..0ef6ffb7 --- /dev/null +++ b/test/fixtures/enzyme-example-mocha/README.md @@ -0,0 +1,2 @@ +# enzyme-example-mocha +Example project with React + Enzyme + Mocha diff --git a/test/fixtures/enzyme-example-mocha/package.json b/test/fixtures/enzyme-example-mocha/package.json new file mode 100644 index 00000000..7fccd08e --- /dev/null +++ b/test/fixtures/enzyme-example-mocha/package.json @@ -0,0 +1,31 @@ +{ + "name": "enzyme-example-mocha", + "version": "0.1.0", + "description": "Example project with React + Enzyme + Mocha", + "main": "build/index.js", + "scripts": { + "test": "mocha test/.setup.js test/**/*-test.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/lelandrichardson/enzyme-example-mocha.git" + }, + "author": "Leland Richardson ", + "license": "MIT", + "bugs": { + "url": "https://github.com/lelandrichardson/enzyme-example-mocha/issues" + }, + "homepage": "https://github.com/lelandrichardson/enzyme-example-mocha", + "devDependencies": { + "babel": "^6.3.26", + "babel-preset-airbnb": "^1.0.1", + "babel-register": "^6.4.3", + "enzyme": "^2.0.0", + "jsdom": "^8.0.1", + "react-addons-test-utils": "^0.14.7" + }, + "dependencies": { + "react": "^0.14.7", + "react-dom": "^0.14.7" + } +} diff --git a/test/fixtures/enzyme-example-mocha/src/Foo.js b/test/fixtures/enzyme-example-mocha/src/Foo.js new file mode 100644 index 00000000..2f6873b4 --- /dev/null +++ b/test/fixtures/enzyme-example-mocha/src/Foo.js @@ -0,0 +1,22 @@ +import React, { PropTypes } from 'react'; + +const propTypes = {}; + +const defaultProps = {}; + +class Foo extends React.Component { + constructor(props) { + super(props); + } + + render() { + return ( +
+ ); + } +} + +Foo.propTypes = propTypes; +Foo.defaultProps = defaultProps; + +export default Foo; diff --git a/test/fixtures/enzyme-example-mocha/test/.setup.js b/test/fixtures/enzyme-example-mocha/test/.setup.js new file mode 100644 index 00000000..718f402a --- /dev/null +++ b/test/fixtures/enzyme-example-mocha/test/.setup.js @@ -0,0 +1,20 @@ +require('babel-register')(); + +var jsdom = require('jsdom').jsdom; + +var exposedProperties = ['window', 'navigator', 'document']; + +global.document = jsdom(''); +global.window = document.defaultView; +Object.keys(document.defaultView).forEach((property) => { + if (typeof global[property] === 'undefined') { + exposedProperties.push(property); + global[property] = document.defaultView[property]; + } +}); + +global.navigator = { + userAgent: 'node.js' +}; + +documentRef = document; diff --git a/test/fixtures/enzyme-example-mocha/test/Foo.test.js b/test/fixtures/enzyme-example-mocha/test/Foo.test.js new file mode 100644 index 00000000..cef63b45 --- /dev/null +++ b/test/fixtures/enzyme-example-mocha/test/Foo.test.js @@ -0,0 +1,18 @@ +import React from 'react'; +import assert from 'assert'; +import { shallow, mount, render } from 'enzyme'; +import Foo from '../src/Foo'; + +describe("A suite", () => { + it("contains foo", () => { + assert(shallow().contains(
)); + }); + + it("contains .foo", () => { + assert(shallow().is('.foo')); + }); + + it("contains .foo length 1", () => { + assert(mount().find('.foo').length === 1); + }); +});