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 }, })