UNPKG

@shelex/cypress-allure-plugin

Version:
336 lines (295 loc) 11.3 kB
require('./commands'); const { EVENT_TEST_BEGIN, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING, EVENT_SUITE_BEGIN, EVENT_SUITE_END, EVENT_TEST_END, EVENT_HOOK_BEGIN, EVENT_HOOK_END, EVENT_TEST_RETRY } = Mocha.Runner.constants; const path = require('path-browserify'); const { AllureRuntime, InMemoryAllureWriter, ContentType } = require('@shelex/allure-js-commons-browser'); const AllureReporter = require('./allure-cypress/AllureReporter'); const stubbedAllure = require('./stubbedAllure'); const logger = require('./debug'); const { shouldUseAfterSpec } = require('../writer/useAfterSpec'); const { env } = Cypress; const shouldEnableGherkinLogging = () => { const logCypress = env('allureLogCypress'); const logGherkin = env('allureLogGherkin'); const isLogCypressDefined = typeof logCypress !== 'undefined'; const isLogGherkinDefined = typeof logGherkin !== 'undefined'; return isLogGherkinDefined ? logGherkin : // in case logGherkin is not defined use logCypress value or true by default isLogCypressDefined ? logCypress : true; }; const config = { allureEnabled: () => env('allure'), resultsPath: () => env('allureResultsPath') || 'allure-results', shouldLogCypress: () => env('allureLogCypress') !== false, shouldAttachRequests: () => env('allureAttachRequests'), shouldLogGherkinSteps: () => shouldEnableGherkinLogging(), clearFilesForPreviousAttempt: () => env('allureOmitPreviousAttemptScreenshots'), clearSkipped: () => env('allureClearSkippedTests') === true, addAnalyticLabels: () => env('allureAddAnalyticLabels'), addVideoOnPass: () => env('allureAddVideoOnPass'), skipAutomaticScreenshots: () => env('allureSkipAutomaticScreenshots') === true, loggingCommandStepsEnabled: true, avoidLoggingCommands: () => { const input = env('allureAvoidLoggingCommands'); if (Array.isArray(input)) { return input; } if (input && typeof input === 'string') { return input.startsWith('[') ? JSON.parse(input) : input.split(','); } return []; } }; class CypressAllureReporter { constructor() { logger.allure( `creating allure reporter instance, cypress env: %O`, env() ); this.createAllureReporter(config); this.config = config; const onTestFail = (test, err) => { logger.mocha(`EVENT_TEST_FAIL: %s %O`, test.title, test); this.reporter.failTestCase(test, err); attachVideo(this.reporter, test, 'failed'); }; const onTestBegin = (test) => { logger.mocha(`EVENT_TEST_BEGIN: %s %O`, test.title, test); this.reporter.startCase(test, config); }; const onTestEnd = (test) => { logger.mocha(`EVENT_TEST_END: %s %O`, test.title, test); attachVideo(this.reporter, test, 'finished'); this.reporter.gherkin.checkLinksInExamplesTable(); this.reporter.gherkin.checkTags(); this.reporter.endTest(test); }; Cypress.mocha .getRunner() .on(EVENT_SUITE_BEGIN, (suite) => { logger.mocha(`EVENT_SUITE_BEGIN: %s %O`, suite.title, suite); this.reporter.startSuite(suite); }) .on(EVENT_SUITE_END, (suite) => { logger.mocha(`EVENT_SUITE_END: %s %O`, suite.title, suite); /** * only global cypress file suite end * should be triggered from here * others are handled on suite start event */ const isGlobal = suite.title === ''; this.reporter.endSuite(isGlobal); if (config && config.allureEnabled() && isGlobal) { try { cy.now( 'task', 'writeAllureResults', { results: this.reporter.runtime.config, files: this.reporter.files, mapping: this.reporter.mochaIdToAllure, clearSkipped: config.clearSkipped() }, { log: false } ) // eslint-disable-next-line no-console .catch((e) => logger.allure( `failed to execute task to write allure results: %O`, e ) ); logger.allure(`writing allure results`); } catch (e) { // happens when cy.task could not be executed due to fired outside of it logger.allure(`failed to write allure results: %O`, e); } } }) .on(EVENT_TEST_BEGIN, (test) => { onTestBegin(test); }) .on(EVENT_TEST_FAIL, (test, err) => { onTestFail(test, err); }) .on(EVENT_TEST_PASS, (test) => { logger.mocha(`EVENT_TEST_PASS: %s %O`, test.title, test); this.reporter.passTestCase(test); }) .on(EVENT_TEST_RETRY, (test) => { logger.mocha(`EVENT_TEST_RETRY: %s %O`, test.title, test); onTestFail(test, test.err); onTestEnd(test); }) .on(EVENT_TEST_PENDING, (test) => { logger.mocha(`EVENT_TEST_PENDING: %s %O`, test.title, test); this.reporter.pendingTestCase(test); }) .on(EVENT_TEST_END, (test) => { onTestEnd(test); }) .on(EVENT_HOOK_BEGIN, (hook) => { logger.mocha(`EVENT_HOOK_BEGIN: %s %O`, hook.title, hook); this.reporter.startHook(hook); }) .on(EVENT_HOOK_END, (hook) => { logger.mocha(`EVENT_HOOK_END: %s %O`, hook.title, hook); this.reporter.endHook(hook); }); Cypress.on('command:enqueued', (command) => { if (this.commandShouldBeLogged(command)) { logger.cy(`command:enqueued %O`, command); this.reporter.cy.enqueued(command); } }); Cypress.on('command:start', (command) => { if (this.commandShouldBeLogged(command)) { logger.cy(`command:start %O`, command); this.reporter.cy.started(command.attributes); } }); Cypress.on('command:end', (command) => { if (this.commandShouldBeLogged(command)) { logger.cy(`command:end %O`, command); this.reporter.cy.finished(command.attributes); } }); Cypress.on('log:added', (log) => { if (log.state === 'failed') { logger.cy('found failed log:added %O', log); if ( this.reporter.currentExecutable && this.reporter.currentExecutable.info ) { this.reporter.currentExecutable.info.status = 'failed'; } } }); } createAllureReporter(config) { this.reporter = Cypress.Allure ? Cypress.Allure.reporter : new AllureReporter( new AllureRuntime({ resultsDir: config.resultsPath(), writer: new InMemoryAllureWriter() }), config ); } commandShouldBeLogged(command) { if (!this.config.loggingCommandStepsEnabled) { logger.cy(`command tracking is disabled`); return; } if (this.config.avoidLoggingCommands().includes(command.name)) { logger.cy(`command %s tracking is disabled`, command.name); return; } if ( !this.config.shouldLogCypress() && !this.config.shouldLogGherkinSteps() ) { return; } return true; } } // when different hosts used in same test // Cypress opens new host URL and loads index.js // so if we already have Allure data we should not replace it with new instance // https://github.com/Shelex/cypress-allure-plugin/issues/125 if (!Cypress.Allure) { Cypress.Allure = config.allureEnabled() ? new CypressAllureReporter() : stubbedAllure; } Cypress.Screenshot.defaults({ onAfterScreenshot(_, details) { logger.cy(`onAfterScreenshot: %O`, details); if ( config.allureEnabled() && !shouldUseAfterSpec(Cypress.config()) && !config.skipAutomaticScreenshots() ) { logger.allure(`allure enabled, attaching screenshot`); Cypress.Allure.reporter.files.push({ name: details.name || `${details.specName}:${details.takenAt}`, path: details.path, type: ContentType.PNG, testName: Cypress.Allure.reporter.testNameForAttachment }); } } }); const attachVideo = (reporter, test, status) => { if (shouldUseAfterSpec(Cypress.config())) { logger.allure( `video attachment will be handled in after:spec plugins event` ); return; } const shouldAttach = status === 'failed' ? true : test.state !== 'failed' && config.addVideoOnPass(); logger.allure(`check video attachment`); if (Cypress.config().video && reporter.currentTest) { // add video to failed test case or for passed in case addVideoOnPass is true if (!shouldAttach) { return; } const absoluteVideoPath = Cypress.config() .videosFolder.split(config.resultsPath()) .pop(); const relativeVideoPath = path.isAbsolute(absoluteVideoPath) ? path.join( '..', path.relative( Cypress.config().fileServerFolder, absoluteVideoPath ) ) : absoluteVideoPath; const fileName = `${Cypress.spec.name}.mp4`; // avoid duplicating videos, especially for after all hook when test is passed if ( reporter.currentTest.info.attachments.some( (attachment) => attachment.name === fileName ) ) { return; } const videoFilePath = path.join(relativeVideoPath, fileName); if (!videoFilePath) { return; } logger.allure(`attaching video %s`, videoFilePath); reporter.currentTest.addAttachment( fileName, 'video/mp4', videoFilePath ); } }; // need empty after hook to prohibit cypress stop the runner when there are skipped tests in the end after(() => {});