From 6b750e0d1d2bd7f75d97457074f9fb6f5fc6bfa8 Mon Sep 17 00:00:00 2001 From: David Laban Date: Sun, 14 Jan 2018 16:19:56 +0000 Subject: [PATCH 1/3] make --lastCommit and --changedFilesWithAncestor work without --onlyChanged --- docs/CLI.md | 8 +++--- .../jest-cli/src/__tests__/cli/args.test.js | 13 +++++++++ packages/jest-cli/src/cli/args.js | 27 +++++++++++-------- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/docs/CLI.md b/docs/CLI.md index a3da06c4b6fc..b9aa40ecbefb 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -105,8 +105,8 @@ If you want to inspect the cache, use `--showConfig` and look at the ### `--changedFilesWithAncestor` -When used together with `--onlyChanged` or `--watch`, it runs tests related to -the current changes and the changes made in the last commit. +Runs tests related to the current changes and the changes made in the last +commit. Behaves similarly to `--onlyChanged`. ### `--ci` @@ -188,8 +188,8 @@ Write test results to a file when the `--json` option is also specified. ### `--lastCommit` -When used together with `--onlyChanged`, it will run all tests affected by file -changes in the last commit made. +Run all tests affected by file changes in the last commit made. Behaves +similarly to `--onlyChanged`. ### `--listTests` diff --git a/packages/jest-cli/src/__tests__/cli/args.test.js b/packages/jest-cli/src/__tests__/cli/args.test.js index 5dc09760e5a5..ad53b7dcc31a 100644 --- a/packages/jest-cli/src/__tests__/cli/args.test.js +++ b/packages/jest-cli/src/__tests__/cli/args.test.js @@ -31,6 +31,19 @@ describe('check', () => { ); }); + it('raises an exception when lastCommit and watchAll are both specified', () => { + const argv: Argv = {lastCommit: true, watchAll: true}; + expect(() => check(argv)).toThrow( + 'Both --lastCommit and --watchAll were specified', + ); + }); + + it('sets onlyChanged if lastCommit is specified', () => { + const argv: Argv = {lastCommit: true}; + check(argv); + expect(argv.onlyChanged).toBe(true); + }); + it('raises an exception if findRelatedTests is specified with no file paths', () => { const argv: Argv = {_: [], findRelatedTests: true}; expect(() => check(argv)).toThrow( diff --git a/packages/jest-cli/src/cli/args.js b/packages/jest-cli/src/cli/args.js index f692493908ca..f56d513c8af3 100644 --- a/packages/jest-cli/src/cli/args.js +++ b/packages/jest-cli/src/cli/args.js @@ -20,12 +20,17 @@ export const check = (argv: Argv) => { ); } - if (argv.onlyChanged && argv.watchAll) { - throw new Error( - 'Both --onlyChanged and --watchAll were specified, but these two ' + - 'options do not make sense together. Try the --watch option which ' + - 'reruns only tests related to changed files.', - ); + for (const key of ['onlyChanged', 'lastCommit', 'changedFilesWithAncestor']) { + if (argv[key]) { + argv.onlyChanged = true; + } + if (argv[key] && argv.watchAll) { + throw new Error( + `Both --${key} and --watchAll were specified, but these two ` + + 'options do not make sense together. Try the --watch option which ' + + 'reruns only tests related to changed files.', + ); + } } if (argv.findRelatedTests && argv._.length === 0) { @@ -104,8 +109,8 @@ export const options = { }, changedFilesWithAncestor: { description: - 'When used together with `--onlyChanged` or `--watch`, it runs tests ' + - 'related to the current changes and the changes made in the last commit. ', + 'Runs tests related to the current changes and the changes made in the ' + + 'last commit. Behaves similarly to `--onlyChanged`.', type: 'boolean', }, ci: { @@ -267,8 +272,8 @@ export const options = { lastCommit: { default: undefined, description: - 'When used together with `--onlyChanged`, it will run all tests ' + - 'affected by file changes in the last commit made.', + 'Run all tests affected by file changes in the last commit made. ' + + 'Behaves similarly to `--onlyChanged`.', type: 'boolean', }, listTests: { @@ -353,7 +358,7 @@ export const options = { description: 'Attempts to identify which tests to run based on which ' + "files have changed in the current repository. Only works if you're " + - 'running tests in a git repository at the moment.', + 'running tests in a git or hg repository at the moment.', type: 'boolean', }, onlyFailures: { From 31fd272b5f14a5fa4bc4ca0f682a03bd3362f710 Mon Sep 17 00:00:00 2001 From: David Laban Date: Sun, 14 Jan 2018 16:46:25 +0000 Subject: [PATCH 2/3] add fix to changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c341855de20..209b1934add4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ ([#5289](https://github.com/facebook/jest/pull/5289)) * `[docs]` Update mention of the minimal version of node supported [#4947](https://github.com/facebook/jest/issues/4947) * `[jest-cli]` Fix missing newline in console message ([#5308](https://github.com/facebook/jest/pull/5308)) +* `[jest-cli]` `--lastCommit` and `--changedFilesWithAncestor` now take effect + even when `--onlyChanged` is not specified. ([#5307](https://github.com/facebook/jest/pull/5307)) ### Chore & Maintenance From 0ba2431a4e7477d39cda859bd7951b94c8faad32 Mon Sep 17 00:00:00 2001 From: David Laban Date: Wed, 27 Dec 2017 22:11:54 +0000 Subject: [PATCH 3/3] --changedFilesToContributeTo=$BRANCH make changedFilesToContributeTo require an arg, and imply onlyChanged --- CHANGELOG.md | 2 + docs/CLI.md | 6 ++ .../__tests__/jest_changed_files.test.js | 64 +++++++++++++++++++ packages/jest-changed-files/src/git.js | 25 ++++++-- packages/jest-changed-files/src/hg.js | 2 + packages/jest-cli/src/cli/args.js | 15 ++++- .../jest-cli/src/get_changed_files_promise.js | 1 + packages/jest-config/src/index.js | 1 + packages/jest-config/src/normalize.js | 1 + packages/jest-config/src/valid_config.js | 1 + test_utils.js | 1 + types/Argv.js | 1 + types/ChangedFiles.js | 1 + types/Config.js | 2 + 14 files changed, 115 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 209b1934add4..3c4af337229b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ * `[jest-cli]` Make Jest exit without an error when no tests are found in the case of `--lastCommit`, `--findRelatedTests`, or `--onlyChanged` options having been passed to the CLI +* `[jest-cli]` Allow selectively running tests for code changed since arbitrary + revisions. ([#5188](https://github.com/facebook/jest/pull/5188)) ### Fixes * `[jest-cli]` Use `import-local` to support global Jest installations. diff --git a/docs/CLI.md b/docs/CLI.md index b9aa40ecbefb..4b3087f9f9cb 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -103,6 +103,12 @@ two times slower._ If you want to inspect the cache, use `--showConfig` and look at the `cacheDirectory` value. If you need to clear the cache, use `--clearCache`. +### `--changedFilesToContributeTo` + +When used together with `--onlyChanged` or `--watch`, it runs tests related the +changes since the provided revision. If the current branch is not a child of the +given commit, then only changes made locally will be tested. + ### `--changedFilesWithAncestor` Runs tests related to the current changes and the changes made in the last diff --git a/integration-tests/__tests__/jest_changed_files.test.js b/integration-tests/__tests__/jest_changed_files.test.js index 759f246f49e8..943fea029df9 100644 --- a/integration-tests/__tests__/jest_changed_files.test.js +++ b/integration-tests/__tests__/jest_changed_files.test.js @@ -191,6 +191,37 @@ test('gets changed files for git', async () => { .map(filePath => path.basename(filePath)) .sort(), ).toEqual(['file1.txt', 'file4.txt']); + + run(`${GIT} add file4.txt`, DIR); + run(`${GIT} commit -m "test3"`, DIR); + + ({changedFiles: files} = await getChangedFilesForRoots(roots, { + toContributeTo: 'HEAD^^', + })); + // Returns files from the last 2 commits + expect( + Array.from(files) + .map(filePath => path.basename(filePath)) + .sort(), + ).toEqual(['file1.txt', 'file4.txt']); + + run(`${GIT} checkout HEAD^^ -b feature-branch`, DIR); + + writeFiles(DIR, { + 'file5.txt': 'file5', + }); + run(`${GIT} add file5.txt`, DIR); + run(`${GIT} commit -m "test5"`, DIR); + + ({changedFiles: files} = await getChangedFilesForRoots(roots, { + toContributeTo: 'master', + })); + // Returns files from this branch but not ones that only exist on master + expect( + Array.from(files) + .map(filePath => path.basename(filePath)) + .sort(), + ).toEqual(['file5.txt']); }); test('gets changed files for hg', async () => { @@ -261,4 +292,37 @@ test('gets changed files for hg', async () => { .map(filePath => path.basename(filePath)) .sort(), ).toEqual(['file1.txt', 'file4.txt']); + + run(`${HG} add file4.txt`, DIR); + run(`${HG} commit -m "test3"`, DIR); + + ({changedFiles: files} = await getChangedFilesForRoots(roots, { + toContributeTo: '-3', + })); + // Returns files from the last 2 commits + expect( + Array.from(files) + .map(filePath => path.basename(filePath)) + .sort(), + ).toEqual(['file1.txt', 'file4.txt']); + + run(`${HG} bookmark master`, DIR); + // Back up and develop on a different branch + run(`${HG} checkout --rev=-2`, DIR); + + writeFiles(DIR, { + 'file5.txt': 'file5', + }); + run(`${HG} add file5.txt`, DIR); + run(`${HG} commit -m "test4"`, DIR); + + ({changedFiles: files} = await getChangedFilesForRoots(roots, { + toContributeTo: 'master', + })); + // Returns files from this branch but not ones that only exist on master + expect( + Array.from(files) + .map(filePath => path.basename(filePath)) + .sort(), + ).toEqual(['file5.txt']); }); diff --git a/packages/jest-changed-files/src/git.js b/packages/jest-changed-files/src/git.js index ef02aaea3dbc..9d67ea443d8b 100644 --- a/packages/jest-changed-files/src/git.js +++ b/packages/jest-changed-files/src/git.js @@ -13,7 +13,10 @@ import type {Options, SCMAdapter} from 'types/ChangedFiles'; import path from 'path'; import childProcess from 'child_process'; -const findChangedFilesUsingCommand = async (args, cwd) => { +const findChangedFilesUsingCommand = async ( + args: Array, + cwd: Path, +): Promise> => { return new Promise((resolve, reject) => { const child = childProcess.spawn('git', args, {cwd}); let stdout = ''; @@ -30,6 +33,7 @@ const findChangedFilesUsingCommand = async (args, cwd) => { resolve( stdout .split('\n') + .filter(s => s !== '') .map(changedPath => path.resolve(cwd, changedPath)), ); } @@ -45,21 +49,28 @@ const adapter: SCMAdapter = { cwd: string, options?: Options, ): Promise> => { + const toContributeTo: ?string = + options && (options.withAncestor ? 'HEAD^' : options.toContributeTo); + if (options && options.lastCommit) { return await findChangedFilesUsingCommand( ['show', '--name-only', '--pretty=%b', 'HEAD'], cwd, ); - } else if (options && options.withAncestor) { - const changed = await findChangedFilesUsingCommand( - ['diff', '--name-only', 'HEAD^'], + } else if (toContributeTo) { + const committed = await findChangedFilesUsingCommand( + ['log', '--name-only', '--pretty=%b', 'HEAD', `^${toContributeTo}`], + cwd, + ); + const staged = await findChangedFilesUsingCommand( + ['diff', '--cached', '--name-only'], cwd, ); - const untracked = await findChangedFilesUsingCommand( - ['ls-files', '--other', '--exclude-standard'], + const unstaged = await findChangedFilesUsingCommand( + ['ls-files', '--other', '--modified', '--exclude-standard'], cwd, ); - return changed.concat(untracked); + return [...committed, ...staged, ...unstaged]; } else { return await findChangedFilesUsingCommand( ['ls-files', '--other', '--modified', '--exclude-standard'], diff --git a/packages/jest-changed-files/src/hg.js b/packages/jest-changed-files/src/hg.js index b02859c66de8..2728dcf51a3c 100644 --- a/packages/jest-changed-files/src/hg.js +++ b/packages/jest-changed-files/src/hg.js @@ -26,6 +26,8 @@ const adapter: SCMAdapter = { let args = ['status', '-amnu']; if (options && options.withAncestor) { args.push('--rev', 'ancestor(.^)'); + } else if (options && options.toContributeTo) { + args.push('--rev', `ancestor(., ${options.toContributeTo})`); } else if (options && options.lastCommit === true) { args = ['tip', '--template', '{files%"{file}\n"}']; } diff --git a/packages/jest-cli/src/cli/args.js b/packages/jest-cli/src/cli/args.js index f56d513c8af3..436365384b0c 100644 --- a/packages/jest-cli/src/cli/args.js +++ b/packages/jest-cli/src/cli/args.js @@ -20,7 +20,12 @@ export const check = (argv: Argv) => { ); } - for (const key of ['onlyChanged', 'lastCommit', 'changedFilesWithAncestor']) { + for (const key of [ + 'onlyChanged', + 'lastCommit', + 'changedFilesWithAncestor', + 'changedFilesToContributeTo', + ]) { if (argv[key]) { argv.onlyChanged = true; } @@ -107,6 +112,14 @@ export const options = { ' dependency information.', type: 'string', }, + changedFilesToContributeTo: { + description: + 'Runs tests related the changes since the provided branch. If the ' + + 'current branch has diverged from the given branch, then only changes ' + + 'made locally will be tested. Behaves similarly to `--onlyChanged`.', + nargs: 1, + type: 'string', + }, changedFilesWithAncestor: { description: 'Runs tests related to the current changes and the changes made in the ' + diff --git a/packages/jest-cli/src/get_changed_files_promise.js b/packages/jest-cli/src/get_changed_files_promise.js index 68005453f5b6..ed67b3e65676 100644 --- a/packages/jest-cli/src/get_changed_files_promise.js +++ b/packages/jest-cli/src/get_changed_files_promise.js @@ -22,6 +22,7 @@ export default ( ); return getChangedFilesForRoots(allRootsForAllProjects, { lastCommit: globalConfig.lastCommit, + toContributeTo: globalConfig.changedFilesToContributeTo, withAncestor: globalConfig.changedFilesWithAncestor, }); } diff --git a/packages/jest-config/src/index.js b/packages/jest-config/src/index.js index 739b6e69bbec..c43fc9954502 100644 --- a/packages/jest-config/src/index.js +++ b/packages/jest-config/src/index.js @@ -93,6 +93,7 @@ const getConfigs = ( return { globalConfig: Object.freeze({ bail: options.bail, + changedFilesToContributeTo: options.changedFilesToContributeTo, changedFilesWithAncestor: options.changedFilesWithAncestor, collectCoverage: options.collectCoverage, collectCoverageFrom: options.collectCoverageFrom, diff --git a/packages/jest-config/src/normalize.js b/packages/jest-config/src/normalize.js index 46e64544a586..4fb964ca021c 100644 --- a/packages/jest-config/src/normalize.js +++ b/packages/jest-config/src/normalize.js @@ -461,6 +461,7 @@ export default function normalize(options: InitialOptions, argv: Argv) { case 'bail': case 'browser': case 'cache': + case 'changedFilesToContributeTo': case 'changedFilesWithAncestor': case 'clearMocks': case 'collectCoverage': diff --git a/packages/jest-config/src/valid_config.js b/packages/jest-config/src/valid_config.js index c1559a7b5419..d9e0b05bf03d 100644 --- a/packages/jest-config/src/valid_config.js +++ b/packages/jest-config/src/valid_config.js @@ -20,6 +20,7 @@ export default ({ browser: false, cache: true, cacheDirectory: '/tmp/user/jest', + changedFilesToContributeTo: '', changedFilesWithAncestor: false, clearMocks: false, collectCoverage: true, diff --git a/test_utils.js b/test_utils.js index 9ae8c272dd59..e4fe05e8ed98 100644 --- a/test_utils.js +++ b/test_utils.js @@ -13,6 +13,7 @@ import type {GlobalConfig, ProjectConfig} from 'types/Config'; const DEFAULT_GLOBAL_CONFIG: GlobalConfig = { bail: false, + changedFilesToContributeTo: '', changedFilesWithAncestor: false, collectCoverage: false, collectCoverageFrom: [], diff --git a/types/Argv.js b/types/Argv.js index 0f1b7943405a..c4906537ec96 100644 --- a/types/Argv.js +++ b/types/Argv.js @@ -17,6 +17,7 @@ export type Argv = {| browser: boolean, cache: boolean, cacheDirectory: string, + changedFilesToContributeTo: string, changedFilesWithAncestor: boolean, clearMocks: boolean, ci: boolean, diff --git a/types/ChangedFiles.js b/types/ChangedFiles.js index e69fa343899e..2ed911288481 100644 --- a/types/ChangedFiles.js +++ b/types/ChangedFiles.js @@ -12,6 +12,7 @@ import type {Path} from 'types/Config'; export type Options = {| lastCommit?: boolean, withAncestor?: boolean, + toContributeTo?: string, |}; export type ChangedFiles = Set; diff --git a/types/Config.js b/types/Config.js index b9ee6cd422fa..a37240606f28 100644 --- a/types/Config.js +++ b/types/Config.js @@ -76,6 +76,7 @@ export type InitialOptions = { cacheDirectory?: Path, clearMocks?: boolean, changedFilesWithAncestor?: boolean, + changedFilesToContributeTo?: string, collectCoverage?: boolean, collectCoverageFrom?: Array, collectCoverageOnlyFrom?: {[key: string]: boolean}, @@ -157,6 +158,7 @@ export type SnapshotUpdateState = 'all' | 'new' | 'none'; export type GlobalConfig = {| bail: boolean, + changedFilesToContributeTo: string, changedFilesWithAncestor: boolean, collectCoverage: boolean, collectCoverageFrom: Array,