node-jest/e2e/__tests__/onlyChanged.test.ts

400 lines
12 KiB
TypeScript

/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {tmpdir} from 'os';
import * as path from 'path';
import semver = require('semver');
import {cleanup, run, testIfHg, writeFiles} from '../Utils';
import runJest from '../runJest';
const DIR = path.resolve(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';
const gitVersionSupportsInitialBranch = (() => {
const {stdout} = run(`${GIT} --version`);
const gitVersion = stdout.trim();
const match = gitVersion.match(/^git version (?<version>\d+\.\d+\.\d+)/);
if (match?.groups?.version == null) {
throw new Error(`Unable to parse git version from string "${gitVersion}"`);
}
const {version} = match.groups;
return semver.gte(version, '2.28.0');
})();
const mainBranchName = gitVersionSupportsInitialBranch ? 'main' : 'master';
function gitInit(dir: string) {
const initCommand = gitVersionSupportsInitialBranch
? `${GIT} init --initial-branch=${mainBranchName}`
: `${GIT} init`;
run(initCommand, dir);
}
beforeEach(() => cleanup(DIR));
afterEach(() => cleanup(DIR));
test('run for "onlyChanged" and "changedSince"', () => {
writeFiles(DIR, {
'.watchmanconfig': '',
'__tests__/file1.test.js': "require('../file1'); test('file1', () => {});",
'file1.js': 'module.exports = {}',
'package.json': '{}',
});
gitInit(DIR);
run(`${GIT} add .`, DIR);
run(`${GIT} commit --no-gpg-sign -m "first"`, DIR);
let stdout = runJest(DIR, ['-o']).stdout;
expect(stdout).toMatch(
/No tests found related to files changed since last commit./,
);
stdout = runJest(DIR, [`--changedSince=${mainBranchName}`]).stdout;
expect(stdout).toMatch(
`No tests found related to files changed since "${mainBranchName}".`,
);
});
test('run only changed files', () => {
writeFiles(DIR, {
'.watchmanconfig': '',
'__tests__/file1.test.js': "require('../file1'); test('file1', () => {});",
'file1.js': 'module.exports = {}',
'package.json': '{}',
});
let stderr;
let stdout;
({stdout} = runJest(DIR, ['-o']));
expect(stdout).toMatch(/Jest can only find uncommitted changed files/);
gitInit(DIR);
run(`${GIT} add .`, DIR);
run(`${GIT} commit --no-gpg-sign -m "first"`, DIR);
({stdout} = runJest(DIR, ['-o']));
expect(stdout).toMatch('No tests found related to files');
({stderr} = runJest(DIR, ['-o', '--lastCommit']));
expect(stderr).toMatch(/PASS __tests__(\/|\\)file1.test.js/);
writeFiles(DIR, {
'__tests__/file2.test.js': "require('../file2'); test('file2', () => {});",
'__tests__/file3.test.js': "require('../file3'); test('file3', () => {});",
'file2.js': 'module.exports = {}',
'file3.js': "require('./file2')",
});
({stderr} = runJest(DIR, ['-o']));
expect(stderr).not.toMatch(/PASS __tests__(\/|\\)file1.test.js/);
expect(stderr).toMatch(/PASS __tests__(\/|\\)file2.test.js/);
expect(stderr).toMatch(/PASS __tests__(\/|\\)file3.test.js/);
run(`${GIT} add .`, DIR);
run(`${GIT} commit --no-gpg-sign -m "second"`, DIR);
({stderr} = runJest(DIR, ['-o']));
expect(stdout).toMatch('No tests found related to files');
writeFiles(DIR, {
'file2.js': 'module.exports = {modified: true}',
});
({stderr} = runJest(DIR, ['-o']));
expect(stderr).not.toMatch(/PASS __tests__(\/|\\)file1.test.j/);
expect(stderr).toMatch(/PASS __tests__(\/|\\)file2.test.js/);
expect(stderr).toMatch(/PASS __tests__(\/|\\)file3.test.js/);
});
test('report test coverage for only changed files', () => {
writeFiles(DIR, {
'__tests__/a.test.js': `
require('../a');
require('../b');
test('a', () => expect(1).toBe(1));
`,
'__tests__/b.test.js': `
require('../b');
test('b', () => expect(1).toBe(1));
`,
'a.js': 'module.exports = {}',
'b.js': 'module.exports = {}',
'package.json': JSON.stringify({
jest: {
collectCoverage: true,
coverageReporters: ['text'],
testEnvironment: 'node',
},
}),
});
gitInit(DIR);
run(`${GIT} add .`, DIR);
run(`${GIT} commit --no-gpg-sign -m "first"`, DIR);
writeFiles(DIR, {
'a.js': 'module.exports = {modified: true}',
});
let stdout;
({stdout} = runJest(DIR));
// both a.js and b.js should be in the coverage
expect(stdout).toMatch('a.js');
expect(stdout).toMatch('b.js');
({stdout} = runJest(DIR, ['-o']));
// coverage should be collected only for a.js
expect(stdout).toMatch('a.js');
expect(stdout).not.toMatch('b.js');
});
test('report test coverage of source on test file change under only changed files', () => {
writeFiles(DIR, {
'__tests__/a.test.js': `
require('../a');
test('a1', () => expect(1).toBe(1));
`,
'a.js': 'module.exports = {}',
'package.json': JSON.stringify({
jest: {
collectCoverage: true,
coverageReporters: ['text'],
testEnvironment: 'node',
},
}),
});
gitInit(DIR);
run(`${GIT} add .`, DIR);
run(`${GIT} commit --no-gpg-sign -m "first"`, DIR);
writeFiles(DIR, {
'__tests__/a.test.js': `
require('../a');
test('a1', () => expect(1).toBe(1));
test('a2', () => expect(2).toBe(2));
`,
});
const {stdout} = runJest(DIR, ['--only-changed']);
expect(stdout).toMatch('a.js');
});
test('do not pickup non-tested files when reporting coverage on only changed files', () => {
writeFiles(DIR, {
'a.js': 'module.exports = {}',
'b.test.js': 'module.exports = {}',
'package.json': JSON.stringify({name: 'original name'}),
});
gitInit(DIR);
run(`${GIT} add .`, DIR);
run(`${GIT} commit --no-gpg-sign -m "first"`, DIR);
writeFiles(DIR, {
'b.test.js': 'require("./package.json"); it("passes", () => {})',
'package.json': JSON.stringify({name: 'new name'}),
});
const {stderr, stdout, exitCode} = runJest(DIR, ['-o', '--coverage']);
expect(stderr).toEqual(
expect.not.stringContaining('Failed to collect coverage from'),
);
expect(stdout).toEqual(expect.not.stringContaining('package.json'));
expect(exitCode).toBe(0);
});
test('collect test coverage when using onlyChanged', () => {
writeFiles(DIR, {
'a.js': 'module.exports = {}',
'b.test.js': 'module.exports = {}',
'package.json': JSON.stringify({
jest: {collectCoverageFrom: ['a.js']},
name: 'original name',
}),
});
gitInit(DIR);
run(`${GIT} add .`, DIR);
run(`${GIT} commit --no-gpg-sign -m "first"`, DIR);
run(`${GIT} checkout -b new-branch`, DIR);
writeFiles(DIR, {
'b.test.js': 'it("passes", () => {expect(1).toBe(1)})',
});
const {stderr, exitCode} = runJest(DIR, ['-o', '--coverage']);
expect(stderr).toEqual(
expect.not.stringContaining('Failed to collect coverage from'),
);
expect(exitCode).toBe(0);
});
test('onlyChanged in config is overwritten by --all or testPathPattern', () => {
writeFiles(DIR, {
'.watchmanconfig': '',
'__tests__/file1.test.js': "require('../file1'); test('file1', () => {});",
'file1.js': 'module.exports = {}',
'package.json': JSON.stringify({jest: {onlyChanged: true}}),
});
let stderr;
let stdout;
({stdout} = runJest(DIR));
expect(stdout).toMatch(/Jest can only find uncommitted changed files/);
gitInit(DIR);
run(`${GIT} add .`, DIR);
run(`${GIT} commit --no-gpg-sign -m "first"`, DIR);
({stdout, stderr} = runJest(DIR));
expect(stdout).toMatch('No tests found related to files');
expect(stderr).not.toMatch(
'Unknown option "onlyChanged" with value true was found',
);
({stderr} = runJest(DIR, ['--lastCommit']));
expect(stderr).toMatch(/PASS __tests__(\/|\\)file1.test.js/);
writeFiles(DIR, {
'__tests__/file2.test.js': "require('../file2'); test('file2', () => {});",
'__tests__/file3.test.js': "require('../file3'); test('file3', () => {});",
'file2.js': 'module.exports = {}',
'file3.js': "require('./file2')",
});
({stderr} = runJest(DIR));
expect(stderr).not.toMatch(/PASS __tests__(\/|\\)file1.test.js/);
expect(stderr).toMatch(/PASS __tests__(\/|\\)file2.test.js/);
expect(stderr).toMatch(/PASS __tests__(\/|\\)file3.test.js/);
run(`${GIT} add .`, DIR);
run(`${GIT} commit --no-gpg-sign -m "second"`, DIR);
({stderr} = runJest(DIR));
expect(stdout).toMatch('No tests found related to files');
({stderr, stdout} = runJest(DIR, ['file2.test.js']));
expect(stdout).not.toMatch('No tests found related to files');
expect(stderr).toMatch(/PASS __tests__(\/|\\)file2.test.js/);
expect(stderr).toMatch('1 total');
writeFiles(DIR, {
'file2.js': 'module.exports = {modified: true}',
});
({stderr} = runJest(DIR));
expect(stderr).not.toMatch(/PASS __tests__(\/|\\)file1.test.js/);
expect(stderr).toMatch(/PASS __tests__(\/|\\)file2.test.js/);
expect(stderr).toMatch(/PASS __tests__(\/|\\)file3.test.js/);
({stderr} = runJest(DIR, ['--all']));
expect(stderr).toMatch(/PASS __tests__(\/|\\)file1.test.js/);
expect(stderr).toMatch(/PASS __tests__(\/|\\)file2.test.js/);
expect(stderr).toMatch(/PASS __tests__(\/|\\)file3.test.js/);
});
testIfHg('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/);
});
test('path on Windows is case-insensitive', () => {
if (process.platform !== 'win32') {
// This test is Windows specific, skip it on other platforms.
return;
}
const modifiedDIR = path.resolve(DIR, 'outer_dir', 'inner_dir');
const incorrectModifiedDIR = path.resolve(DIR, 'OUTER_dir', 'inner_dir');
writeFiles(modifiedDIR, {
'.watchmanconfig': '',
'__tests__/file1.test.js': "require('../file1'); test('file1', () => {});",
'file1.js': 'module.exports = {}',
'package.json': '{}',
});
gitInit(modifiedDIR);
run(`${GIT} add .`, modifiedDIR);
run(`${GIT} commit --no-gpg-sign -m "first"`, modifiedDIR);
const {stdout} = runJest(incorrectModifiedDIR, ['-o']);
expect(stdout).toMatch('No tests found related to files');
writeFiles(modifiedDIR, {
'__tests__/file2.test.js': "require('../file2'); test('file2', () => {});",
'__tests__/file3.test.js': "require('../file3'); test('file3', () => {});",
'file2.js': 'module.exports = {}',
'file3.js': "require('./file2')",
});
const {stderr} = runJest(incorrectModifiedDIR, ['-o']);
expect(stderr).not.toMatch(/PASS __tests__(\/|\\)file1.test.js/);
expect(stderr).toMatch(/PASS __tests__(\/|\\)file2.test.js/);
expect(stderr).toMatch(/PASS __tests__(\/|\\)file3.test.js/);
});