Skip to content

Commit

Permalink
Handle Flow JUnit report generation
Browse files Browse the repository at this point in the history
Fixes #11949
  • Loading branch information
benbraou committed Jan 9, 2018
1 parent 59ad2d5 commit 6c95a0d
Show file tree
Hide file tree
Showing 12 changed files with 387 additions and 163 deletions.
2 changes: 2 additions & 0 deletions .circleci/config.yml
Expand Up @@ -39,6 +39,8 @@ jobs:
JEST_PROCESSOR: "jest-junit"
ESLINT_FORMATTER: "junit"
ESLINT_OUTPUT: "reports/junit/eslint-results.xml"
FLOW_FORMATTER: "junit"
FLOW_OUTPUT: "reports/junit/flow-results.xml"

- store_test_results:
path: reports/junit
Expand Down
16 changes: 14 additions & 2 deletions scripts/bench/build.js
Expand Up @@ -4,16 +4,28 @@ const Git = require('nodegit');
const rimraf = require('rimraf');
const ncp = require('ncp').ncp;
const {existsSync} = require('fs');
const exec = require('child_process').exec;
const {join} = require('path');

const {executeCommand} = require('../shared/processCommands');

const reactUrl = 'https://github.com/facebook/react.git';

function cleanDir() {
return new Promise(_resolve => rimraf('remote-repo', _resolve));
}

function executeCommand(command) {
return new Promise(_resolve =>
exec(command, error => {
if (!error) {
_resolve();
} else {
console.error(error);
process.exit(1);
}
})
);
}

