fusion-cli
Version:
428 lines (333 loc) • 15.8 kB
JavaScript
// @flow
/* eslint-env node */
const t = require('assert');
const fs = require('fs');
const path = require('path');
const CDP = require('chrome-remote-interface');
const spawn = require('child_process').spawn;
const {promisify} = require('util');
const exec = promisify(require('child_process').exec);
const readFile = promisify(fs.readFile);
const countTests = require('./fixture/src/count-tests');
const runnerPath = require.resolve('../../../bin/cli-runner');
const dir = path.resolve(__dirname, './fixture');
jest.setTimeout(10000);
test('`fusion test` passes', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --match=passes`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
const response = await exec(`node -e "${cmd}"`);
t.equal(countTests(response.stderr), 2, 'ran 2 tests');
});
test('`fusion test` failure', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --match=fails`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
try {
await exec(`node -e "${cmd}"`);
// $FlowFixMe
t.fail('should not succeed');
} catch (e) {
t.equal(countTests(e.message), 2, 'ran 2 tests');
t.notEqual(e.code, 0, 'exits with non-zero status code');
}
});
test('`fusion test` all passing tests', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --match=pass`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
const response = await exec(`node -e "${cmd}"`);
t.equal(countTests(response.stderr), 4, 'ran 4 tests');
});
test('`fusion test` expected test passes in browser/node', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --match=pass-`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
const response = await exec(`node -e "${cmd}"`);
t.equal(countTests(response.stderr), 2, 'ran 2 tests');
});
test('`fusion test` expected tests fail when run in browser/node', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --match=fail-`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
try {
await exec(`node -e "${cmd}"`);
// $FlowFixMe
t.fail('should not succeed');
} catch (e) {
t.notEqual(e.code, 0, 'exits with non-zero status code');
t.equal(countTests(e.message), 2, 'ran 2 tests');
}
});
test('`fusion test --testFolder=integration` runs correct tests', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --env=node --testFolder=__integration__`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
const response = await exec(`node -e "${cmd}"`);
t.equal(countTests(response.stderr), 1, 'ran 1 test');
});
test('`fusion test --testMatch=**/__foo__/**/*js` runs correct tests', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --env=node --testMatch=**/__foo__/**/*.js`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
const response = await exec(`node -e "${cmd}"`);
t.equal(countTests(response.stderr), 1, 'ran 1 test');
});
test('`fusion test --testMatch=**/__foo__/**/*js,**/__integration__/**/*.js` runs correct tests', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --env=node --testMatch=**/__foo__/**/*.js,**/__integration__/**/*.js`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
const response = await exec(`node -e "${cmd}"`);
t.equal(countTests(response.stderr), 2, 'ran 2 tests');
});
test('`fusion test --testRegex=/__foo__/.*` runs correct tests', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --env=node --testRegex=.*/__foo__/.*`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
const response = await exec(`node -e "${cmd}"`);
t.equal(countTests(response.stderr), 1, 'ran 1 test');
});
test('`fusion test --testRegex and --testMatch cannot occur at same time', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --env=node --testMatch=**/__foo__/**/*.js --testRegex=.*/__foo__/.*`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
await exec(`node -e "${cmd}"`).catch(e => t.ok('ok'));
});
test('`fusion test --testFolder and --testMatch cannot occur at same time', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --env=node --testMatch=**/__foo__/**/*.js --testFolder=__foo__`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
// $FlowFixMe
await exec(`node -e "${cmd}"`).then(() => t.fail(), e => t.ok('ok'));
});
test('`fusion test --testFolder and --testRegex cannot occur at same time', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --env=node --testRegex=.*/__foo__/.* --testFolder=__foo__`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
// $FlowFixMe
await exec(`node -e "${cmd}"`).then(() => t.fail(), e => t.ok('ok'));
});
test('`fusion test` snapshotting', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --match=snapshot-no-match`;
const snapshotFile = path.join(
dir,
'src/__tests__/__snapshots__/snapshot-no-match.js.fixture'
);
const backupSnapshot = path.join(
__dirname,
'backup-snapshots/snapshot-no-match.js.fixture'
);
// Copy fixture to snapshot
fs.createReadStream(snapshotFile).pipe(
fs.createWriteStream(snapshotFile.replace(/fixture$/, 'snap'))
);
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
try {
await exec(`node -e "${cmd}"`);
// $FlowFixMe
t.fail('should not succeed');
} catch (e) {
t.notEqual(e.code, 0, 'exits with non-zero status code');
t.equal(countTests(e.message), 2, 'ran 2 tests');
}
const updateSnapshot = `require('${runnerPath}').run('node ${runnerPath} ${args} --updateSnapshot')`;
await exec(`node -e "${updateSnapshot}"`);
const newSnapshotCode = await readFile(snapshotFile);
const originalSnapshotCode = await readFile(backupSnapshot);
t.notEqual(newSnapshotCode, originalSnapshotCode, 'snapshot is updated');
fs.unlinkSync(snapshotFile.replace(/fixture$/, 'snap'));
}, 60000);
test('`fusion test` snapshotting with -u option', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --match=snapshot-no-match`;
const snapshotFile = path.join(
dir,
'src/__tests__/__snapshots__/snapshot-no-match.js.fixture'
);
const backupSnapshot = path.join(
__dirname,
'backup-snapshots/snapshot-no-match.js.fixture'
);
// Copy fixture to snapshot
fs.createReadStream(snapshotFile).pipe(
fs.createWriteStream(snapshotFile.replace(/fixture$/, 'snap'))
);
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
try {
await exec(`node -e "${cmd}"`);
// $FlowFixMe
t.fail('should not succeed');
} catch (e) {
t.notEqual(e.code, 0, 'exits with non-zero status code');
t.equal(countTests(e.message), 2, 'ran 2 tests');
}
const updateSnapshot = `require('${runnerPath}').run('node ${runnerPath} ${args} -u')`;
await exec(`node -e "${updateSnapshot}"`);
const newSnapshotCode = await readFile(snapshotFile);
const originalSnapshotCode = await readFile(backupSnapshot);
t.notEqual(newSnapshotCode, originalSnapshotCode, 'snapshot is updated');
fs.unlinkSync(snapshotFile.replace(/fixture$/, 'snap'));
}, 60000);
test('`fusion test` snapshotting - enzyme serializer', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --match=snapshot-enzyme-no-match`;
const snapshotFile = path.join(
dir,
'src/__tests__/__snapshots__/snapshot-enzyme-no-match.js.fixture'
);
const backupSnapshot = path.join(
__dirname,
'backup-snapshots/snapshot-enzyme-no-match.js.fixture'
);
// Copy fixture to snapshot
fs.createReadStream(snapshotFile).pipe(
fs.createWriteStream(snapshotFile.replace(/fixture$/, 'snap'))
);
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
try {
await exec(`node -e "${cmd}"`);
// $FlowFixMe
t.fail('should not succeed');
} catch (e) {
t.notEqual(e.code, 0, 'exits with non-zero status code');
t.equal(countTests(e.message), 2, 'ran 2 tests');
}
await exec(`node ${runnerPath} ${args} --updateSnapshot`);
const newSnapshotCode = await readFile(snapshotFile);
const originalSnapshotCode = await readFile(backupSnapshot);
t.notEqual(newSnapshotCode, originalSnapshotCode, 'snapshot is updated');
fs.unlinkSync(snapshotFile.replace(/fixture$/, 'snap'));
});
test('`fusion test` dynamic imports', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --match=dynamic-imports`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
const response = await exec(`node -e "${cmd}"`);
t.equal(countTests(response.stderr), 2, 'ran 2 tests');
});
test('`fusion test` coverage', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --coverage --match=passes --collectCoverageFrom=!**/istanbul-ignore-coverage-cli.js`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
const response = await exec(`node -e "${cmd}"`);
t.equal(countTests(response.stderr), 2, 'ran 2 tests');
// Look for something like coverage
t.ok(response.stdout.includes('Uncovered Line #s'));
// This file is outside of src and should not be included in coverage
t.ok(!response.stdout.includes('should-not-count-for-coverage.js'));
// These files instruments the istanbul ignore annotation and should not be included in coverage
t.ok(!response.stdout.includes('istanbul-ignore-coverage.js'));
// Ignored by the CLI flag
t.ok(!response.stdout.includes('istanbul-ignore-coverage-cli.js'));
});
test('`fusion test` coverage ignore multiple globs from collectCoverageFrom', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --coverage --match=passes --collectCoverageFrom=!**/istanbul-ignore-coverage-cli.js --collectCoverageFrom=!**/class-props.js`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
const response = await exec(`node -e "${cmd}"`);
// Ignored by the CLI flags
t.ok(!response.stdout.includes('istanbul-ignore-coverage-cli.js'));
t.ok(!response.stdout.includes('class-props.js'));
});
test('`fusion test` class properties', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --match=class-props`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
const response = await exec(`node -e "${cmd}"`);
t.equal(countTests(response.stderr), 2, 'ran 2 tests');
});
test('`fusion test` cobertura coverage reports', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --coverage --match=passes`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args} --env=jsdom')`;
const response = await exec(`node -e "${cmd}"`);
t.equal(countTests(response.stderr), 1, 'ran 1 tests');
const cobertunaReport = await readFile(
path.resolve(dir, 'coverage/cobertura-coverage.xml'),
'utf8'
);
// Only a single report should be generated
t.ok(cobertunaReport.includes('<line number="2" hits="1"/>'));
t.ok(!cobertunaReport.includes('<line number="2" hits="2"/>'));
t.ok(
cobertunaReport.includes('not-imported-in-tests.js'),
'report includes files not imported in tests'
);
// Assert that there's two hits when combining coverage
const cmd2 = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
const response2 = await exec(`node -e "${cmd2}"`);
t.equal(countTests(response2.stderr), 2, 'ran 2 tests');
const combinedReport = await readFile(
path.resolve(dir, 'coverage/cobertura-coverage.xml')
);
t.ok(combinedReport.includes('<line number="2" hits="2"/>'));
t.ok(!combinedReport.includes('<line number="2" hits="1"/>'));
t.ok(
combinedReport.includes('not-imported-in-tests.js'),
'report includes files not imported in tests'
);
}, 60000);
test('`fusion test` environment variables', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --match=environment-variables`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
const response = await exec(`node -e "${cmd}"`, {
env: Object.assign({}, process.env, {
NODE_ENV: 'development',
}),
});
t.equal(countTests(response.stderr), 2, 'ran 2 tests');
});
test('`fusion test` writes results to disk based on env var', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --match=passes`;
const testMetadataPath = path.join(dir, 'test-results.json');
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
const response = await exec(`node -e "${cmd}"`, {
env: Object.assign({}, process.env, {
FUSION_TEST_METADATA_PATH: testMetadataPath,
}),
});
t.equal(countTests(response.stderr), 2, 'ran 2 tests');
const results = require(testMetadataPath);
t.equal(results.numTotalTests, 2, 'two tests in results json');
fs.unlinkSync(testMetadataPath);
});
async function triggerCodeStep() {
return new Promise(resolve => {
CDP({port: '9229'}, async client => {
const {Runtime} = client;
await Runtime.runIfWaitingForDebugger();
await client.close();
resolve();
}).on('error', err => {
throw err;
});
});
}
test('`fusion test --debug --env=jsdom,node`', async () => {
const args = `test --dir=${dir} --configPath=../../../../build/jest/jest-config.js --debug --env=jsdom,node --match=passes`;
const cmd = `require('${runnerPath}').run('node ${runnerPath} ${args}')`;
const stderrLines = [];
const child = spawn('node', ['-e', cmd]);
const listenAddresses = {};
let numResults = 0;
let completed = new Promise(resolve => {
child.stderr &&
child.stderr.on('data', data => {
const line = data.toString();
// eslint-disable-next-line no-console
console.log(` - received spawn line: ${line}`);
stderrLines.push(line);
// Keep track of all addresses that we start listening to.
if (line.startsWith('Debugger listening on ws')) {
listenAddresses[line] = true;
}
// Wait until we have results for both environments before ending the test.
if (/Tests:.*2\s+passed,\s+2\s+total/.test(line)) {
numResults += 1;
if (numResults == 1) {
t.ok('ok');
resolve();
}
}
});
});
// Poll until we get a listener message.
async function checkStartedMessageCount(count) {
return new Promise(async resolve => {
while (Object.keys(listenAddresses).length < count) {
await new Promise(r => setTimeout(r, 100));
}
resolve();
});
}
// Step through the environment
await checkStartedMessageCount(1);
await triggerCodeStep();
await completed;
t.ok(
Object.keys(listenAddresses).length >= 1,
'found a remote debug connection'
);
child.kill();
}, 100000);