UNPKG

wdio-allure-reporter

Version:

A WebdriverIO plugin. Report results in Allure format.

440 lines (353 loc) 13.4 kB
import process from 'process' import events from 'events' import Allure from 'allure-js-commons' import Step from 'allure-js-commons/beans/step' import {getTestStatus, isEmpty, PASSED, BROKEN, FAILED} from './utils' const LOGGING_HOOKS = ['"before all" hook', '"after all" hook'] const STEP_STATUSES = [FAILED, PASSED, BROKEN] /** * Initialize a new `Allure` test reporter. * * @param {Runner} runner * @api public */ class AllureReporter extends events.EventEmitter { constructor (baseReporter, config, options = {}) { super() this.baseReporter = baseReporter this.config = config this.isMultiremote = false this.options = options this.allures = {} const { epilogue } = this.baseReporter if (this.options.useCucumberStepReporter) { // Hook events this.on('hook:start', ::this.cucumberHookStart) this.on('hook:end', ::this.cucumberHookEnd) // Test framework events this.on('suite:start', ::this.cucumberSuiteStart) this.on('suite:end', ::this.cucumberSuiteEnd) this.on('test:start', ::this.cucumberTestStart) this.on('test:pass', ::this.cucumberTestPass) this.on('test:fail', ::this.cucumberTestFail) this.on('test:pending', ::this.cucumberTestPending) } else { // Hook events this.on('hook:start', ::this.hookStart) this.on('hook:end', ::this.hookEnd) // Test framework events this.on('suite:start', ::this.suiteStart) this.on('suite:end', ::this.suiteEnd) this.on('test:start', ::this.testStart) this.on('test:pass', ::this.testPass) this.on('test:fail', ::this.testFail) this.on('test:pending', ::this.testPending) } // Runner events (webdriver) this.on('start', ::this.start) this.on('runner:command', ::this.runnerCommand) this.on('runner:step', ::this.step) this.on('runner:result', ::this.runnerResult) this.on('end', () => { epilogue.call(baseReporter) }) // Allure events this.on('allure:feature', ::this.allureFeature) this.on('allure:addEnvironment', ::this.allureAddEnvironment) this.on('allure:addDescription', ::this.allureAddDescription) this.on('allure:attachment', ::this.allureAttachment) this.on('allure:story', ::this.allureStory) this.on('allure:severity', ::this.allureSeverity) this.on('allure:issue', ::this.allureIssue) this.on('allure:testId', ::this.allureTestId) } suiteStart (suite) { const allure = this.getAllure(suite.cid) const currentSuite = allure.getCurrentSuite() const prefix = currentSuite ? currentSuite.name + ' ' : '' allure.startSuite(prefix + suite.title) } suiteEnd (suite) { this.getAllure(suite.cid).endSuite() } testStart (test) { const allure = this.getAllure(test.cid) allure.startCase(test.title) const currentTest = allure.getCurrentTest() const browserName = test.runner[test.cid].browserName || test.cid const version = test.runner[test.cid].version || '' currentTest.addParameter('argument', 'browser', `${browserName}-${version}`) currentTest.addParameter('environment-variable', 'capabilities', JSON.stringify(test.runner[test.cid])) currentTest.addParameter('environment-variable', 'spec files', JSON.stringify(test.specs)) if (test.featureName && test.scenarioName) { currentTest.addLabel('feature', test.featureName) currentTest.addLabel('story', test.scenarioName) } // Analytics labels More: https://github.com/allure-framework/allure2/blob/master/Analytics.md currentTest.addLabel('language', 'javascript') currentTest.addLabel('framework', 'wdio') currentTest.addLabel('thread', test.cid) } testPass (test) { this.getAllure(test.cid).endCase(PASSED) } testFail (test) { const allure = this.getAllure(test.cid) if (!allure.getCurrentTest()) { allure.startCase(test.title) } else { allure.getCurrentTest().name = test.title } const status = getTestStatus(test, this.config) while (allure.getCurrentSuite().currentStep instanceof Step) { allure.endStep(status) } allure.endCase(status, test.err) } testPending (test) { const allure = this.getAllure(test.cid) if (allure.getCurrentTest() && allure.getCurrentTest().status !== 'pending') { allure.endCase('pending') } else { allure.pendingCase(test.title) } } runnerCommand (command) { if (this.options.disableWebdriverStepsReporting || this.isMultiremote) { return } const allure = this.getAllure(command.cid) if (!this.isAnyTestRunning(allure)) { return } allure.startStep(`${command.method} ${command.uri.path}`) if (!isEmpty(command.data)) { this.dumpJSON(allure, 'Request', command.data) } } step ({event, cid, step}) { const allure = this.getAllure(cid) if (!this.isAnyTestRunning(allure)) { return } allure.startStep(step.title) if (!isEmpty(step.body)) { allure.addAttachment(step.bodyLabel, step.body) } if (STEP_STATUSES.indexOf(step.status) === -1) { throw new Error(`Step status must be ${STEP_STATUSES.join(' or ')}. You tried to set "${step.status}"`) } allure.endStep(step.status) } start (event) { this.isMultiremote = event.isMultiremote } runnerResult (command) { if (this.isMultiremote) { return } const allure = this.getAllure(command.cid) if (!this.isAnyTestRunning(allure)) { return } if (command.requestOptions.uri.path.match(/\/session\/[^/]*\/screenshot/) && command.body.value) { if (!this.options.disableWebdriverScreenshotsReporting) { allure.addAttachment('Screenshot', Buffer.from(command.body.value, 'base64')) } } else if (!this.options.disableWebdriverStepsReporting) { if (command.body) { this.dumpJSON(allure, 'Response', command.body) } allure.endStep(PASSED) } } hookStart (hook) { const allure = this.getAllure(hook.cid) if (!allure.getCurrentSuite() || LOGGING_HOOKS.indexOf(hook.title) === -1) { return } allure.startCase(hook.title) } hookEnd (hook) { const allure = this.getAllure(hook.cid) if (!allure.getCurrentSuite() || LOGGING_HOOKS.indexOf(hook.title) === -1) { return } allure.endCase(PASSED) if (allure.getCurrentTest().steps.length === 0) { allure.getCurrentSuite().testcases.pop() } } cucumberHookStart (hook) { const allure = this.getAllure(hook.cid) if (hook.title) { allure.startStep(hook.title) } } cucumberHookEnd (hook) { const allure = this.getAllure(hook.cid) if (hook.title) { allure.endStep(hook.title) } allure.endCase(PASSED) } cucumberSuiteStart (suite) { const allure = this.getAllure(suite.cid) if (!suite.parent) { const currentSuite = allure.getCurrentSuite() const prefix = currentSuite ? `${currentSuite.name} ` : '' allure.startSuite(prefix + suite.title) } else { if (suite.title) { allure.startCase(suite.title) const currentSuite = allure.getCurrentSuite() const test = allure.getCurrentTest() const browserName = suite.runner[suite.cid].browserName || suite.cid const version = suite.runner[suite.cid].version || '' test.addParameter('argument', 'browser', `${browserName}-${version}`) test.addLabel('feature', currentSuite.name) // Analytics labels More: https://github.com/allure-framework/allure2/blob/master/Analytics.md test.addLabel('language', 'javascript') test.addLabel('framework', 'wdio') test.addLabel('thread', suite.cid) } } } cucumberSuiteEnd (suite) { if (!suite.parent) { this.getAllure(suite.cid).endSuite() } else { if (suite.title) { this.getAllure(suite.cid).endCase(PASSED) } } } cucumberTestStart (test) { const allure = this.getAllure(test.cid) if (!this.isAnyTestRunning(allure)) { return } if (test.title) { allure.startStep(test.title) } } cucumberTestPass (test) { const allure = this.getAllure(test.cid) while (allure.getCurrentSuite().currentStep instanceof Step) { allure.endStep(PASSED) } } cucumberTestFail (test) { if (test.title) { const allure = this.getAllure(test.cid) if (!this.isAnyTestRunning(allure)) { return } const status = getTestStatus(test, this.config) while (allure.getCurrentSuite().currentStep instanceof Step) { allure.endStep(status) } allure.endCase(status, test.err) } } cucumberTestPending (test) { const allure = this.getAllure(test.cid) if (test.title) { allure.endStep('pending') } } allureFeature ({ cid, featureName }) { const allure = this.getAllure(cid) const test = allure.getCurrentTest() test.addLabel('feature', featureName) } allureAddEnvironment ({ cid, name, value }) { const allure = this.getAllure(cid) const test = allure.getCurrentTest() test.addParameter('environment-variable', name, value) } allureAddDescription ({ cid, description, type }) { const allure = this.getAllure(cid) const test = allure.getCurrentTest() test.setDescription(description, type) } allureAttachment ({cid, name, content, type}) { const allure = this.getAllure(cid) if (!content || !allure) { return } if (type === 'application/json') { this.dumpJSON(allure, name, content) } else { allure.addAttachment(name, Buffer.from(content), type) } } allureStory ({ cid, storyName }) { const test = this.getAllure(cid).getCurrentTest() test.addLabel('story', storyName) } allureSeverity ({ cid, severity }) { const test = this.getAllure(cid).getCurrentTest() test.addLabel('severity', severity) } allureIssue ({ cid, issue }) { const test = this.getAllure(cid).getCurrentTest() test.addLabel('issue', issue) } allureTestId ({ cid, testId }) { const test = this.getAllure(cid).getCurrentTest() test.addLabel('testId', testId) } static createStep (title, body, bodyLabel = 'attachment', status = PASSED) { const step = { title, body, bodyLabel, status } AllureReporter.tellReporter('runner:step', { step }) } static feature (featureName) { AllureReporter.tellReporter('allure:feature', { featureName }) } static addEnvironment (name, value) { AllureReporter.tellReporter('allure:addEnvironment', { name, value }) } static addDescription (description, type) { AllureReporter.tellReporter('allure:addDescription', { description, type }) } static createAttachment (name, content, type = 'text/plain') { AllureReporter.tellReporter('allure:attachment', {name, content, type}) } static story (storyName) { AllureReporter.tellReporter('allure:story', { storyName }) } static severity (severity) { AllureReporter.tellReporter('allure:severity', { severity }) } static issue (issue) { AllureReporter.tellReporter('allure:issue', { issue }) } static testId (testId) { AllureReporter.tellReporter('allure:testId', { testId }) } static tellReporter (event, msg = {}) { process.send({ event, ...msg }) } getAllure (cid) { if (this.allures[cid]) { return this.allures[cid] } const allure = new Allure() allure.setOptions({ targetDir: this.options.outputDir || 'allure-results' }) this.allures[cid] = allure return this.allures[cid] } isAnyTestRunning (allure) { return allure.getCurrentSuite() && allure.getCurrentTest() } dumpJSON (allure, name, json) { allure.addAttachment(name, JSON.stringify(json, null, ' '), 'application/json') } } export default AllureReporter