function asyncCopyTo(from, to) {
return new Promise(_resolve => {
ncp(from, to, error => {
Expand Down
2 changes: 1 addition & 1 deletion scripts/circleci/test_entry_point.sh
Expand Up @@ -8,7 +8,7 @@ COMMANDS_TO_RUN=()

if [ $((0 % CIRCLE_NODE_TOTAL)) -eq "$CIRCLE_NODE_INDEX" ]; then
COMMANDS_TO_RUN+=('node ./scripts/prettier/index')
COMMANDS_TO_RUN+=('node ./scripts/tasks/flow')
COMMANDS_TO_RUN+=("node ./scripts/tasks/flow --formatter=${FLOW_FORMATTER} --output=${FLOW_OUTPUT}")
COMMANDS_TO_RUN+=("node ./scripts/tasks/eslint --formatter=${ESLINT_FORMATTER} --output=${ESLINT_OUTPUT}")
COMMANDS_TO_RUN+=("yarn test --runInBand --testResultsProcessor=${JEST_PROCESSOR}")
COMMANDS_TO_RUN+=('./scripts/circleci/check_license.sh')
Expand Down
44 changes: 1 addition & 43 deletions scripts/eslint/__tests__/formatterUtils-test.js
Expand Up @@ -9,49 +9,7 @@

'use strict';

const {
getFormatterConfigFromProcessArgs,
getPartialJUnitReportFileName,
} = require('../formatterUtils');

describe('getFormatterConfigFromProcessArgs', () => {
it('should work with empty process arguments', () => {
expect(getFormatterConfigFromProcessArgs([])).toEqual({
name: 'stylish',
outputFile: '',
});
});
it('should work when provided a formatter name', () => {
expect(getFormatterConfigFromProcessArgs(['--formatter=junit'])).toEqual({
name: 'junit',
outputFile: '',
});
});
it('should work when provided a file output path', () => {
expect(
getFormatterConfigFromProcessArgs([
'Hello',
'--output=some/path/result.xml',
])
).toEqual({
name: 'stylish',
outputFile: 'some/path/result.xml',
});
});
it('should work when provided a formatter name and a file output path', () => {
expect(
getFormatterConfigFromProcessArgs([
'Hi',
'--output=some/path/result.xml',
'Hello',
'--formatter=junit',
])
).toEqual({
name: 'junit',
outputFile: 'some/path/result.xml',
});
});
});
const {getPartialJUnitReportFileName} = require('../formatterUtils');

describe('getPartialJUnitReportFileName', () => {
it('should return a correct JUnit report file name provided a base name and an index', () => {
Expand Down
102 changes: 16 additions & 86 deletions scripts/eslint/formatterUtils.js
Expand Up @@ -6,87 +6,26 @@
*
*/

/**
* FormatterConfig describes:
* <ul>
* <li>the name of the output format</li>
* <li>the file into which the report will be written</li>
* </ul>
* @typedef {Object} FormatterConfig
* @property {String} name
* @property {String} outputFile
*/

'use strict';

const path = require('path');
const fs = require('fs');
const {execSync} = require('child_process');

const defaultFormatterName = 'stylish';
const formatterFlag = '--formatter=';
const outputFlag = '--output=';
const formatterConfig = require('../shared/formatterConfig');
const {createDirectoriesIfMissing} = require('../shared/junitReporting');

const defaultFormatConfig = {
name: defaultFormatterName,
name: 'stylish',
outputFile: '',
formatterFlag: '--formatter',
outputFlag: '--output',
};

/**
* Returns based on the provided process arguments,the formatter configuration
* @see FormatterConfig
*
* If a formatter name has been provided as a command line argument , it is returned. Otherwise,the
* default formatter name `stylish` is returned.
*
* More information can be found in https://eslint.org/docs/developer-guide/nodejs-api#getformatter
*
* If an output file has been provided as a command line argument, it is returned.
*
* @param {Array.<String>} processArgs An array of command line arguments
* @returns {FormatterConfig} the formatter configuration
*/
function getFormatterConfigFromProcessArgs(processArgs) {
let config = {...defaultFormatConfig};
for (let arg of processArgs) {
if (arg.startsWith(formatterFlag)) {
config.name = arg.replace(formatterFlag, '');
}
if (arg.startsWith(outputFlag)) {
config.outputFile = arg.replace(outputFlag, '');
}
}
return config;
}

/**
* Returns the formatter configuration. @see FormatterConfig
*
* If a formatter name has been provided as a command line argument , it is returned. Otherwise,the
* default formatter name `stylish` is returned.
*
* More information can be found in https://eslint.org/docs/developer-guide/nodejs-api#getformatter
*
* If an output file has been provided as a command line argument, it is returned.
*
* @returns {FormatterConfig} the formatter configuration
* Returns the formatter configuration based on the default configuration and process args
*/
function getFormatterConfig() {
return getFormatterConfigFromProcessArgs(process.argv);
}

/**
* Creates directories (if missing corresponding) to a provided file path
*
* @param {String} filePath The file path
*/
function createDirectoriesIfMissing(filePath) {
const dirname = path.dirname(filePath);
if (fs.existsSync(dirname)) {
return;
}
createDirectoriesIfMissing(dirname);
fs.mkdirSync(dirname);
return formatterConfig(defaultFormatConfig, process.argv);
}

/**
Expand Down Expand Up @@ -130,16 +69,13 @@ function createPartialJUnitReportAndLogToConsole(
index,
mutableESLintTemporaryFiles
) {
const formatterConfig = getFormatterConfig();
if (!formatterConfig.outputFile) {
const config = getFormatterConfig();
if (!config.outputFile) {
return;
}
const fileName = getPartialJUnitReportFileName(
formatterConfig.outputFile,
index
);
const fileName = getPartialJUnitReportFileName(config.outputFile, index);
console.log(`Writing lint results to: ${fileName}`);
createDirectoriesIfMissing(formatterConfig.outputFile);
createDirectoriesIfMissing(config.outputFile);
fs.writeFileSync(fileName, output, 'utf8');
// Side effect whose goal is to keep track of partial JUnit reports. This list will be used at the
// merge step into one single JUnit report
Expand All @@ -153,8 +89,8 @@ function createPartialJUnitReportAndLogToConsole(
* @param {Array.<String>} mutableESLintTemporaryFiles The list of ESLint report file names
*/
function mergePartialJUnitReportFiles(mutableESLintTemporaryFiles) {
const formatterConfig = getFormatterConfig();
if (!formatterConfig.outputFile) {
const config = getFormatterConfig();
if (!config.outputFile) {
return;
}
// Merge synchronously partial JUnit report files into a single one. This can be done
Expand All @@ -166,16 +102,12 @@ function mergePartialJUnitReportFiles(mutableESLintTemporaryFiles) {
'junit-merge',
'bin',
'junit-merge'
)} ${mutableESLintTemporaryFiles.join(' ')} --out ${
formatterConfig.outputFile
}`
)} ${mutableESLintTemporaryFiles.join(' ')} --out ${config.outputFile}`
);
} catch (e) {
throw new Error('could not merge eslint reports');
}
console.log(
`Created ESLint JUnit report file: ${formatterConfig.outputFile}`
);
console.log(`Created ESLint JUnit report file: ${config.outputFile}`);
// Now, we delete the partial JUnit report files again synchronously
mutableESLintTemporaryFiles.forEach(file => {
try {
Expand All @@ -189,10 +121,8 @@ function mergePartialJUnitReportFiles(mutableESLintTemporaryFiles) {
}

module.exports = {
getFormatterConfigFromProcessArgs,
getFormatterConfig,
createDirectoriesIfMissing,
getPartialJUnitReportFileName,
createPartialJUnitReportAndLogToConsole,
mergePartialJUnitReportFiles,
getFormatterConfig,
};
75 changes: 75 additions & 0 deletions scripts/shared/JUnitReporting.js
@@ -0,0 +1,75 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

/**
* @typedef {Object} ReportInfo
* @property {String} data
* @property {String} packageName
*/

const path = require('path');
const fs = require('fs');

/**
* Creates directories (if missing corresponding) to a provided file path
*
* @param {String} filePath The file path
*/
function createDirectoriesIfMissing(filePath) {
const dirname = path.dirname(filePath);
if (fs.existsSync(dirname)) {
return;
}
createDirectoriesIfMissing(dirname);
fs.mkdirSync(dirname);
}

/**
* Writes a JUnit report as a single test
*
* @param {ReportInfo}
* @param {String} outputFile The file path describing the file that will hold the JUnit report
*/
function writeReportAsSingleTest({data, packageName}, outputFile) {
const xmlOutput = buildXMLOutputAsSingleTest({data, packageName});
createDirectoriesIfMissing(outputFile);
fs.writeFileSync(outputFile, xmlOutput, 'utf8');
}

/**
* Builds the output that will be written in the XML JUnit report
*
* @param {ReportInfo}
* @returns {String} The output that will be written in the JUnit report
*/
function buildXMLOutputAsSingleTest({data, packageName}) {
const testSuite = !data
? ''
: `
<testsuite package="${packageName}" time="0" tests="1" errors="1" name="">
<testcase time="0" name="${packageName} merged report">
<failure message=""><![CDATA[
${data}
]]>
</failure>
</testcase>
</testsuite>
`;
return `<?xml version="1.0"?>
<testsuites>
${testSuite}
</testsuites>
`;
}

module.exports = {
writeReportAsSingleTest,
createDirectoriesIfMissing,
buildXMLOutputAsSingleTest,
};
64 changes: 64 additions & 0 deletions scripts/shared/__tests__/formatterConfig-test.js
@@ -0,0 +1,64 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

const formatterConfig = require('../formatterConfig');

const immutableDefaultConfig = {
name: 'defaultName',
outputFile: 'defaultFile',
formatterFlag: '--formatter',
outputFlag: '--output',
};
let defaultConfig;

describe('formatterConfig', () => {
beforeEach(() => {
defaultConfig = {...immutableDefaultConfig};
});

it('should work with empty process arguments', () => {
expect(formatterConfig(defaultConfig)).toEqual(immutableDefaultConfig);
});

it('should work when provided a formatter name', () => {
expect(formatterConfig(defaultConfig, ['--formatter=junit'])).toEqual({
name: 'junit',
outputFile: 'defaultFile',
formatterFlag: '--formatter',
outputFlag: '--output',
});
});

it('should work when provided a file output path', () => {
expect(
formatterConfig(defaultConfig, ['Hello', '--output=some/path/result.xml'])
).toEqual({
name: 'defaultName',
outputFile: 'some/path/result.xml',
formatterFlag: '--formatter',
outputFlag: '--output',
});
});

it('should work when provided a formatter name and a file output path', () => {
expect(
formatterConfig(defaultConfig, [
'Hi',
'--output=some/path/result.xml',
'Hello',
'--formatter=junit',
])
).toEqual({
name: 'junit',
outputFile: 'some/path/result.xml',
formatterFlag: '--formatter',
outputFlag: '--output',
});
});
});

0 comments on commit 6c95a0d

Please sign in to comment.