Skip to content

Commit

Permalink
jest --changedSince=origin/master (#5312)
Browse files Browse the repository at this point in the history
* --changedFilesToContributeTo=$BRANCH

make changedFilesToContributeTo require an arg, and imply onlyChanged

* rename; reorder; fix CLI.md

* doc changes from review comments
  • Loading branch information
alsuren authored and cpojer committed Jan 19, 2018
1 parent 630b5c0 commit 9f9c5e8
Show file tree
Hide file tree
Showing 14 changed files with 120 additions and 8 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
## master

### Features
* `[jest-cli]` `--changedSince`: allow selectively running tests for code
changed since arbitrary revisions.
([#5312](https://github.com/facebook/jest/pull/5312))

## jest 22.1.3

### Fixes
Expand Down
8 changes: 8 additions & 0 deletions docs/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ If you want to inspect the cache, use `--showConfig` and look at the
Runs tests related to the current changes and the changes made in the last
commit. Behaves similarly to `--onlyChanged`.

### `--changedSince`

##### available in Jest **22.2.0+**

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`.

### `--ci`

When this option is provided, Jest will assume it is running in a CI
Expand Down
64 changes: 64 additions & 0 deletions integration-tests/__tests__/jest_changed_files.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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, {
changedSince: '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, {
changedSince: '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 () => {
Expand Down Expand Up @@ -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, {
changedSince: '-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, {
changedSince: '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']);
});
25 changes: 18 additions & 7 deletions packages/jest-changed-files/src/git.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>,
cwd: Path,
): Promise<Array<Path>> => {
return new Promise((resolve, reject) => {
const child = childProcess.spawn('git', args, {cwd});
let stdout = '';
Expand All @@ -30,6 +33,7 @@ const findChangedFilesUsingCommand = async (args, cwd) => {
resolve(
stdout
.split('\n')
.filter(s => s !== '')
.map(changedPath => path.resolve(cwd, changedPath)),
);
}
Expand All @@ -45,21 +49,28 @@ const adapter: SCMAdapter = {
cwd: string,
options?: Options,
): Promise<Array<Path>> => {
const changedSince: ?string =
options && (options.withAncestor ? 'HEAD^' : options.changedSince);

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 (changedSince) {
const committed = await findChangedFilesUsingCommand(
['log', '--name-only', '--pretty=%b', 'HEAD', `^${changedSince}`],
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'],
Expand Down
2 changes: 2 additions & 0 deletions packages/jest-changed-files/src/hg.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const adapter: SCMAdapter = {
let args = ['status', '-amnu'];
if (options && options.withAncestor) {
args.push('--rev', 'ancestor(.^)');
} else if (options && options.changedSince) {
args.push('--rev', `ancestor(., ${options.changedSince})`);
} else if (options && options.lastCommit === true) {
args = ['tip', '--template', '{files%"{file}\n"}'];
}
Expand Down
15 changes: 14 additions & 1 deletion packages/jest-cli/src/cli/args.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ export const check = (argv: Argv) => {
);
}

for (const key of ['onlyChanged', 'lastCommit', 'changedFilesWithAncestor']) {
for (const key of [
'onlyChanged',
'lastCommit',
'changedFilesWithAncestor',
'changedSince',
]) {
if (argv[key]) {
argv.onlyChanged = true;
}
Expand Down Expand Up @@ -113,6 +118,14 @@ export const options = {
'last commit. Behaves similarly to `--onlyChanged`.',
type: 'boolean',
},
changedSince: {
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',
},
ci: {
default: isCI,
description:
Expand Down
1 change: 1 addition & 0 deletions packages/jest-cli/src/get_changed_files_promise.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default (
[],
);
return getChangedFilesForRoots(allRootsForAllProjects, {
changedSince: globalConfig.changedSince,
lastCommit: globalConfig.lastCommit,
withAncestor: globalConfig.changedFilesWithAncestor,
});
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ const getConfigs = (
globalConfig: Object.freeze({
bail: options.bail,
changedFilesWithAncestor: options.changedFilesWithAncestor,
changedSince: options.changedSince,
collectCoverage: options.collectCoverage,
collectCoverageFrom: options.collectCoverageFrom,
collectCoverageOnlyFrom: options.collectCoverageOnlyFrom,
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/normalize.js
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ export default function normalize(options: InitialOptions, argv: Argv) {
case 'bail':
case 'browser':
case 'cache':
case 'changedSince':
case 'changedFilesWithAncestor':
case 'clearMocks':
case 'collectCoverage':
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/valid_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default ({
cache: true,
cacheDirectory: '/tmp/user/jest',
changedFilesWithAncestor: false,
changedSince: '',
clearMocks: false,
collectCoverage: true,
collectCoverageFrom: ['src', '!public'],
Expand Down
1 change: 1 addition & 0 deletions test_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {GlobalConfig, ProjectConfig} from 'types/Config';
const DEFAULT_GLOBAL_CONFIG: GlobalConfig = {
bail: false,
changedFilesWithAncestor: false,
changedSince: '',
collectCoverage: false,
collectCoverageFrom: [],
collectCoverageOnlyFrom: null,
Expand Down
1 change: 1 addition & 0 deletions types/Argv.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type Argv = {|
cache: boolean,
cacheDirectory: string,
changedFilesWithAncestor: boolean,
changedSince: string,
clearMocks: boolean,
ci: boolean,
collectCoverage: boolean,
Expand Down
1 change: 1 addition & 0 deletions types/ChangedFiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {Path} from 'types/Config';
export type Options = {|
lastCommit?: boolean,
withAncestor?: boolean,
changedSince?: string,
|};

export type ChangedFiles = Set<Path>;
Expand Down
2 changes: 2 additions & 0 deletions types/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export type InitialOptions = {
cacheDirectory?: Path,
clearMocks?: boolean,
changedFilesWithAncestor?: boolean,
changedSince?: string,
collectCoverage?: boolean,
collectCoverageFrom?: Array<Glob>,
collectCoverageOnlyFrom?: {[key: string]: boolean},
Expand Down Expand Up @@ -159,6 +160,7 @@ export type SnapshotUpdateState = 'all' | 'new' | 'none';

export type GlobalConfig = {|
bail: boolean,
changedSince: string,
changedFilesWithAncestor: boolean,
collectCoverage: boolean,
collectCoverageFrom: Array<Glob>,
Expand Down

0 comments on commit 9f9c5e8

Please sign in to comment.