Created
December 16, 2018 08:34
-
-
Save NicholasBoll/c1ee8ddd0c3749db7480e0f84e851a30 to your computer and use it in GitHub Desktop.
Revisions
-
NicholasBoll created this gist
Dec 16, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,50 @@ const addContext = (report, screenshots, videoUrl) => { const getTests = t => t.tests const getSuites = t => t.suites const addSuiteContext = (suite, previousTitles = []) => { const titles = suite.title ? previousTitles.concat(suite.title) : previousTitles getTests(suite).forEach(test => { test.timedOut = false // for some reason this is dropped const context = [ { title: 'Video', value: videoUrl, }, ] const testFileName = titles .concat(test.title) .join(' -- ') .replace(/,/g, '') const testScreenshots = screenshots.filter(s => s.includes(testFileName)) testScreenshots.forEach(screenshot => { // There could be multiple screenshots for various reasons. `${testFileName}.png` is the failure one. Others are postfixed with a name if (screenshot.includes(`${testFileName}.png`)) { context.splice(0, 0, { title: 'Failure Screenshot', value: screenshot, }) } else { context.splice(0, 0, { title: screenshot.match(`${testFileName}(.+).png`)[1].replace(' -- ', ''), value: screenshot, }) } }) test.context = JSON.stringify(context) }) getSuites(suite).forEach(s => { addSuiteContext(s, titles) }) } addSuiteContext(report.suites) return report } module.exports = addContext This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,74 @@ function getPercentClass(pct) { if (pct <= 50) { return 'danger' } else if (pct > 50 && pct < 80) { return 'warning' } else { return 'success' } } const min = (a, b) => (a < b ? a : b) const max = (a, b) => (a > b ? a : b) function mergedReports(reports) { const baseStats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0, start: '2050-12-31T00:00:00.000Z', end: '1940-01-01T00:00:00.000Z', duration: 0, testsRegistered: 0, passPercent: 0, pendingPercent: 0, other: 0, hasOther: false, skipped: 0, hasSkipped: false, passPercentClass: 'success', pendingPercentClass: 'success', } const mergeStats = (result, stat) => ({ suites: result.suites + stat.suites, tests: result.tests + stat.tests, passes: result.passes + stat.passes, pending: result.pending + stat.pending, failures: result.failures + stat.failures, start: min(result.start, stat.start), end: max(result.end, stat.end), duration: result.duration + stat.duration, testsRegistered: result.testsRegistered + stat.testsRegistered, other: result.other + stat.other, hasOther: result.hasOther || stat.hasOther, skipped: result.skipped + stat.skipped, hasSkipped: result.hasSkipped || stat.hasSkipped, }) const stats = reports.map(f => f.stats).reduce(mergeStats, baseStats) // calculated stats stats.passPercent = Math.round(stats.passes / stats.tests * 100) stats.pendingPercent = Math.round(stats.pending / stats.tests * 100) stats.passPercentClass = getPercentClass(stats.passPercent) stats.pendingPercentClass = getPercentClass(stats.pendingPercent) /** Combine fields by merging the arrays */ const concatFields = field => (result, item) => result.concat(item[field]) const baseSuites = reports[0].suites const suites = reports.reduce(concatFields('suites'), []) const mergedReport = { stats, suites: Object.assign({}, baseSuites, { suites }), copyrightYear: new Date().getFullYear(), } return mergedReport } module.exports = mergedReports This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,147 @@ const Rx = require('rxjs') const cypress = require('cypress') const path = require('path') const glob = require('glob') const fs = require('fs-extra') const addContext = require('./addContext') const mergeReports = require('./mergeReports') const chalk = require('chalk') const { argv } = require('yargs') const cypressConfig = require('../cypress.json') const marge = require('mochawesome-report-generator') const flatten = a => [].concat(...a) const getVideoPath = filePath => path.resolve(__dirname, `videos_${path.basename(filePath, path.extname(filePath))}`) const getScreenshotPath = filePath => path.resolve(__dirname, `screenshots_${path.basename(filePath, path.extname(filePath))}`) const files = flatten( (argv._.length ? argv._ : ['**/*']).map(f => glob.sync(path.resolve(__dirname, 'integration', f))) ).filter(f => /\.[a-z]{1,6}$/.test(f)) const assetPath = argv.assetPath || '' const baseUrl = argv.baseUrl || cypressConfig.baseUrl const usersFileName = argv.usersFileName || 'users.json' const concurrency = parseInt(argv.concurrency, 10) || 1 const retries = parseInt(argv.retries, 10) || 0 if (files.length === 0) { console.error(chalk.bold.red('No test files found')) process.exit(1) } console.log('Running test files:') console.log(files.map(f => path.relative(__dirname, f)).join('\n')) // used to round-robin users let testNumber = -1 const getTestNumber = () => { return testNumber++ } const getReporterOptions = filename => ({ reportDir: './cypress/reports', reportFilename: filename, reportTitle: filename, reportPageTitle: filename, overwrite: true, inline: true, html: false, json: true, }) const getConfig = file => ({ spec: file, config: { videosFolder: getVideoPath(file), screenshotsFolder: getScreenshotPath(file), baseUrl, }, env: { TEST_NUMBER: getTestNumber(), USERS_FILE_NAME: usersFileName, }, reporter: 'mochawesome', reporterOptions: getReporterOptions(path.basename(file)), }) fs.removeSync(path.resolve(__dirname, 'reports')) fs.mkdirsSync(path.resolve(__dirname, 'reports', 'videos')) fs.mkdirsSync(path.resolve(__dirname, 'reports', 'screenshots')) const cypressRun = file => { return cypress.run(getConfig(file)).then(results => { if (results.totalTests === undefined) { // no results were run - probably an error messages throw results } const testName = path.basename(file, path.extname(file)) let screenshots = [] if (fs.pathExistsSync(getScreenshotPath(file))) { fs.copySync(getScreenshotPath(file), path.resolve(__dirname, 'reports', 'screenshots')) screenshots = glob .sync(`${getScreenshotPath(file)}/**/*.png`, { cwd: getScreenshotPath(file), }) .map(s => s.replace(getScreenshotPath(file), 'screenshots')) fs.removeSync(getScreenshotPath(file)) } const video = glob.sync(`${getVideoPath(file)}/**/*.mp4`)[0] fs.copySync(video, path.resolve(__dirname, 'reports', 'videos', `${testName}.mp4`)) fs.removeSync(getVideoPath(file)) const json = addContext( JSON.parse(fs.readFileSync(path.resolve(__dirname, 'reports', `${testName}.json`))), screenshots, `${assetPath}videos/${testName}.mp4` ) if (json.suites) { json.suites.title = path.relative(`${__dirname}/integration`, file) } if (json.stats.failures || json.stats.tests === 0 || json.stats.other) { throw json } return json }) } const runSpec = file => Rx.Observable.defer(() => cypressRun(file)) .retry(retries) .catch(error => { if (error.stats && (error.stats.failures || error.stats.other)) { return Rx.Observable.of(error) } else { return Rx.Observable.throw(error) } }) const combineReports = reports => { const mergedReports = mergeReports(reports) marge.create( mergedReports, Object.assign(getReporterOptions('UI Test Results'), { saveJson: true, reportFilename: 'index', }) ) if (mergedReports.stats.failures || mergedReports.stats.other) { process.exitCode = 1 console.log(chalk.bold.red('Exit Code:'), process.exitCode) } } Rx.Observable.of(...files) .flatMap(runSpec, null, concurrency) .filter(r => r) // only process good results .toArray() .subscribe({ next: combineReports, error: err => { console.error(chalk.bold.red('Processing Error:'), err) process.exitCode = 1 }, })