From 51f93aaa7506c9d0b90e3385c5bb6fa2cb488bc0 Mon Sep 17 00:00:00 2001 From: Yiyu He Date: Wed, 19 Sep 2018 10:32:35 +0800 Subject: [PATCH] feat: support egg-bin test --changed (#111) --- README.md | 1 + lib/cmd/cov.js | 6 +++--- lib/cmd/test.js | 44 +++++++++++++++++++++++++++++++++++---- package.json | 3 ++- test/lib/cmd/test.test.js | 42 +++++++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d8e32886..1498ed10 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,7 @@ You can pass any mocha argv. - `--timeout` milliseconds, default to 30000 - `--full-trace` display the full stack trace, default to false. - `--typescript` / `--ts` enable typescript support, default to `false`. +- `--changed` / `-c` only test changed test files(test files means files that match `${pwd}/test/**/*.test.(js|ts)`) - see more at https://mochajs.org/#usage #### environment diff --git a/lib/cmd/cov.js b/lib/cmd/cov.js index d0cb824f..9cf2b7e3 100644 --- a/lib/cmd/cov.js +++ b/lib/cmd/cov.js @@ -84,7 +84,7 @@ class CovCommand extends Command { }; // save coverage-xxxx.json to $PWD/coverage - const covArgs = this.getCovArgs(context); + const covArgs = yield this.getCovArgs(context); if (!covArgs) return; debug('covArgs: %j', covArgs); yield this.helper.forkNode(nycCli, covArgs, opt); @@ -104,7 +104,7 @@ class CovCommand extends Command { * @return {Array} args for nyc * @protected */ - getCovArgs(context) { + * getCovArgs(context) { let covArgs = [ // '--show-process-tree', ]; @@ -128,7 +128,7 @@ class CovCommand extends Command { covArgs.push(exclude); } covArgs.push(require.resolve('mocha/bin/_mocha')); - const testArgs = this.formatTestArgs(context); + const testArgs = yield this.formatTestArgs(context); if (!testArgs) return; covArgs = covArgs.concat(testArgs); return covArgs; diff --git a/lib/cmd/test.js b/lib/cmd/test.js index 88a25df0..03a90b2a 100644 --- a/lib/cmd/test.js +++ b/lib/cmd/test.js @@ -5,6 +5,7 @@ const fs = require('fs'); const path = require('path'); const globby = require('globby'); const Command = require('../command'); +const changed = require('jest-changed-files'); class TestCommand extends Command { constructor(rawArgv) { @@ -29,6 +30,10 @@ class TestCommand extends Command { 'full-trace': { description: 'display the full stack trace', }, + changed: { + description: 'only test with changed files and match ${cwd}/test/**/*.test.(js|ts)', + alias: 'c', + }, }; } @@ -44,7 +49,7 @@ class TestCommand extends Command { execArgv: context.execArgv, }; const mochaFile = require.resolve('mocha/bin/_mocha'); - const testArgs = this.formatTestArgs(context); + const testArgs = yield this.formatTestArgs(context); if (!testArgs) return; debug('run test: %s %s', mochaFile, testArgs.join(' ')); yield this.helper.forkNode(mochaFile, testArgs, opt); @@ -69,7 +74,7 @@ class TestCommand extends Command { * @return {Array} [ '--require=xxx', 'xx.test.js' ] * @protected */ - formatTestArgs({ argv, debug }) { + * formatTestArgs({ argv, debug }) { const testArgv = Object.assign({}, argv); /* istanbul ignore next */ @@ -109,10 +114,27 @@ class TestCommand extends Command { testArgv.require = requireArr; + let pattern; + // changed + if (testArgv.changed) { + pattern = yield this._getChangedTestFiles(); + if (!pattern.length) { + console.log('No changed test files'); + return; + } + } + + if (!pattern) { + // specific test files + pattern = testArgv._.slice(); + } + if (!pattern.length && process.env.TESTS) { + pattern = process.env.TESTS.split(','); + } + // collect test files - let pattern = testArgv._.slice(); if (!pattern.length) { - pattern = [ process.env.TESTS || `test/**/*.test.${testArgv.typescript ? 'ts' : 'js'}` ]; + pattern = [ `test/**/*.test.${testArgv.typescript ? 'ts' : 'js'}` ]; } pattern = pattern.concat([ '!test/fixtures', '!test/node_modules' ]); @@ -141,6 +163,20 @@ class TestCommand extends Command { return this.helper.unparseArgv(testArgv); } + + * _getChangedTestFiles() { + const cwd = process.cwd(); + const res = yield changed.getChangedFilesForRoots([ cwd ]); + const changedFiles = res.changedFiles; + const files = []; + for (const file of changedFiles) { + // only find ${cwd}/test/**/*.test.(js|ts) + if (file.startsWith(path.join(cwd, 'test')) && file.match(/\.test\.(js|ts)$/)) { + files.push(file); + } + } + return files; + } } module.exports = TestCommand; diff --git a/package.json b/package.json index 44137ee0..9221627d 100644 --- a/package.json +++ b/package.json @@ -19,12 +19,13 @@ "globby": "^8.0.1", "inspector-proxy": "^1.2.1", "intelli-espower-loader": "^1.0.1", - "source-map-support": "^0.5.6", + "jest-changed-files": "^23.4.2", "mocha": "^5.2.0", "mz-modules": "^2.1.0", "nyc": "^13.0.1", "power-assert": "^1.6.0", "semver": "^5.5.0", + "source-map-support": "^0.5.6", "test-exclude": "^5.0.0", "ts-node": "^7.0.0", "ypkgfiles": "^1.6.0" diff --git a/test/lib/cmd/test.test.js b/test/lib/cmd/test.test.js index b7f76bb5..319346e1 100644 --- a/test/lib/cmd/test.test.js +++ b/test/lib/cmd/test.test.js @@ -5,6 +5,8 @@ const coffee = require('coffee'); const mm = require('mm'); const assert = require('assert'); const semver = require('semver'); +const changed = require('jest-changed-files'); +const Command = require('../../../lib/cmd/test'); describe('test/lib/cmd/test.test.js', () => { const eggBin = require.resolve('../../../bin/egg-bin.js'); @@ -45,6 +47,16 @@ describe('test/lib/cmd/test.test.js', () => { .end(done); }); + it('should only test files specified by TESTS with multi pattern', done => { + mm(process.env, 'TESTS', 'test/a.test.js,test/b/b.test.js'); + coffee.fork(eggBin, [ 'test' ], { cwd }) + .expect('stdout', /should success/) + .expect('stdout', /a\.test\.js/) + .expect('stdout', /b\/b.test.js/) + .expect('code', 0) + .end(done); + }); + it('should only test files specified by TESTS argv', done => { mm(process.env, 'TESTS', 'test/**/*.test.js'); coffee.fork(eggBin, [ 'test', 'test/a.test.js' ], { cwd }) @@ -189,4 +201,34 @@ describe('test/lib/cmd/test.test.js', () => { }); }); }); + + // changed need to mock getChangedFilesForRoots, so we just test formatTestArgs directly + describe('changed', () => { + it('should return undefined if no test file changed', function* () { + const cmd = new Command([ '--changed' ]); + mm.data(changed, 'getChangedFilesForRoots', { + changedFiles: new Set(), + }); + const args = yield cmd.formatTestArgs(cmd.context); + assert(!args); + }); + + it('should return file changed', function* () { + const cmd = new Command([ '--changed' ]); + mm.data(changed, 'getChangedFilesForRoots', { + changedFiles: new Set([ __filename ]), + }); + const args = yield cmd.formatTestArgs(cmd.context); + assert(args.includes('--changed', __filename)); + }); + + it('should filter not test file', function* () { + const cmd = new Command([ '--changed' ]); + mm.data(changed, 'getChangedFilesForRoots', { + changedFiles: new Set([ __filename + '.tmp', 'abc.test.js' ]), + }); + const args = yield cmd.formatTestArgs(cmd.context); + assert(!args); + }); + }); });