diff --git a/integration_tests/__tests__/__snapshots__/show_config.test.js.snap b/integration_tests/__tests__/__snapshots__/show_config.test.js.snap index 5911376175f3..153de9be7afe 100644 --- a/integration_tests/__tests__/__snapshots__/show_config.test.js.snap +++ b/integration_tests/__tests__/__snapshots__/show_config.test.js.snap @@ -54,6 +54,7 @@ exports[`--showConfig outputs config info and exits 1`] = ` \\"framework\\": \\"jasmine2\\", \\"globalConfig\\": { \\"bail\\": false, + \\"changedFilesWithAncestor\\": false, \\"coverageDirectory\\": \\"<>/coverage\\", \\"coverageReporters\\": [ \\"json\\", diff --git a/integration_tests/__tests__/only_changed.test.js b/integration_tests/__tests__/only_changed.test.js index cc5fbb7a5f73..b8798eadaa2c 100644 --- a/integration_tests/__tests__/only_changed.test.js +++ b/integration_tests/__tests__/only_changed.test.js @@ -16,6 +16,7 @@ import path from 'path'; const skipOnWindows = require('skipOnWindows'); const DIR = path.resolve(os.tmpdir(), 'jest_only_changed'); const GIT = 'git -c user.name=jest_test -c user.email=jest_test@test.com'; +const HG = 'hg --config ui.username=jest_test'; skipOnWindows.suite(); @@ -138,3 +139,53 @@ test('onlyChanged in config is overwritten by --all or testPathPattern', () => { expect(stderr).toMatch('PASS __tests__/file2.test.js'); expect(stderr).toMatch('PASS __tests__/file3.test.js'); }); + +test('gets changed files for hg', async () => { + if (process.env.CI) { + // Circle and Travis have very old version of hg (v2, and current + // version is v4.2) and its API changed since then and not compatible + // any more. Changing the SCM version on CIs is not trivial, so we'll just + // skip this test and run it only locally. + return; + } + writeFiles(DIR, { + '.watchmanconfig': '', + '__tests__/file1.test.js': `require('../file1'); test('file1', () => {});`, + 'file1.js': 'module.exports = {}', + 'package.json': JSON.stringify({jest: {testEnvironment: 'node'}}), + }); + + run(`${HG} init`, DIR); + run(`${HG} add .`, DIR); + run(`${HG} commit -m "test"`, DIR); + + let stdout; + let stderr; + + ({stdout, stderr} = runJest(DIR, ['-o'])); + expect(stdout).toMatch('No tests found related to files changed'); + + writeFiles(DIR, { + '__tests__/file2.test.js': `require('../file2'); test('file2', () => {});`, + 'file2.js': 'module.exports = {}', + 'file3.js': `require('./file2')`, + }); + + ({stdout, stderr} = runJest(DIR, ['-o'])); + expect(stderr).toMatch('PASS __tests__/file2.test.js'); + + run(`${HG} add .`, DIR); + run(`${HG} commit -m "test2"`, DIR); + + writeFiles(DIR, { + '__tests__/file3.test.js': `require('../file3'); test('file3', () => {});`, + }); + + ({stdout, stderr} = runJest(DIR, ['-o'])); + expect(stderr).toMatch('PASS __tests__/file3.test.js'); + expect(stderr).not.toMatch('PASS __tests__/file2.test.js'); + + ({stdout, stderr} = runJest(DIR, ['-o', '--changedFilesWithAncestor'])); + expect(stderr).toMatch('PASS __tests__/file2.test.js'); + expect(stderr).toMatch('PASS __tests__/file3.test.js'); +}); diff --git a/packages/jest-changed-files/src/git.js b/packages/jest-changed-files/src/git.js index f725bf5881cd..e3e7f95b9551 100644 --- a/packages/jest-changed-files/src/git.js +++ b/packages/jest-changed-files/src/git.js @@ -19,6 +19,11 @@ const adapter: SCMAdapter = { cwd: string, options?: Options, ): Promise> => { + if (options && options.withAncestor) { + throw new Error( + '`changedFilesWithAncestor` is not supported in git repos.', + ); + } return new Promise((resolve, reject) => { const args = options && options.lastCommit diff --git a/packages/jest-changed-files/src/hg.js b/packages/jest-changed-files/src/hg.js index 767e03eb3053..a33275f596c7 100644 --- a/packages/jest-changed-files/src/hg.js +++ b/packages/jest-changed-files/src/hg.js @@ -35,7 +35,7 @@ const adapter: SCMAdapter = { let stderr = ''; child.stdout.on('data', data => (stdout += data)); child.stderr.on('data', data => (stderr += data)); - child.on('error', e => reject(e)); + child.on('error', (error: Error) => reject(error)); child.on('close', code => { if (code === 0) { stdout = stdout.trim(); @@ -49,7 +49,7 @@ const adapter: SCMAdapter = { ); } } else { - reject(code + ': ' + stderr); + reject(new Error(code + ': ' + stderr)); } }); }); diff --git a/packages/jest-cli/src/cli/args.js b/packages/jest-cli/src/cli/args.js index d320b8f616ab..31916f048a64 100644 --- a/packages/jest-cli/src/cli/args.js +++ b/packages/jest-cli/src/cli/args.js @@ -90,6 +90,13 @@ const options = { ' dependency information.', type: 'string', }, + changedFilesWithAncestor: { + description: + 'When used together with `--onlyChanged`, it runs tests ' + + 'related to the current changes and the changes made in the last commit. ' + + '(NOTE: this only works for hg repos)', + type: 'boolean', + }, ci: { default: isCI, description: diff --git a/packages/jest-cli/src/get_changed_files_promise.js b/packages/jest-cli/src/get_changed_files_promise.js index 896c0162aafa..0ab3b0b740d2 100644 --- a/packages/jest-cli/src/get_changed_files_promise.js +++ b/packages/jest-cli/src/get_changed_files_promise.js @@ -23,6 +23,7 @@ module.exports = ( ); return getChangedFilesForRoots(allRootsForAllProjects, { lastCommit: globalConfig.lastCommit, + withAncestor: globalConfig.changedFilesWithAncestor, }); } diff --git a/packages/jest-config/src/defaults.js b/packages/jest-config/src/defaults.js index a6814b4995c8..6aa3e47409d5 100644 --- a/packages/jest-config/src/defaults.js +++ b/packages/jest-config/src/defaults.js @@ -34,6 +34,7 @@ module.exports = ({ browser: false, cache: true, cacheDirectory, + changedFilesWithAncestor: false, clearMocks: false, coveragePathIgnorePatterns: [NODE_MODULES_REGEXP], coverageReporters: ['json', 'text', 'lcov', 'clover'], diff --git a/packages/jest-config/src/index.js b/packages/jest-config/src/index.js index da835ee3710a..406408843011 100644 --- a/packages/jest-config/src/index.js +++ b/packages/jest-config/src/index.js @@ -64,6 +64,7 @@ const getConfigs = ( return { globalConfig: Object.freeze({ bail: options.bail, + changedFilesWithAncestor: options.changedFilesWithAncestor, collectCoverage: options.collectCoverage, collectCoverageFrom: options.collectCoverageFrom, collectCoverageOnlyFrom: options.collectCoverageOnlyFrom, diff --git a/packages/jest-config/src/normalize.js b/packages/jest-config/src/normalize.js index 3d664add0078..263a9a83868e 100644 --- a/packages/jest-config/src/normalize.js +++ b/packages/jest-config/src/normalize.js @@ -448,6 +448,7 @@ function normalize(options: InitialOptions, argv: Argv) { case 'bail': case 'browser': case 'cache': + case 'changedFilesWithAncestor': case 'clearMocks': case 'collectCoverage': case 'coverageReporters': diff --git a/packages/jest-config/src/valid_config.js b/packages/jest-config/src/valid_config.js index 6d6f89f23bbd..6e69d1110f4b 100644 --- a/packages/jest-config/src/valid_config.js +++ b/packages/jest-config/src/valid_config.js @@ -21,6 +21,7 @@ module.exports = ({ browser: false, cache: true, cacheDirectory: '/tmp/user/jest', + changedFilesWithAncestor: false, clearMocks: false, collectCoverage: true, collectCoverageFrom: ['src', '!public'], diff --git a/types/Argv.js b/types/Argv.js index 820153e01bef..2c61efa2db67 100644 --- a/types/Argv.js +++ b/types/Argv.js @@ -18,6 +18,7 @@ export type Argv = {| browser: boolean, cache: boolean, cacheDirectory: string, + changedFilesWithAncestor: boolean, clearMocks: boolean, ci: boolean, collectCoverage: boolean, diff --git a/types/Config.js b/types/Config.js index 5e1b2c6f1520..136e7aa3e3c1 100644 --- a/types/Config.js +++ b/types/Config.js @@ -28,6 +28,7 @@ export type DefaultOptions = {| browser: boolean, cache: boolean, cacheDirectory: Path, + changedFilesWithAncestor: boolean, clearMocks: boolean, coveragePathIgnorePatterns: Array, coverageReporters: Array, @@ -67,6 +68,7 @@ export type InitialOptions = {| cache?: boolean, cacheDirectory?: Path, clearMocks?: boolean, + changedFilesWithAncestor?: boolean, collectCoverage?: boolean, collectCoverageFrom?: Array, collectCoverageOnlyFrom?: {[key: string]: boolean}, @@ -134,6 +136,7 @@ export type SnapshotUpdateState = 'all' | 'new' | 'none'; export type GlobalConfig = {| bail: boolean, + changedFilesWithAncestor: boolean, collectCoverage: boolean, collectCoverageFrom: Array, collectCoverageOnlyFrom: ?{[key: string]: boolean},