Skip to content
This repository has been archived by the owner on May 22, 2024. It is now read-only.

[terra-functional-testing] Added FileOutputReporter reporter #665

Merged
merged 12 commits into from
Jun 7, 2021
3 changes: 3 additions & 0 deletions packages/terra-functional-testing/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

## Unreleased

* Added
* Added FileOutputReporter reporter that logs wdio test output to separate files based on locale, theme, and form-factor

## 1.6.0 - (May 25, 2021)

* Added
Expand Down
1 change: 1 addition & 0 deletions packages/terra-functional-testing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"lodash.isstring": "^4.0.1",
"lodash.isundefined": "^3.0.1",
"lodash.pickby": "^4.6.0",
"strip-ansi": "^6.0.0",
"uuid": "^3.0.0",
"webdriverio": "^6.5.0",
"webpack-dev-server": "^3.11.0",
Expand Down
3 changes: 2 additions & 1 deletion packages/terra-functional-testing/src/config/wdio.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const VisualRegressionLauncher = require('../services/wdio-visual-regression-ser

const { AccessibilityReporter } = require('../reporters/accessibility-reporter');
const { SpecReporter, cleanResults, mergeResults } = require('../reporters/spec-reporter');
const FileOutputReporter = require('../reporters/file-output-reporter');

exports.config = {
//
Expand Down Expand Up @@ -126,7 +127,7 @@ exports.config = {
// Test reporter for stdout.
// The only one supported by default is 'dot'
// see also: https://webdriver.io/docs/dot-reporter.html
reporters: ['spec', AccessibilityReporter, SpecReporter],
reporters: ['spec', AccessibilityReporter, SpecReporter, FileOutputReporter],
//
// Options to be passed to Mocha.
// See the full list at http://mochajs.org/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
const fs = require('fs-extra');
const path = require('path');
const stripAnsi = require('strip-ansi');
const SpecReporter = require('@wdio/spec-reporter').default;
const endOfLine = require('os').EOL;
const { Logger } = require('@cerner/terra-cli');

const LOG_CONTEXT = '[Terra-Toolkit:fileOutput-reporter]';
benbcai marked this conversation as resolved.
Show resolved Hide resolved

class FileOutputReporter extends SpecReporter {
constructor() {
super();
this.runners = [];
this.resultJsonObject = {
startDate: '',
type: 'wdio',
locale: '',
formFactor: '',
theme: '',
output: {},
endDate: '',
};
this.fileName = '';
this.moduleName = process.cwd().split(path.sep).pop();

this.setResultsDir = this.setResultsDir.bind(this);
this.hasResultsDir = this.hasResultsDir.bind(this);
this.setTestModule = this.setTestModule.bind(this);
this.printReport = this.printReport.bind(this);
this.getMessage = this.getMessage.bind(this);

this.setResultsDir();
this.hasResultsDir();
}

/**
* Sets results directory for the test run. Uses the wdio reporterOptions.outputDir if set, otherwise
* it outputs to tests/wdio/reports.
* @return null;
*/
setResultsDir() {
this.resultsDir = path.join(process.cwd(), 'tests', 'wdio', 'reports');
benbcai marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Check and create reports dir if doesn't exist
* @return null
*/
hasResultsDir() {
if (!fs.existsSync(this.resultsDir)) {
fs.mkdirSync(this.resultsDir, { recursive: true }, (err) => {
if (err) {
Logger.error(err.message, { context: LOG_CONTEXT });
}
});
}
}

/**
* Formatting the filename based on locale, theme, and formFactor
* @return null
*/
fileNameCheck({ formFactor, locale, theme }, browserName, cid) {
benbcai marked this conversation as resolved.
Show resolved Hide resolved
const fileNameConf = ['fileOutput'];
if (locale) {
fileNameConf.push(locale);
this.resultJsonObject.locale = locale;
benbcai marked this conversation as resolved.
Show resolved Hide resolved
}

if (theme) {
fileNameConf.push(theme);
this.resultJsonObject.theme = theme;
}

if (formFactor) {
fileNameConf.push(formFactor);
this.resultJsonObject.formFactor = formFactor;
}

if (browserName) {
fileNameConf.push(browserName);
}

if (cid) {
fileNameConf.push(cid);
}

this.fileName = fileNameConf.join('-');
}

/**
* Set the package name to moduleName property if specsValue contains /package string
* @param {string} specsValue - File path of current spec file from runners
* @return null
*/
setTestModule(specsValue) {
const index = specsValue.lastIndexOf(`packages${path.sep}`);
benbcai marked this conversation as resolved.
Show resolved Hide resolved
if (index > -1) {
const testFilePath = specsValue.substring(index).split(path.sep);
const moduleName = testFilePath && testFilePath[1]
? testFilePath[1]
: process.cwd().split(path.sep).pop();
benbcai marked this conversation as resolved.
Show resolved Hide resolved
if (moduleName && moduleName !== this.moduleName) {
this.moduleName = moduleName;
}
}
}

onRunnerEnd(runner) {
this.runners.push(runner);
this.printReport(runner);
}

getMessage(runner) {
const results = this.getResultDisplay();
if (results.length === 0) {
return null;
}
const testLinks = runner.isMultiremote
? Object.entries(runner.capabilities).map(
([instanceName, capabilities]) => this.getTestLink({
config: { ...runner.config, ...{ capabilities } },
sessionId: capabilities.sessionId,
isMultiremote: runner.isMultiremote,
instanceName,
}),
)
: this.getTestLink(runner);
const output = [
...this.getHeaderDisplay(runner),
'',
benbcai marked this conversation as resolved.
Show resolved Hide resolved
benbcai marked this conversation as resolved.
Show resolved Hide resolved
...results,
...this.getFailureDisplay(),
...(testLinks.length ? ['', ...testLinks] : []),
];
const preface = `[${this.getEnviromentCombo(
runner.capabilities,
false,
runner.isMultiremote,
).trim()} #${runner.cid}]`;
const divider = '------------------------------------------------------------------';
const prefacedOutput = output.map((value) => (value ? `${preface} ${value}` : preface));
return `${divider}\n${prefacedOutput}`;
}

printReport() {
const { runners } = this;
if (runners && runners.length) {
runners.forEach((runner, index) => {
// determine correct file name given configuration for run
if (index === 0) {
const { cid, capabilities, config } = runner;
const { browserName } = capabilities;
this.fileNameCheck(config.launcherOptions, browserName, cid);
}

this.setTestModule(runner.specs[0]);

if (!this.resultJsonObject.output[this.moduleName]) {
this.resultJsonObject.output[this.moduleName] = [];
}
const readableMessage = this.getMessage(runner);
const readableArrayMessage = readableMessage.toString().split(',');
const messages = readableArrayMessage.map((message) => stripAnsi(`${message}${endOfLine}`));
this.resultJsonObject.output[this.moduleName].push(messages.join(''));
this.resultJsonObject.startDate = new Date(
runner.start,
).toLocaleString();
this.resultJsonObject.endDate = new Date(runner.end).toLocaleString();
});
}
const {
endDate, startDate, locale, formFactor, theme, output,
} = this.resultJsonObject;

const moduleKeys = Object.keys(output) || [];
if (output && moduleKeys.length) {
moduleKeys.forEach((key) => {
const fileData = {
startDate,
locale,
theme,
formFactor,
output: output[key],
endDate,
};

const filePathLocation = path.join(
this.resultsDir,
`${this.fileName}-${key}.json`,
);
fs.writeFileSync(
filePathLocation,
`${JSON.stringify(fileData, null, 2)}`,
{ flag: 'w+' },
(err) => {
if (err) {
Logger.error(err.message, { context: LOG_CONTEXT });
}
},
);
});
}
}
}

module.exports = FileOutputReporter;
Loading