UNPKG

@revoloo/cypress6

Version:

Cypress.io end to end testing tool

239 lines (190 loc) 5.3 kB
/* eslint-disable no-console, prefer-rest-params */ const { codeFrameColumns } = require('@babel/code-frame') const fs = require('fs-extra') const _ = require('lodash') const path = require('path') const { expect } = require('chai') const bluebird = require('bluebird') const Debug = require('debug') const chalk = require('chalk') const stripAnsi = require('strip-ansi') const cypress = require('cypress') const debug = Debug('test') const rootDir = process.cwd() const cp = require('child_process') const _spawn = cp.spawn cp.spawn = function () { arguments[2].stdio = 'pipe' const ret = _spawn.apply(this, arguments) return ret } afterEach(function () { const err = this.currentTest.err if (err) { mapError(err) } }) beforeEach(function () { this.currentTest.timeout(50000) }) exports.runTest = async (options = {}) => { if (!options.spec) { throw new Error('options.spec not supplied') } let parsedSpecOptions = {} if (!_.isArray(options.spec)) { const fileStr = (await fs.readFile(options.spec)).toString() const match = /\/\*\s*EXPECT:\s*({.*})\s*\*\//s.exec(fileStr) if (match) { console.log(match[1]) parsedSpecOptions = require('json5').parse(match[1]) } } const opts = _.defaults(options, { spec: '', expectedResults: { totalFailed: 0, }, stdoutInclude: null, browser: 'electron', exit: true, }) _.merge(opts, parsedSpecOptions) if (_.isString(opts.stdoutInclude)) { opts.stdoutInclude = [opts.stdoutInclude] } console.log(chalk.cyanBright(`starting test run: ${opts.spec}`)) const stdio = captureStdio(process.stdout) let stdout _.extend(process.env, { FAKE_CWD_PATH: '/[cwd]', DEBUG_COLORS: '1', // prevent any Compression progress // messages from showing up VIDEO_COMPRESSION_THROTTLE: 120000, // don't fail our own tests running from forked PR's CYPRESS_INTERNAL_E2E_TESTS: '1', CYPRESS_ENV: 'test', }) return cypress.run({ spec: opts.spec, browser: opts.browser, exit: opts.exit, config: { video: false, }, dev: true, }) .finally(() => { stdout = stdio.toString() stdio.restore() }) .then((res) => { expect(res).includes(opts.expectedResults) }) .then(() => { if (opts.stdoutInclude) { _.forEach(opts.stdoutInclude, (v) => { expect(stdout).include(v) console.log(`${chalk.bold('run matched stdout:')}\n${v}`) }) } // console.log(stdout) console.log(`${chalk.bold('run matched these results:')} ${JSON.stringify(opts.expectedResults, null, 2)}`) }) } const mapError = async (e) => { const slicedStack = e.stack.split('\n') //.slice(1) debug({ slicedStack }) const lastSrcStack = _.findIndex( slicedStack, (v) => !v.includes('node_modules') && v.split(path.sep).length > 2, ) debug({ lastSrcStack }) const entryNodeModuleStack = null //slicedStack[lastSrcStack - 1] debug({ entryNodeModuleStack }) const entryNodeModuleRE = /node_modules\/(.*?)\//.exec( entryNodeModuleStack, ) let entryNodeModule if (entryNodeModuleRE) { entryNodeModule = entryNodeModuleRE[1] } // debug({ entryNodeModule }) let codeFrame debug({ stack: e.stack.split('\n'), rootDir }) const srcStackArr = await bluebird .resolve( e.stack .split('\n') .filter( (v, i) => { return i === 0 || (!v.includes('/node_modules/')) // && v.includes(rootDir)) }, ), ) .mapSeries(async (v) => { const match = /^(\W+)(at[^(]*)\(?(.+?)(:)(\d+)(:)(\d+)(\)?)/.exec(v) debug({ mapStack: v, match }) if (match) { const relativePath = match[3] //path.relative(rootDir, match[3]) match[3] = relativePath if (!codeFrame) { codeFrame = await getCodeFrame(match) } match[3] = chalk.rgb(72, 160, 191)(relativePath) return match.slice(1).join('') } return v }) const srcStack = srcStackArr.join('\n') const srcStackShort = srcStackArr.slice(1, 2).join('\n') debug(srcStack) console.log(chalk.dim(srcStack)) console.log(codeFrame) console.log(` ☠️ ${ entryNodeModule ? ` [${chalk.bold(entryNodeModule)}] ` : '' }${chalk.red(e.message)} ${srcStackShort} `) } const getCodeFrame = async (info) => { if (await fs.pathExists(info[3])) { const location = { start: { line: +info[5], column: +info[7] } } const rawlines = (await fs.readFile(info[3])).toString() // .split('\n') // .slice(location.start.line - 2, location.start.line + 2) // .join('\n') // debug({ path: info[1], location }) const result = codeFrameColumns(rawlines, location, { highlightCode: true, linesAbove: 2, linesBelow: 3, }) return `\n${result}` } } const captureStdio = (stdio, tty) => { let logs = [] let passThrough = null const write = stdio.write const isTTY = stdio.isTTY stdio.write = function (str) { logs.push(str) if (passThrough) { return write.apply(this, [passThrough(str)]) } } if (tty !== undefined) stdio.isTTY = tty return { toString: () => { return stripAnsi(logs.join('')) }, restore () { stdio.write = write stdio.isTTY = isTTY }, } }