How to use endVideoCapture method in Cypress

Best JavaScript code snippet using cypress

run.js

Source:run.js Github

copy

Full Screen

1/* eslint-disable no-console */2const _ = require('lodash')3const la = require('lazy-ass')4const pkg = require('../../../root')5const path = require('path')6const chalk = require('chalk')7const human = require('human-interval')8const debug = require('debug')('cypress:server:run')9const Promise = require('bluebird')10const logSymbols = require('log-symbols')11const recordMode = require('./record')12const errors = require('../errors')13const Project = require('../project')14const Reporter = require('../reporter')15const browsers = require('../browsers')16const openProject = require('../open_project')17const videoCapture = require('../video_capture')18const fs = require('../util/fs')19const env = require('../util/env')20const trash = require('../util/trash')21const random = require('../util/random')22const system = require('../util/system')23const duration = require('../util/duration')24const newlines = require('../util/newlines')25const terminal = require('../util/terminal')26const specsUtil = require('../util/specs')27const humanTime = require('../util/human_time')28const electronApp = require('../util/electron_app')29const settings = require('../util/settings')30const chromePolicyCheck = require('../util/chrome_policy_check')31const DELAY_TO_LET_VIDEO_FINISH_MS = 100032const color = (val, c) => {33 return chalk[c](val)34}35const gray = (val) => {36 return color(val, 'gray')37}38const colorIf = function (val, c) {39 if (val === 0) {40 val = '-'41 c = 'gray'42 }43 return color(val, c)44}45const getSymbol = function (num) {46 if (num) {47 return logSymbols.error48 }49 return logSymbols.success50}51const getWidth = (table, index) => {52 // get the true width of a table's column,53 // based off of calculated table options for that column54 const columnWidth = table.options.colWidths[index]55 if (columnWidth) {56 return columnWidth - (table.options.style['padding-left'] + table.options.style['padding-right'])57 }58}59const formatBrowser = (browser) => {60 // TODO: finish browser61 return _.compact([62 browser.displayName,63 browser.majorVersion,64 browser.isHeadless && gray('(headless)'),65 ]).join(' ')66}67const formatFooterSummary = (results) => {68 const { totalFailed, runs } = results69 // pass or fail color70 const c = totalFailed ? 'red' : 'green'71 const phrase = (() => {72 // if we have any specs failing...73 if (!totalFailed) {74 return 'All specs passed!'75 }76 // number of specs77 const total = runs.length78 const failingRuns = _.filter(runs, 'stats.failures').length79 const percent = Math.round((failingRuns / total) * 100)80 return `${failingRuns} of ${total} failed (${percent}%)`81 })()82 return [83 formatSymbolSummary(totalFailed),84 color(phrase, c),85 gray(duration.format(results.totalDuration)),86 colorIf(results.totalTests, 'reset'),87 colorIf(results.totalPassed, 'green'),88 colorIf(totalFailed, 'red'),89 colorIf(results.totalPending, 'cyan'),90 colorIf(results.totalSkipped, 'blue'),91 ]92}93const formatSymbolSummary = (failures) => {94 return getSymbol(failures)95}96const formatPath = (name, n, colour = 'reset') => {97 if (!name) return ''98 const fakeCwdPath = env.get('FAKE_CWD_PATH')99 if (fakeCwdPath && env.get('CYPRESS_ENV') === 'test') {100 // if we're testing within Cypress, we want to strip out101 // the current working directory before calculating the stdout tables102 // this will keep our snapshots consistent everytime we run103 const cwdPath = process.cwd()104 name = name105 .split(cwdPath)106 .join(fakeCwdPath)107 }108 // add newLines at each n char and colorize the path109 if (n) {110 let nameWithNewLines = newlines.addNewlineAtEveryNChar(name, n)111 return `${color(nameWithNewLines, colour)}`112 }113 return `${color(name, colour)}`114}115const formatNodeVersion = ({ resolvedNodeVersion, resolvedNodePath }, width) => {116 debug('formatting Node version. %o', { version: resolvedNodeVersion, path: resolvedNodePath })117 if (resolvedNodePath) {118 return formatPath(`v${resolvedNodeVersion} (${resolvedNodePath})`, width)119 }120}121const formatRecordParams = function (runUrl, parallel, group) {122 if (runUrl) {123 if (!group) {124 group = false125 }126 return `Group: ${group}, Parallel: ${Boolean(parallel)}`127 }128}129const displayRunStarting = function (options = {}) {130 const { config, specs, specPattern, browser, runUrl, parallel, group } = options131 console.log('')132 terminal.divider('=')133 console.log('')134 terminal.header('Run Starting', {135 color: ['reset'],136 })137 console.log('')138 // if we show Node Version, then increase 1st column width139 // to include wider 'Node Version:'140 const colWidths = config.resolvedNodePath ? [16, 84] : [12, 88]141 const table = terminal.table({142 colWidths,143 type: 'outsideBorder',144 })145 const formatSpecPattern = () => {146 // foo.spec.js, bar.spec.js, baz.spec.js147 // also inserts newlines at col width148 if (specPattern) {149 return formatPath(specPattern.join(', '), getWidth(table, 1))150 }151 }152 const formatSpecs = (specs) => {153 // 25 found: (foo.spec.js, bar.spec.js, baz.spec.js)154 const names = _.map(specs, 'name')155 const specsTruncated = _.truncate(names.join(', '), { length: 250 })156 const stringifiedSpecs = [157 `${names.length} found `,158 '(',159 specsTruncated,160 ')',161 ]162 .join('')163 return formatPath(stringifiedSpecs, getWidth(table, 1))164 }165 const data = _166 .chain([167 [gray('Cypress:'), pkg.version],168 [gray('Browser:'), formatBrowser(browser)],169 [gray('Node Version:'), formatNodeVersion(config, getWidth(table, 1))],170 [gray('Specs:'), formatSpecs(specs)],171 [gray('Searched:'), formatSpecPattern(specPattern)],172 [gray('Params:'), formatRecordParams(runUrl, parallel, group)],173 [gray('Run URL:'), runUrl ? formatPath(runUrl, getWidth(table, 1)) : ''],174 ])175 .filter(_.property(1))176 .value()177 table.push(...data)178 console.log(table.toString())179 return console.log('')180}181const displaySpecHeader = function (name, curr, total, estimated) {182 console.log('')183 const PADDING = 2184 const table = terminal.table({185 colWidths: [10, 70, 20],186 colAligns: ['left', 'left', 'right'],187 type: 'pageDivider',188 style: {189 'padding-left': PADDING,190 'padding-right': 0,191 },192 })193 table.push(['', ''])194 table.push([195 'Running:',196 `${formatPath(name, getWidth(table, 1), 'gray')}`,197 gray(`(${curr} of ${total})`),198 ])199 console.log(table.toString())200 if (estimated) {201 const estimatedLabel = `${' '.repeat(PADDING)}Estimated:`202 return console.log(estimatedLabel, gray(humanTime.long(estimated)))203 }204}205const collectTestResults = (obj = {}, estimated) => {206 return {207 name: _.get(obj, 'spec.name'),208 tests: _.get(obj, 'stats.tests'),209 passes: _.get(obj, 'stats.passes'),210 pending: _.get(obj, 'stats.pending'),211 failures: _.get(obj, 'stats.failures'),212 skipped: _.get(obj, 'stats.skipped'),213 duration: humanTime.long(_.get(obj, 'stats.wallClockDuration')),214 estimated: estimated && humanTime.long(estimated),215 screenshots: obj.screenshots && obj.screenshots.length,216 video: Boolean(obj.video),217 }218}219const renderSummaryTable = (runUrl) => {220 return function (results) {221 const { runs } = results222 console.log('')223 terminal.divider('=')224 console.log('')225 terminal.header('Run Finished', {226 color: ['reset'],227 })228 if (runs && runs.length) {229 const colAligns = ['left', 'left', 'right', 'right', 'right', 'right', 'right', 'right']230 const colWidths = [3, 41, 11, 9, 9, 9, 9, 9]231 const table1 = terminal.table({232 colAligns,233 colWidths,234 type: 'noBorder',235 head: [236 '',237 gray('Spec'),238 '',239 gray('Tests'),240 gray('Passing'),241 gray('Failing'),242 gray('Pending'),243 gray('Skipped'),244 ],245 })246 const table2 = terminal.table({247 colAligns,248 colWidths,249 type: 'border',250 })251 const table3 = terminal.table({252 colAligns,253 colWidths,254 type: 'noBorder',255 head: formatFooterSummary(results),256 })257 _.each(runs, (run) => {258 const { spec, stats } = run259 const ms = duration.format(stats.wallClockDuration)260 return table2.push([261 formatSymbolSummary(stats.failures),262 formatPath(spec.name, getWidth(table2, 1)),263 color(ms, 'gray'),264 colorIf(stats.tests, 'reset'),265 colorIf(stats.passes, 'green'),266 colorIf(stats.failures, 'red'),267 colorIf(stats.pending, 'cyan'),268 colorIf(stats.skipped, 'blue'),269 ])270 })271 console.log('')272 console.log('')273 console.log(terminal.renderTables(table1, table2, table3))274 console.log('')275 if (runUrl) {276 console.log('')277 const table4 = terminal.table({278 colWidths: [100],279 type: 'pageDivider',280 style: {281 'padding-left': 2,282 },283 })284 table4.push(['', ''])285 table4.push([`Recorded Run: ${formatPath(runUrl, getWidth(table4, 0), 'gray')}`])286 console.log(terminal.renderTables(table4))287 console.log('')288 }289 }290 }291}292const iterateThroughSpecs = function (options = {}) {293 const { specs, runEachSpec, parallel, beforeSpecRun, afterSpecRun, config } = options294 const serial = () => {295 return Promise.mapSeries(specs, runEachSpec)296 }297 const serialWithRecord = () => {298 return Promise299 .mapSeries(specs, (spec, index, length) => {300 return beforeSpecRun(spec)301 .then(({ estimated }) => {302 return runEachSpec(spec, index, length, estimated)303 })304 .tap((results) => {305 return afterSpecRun(spec, results, config)306 })307 })308 }309 const parallelWithRecord = (runs) => {310 return beforeSpecRun()311 .then(({ spec, claimedInstances, totalInstances, estimated }) => {312 // no more specs to run?313 if (!spec) {314 // then we're done!315 return runs316 }317 // find the actual spec object amongst318 // our specs array since the API sends us319 // the relative name320 spec = _.find(specs, { relative: spec })321 return runEachSpec(322 spec,323 claimedInstances - 1,324 totalInstances,325 estimated326 )327 .tap((results) => {328 runs.push(results)329 return afterSpecRun(spec, results, config)330 })331 .then(() => {332 // recurse333 return parallelWithRecord(runs)334 })335 })336 }337 if (parallel) {338 // if we are running in parallel339 // then ask the server for the next spec340 return parallelWithRecord([])341 }342 if (beforeSpecRun) {343 // else iterate serialially and record344 // the results of each spec345 return serialWithRecord()346 }347 // else iterate in serial348 return serial()349}350const getProjectId = Promise.method((project, id) => {351 if (id == null) {352 id = env.get('CYPRESS_PROJECT_ID')353 }354 // if we have an ID just use it355 if (id) {356 return id357 }358 return project.getProjectId()359 .catch(() => {360 // no id no problem361 return null362 })363})364const getDefaultBrowserOptsByFamily = (browser, project, writeVideoFrame) => {365 la(browsers.isBrowserFamily(browser.family), 'invalid browser family in', browser)366 if (browser.family === 'electron') {367 return getElectronProps(browser.isHeaded, project, writeVideoFrame)368 }369 if (browser.family === 'chrome') {370 return getChromeProps(browser.isHeaded, project, writeVideoFrame)371 }372 return {}373}374const getChromeProps = (isHeaded, project, writeVideoFrame) => {375 const shouldWriteVideo = Boolean(writeVideoFrame)376 debug('setting Chrome properties %o', { isHeaded, shouldWriteVideo })377 return _378 .chain({})379 .tap((props) => {380 if (isHeaded && writeVideoFrame) {381 props.screencastFrame = (e) => {382 // https://chromedevtools.github.io/devtools-protocol/tot/Page#event-screencastFrame383 writeVideoFrame(Buffer.from(e.data, 'base64'))384 }385 }386 })387 .value()388}389const getElectronProps = (isHeaded, project, writeVideoFrame) => {390 return _391 .chain({392 width: 1280,393 height: 720,394 show: isHeaded,395 onCrashed () {396 const err = errors.get('RENDERER_CRASHED')397 errors.log(err)398 return project.emit('exitEarlyWithErr', err.message)399 },400 onNewWindow (e, url, frameName, disposition, options) {401 // force new windows to automatically open with show: false402 // this prevents window.open inside of javascript client code403 // to cause a new BrowserWindow instance to open404 // https://github.com/cypress-io/cypress/issues/123405 options.show = false406 },407 })408 .tap((props) => {409 if (writeVideoFrame) {410 props.recordFrameRate = 20411 props.onPaint = (event, dirty, image) => {412 return writeVideoFrame(image.toJPEG(100))413 }414 }415 })416 .value()417}418const sumByProp = (runs, prop) => {419 return _.sumBy(runs, prop) || 0420}421const getRun = (run, prop) => {422 return _.get(run, prop)423}424const writeOutput = (outputPath, results) => {425 return Promise.try(() => {426 if (!outputPath) {427 return428 }429 debug('saving output results %o', { outputPath })430 return fs.outputJsonAsync(outputPath, results)431 })432}433const onWarning = (err) => {434 console.log(chalk.yellow(err.message))435}436const openProjectCreate = (projectRoot, socketId, options) => {437 // now open the project to boot the server438 // putting our web client app in headless mode439 // - NO display server logs (via morgan)440 // - YES display reporter results (via mocha reporter)441 return openProject442 .create(projectRoot, options, {443 socketId,444 morgan: false,445 report: true,446 isTextTerminal: options.isTextTerminal,447 onWarning,448 onError (err) {449 console.log('')450 if (err.details) {451 console.log(err.message)452 console.log('')453 console.log(chalk.yellow(err.details))454 } else {455 console.log(err.stack)456 }457 return openProject.emit('exitEarlyWithErr', err.message)458 },459 })460 .catch({ portInUse: true }, (err) => {461 // TODO: this needs to move to emit exitEarly462 // so we record the failure in CI463 return errors.throw('PORT_IN_USE_LONG', err.port)464 })465}466const createAndOpenProject = function (socketId, options) {467 const { projectRoot, projectId } = options468 return Project469 .ensureExists(projectRoot, options)470 .then(() => {471 // open this project without472 // adding it to the global cache473 return openProjectCreate(projectRoot, socketId, options)474 })475 .call('getProject')476 .then((project) => {477 return Promise.props({478 project,479 config: project.getConfig(),480 projectId: getProjectId(project, projectId),481 })482 })483}484const removeOldProfiles = () => {485 return browsers.removeOldProfiles()486 .catch((err) => {487 // dont make removing old browsers profiles break the build488 return errors.warning('CANNOT_REMOVE_OLD_BROWSER_PROFILES', err.stack)489 })490}491const trashAssets = Promise.method((config = {}) => {492 if (config.trashAssetsBeforeRuns !== true) {493 return494 }495 return Promise.join(496 trash.folder(config.videosFolder),497 trash.folder(config.screenshotsFolder)498 )499 .catch((err) => {500 // dont make trashing assets fail the build501 return errors.warning('CANNOT_TRASH_ASSETS', err.stack)502 })503})504// if we've been told to record and we're not spawning a headed browser505const browserCanBeRecorded = (browser) => {506 if (browser.family === 'electron' && browser.isHeadless) {507 return true508 }509 if (browser.family === 'chrome' && browser.isHeaded) {510 return true511 }512 return false513}514const createVideoRecording = function (videoName) {515 const outputDir = path.dirname(videoName)516 return fs517 .ensureDirAsync(outputDir)518 .then(() => {519 return videoCapture520 .start(videoName, {521 onError (err) {522 // catch video recording failures and log them out523 // but don't let this affect the run at all524 return errors.warning('VIDEO_RECORDING_FAILED', err.stack)525 },526 })527 })528}529const getVideoRecordingDelay = function (startedVideoCapture) {530 if (startedVideoCapture) {531 return DELAY_TO_LET_VIDEO_FINISH_MS532 }533 return 0534}535const maybeStartVideoRecording = Promise.method(function (options = {}) {536 const { spec, browser, video, videosFolder } = options537 // bail if we've been told not to capture538 // a video recording539 if (!video) {540 return541 }542 // handle if this browser cannot actually543 // be recorded544 if (!browserCanBeRecorded(browser)) {545 console.log('')546 // TODO update error messages and included browser name and headed mode547 if (browser.family === 'electron' && browser.isHeaded) {548 errors.warning('CANNOT_RECORD_VIDEO_HEADED')549 } else {550 errors.warning('CANNOT_RECORD_VIDEO_FOR_THIS_BROWSER', browser.name)551 }552 return553 }554 // make sure we have a videosFolder555 if (!videosFolder) {556 throw new Error('Missing videoFolder for recording')557 }558 const videoPath = (suffix) => {559 return path.join(videosFolder, spec.name + suffix)560 }561 const videoName = videoPath('.mp4')562 const compressedVideoName = videoPath('-compressed.mp4')563 return this.createVideoRecording(videoName)564 .then((props = {}) => {565 return {566 videoName,567 compressedVideoName,568 endVideoCapture: props.endVideoCapture,569 writeVideoFrame: props.writeVideoFrame,570 startedVideoCapture: props.startedVideoCapture,571 }572 })573})574module.exports = {575 collectTestResults,576 getProjectId,577 writeOutput,578 openProjectCreate,579 createVideoRecording,580 getVideoRecordingDelay,581 maybeStartVideoRecording,582 getChromeProps,583 getElectronProps,584 displayResults (obj = {}, estimated) {585 const results = collectTestResults(obj, estimated)586 const c = results.failures ? 'red' : 'green'587 console.log('')588 terminal.header('Results', {589 color: [c],590 })591 const table = terminal.table({592 colWidths: [14, 86],593 type: 'outsideBorder',594 })595 const data = _.chain([596 ['Tests:', results.tests],597 ['Passing:', results.passes],598 ['Failing:', results.failures],599 ['Pending:', results.pending],600 ['Skipped:', results.skipped],601 ['Screenshots:', results.screenshots],602 ['Video:', results.video],603 ['Duration:', results.duration],604 estimated ? ['Estimated:', results.estimated] : undefined,605 ['Spec Ran:', formatPath(results.name, getWidth(table, 1), c)],606 ])607 .compact()608 .map((arr) => {609 const [key, val] = arr610 return [color(key, 'gray'), color(val, c)]611 })612 .value()613 table.push(...data)614 console.log('')615 console.log(table.toString())616 console.log('')617 },618 displayScreenshots (screenshots = []) {619 console.log('')620 terminal.header('Screenshots', { color: ['yellow'] })621 console.log('')622 const table = terminal.table({623 colWidths: [3, 82, 15],624 colAligns: ['left', 'left', 'right'],625 type: 'noBorder',626 style: {627 'padding-right': 0,628 },629 chars: {630 'left': ' ',631 'right': '',632 },633 })634 screenshots.forEach((screenshot) => {635 const dimensions = gray(`(${screenshot.width}x${screenshot.height})`)636 table.push([637 '-',638 formatPath(`${screenshot.path}`, getWidth(table, 1)),639 gray(dimensions),640 ])641 })642 console.log(table.toString())643 console.log('')644 },645 postProcessRecording (end, name, cname, videoCompression, shouldUploadVideo) {646 debug('ending the video recording %o', { name, videoCompression, shouldUploadVideo })647 // once this ended promises resolves648 // then begin processing the file649 return end()650 .then(() => {651 // dont process anything if videoCompress is off652 // or we've been told not to upload the video653 if (videoCompression === false || shouldUploadVideo === false) {654 return655 }656 console.log('')657 terminal.header('Video', {658 color: ['cyan'],659 })660 console.log('')661 const table = terminal.table({662 colWidths: [3, 21, 76],663 colAligns: ['left', 'left', 'left'],664 type: 'noBorder',665 style: {666 'padding-right': 0,667 },668 chars: {669 'left': ' ',670 'right': '',671 },672 })673 table.push([674 gray('-'),675 gray('Started processing:'),676 chalk.cyan(`Compressing to ${videoCompression} CRF`),677 ])678 console.log(table.toString())679 const started = Date.now()680 let progress = Date.now()681 const throttle = env.get('VIDEO_COMPRESSION_THROTTLE') || human('10 seconds')682 const onProgress = function (float) {683 if (float === 1) {684 const finished = Date.now() - started685 const dur = `(${humanTime.long(finished)})`686 const table = terminal.table({687 colWidths: [3, 21, 61, 15],688 colAligns: ['left', 'left', 'left', 'right'],689 type: 'noBorder',690 style: {691 'padding-right': 0,692 },693 chars: {694 'left': ' ',695 'right': '',696 },697 })698 table.push([699 gray('-'),700 gray('Finished processing:'),701 `${formatPath(name, getWidth(table, 2), 'cyan')}`,702 gray(dur),703 ])704 console.log(table.toString())705 console.log('')706 }707 if (Date.now() - progress > throttle) {708 // bump up the progress so we dont709 // continuously get notifications710 progress += throttle711 const percentage = `${Math.ceil(float * 100)}%`712 console.log(' Compression progress: ', chalk.cyan(percentage))713 }714 }715 // bar.tickTotal(float)716 return videoCapture.process(name, cname, videoCompression, onProgress)717 })718 .catch((err) => {719 // log that post processing was attempted720 // but failed and dont let this change the run exit code721 errors.warning('VIDEO_POST_PROCESSING_FAILED', err.stack)722 })723 },724 launchBrowser (options = {}) {725 const { browser, spec, writeVideoFrame, project, screenshots, projectRoot } = options726 const browserOpts = getDefaultBrowserOptsByFamily(browser, project, writeVideoFrame)727 browserOpts.automationMiddleware = {728 onAfterResponse: (message, data, resp) => {729 if (message === 'take:screenshot' && resp) {730 screenshots.push(this.screenshotMetadata(data, resp))731 }732 return resp733 },734 }735 browserOpts.projectRoot = projectRoot736 return openProject.launch(browser, spec, browserOpts)737 },738 listenForProjectEnd (project, exit) {739 return new Promise((resolve) => {740 if (exit === false) {741 resolve = () => {742 console.log('not exiting due to options.exit being false')743 }744 }745 const onEarlyExit = function (errMsg) {746 // probably should say we ended747 // early too: (Ended Early: true)748 // in the stats749 const obj = {750 error: errors.stripAnsi(errMsg),751 stats: {752 failures: 1,753 tests: 0,754 passes: 0,755 pending: 0,756 suites: 0,757 skipped: 0,758 wallClockDuration: 0,759 wallClockStartedAt: new Date().toJSON(),760 wallClockEndedAt: new Date().toJSON(),761 },762 }763 return resolve(obj)764 }765 const onEnd = (obj) => {766 return resolve(obj)767 }768 // when our project fires its end event769 // resolve the promise770 project.once('end', onEnd)771 return project.once('exitEarlyWithErr', onEarlyExit)772 })773 },774 waitForBrowserToConnect (options = {}) {775 const { project, socketId, timeout } = options776 let attempts = 0777 const wait = () => {778 return Promise.join(779 this.waitForSocketConnection(project, socketId),780 this.launchBrowser(options)781 )782 .timeout(timeout || 30000)783 .catch(Promise.TimeoutError, (err) => {784 attempts += 1785 console.log('')786 // always first close the open browsers787 // before retrying or dieing788 return openProject.closeBrowser()789 .then(() => {790 if (attempts === 1 || attempts === 2) {791 // try again up to 3 attempts792 const word = attempts === 1 ? 'Retrying...' : 'Retrying again...'793 errors.warning('TESTS_DID_NOT_START_RETRYING', word)794 return wait()795 }796 err = errors.get('TESTS_DID_NOT_START_FAILED')797 errors.log(err)798 return project.emit('exitEarlyWithErr', err.message)799 })800 })801 }802 return wait()803 },804 waitForSocketConnection (project, id) {805 debug('waiting for socket connection... %o', { id })806 return new Promise((resolve, reject) => {807 const fn = function (socketId) {808 debug('got socket connection %o', { id: socketId })809 if (socketId === id) {810 // remove the event listener if we've connected811 project.removeListener('socket:connected', fn)812 // resolve the promise813 return resolve()814 }815 }816 // when a socket connects verify this817 // is the one that matches our id!818 return project.on('socket:connected', fn)819 })820 },821 waitForTestsToFinishRunning (options = {}) {822 const { project, screenshots, startedVideoCapture, endVideoCapture, videoName, compressedVideoName, videoCompression, videoUploadOnPasses, exit, spec, estimated } = options823 // https://github.com/cypress-io/cypress/issues/2370824 // delay 1 second if we're recording a video to give825 // the browser padding to render the final frames826 // to avoid chopping off the end of the video827 const delay = this.getVideoRecordingDelay(startedVideoCapture)828 return this.listenForProjectEnd(project, exit)829 .delay(delay)830 .then((obj) => {831 _.defaults(obj, {832 error: null,833 hooks: null,834 tests: null,835 video: null,836 screenshots: null,837 reporterStats: null,838 })839 if (startedVideoCapture) {840 obj.video = videoName841 }842 if (screenshots) {843 obj.screenshots = screenshots844 }845 obj.spec = spec846 const finish = () => {847 return obj848 }849 this.displayResults(obj, estimated)850 if (screenshots && screenshots.length) {851 this.displayScreenshots(screenshots)852 }853 const { tests, stats } = obj854 const hasFailingTests = _.get(stats, 'failures') > 0855 // if we have a video recording856 if (startedVideoCapture && tests && tests.length) {857 // always set the video timestamp on tests858 obj.tests = Reporter.setVideoTimestamp(startedVideoCapture, tests)859 }860 // we should upload the video if we upload on passes (by default)861 // or if we have any failures and have started the video862 const suv = Boolean(videoUploadOnPasses === true || (startedVideoCapture && hasFailingTests))863 obj.shouldUploadVideo = suv864 debug('attempting to close the browser')865 // always close the browser now as opposed to letting866 // it exit naturally with the parent process due to867 // electron bug in windows868 return openProject869 .closeBrowser()870 .then(() => {871 if (endVideoCapture) {872 return this.postProcessRecording(873 endVideoCapture,874 videoName,875 compressedVideoName,876 videoCompression,877 suv878 ).then(finish)879 // TODO: add a catch here880 }881 return finish()882 })883 })884 },885 screenshotMetadata (data, resp) {886 return {887 screenshotId: random.id(),888 name: data.name || null,889 testId: data.testId,890 takenAt: resp.takenAt,891 path: resp.path,892 height: resp.dimensions.height,893 width: resp.dimensions.width,894 }895 },896 runSpecs (options = {}) {897 const { config, browser, sys, headed, outputPath, specs, specPattern, beforeSpecRun, afterSpecRun, runUrl, parallel, group } = options898 const isHeadless = browser.family === 'electron' && !headed899 browser.isHeadless = isHeadless900 browser.isHeaded = !isHeadless901 const results = {902 startedTestsAt: null,903 endedTestsAt: null,904 totalDuration: null,905 totalSuites: null,906 totalTests: null,907 totalFailed: null,908 totalPassed: null,909 totalPending: null,910 totalSkipped: null,911 runs: null,912 browserPath: browser.path,913 browserName: browser.name,914 browserVersion: browser.version,915 osName: sys.osName,916 osVersion: sys.osVersion,917 cypressVersion: pkg.version,918 runUrl,919 config,920 }921 displayRunStarting({922 config,923 specs,924 group,925 runUrl,926 browser,927 parallel,928 specPattern,929 })930 const runEachSpec = (spec, index, length, estimated) => {931 displaySpecHeader(spec.name, index + 1, length, estimated)932 return this.runSpec(spec, options, estimated)933 .get('results')934 .tap((results) => {935 return debug('spec results %o', results)936 })937 }938 return iterateThroughSpecs({939 specs,940 config,941 parallel,942 runEachSpec,943 afterSpecRun,944 beforeSpecRun,945 })946 .then((runs = []) => {947 results.startedTestsAt = getRun(_.first(runs), 'stats.wallClockStartedAt')948 results.endedTestsAt = getRun(_.last(runs), 'stats.wallClockEndedAt')949 results.totalDuration = sumByProp(runs, 'stats.wallClockDuration')950 results.totalSuites = sumByProp(runs, 'stats.suites')951 results.totalTests = sumByProp(runs, 'stats.tests')952 results.totalPassed = sumByProp(runs, 'stats.passes')953 results.totalPending = sumByProp(runs, 'stats.pending')954 results.totalFailed = sumByProp(runs, 'stats.failures')955 results.totalSkipped = sumByProp(runs, 'stats.skipped')956 results.runs = runs957 debug('final results of all runs: %o', results)958 return writeOutput(outputPath, results).return(results)959 })960 },961 runSpec (spec = {}, options = {}, estimated) {962 const { project, browser } = options963 const { isHeadless } = browser964 debug('about to run spec %o', {965 spec,966 isHeadless,967 browser,968 })969 const screenshots = []970 // we know we're done running headlessly971 // when the renderer has connected and972 // finishes running all of the tests.973 // we're using an event emitter interface974 // to gracefully handle this in promise land975 return this.maybeStartVideoRecording({976 spec,977 browser,978 video: options.video,979 videosFolder: options.videosFolder,980 })981 .then((videoRecordProps = {}) => {982 return Promise.props({983 results: this.waitForTestsToFinishRunning({984 spec,985 project,986 estimated,987 screenshots,988 videoName: videoRecordProps.videoName,989 compressedVideoName: videoRecordProps.compressedVideoName,990 endVideoCapture: videoRecordProps.endVideoCapture,991 startedVideoCapture: videoRecordProps.startedVideoCapture,992 exit: options.exit,993 videoCompression: options.videoCompression,994 videoUploadOnPasses: options.videoUploadOnPasses,995 }),996 connection: this.waitForBrowserToConnect({997 spec,998 project,999 browser,1000 screenshots,1001 writeVideoFrame: videoRecordProps.writeVideoFrame,1002 socketId: options.socketId,1003 webSecurity: options.webSecurity,1004 projectRoot: options.projectRoot,1005 }),1006 })1007 })1008 },1009 findSpecs (config, specPattern) {1010 return specsUtil1011 .find(config, specPattern)1012 .tap((specs = []) => {1013 if (debug.enabled) {1014 const names = _.map(specs, 'name')1015 return debug(1016 'found \'%d\' specs using spec pattern \'%s\': %o',1017 names.length,1018 specPattern,1019 names1020 )1021 }1022 })1023 },1024 ready (options = {}) {1025 debug('run mode ready with options %o', options)1026 _.defaults(options, {1027 isTextTerminal: true,1028 browser: 'electron',1029 })1030 const socketId = random.id()1031 const { projectRoot, record, key, ciBuildId, parallel, group } = options1032 const browserName = options.browser1033 // alias and coerce to null1034 let specPattern = options.spec || null1035 // warn if we're using deprecated --ci flag1036 recordMode.warnIfCiFlag(options.ci)1037 // ensure the project exists1038 // and open up the project1039 return createAndOpenProject(socketId, options)1040 .then(({ project, projectId, config }) => {1041 debug('project created and opened with config %o', config)1042 // if we have a project id and a key but record hasnt been given1043 recordMode.warnIfProjectIdButNoRecordOption(projectId, options)1044 recordMode.throwIfRecordParamsWithoutRecording(record, ciBuildId, parallel, group)1045 if (record) {1046 recordMode.throwIfNoProjectId(projectId, settings.configFile(options))1047 recordMode.throwIfIncorrectCiBuildIdUsage(ciBuildId, parallel, group)1048 recordMode.throwIfIndeterminateCiBuildId(ciBuildId, parallel, group)1049 }1050 return Promise.all([1051 system.info(),1052 browsers.ensureAndGetByNameOrPath(browserName),1053 this.findSpecs(config, specPattern),1054 trashAssets(config),1055 removeOldProfiles(),1056 ])1057 .spread((sys = {}, browser = {}, specs = []) => {1058 // return only what is return to the specPattern1059 if (specPattern) {1060 specPattern = specsUtil.getPatternRelativeToProjectRoot(specPattern, projectRoot)1061 }1062 if (!specs.length) {1063 errors.throw('NO_SPECS_FOUND', config.integrationFolder, specPattern)1064 }1065 if (browser.family === 'chrome') {1066 chromePolicyCheck.run(onWarning)1067 }1068 const runAllSpecs = ({ beforeSpecRun, afterSpecRun, runUrl, parallel }) => {1069 return this.runSpecs({1070 beforeSpecRun,1071 afterSpecRun,1072 projectRoot,1073 specPattern,1074 socketId,1075 parallel,1076 browser,1077 project,1078 runUrl,1079 group,1080 config,1081 specs,1082 sys,1083 videosFolder: config.videosFolder,1084 video: config.video,1085 videoCompression: config.videoCompression,1086 videoUploadOnPasses: config.videoUploadOnPasses,1087 exit: options.exit,1088 headed: options.headed,1089 outputPath: options.outputPath,1090 })1091 .tap(renderSummaryTable(runUrl))1092 }1093 if (record) {1094 const { projectName } = config1095 return recordMode.createRunAndRecordSpecs({1096 key,1097 sys,1098 specs,1099 group,1100 browser,1101 parallel,1102 ciBuildId,1103 projectId,1104 projectRoot,1105 projectName,1106 specPattern,1107 runAllSpecs,1108 })1109 }1110 // not recording, can't be parallel1111 return runAllSpecs({1112 parallel: false,1113 })1114 })1115 })1116 },1117 run (options) {1118 return electronApp1119 .ready()1120 .then(() => {1121 return this.ready(options)1122 })1123 },...

Full Screen

Full Screen

run_spec.js

Source:run_spec.js Github

copy

Full Screen

1require('../../spec_helper')2const Promise = require('bluebird')3const electron = require('electron')4const stripAnsi = require('strip-ansi')5const snapshot = require('snap-shot-it')6const R = require('ramda')7const pkg = require('@packages/root')8const { ProjectBase } = require('../../../lib/project-base')9const { fs } = require(`${root}../lib/util/fs`)10const user = require(`${root}../lib/user`)11const errors = require(`${root}../lib/errors`)12const config = require(`${root}../lib/config`)13const { ProjectE2E } = require(`${root}../lib/project-e2e`)14const browsers = require(`${root}../lib/browsers`)15const Reporter = require(`${root}../lib/reporter`)16const runMode = require(`${root}../lib/modes/run`)17const openProject = require(`${root}../lib/open_project`)18const videoCapture = require(`${root}../lib/video_capture`)19const env = require(`${root}../lib/util/env`)20const random = require(`${root}../lib/util/random`)21const system = require(`${root}../lib/util/system`)22const specsUtil = require(`${root}../lib/util/specs`)23const { experimental } = require(`${root}../lib/experiments`)24describe('lib/modes/run', () => {25 beforeEach(function () {26 this.projectInstance = new ProjectBase('/_test-output/path/to/project-e2e')27 })28 context('.getProjectId', () => {29 it('resolves if id', () => {30 return runMode.getProjectId('project', 'id123')31 .then((ret) => {32 expect(ret).to.eq('id123')33 })34 })35 it('resolves if CYPRESS_PROJECT_ID set', () => {36 sinon.stub(env, 'get').withArgs('CYPRESS_PROJECT_ID').returns('envId123')37 return runMode.getProjectId('project')38 .then((ret) => {39 expect(ret).to.eq('envId123')40 })41 })42 it('is null when no projectId', () => {43 const project = {44 getProjectId: sinon.stub().rejects(new Error),45 }46 return runMode.getProjectId(project)47 .then((ret) => {48 expect(ret).to.be.null49 })50 })51 })52 context('.openProjectCreate', () => {53 let onError54 beforeEach(() => {55 sinon.stub(openProject, 'create').resolves()56 onError = sinon.spy()57 const options = {58 onError,59 port: 8080,60 env: { foo: 'bar' },61 isTextTerminal: true,62 projectRoot: '/_test-output/path/to/project/foo',63 }64 return runMode.openProjectCreate(options.projectRoot, 1234, options)65 })66 it('calls openProject.create with projectRoot + options', () => {67 expect(openProject.create).to.be.calledWithMatch('/_test-output/path/to/project/foo', {68 port: 8080,69 projectRoot: '/_test-output/path/to/project/foo',70 env: { foo: 'bar' },71 }, {72 morgan: false,73 socketId: 1234,74 report: true,75 isTextTerminal: true,76 })77 })78 it('calls options.onError with error message onError', () => {79 const error = { message: 'the message' }80 expect(openProject.create.lastCall.args[2].onError).to.be.a('function')81 openProject.create.lastCall.args[2].onError(error)82 expect(onError).to.be.calledWith(error)83 })84 })85 context('.getElectronProps', () => {86 it('sets width and height', () => {87 const props = runMode.getElectronProps()88 expect(props.width).to.eq(1920)89 expect(props.height).to.eq(1080)90 })91 it('sets show to boolean', () => {92 let props = runMode.getElectronProps(false)93 expect(props.show).to.be.false94 props = runMode.getElectronProps(true)95 expect(props.show).to.be.true96 })97 it('sets onScreencastFrame when write is true', () => {98 const write = sinon.stub()99 const image = {100 data: '',101 }102 const props = runMode.getElectronProps(true, write)103 props.onScreencastFrame(image)104 expect(write).to.be.calledOnce105 })106 it('does not set onScreencastFrame when write is falsy', () => {107 const props = runMode.getElectronProps(true, false)108 expect(props).not.to.have.property('recordFrameRate')109 expect(props).not.to.have.property('onScreencastFrame')110 })111 it('sets options.show = false onNewWindow callback', () => {112 const options = { show: true }113 const props = runMode.getElectronProps()114 props.onNewWindow(null, null, null, null, options)115 expect(options.show).to.eq(false)116 })117 it('calls options.onError when webContents crashes', function () {118 sinon.spy(errors, 'get')119 sinon.spy(errors, 'log')120 const onError = sinon.spy()121 const props = runMode.getElectronProps(true, this.projectInstance, onError)122 props.onCrashed()123 expect(errors.get).to.be.calledWith('RENDERER_CRASHED')124 expect(errors.log).to.be.calledOnce125 expect(onError).to.be.called126 expect(onError.lastCall.args[0].message).to.include('We detected that the Chromium Renderer process just crashed.')127 })128 })129 context('.launchBrowser', () => {130 beforeEach(function () {131 this.launch = sinon.stub(openProject, 'launch')132 sinon.stub(runMode, 'screenshotMetadata').returns({ a: 'a' })133 })134 it('can launch electron', function () {135 const screenshots = []136 const spec = {137 absolute: '/path/to/spec',138 }139 const browser = {140 name: 'electron',141 family: 'chromium',142 isHeaded: false,143 }144 runMode.launchBrowser({145 spec,146 browser,147 project: this.projectInstance,148 writeVideoFrame: 'write',149 screenshots,150 })151 expect(this.launch).to.be.calledWithMatch(browser, spec)152 const browserOpts = this.launch.firstCall.args[2]153 const { onAfterResponse } = browserOpts.automationMiddleware154 expect(onAfterResponse).to.be.a('function')155 onAfterResponse('take:screenshot', {}, {})156 onAfterResponse('get:cookies')157 expect(screenshots).to.deep.eq([{ a: 'a' }])158 })159 it('can launch chrome', function () {160 const spec = {161 absolute: '/path/to/spec',162 }163 const browser = {164 name: 'chrome',165 family: 'chromium',166 isHeaded: true,167 }168 runMode.launchBrowser({169 spec,170 browser,171 project: {},172 })173 expect(this.launch).to.be.calledWithMatch(browser, spec, {})174 })175 })176 context('.postProcessRecording', () => {177 beforeEach(() => {178 sinon.stub(videoCapture, 'process').resolves()179 })180 it('calls video process with name, cname and videoCompression', () => {181 return runMode.postProcessRecording('foo', 'foo-compress', 32, true)182 .then(() => {183 expect(videoCapture.process).to.be.calledWith('foo', 'foo-compress', 32)184 })185 })186 it('does not call video process when videoCompression is false', () => {187 return runMode.postProcessRecording('foo', 'foo-compress', false, true)188 .then(() => {189 expect(videoCapture.process).not.to.be.called190 })191 })192 it('calls video process if we have been told to upload videos', () => {193 return runMode.postProcessRecording('foo', 'foo-compress', 32, true)194 .then(() => {195 expect(videoCapture.process).to.be.calledWith('foo', 'foo-compress', 32)196 })197 })198 it('does not call video process if there are no failing tests and we have set not to upload video on passing', () => {199 return runMode.postProcessRecording('foo', 'foo-compress', 32, false)200 .then(() => {201 expect(videoCapture.process).not.to.be.called202 })203 })204 })205 context('.waitForBrowserToConnect', () => {206 it('throws TESTS_DID_NOT_START_FAILED after 3 connection attempts', function () {207 sinon.spy(errors, 'warning')208 sinon.spy(errors, 'get')209 sinon.spy(openProject, 'closeBrowser')210 sinon.stub(runMode, 'launchBrowser').resolves()211 sinon.stub(runMode, 'waitForSocketConnection').callsFake(() => {212 return Promise.delay(1000)213 })214 const onError = sinon.spy()215 return runMode.waitForBrowserToConnect({ project: this.projectInstance, timeout: 10, onError })216 .then(() => {217 expect(openProject.closeBrowser).to.be.calledThrice218 expect(runMode.launchBrowser).to.be.calledThrice219 expect(errors.warning).to.be.calledWith('TESTS_DID_NOT_START_RETRYING', 'Retrying...')220 expect(errors.warning).to.be.calledWith('TESTS_DID_NOT_START_RETRYING', 'Retrying again...')221 expect(errors.get).to.be.calledWith('TESTS_DID_NOT_START_FAILED')222 expect(onError).to.be.called223 expect(onError.lastCall.args[0].message).to.include('The browser never connected. Something is wrong. The tests cannot run. Aborting...')224 })225 })226 })227 context('.waitForSocketConnection', () => {228 beforeEach(function () {229 this.projectStub = sinon.stub({230 on () {},231 removeListener () {},232 })233 })234 it('attaches fn to \'socket:connected\' event', function () {235 runMode.waitForSocketConnection(this.projectStub, 1234)236 expect(this.projectStub.on).to.be.calledWith('socket:connected')237 })238 it('calls removeListener if socketId matches id', function () {239 this.projectStub.on.yields(1234)240 return runMode.waitForSocketConnection(this.projectStub, 1234)241 .then(() => {242 expect(this.projectStub.removeListener).to.be.calledWith('socket:connected')243 })244 })245 describe('integration', () => {246 it('resolves undefined when socket:connected fires', function () {247 process.nextTick(() => {248 return this.projectInstance.emit('socket:connected', 1234)249 })250 return runMode.waitForSocketConnection(this.projectInstance, 1234)251 .then((ret) => {252 expect(ret).to.be.undefined253 })254 })255 it('does not resolve if socketId does not match id', function () {256 process.nextTick(() => {257 return this.projectInstance.emit('socket:connected', 12345)258 })259 return runMode260 .waitForSocketConnection(this.projectInstance, 1234)261 .timeout(50)262 .then(() => {263 throw new Error('should time out and not resolve')264 }).catch(Promise.TimeoutError, (err) => {})265 })266 it('actually removes the listener', function () {267 process.nextTick(() => {268 this.projectInstance.emit('socket:connected', 12345)269 expect(this.projectInstance.listeners('socket:connected')).to.have.length(1)270 this.projectInstance.emit('socket:connected', '1234')271 expect(this.projectInstance.listeners('socket:connected')).to.have.length(1)272 this.projectInstance.emit('socket:connected', 1234)273 expect(this.projectInstance.listeners('socket:connected')).to.have.length(0)274 })275 return runMode.waitForSocketConnection(this.projectInstance, 1234)276 })277 })278 })279 context('.waitForTestsToFinishRunning', () => {280 beforeEach(function () {281 sinon.stub(fs, 'pathExists').resolves(true)282 sinon.stub(this.projectInstance, 'getConfig').resolves({})283 sinon.spy(runMode, 'getVideoRecordingDelay')284 sinon.spy(errors, 'warning')285 this.setupProjectEnd = (results) => {286 results = results || {287 stats: {288 failures: 0,289 },290 }291 process.nextTick(() => {292 this.projectInstance.emit('end', results)293 })294 }295 })296 it('end event resolves with obj, displays stats, displays screenshots, sets video timestamps', function () {297 const startedVideoCapture = new Date298 const screenshots = [{}, {}, {}]299 const endVideoCapture = sinon.stub().resolves()300 const results = {301 tests: [{ attempts: [1] }, { attempts: [2] }, { attempts: [3] }],302 stats: {303 tests: 1,304 passes: 2,305 failures: 3,306 pending: 4,307 duration: 5,308 },309 }310 sinon.stub(Reporter, 'setVideoTimestamp')311 sinon.stub(runMode, 'postProcessRecording').resolves()312 sinon.spy(runMode, 'displayResults')313 sinon.spy(runMode, 'displayScreenshots')314 sinon.spy(Promise.prototype, 'delay')315 this.setupProjectEnd(results)316 return runMode.waitForTestsToFinishRunning({317 project: this.projectInstance,318 videoName: 'foo.mp4',319 compressedVideoName: 'foo-compressed.mp4',320 videoCompression: 32,321 videoUploadOnPasses: true,322 gui: false,323 screenshots,324 startedVideoCapture,325 endVideoCapture,326 spec: {327 path: 'cypress/integration/spec.js',328 },329 })330 .then((obj) => {331 // since video was recording, there was a delay to let video finish332 expect(Reporter.setVideoTimestamp).calledWith(startedVideoCapture, [1, 2, 3])333 expect(runMode.getVideoRecordingDelay).to.have.returned(1000)334 expect(Promise.prototype.delay).to.be.calledWith(1000)335 expect(runMode.postProcessRecording).to.be.calledWith('foo.mp4', 'foo-compressed.mp4', 32, true)336 expect(runMode.displayResults).to.be.calledWith(results)337 expect(runMode.displayScreenshots).to.be.calledWith(screenshots)338 expect(obj).to.deep.eq({339 screenshots,340 video: 'foo.mp4',341 error: null,342 hooks: null,343 reporterStats: null,344 shouldUploadVideo: true,345 tests: results.tests,346 spec: {347 path: 'cypress/integration/spec.js',348 },349 stats: {350 tests: 1,351 passes: 2,352 failures: 3,353 pending: 4,354 duration: 5,355 },356 })357 })358 })359 it('exiting early resolves with no tests, and error', function () {360 sinon.useFakeTimers({ shouldAdvanceTime: true })361 const err = new Error('foo')362 const startedVideoCapture = new Date363 const wallClock = new Date()364 const screenshots = [{}, {}, {}]365 const endVideoCapture = sinon.stub().resolves()366 sinon.stub(runMode, 'postProcessRecording').resolves()367 sinon.spy(runMode, 'displayResults')368 sinon.spy(runMode, 'displayScreenshots')369 sinon.spy(Promise.prototype, 'delay')370 process.nextTick(() => {371 runMode.exitEarly(err)372 })373 return runMode.waitForTestsToFinishRunning({374 project: this.projectInstance,375 videoName: 'foo.mp4',376 compressedVideoName: 'foo-compressed.mp4',377 videoCompression: 32,378 videoUploadOnPasses: true,379 gui: false,380 screenshots,381 startedVideoCapture,382 endVideoCapture,383 spec: {384 path: 'cypress/integration/spec.js',385 },386 })387 .then((obj) => {388 // since video was recording, there was a delay to let video finish389 expect(runMode.getVideoRecordingDelay).to.have.returned(1000)390 expect(Promise.prototype.delay).to.be.calledWith(1000)391 expect(runMode.postProcessRecording).to.be.calledWith('foo.mp4', 'foo-compressed.mp4', 32, true)392 expect(runMode.displayResults).to.be.calledWith(obj)393 expect(runMode.displayScreenshots).to.be.calledWith(screenshots)394 expect(obj).to.deep.eq({395 screenshots,396 error: err.message,397 video: 'foo.mp4',398 hooks: null,399 tests: null,400 reporterStats: null,401 shouldUploadVideo: true,402 spec: {403 path: 'cypress/integration/spec.js',404 },405 stats: {406 failures: 1,407 tests: 0,408 passes: 0,409 pending: 0,410 suites: 0,411 skipped: 0,412 wallClockDuration: 0,413 wallClockStartedAt: wallClock.toJSON(),414 wallClockEndedAt: wallClock.toJSON(),415 },416 })417 })418 })419 it('logs warning and resolves on failed video end', async function () {420 this.setupProjectEnd()421 sinon.spy(videoCapture, 'process')422 const endVideoCapture = sinon.stub().rejects()423 await runMode.waitForTestsToFinishRunning({424 project: this.projectInstance,425 videoName: 'foo.mp4',426 compressedVideoName: 'foo-compressed.mp4',427 videoCompression: 32,428 videoUploadOnPasses: true,429 gui: false,430 endVideoCapture,431 })432 expect(errors.warning).to.be.calledWith('VIDEO_POST_PROCESSING_FAILED')433 expect(videoCapture.process).not.to.be.called434 })435 it('logs warning and resolves on failed video compression', async function () {436 this.setupProjectEnd()437 const endVideoCapture = sinon.stub().resolves()438 sinon.stub(videoCapture, 'process').rejects()439 await runMode.waitForTestsToFinishRunning({440 project: this.projectInstance,441 videoName: 'foo.mp4',442 compressedVideoName: 'foo-compressed.mp4',443 videoCompression: 32,444 videoUploadOnPasses: true,445 gui: false,446 endVideoCapture,447 })448 expect(errors.warning).to.be.calledWith('VIDEO_POST_PROCESSING_FAILED')449 })450 it('does not upload video when videoUploadOnPasses is false and no failures', function () {451 this.setupProjectEnd()452 sinon.spy(runMode, 'postProcessRecording')453 sinon.spy(videoCapture, 'process')454 const endVideoCapture = sinon.stub().resolves()455 return runMode.waitForTestsToFinishRunning({456 project: this.projectInstance,457 videoName: 'foo.mp4',458 compressedVideoName: 'foo-compressed.mp4',459 videoCompression: 32,460 videoUploadOnPasses: false,461 gui: false,462 endVideoCapture,463 })464 .then(() => {465 expect(runMode.postProcessRecording).to.be.calledWith('foo.mp4', 'foo-compressed.mp4', 32, false)466 expect(videoCapture.process).not.to.be.called467 })468 })469 it('does not delay when not capturing a video', () => {470 sinon.stub(runMode, 'listenForProjectEnd').resolves({})471 return runMode.waitForTestsToFinishRunning({472 startedVideoCapture: null,473 })474 .then(() => {475 expect(runMode.getVideoRecordingDelay).to.have.returned(0)476 })477 })478 describe('when video is deleted in after:spec event', function () {479 beforeEach(function () {480 this.setupProjectEnd()481 sinon.spy(runMode, 'postProcessRecording')482 sinon.spy(videoCapture, 'process')483 fs.pathExists.resolves(false)484 })485 it('does not process or upload video', function () {486 return runMode.waitForTestsToFinishRunning({487 project: this.projectInstance,488 startedVideoCapture: new Date(),489 videoName: 'foo.mp4',490 endVideoCapture: sinon.stub().resolves(),491 })492 .then((results) => {493 expect(runMode.postProcessRecording).not.to.be.called494 expect(videoCapture.process).not.to.be.called495 expect(results.shouldUploadVideo).to.be.false496 })497 })498 it('nulls out video value from results', function () {499 return runMode.waitForTestsToFinishRunning({500 project: this.projectInstance,501 startedVideoCapture: new Date(),502 videoName: 'foo.mp4',503 endVideoCapture: sinon.stub().resolves(),504 })505 .then((results) => {506 expect(results.video).to.be.null507 })508 })509 })510 })511 context('.listenForProjectEnd', () => {512 it('resolves with end event + argument', function () {513 process.nextTick(() => {514 return this.projectInstance.emit('end', { foo: 'bar' })515 })516 return runMode.listenForProjectEnd(this.projectInstance)517 .then((obj) => {518 expect(obj).to.deep.eq({519 foo: 'bar',520 })521 })522 })523 it('stops listening to end event', function () {524 process.nextTick(() => {525 expect(this.projectInstance.listeners('end')).to.have.length(1)526 this.projectInstance.emit('end', { foo: 'bar' })527 expect(this.projectInstance.listeners('end')).to.have.length(0)528 })529 return runMode.listenForProjectEnd(this.projectInstance)530 })531 })532 context('.run browser vs video recording', () => {533 beforeEach(function () {534 sinon.stub(electron.app, 'on').withArgs('ready').yieldsAsync()535 sinon.stub(user, 'ensureAuthToken')536 sinon.stub(ProjectE2E, 'ensureExists').resolves()537 sinon.stub(ProjectBase, 'ensureExists').resolves()538 sinon.stub(random, 'id').returns(1234)539 sinon.stub(openProject, 'create').resolves(openProject)540 sinon.stub(runMode, 'waitForSocketConnection').resolves()541 sinon.stub(runMode, 'waitForTestsToFinishRunning').resolves({542 stats: { failures: 10 },543 spec: {},544 })545 sinon.spy(runMode, 'waitForBrowserToConnect')546 sinon.stub(videoCapture, 'start').resolves()547 sinon.stub(openProject, 'launch').resolves()548 sinon.stub(openProject, 'getProject').resolves(this.projectInstance)549 sinon.spy(errors, 'warning')550 sinon.stub(config, 'get').resolves({551 proxyUrl: 'http://localhost:12345',552 video: true,553 videosFolder: 'videos',554 integrationFolder: '/path/to/integrationFolder',555 })556 sinon.stub(specsUtil, 'find').resolves([557 {558 name: 'foo_spec.js',559 path: 'cypress/integration/foo_spec.js',560 absolute: '/path/to/spec.js',561 },562 ])563 })564 it('shows no warnings for default browser', () => {565 return runMode.run()566 .then(() => {567 expect(errors.warning).to.not.be.called568 })569 })570 it('throws an error if invalid browser family supplied', () => {571 const browser = { name: 'opera', family: 'opera - btw when is Opera support coming?' }572 sinon.stub(browsers, 'ensureAndGetByNameOrPath').resolves(browser)573 return expect(runMode.run({ browser: 'opera' }))574 .to.be.rejectedWith(/invalid browser family in/)575 })576 it('shows no warnings for chrome browser', () => {577 return runMode.run({ browser: 'chrome' })578 .then(() => {579 expect(errors.warning).to.not.be.called580 })581 })582 it('names video file with spec name', () => {583 return runMode.run()584 .then(() => {585 expect(videoCapture.start).to.be.calledWith('videos/foo_spec.js.mp4')586 expect(runMode.waitForTestsToFinishRunning).to.be.calledWithMatch({587 compressedVideoName: 'videos/foo_spec.js-compressed.mp4',588 })589 })590 })591 })592 context('.run', () => {593 beforeEach(function () {594 sinon.stub(this.projectInstance, 'getConfig').resolves({595 proxyUrl: 'http://localhost:12345',596 })597 sinon.stub(electron.app, 'on').withArgs('ready').yieldsAsync()598 sinon.stub(user, 'ensureAuthToken')599 sinon.stub(ProjectE2E, 'ensureExists').resolves()600 sinon.stub(ProjectBase, 'ensureExists').resolves()601 sinon.stub(random, 'id').returns(1234)602 sinon.stub(openProject, 'create').resolves(openProject)603 sinon.stub(system, 'info').resolves({ osName: 'osFoo', osVersion: 'fooVersion' })604 sinon.stub(browsers, 'ensureAndGetByNameOrPath').resolves({605 name: 'fooBrowser',606 path: 'path/to/browser',607 version: '777',608 family: 'chromium',609 })610 sinon.stub(runMode, 'waitForSocketConnection').resolves()611 sinon.stub(runMode, 'waitForTestsToFinishRunning').resolves({612 stats: { failures: 10 },613 spec: {},614 })615 sinon.spy(runMode, 'waitForBrowserToConnect')616 sinon.spy(runMode, 'runSpecs')617 sinon.stub(openProject, 'launch').resolves()618 sinon.stub(openProject, 'getProject').resolves(this.projectInstance)619 sinon.stub(specsUtil, 'find').resolves([620 {621 name: 'foo_spec.js',622 path: 'cypress/integration/foo_spec.js',623 absolute: '/path/to/spec.js',624 },625 ])626 })627 it('no longer ensures user session', () => {628 return runMode.run()629 .then(() => {630 expect(user.ensureAuthToken).not.to.be.called631 })632 })633 it('resolves with object and totalFailed', () => {634 return runMode.run()635 .then((results) => {636 expect(results).to.have.property('totalFailed', 10)637 })638 })639 it('passes projectRoot + options to openProject', () => {640 const opts = { projectRoot: '/path/to/project', foo: 'bar' }641 return runMode.run(opts)642 .then(() => {643 expect(openProject.create).to.be.calledWithMatch(opts.projectRoot, opts)644 })645 })646 it('passes project + id to waitForBrowserToConnect', function () {647 return runMode.run()648 .then(() => {649 expect(runMode.waitForBrowserToConnect).to.be.calledWithMatch({650 project: this.projectInstance,651 socketId: 1234,652 })653 })654 })655 it('passes project to waitForTestsToFinishRunning', function () {656 return runMode.run()657 .then(() => {658 expect(runMode.waitForTestsToFinishRunning).to.be.calledWithMatch({659 project: this.projectInstance,660 })661 })662 })663 it('passes headed to openProject.launch', () => {664 const browser = { name: 'electron', family: 'chromium' }665 browsers.ensureAndGetByNameOrPath.resolves(browser)666 return runMode.run({ headed: true })667 .then(() => {668 expect(openProject.launch).to.be.calledWithMatch(669 browser,670 {671 name: 'foo_spec.js',672 path: 'cypress/integration/foo_spec.js',673 absolute: '/path/to/spec.js',674 },675 {676 show: true,677 },678 )679 })680 })681 it('passes sys to runSpecs', () => {682 return runMode.run()683 .then(() => {684 expect(runMode.runSpecs).to.be.calledWithMatch({685 sys: {686 osName: 'osFoo',687 osVersion: 'fooVersion',688 },689 })690 })691 })692 it('passes browser to runSpecs', () => {693 return runMode.run()694 .then(() => {695 expect(runMode.runSpecs).to.be.calledWithMatch({696 browser: {697 name: 'fooBrowser',698 path: 'path/to/browser',699 version: '777',700 },701 })702 })703 })704 })705 context('#displayRunStarting', () => {706 // restore pkg.version property707 // for some reason I cannot stub property value using Sinon708 let version709 // save a copy of "true" experiments right away710 const names = R.clone(experimental.names)711 before(() => {712 // reset experiments names before each test713 experimental.names = {}714 version = pkg.version715 })716 afterEach(() => {717 pkg.version = version718 experimental.names = names719 })720 it('returns heading with experiments', () => {721 pkg.version = '1.2.3'722 experimental.names = {723 experimentalFeatureA: 'experimentalFeatureA',724 experimentalFeatureB: 'experimentalFeatureB',725 }726 const options = {727 browser: {728 displayName: 'Electron',729 majorVersion: 99,730 isHeadless: true,731 },732 config: {733 resolved: {734 experimentalFeatureA: {735 value: true,736 from: 'config',737 },738 experimentalFeatureB: {739 value: 4,740 from: 'cli',741 },742 },743 },744 }745 const heading = runMode.displayRunStarting(options)746 snapshot('enabled experiments', stripAnsi(heading))747 })748 it('resets the experiments names', () => {749 expect(experimental.names, 'experiments were reset').to.deep.equal(names)750 })751 it('returns heading with some enabled experiments', () => {752 pkg.version = '1.2.3'753 experimental.names = {754 experimentalFeatureA: 'experimentalFeatureA',755 experimentalFeatureB: 'experimentalFeatureB',756 }757 const options = {758 browser: {759 displayName: 'Electron',760 majorVersion: 99,761 isHeadless: true,762 },763 config: {764 resolved: {765 // means this feature is not enabled, should not appear in the heading766 experimentalFeatureA: {767 value: true,768 from: 'default',769 },770 experimentalFeatureB: {771 value: 4,772 from: 'cli',773 },774 },775 },776 }777 const heading = runMode.displayRunStarting(options)778 const text = stripAnsi(heading)779 snapshot('some enabled experiments', text)780 // explicit assertions for test clarity781 expect(text).to.not.include('experimentalFeatureA')782 expect(text).to.include('experimentalFeatureB')783 })784 it('returns heading without experiments', () => {785 pkg.version = '1.2.3'786 const options = {787 browser: {788 displayName: 'Electron',789 majorVersion: 99,790 isHeadless: true,791 },792 config: {793 resolved: {},794 },795 }796 const heading = runMode.displayRunStarting(options)797 snapshot('without enabled experiments', stripAnsi(heading))798 })799 it('restores pkg.version', () => {800 expect(pkg.version).to.not.equal('1.2.3')801 })802 })...

Full Screen

Full Screen

video_capture.js

Source:video_capture.js Github

copy

Full Screen

1(function() {2 var BlackHoleStream, Promise, _, debug, debugFrames, ffmpeg, ffmpegPath, fs, la, os, path, stream, utils;3 _ = require("lodash");4 la = require("lazy-ass");5 os = require("os");6 path = require("path");7 utils = require("fluent-ffmpeg/lib/utils");8 debug = require("debug")("cypress:server:video");9 ffmpeg = require("fluent-ffmpeg");10 stream = require("stream");11 Promise = require("bluebird");12 ffmpegPath = require("@ffmpeg-installer/ffmpeg").path;13 BlackHoleStream = require("black-hole-stream");14 fs = require("./util/fs");15 debugFrames = require("debug")("cypress:server:video:frames");16 debug("using ffmpeg from %s", ffmpegPath);17 ffmpeg.setFfmpegPath(ffmpegPath);18 module.exports = {19 getMsFromDuration: function(duration) {20 return utils.timemarkToSeconds(duration) * 1000;21 },22 getCodecData: function(src) {23 return new Promise(function(resolve, reject) {24 return ffmpeg().on("stderr", function(stderr) {25 return debug("get codecData stderr log %o", {26 message: stderr27 });28 }).on("codecData", resolve).input(src).format("null").output(new BlackHoleStream()).run();29 }).tap(function(data) {30 return debug('codecData %o', {31 src: src,32 data: data33 });34 }).tapCatch(function(err) {35 return debug("getting codecData failed", {36 err: err37 });38 });39 },40 copy: function(src, dest) {41 debug("copying from %s to %s", src, dest);42 return fs.copyAsync(src, dest, {43 overwrite: true44 })["catch"]({45 code: "ENOENT"46 }, function() {});47 },48 start: function(name, options) {49 var done, endVideoCapture, ended, errored, logErrors, pt, skipped, startCapturing, wantsWrite, writeVideoFrame, written;50 if (options == null) {51 options = {};52 }53 pt = stream.PassThrough();54 ended = Promise.pending();55 done = false;56 errored = false;57 written = false;58 logErrors = true;59 wantsWrite = true;60 skipped = 0;61 _.defaults(options, {62 onError: function() {}63 });64 endVideoCapture = function() {65 done = true;66 if (!written) {67 logErrors = false;68 }69 pt.end();70 return ended.promise;71 };72 writeVideoFrame = function(data) {73 if (done) {74 return;75 }76 written = true;77 debugFrames("writing video frame");78 if (wantsWrite) {79 if (!(wantsWrite = pt.write(data))) {80 return pt.once("drain", function() {81 debugFrames("video stream drained");82 return wantsWrite = true;83 });84 }85 } else {86 skipped += 1;87 return debugFrames("skipping video frame %o", {88 skipped: skipped89 });90 }91 };92 startCapturing = function() {93 return new Promise(function(resolve) {94 var cmd;95 return cmd = ffmpeg({96 source: pt,97 priority: 2098 }).inputFormat("image2pipe").inputOptions("-use_wallclock_as_timestamps 1").videoCodec("libx264").outputOptions("-preset ultrafast").on("start", function(command) {99 debug("capture started %o", {100 command: command101 });102 return resolve({103 cmd: cmd,104 startedVideoCapture: new Date105 });106 }).on("codecData", function(data) {107 return debug("capture codec data: %o", data);108 }).on("stderr", function(stderr) {109 return debug("capture stderr log %o", {110 message: stderr111 });112 }).on("error", function(err, stdout, stderr) {113 debug("capture errored: %o", {114 error: err.message,115 stdout: stdout,116 stderr: stderr117 });118 if (logErrors) {119 options.onError(err, stdout, stderr);120 }121 return ended.reject(err);122 }).on("end", function() {123 debug("capture ended");124 return ended.resolve();125 }).save(name);126 });127 };128 return startCapturing().then(function(arg) {129 var cmd, startedVideoCapture;130 cmd = arg.cmd, startedVideoCapture = arg.startedVideoCapture;131 return {132 cmd: cmd,133 endVideoCapture: endVideoCapture,134 writeVideoFrame: writeVideoFrame,135 startedVideoCapture: startedVideoCapture136 };137 });138 },139 process: function(name, cname, videoCompression, onProgress) {140 var total;141 if (onProgress == null) {142 onProgress = function() {};143 }144 total = null;145 return new Promise(function(resolve, reject) {146 var cmd;147 debug("processing video from %s to %s video compression %o", name, cname, videoCompression);148 return cmd = ffmpeg().input(name).videoCodec("libx264").outputOptions(["-preset fast", "-crf " + videoCompression]).on("start", function(command) {149 return debug("compression started %o", {150 command: command151 });152 }).on("codecData", function(data) {153 debug("compression codec data: %o", data);154 return total = utils.timemarkToSeconds(data.duration);155 }).on("stderr", function(stderr) {156 return debug("compression stderr log %o", {157 message: stderr158 });159 }).on("progress", function(progress) {160 var progressed;161 if (!total) {162 return;163 }164 debug("compression progress: %o", progress);165 progressed = utils.timemarkToSeconds(progress.timemark);166 return onProgress(progressed / total);167 }).on("error", function(err, stdout, stderr) {168 debug("compression errored: %o", {169 error: err.message,170 stdout: stdout,171 stderr: stderr172 });173 return reject(err);174 }).on("end", function() {175 debug("compression ended");176 onProgress(1);177 return fs.moveAsync(cname, name, {178 overwrite: true179 }).then(function() {180 return resolve();181 });182 }).save(cname);183 });184 }185 };...

Full Screen

Full Screen

videoCapture.js

Source:videoCapture.js Github

copy

Full Screen

...28 if (this.writtenChunksCount < 2) {29 return new Bluebird((resolve) => {30 this.pt.once("data", resolve);31 })32 .then(() => endVideoCapture())33 .timeout(waitForMoreChunksTimeout);34 }35 this.done = true;36 this.pt.end();37 // return the ended promise which will eventually38 // get resolve or rejected39 return this.ended.promise;40 }41 writeVideoFrame(data) {42 // make sure we haven't ended43 // our stream yet because paint44 // events can linger beyond45 // finishing the actual video46 if (this.done) {...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1module.exports = (on, config) => {2 on('after:spec', (spec, results) => {3 if (results.stats.failures === 0) {4 return endVideoCapture()5 }6 })7}8{

Full Screen

Using AI Code Generation

copy

Full Screen

1endVideoCapture()2startVideoCapture()3endVideoCapture()4startVideoCapture()5endVideoCapture()6startVideoCapture()7endVideoCapture()8startVideoCapture()9endVideoCapture()10startVideoCapture()11endVideoCapture()12startVideoCapture()13endVideoCapture()14startVideoCapture()15endVideoCapture()16startVideoCapture()17endVideoCapture()18startVideoCapture()19endVideoCapture()20startVideoCapture()21endVideoCapture()22startVideoCapture()23endVideoCapture()24startVideoCapture()25endVideoCapture()26startVideoCapture()

Full Screen

Using AI Code Generation

copy

Full Screen

1endVideoCapture()2playVideo()3pauseVideo()4playVideoFromBeginning()5muteVideo()6unmuteVideo()7toggleVideoMute()8setVideoVolume(volume)9getVideoVolume()10getVideoDuration()11getVideoCurrentTime()12setVideoCurrentTime(time)13getVideoPlaybackRate()14setVideoPlaybackRate(rate)15getVideoSource()16getVideoPoster()

Full Screen

Using AI Code Generation

copy

Full Screen

1describe('My First Test', () => {2 it('Does not do much!', () => {3 cy.endVideoCapture()4 })5 })6describe('My First Test', () => {7 it('Does not do much!', () => {8 cy.startVideoCapture()9 cy.endVideoCapture()10 })11 })12describe('My First Test', () => {13 it('Does not do much!', () => {14 cy.startVideoCapture()15 })16 })17describe('My Second Test', () => {18 it('Does not do much!', () => {19 cy.endVideoCapture()20 })21 })22describe('My First Test', () => {23 it('Does not do much!', () => {24 cy.startVideoCapture()25 })26 })27describe('My Second Test', () => {28 it('Does not do much!', () => {29 cy.endVideoCapture()30 })31 })32describe('My First Test', () => {33 it('Does not do much!', () => {34 cy.startVideoCapture()35 })36 })37describe('My Second Test', () => {38 it('Does not do much!', () => {39 cy.endVideoCapture()40 })41 })

Full Screen

Using AI Code Generation

copy

Full Screen

1it('Test', () => {2 cy.endVideoCapture();3});4{5}6it('Test', () => {7 cy.startVideoCapture();8 cy.endVideoCapture();9});10it('Test', () => {11 cy.startVideoCapture();12 cy.endVideoCapture();13 cy.startVideoCapture();14 cy.endVideoCapture();15});16it('Test', () => {17 cy.startVideoCapture();18 cy.endVideoCapture();19});

Full Screen

Using AI Code Generation

copy

Full Screen

1describe('Video Capture', () => {2 it('should capture the video', () => {3 cy.endVideoCapture()4 })5})6{7}8{9}10{11}12{13}14{

Full Screen

Cypress Tutorial

Cypress is a renowned Javascript-based open-source, easy-to-use end-to-end testing framework primarily used for testing web applications. Cypress is a relatively new player in the automation testing space and has been gaining much traction lately, as evidenced by the number of Forks (2.7K) and Stars (42.1K) for the project. LambdaTest’s Cypress Tutorial covers step-by-step guides that will help you learn from the basics till you run automation tests on LambdaTest.

Chapters:

  1. What is Cypress? -
  2. Why Cypress? - Learn why Cypress might be a good choice for testing your web applications.
  3. Features of Cypress Testing - Learn about features that make Cypress a powerful and flexible tool for testing web applications.
  4. Cypress Drawbacks - Although Cypress has many strengths, it has a few limitations that you should be aware of.
  5. Cypress Architecture - Learn more about Cypress architecture and how it is designed to be run directly in the browser, i.e., it does not have any additional servers.
  6. Browsers Supported by Cypress - Cypress is built on top of the Electron browser, supporting all modern web browsers. Learn browsers that support Cypress.
  7. Selenium vs Cypress: A Detailed Comparison - Compare and explore some key differences in terms of their design and features.
  8. Cypress Learning: Best Practices - Take a deep dive into some of the best practices you should use to avoid anti-patterns in your automation tests.
  9. How To Run Cypress Tests on LambdaTest? - Set up a LambdaTest account, and now you are all set to learn how to run Cypress tests.

Certification

You can elevate your expertise with end-to-end testing using the Cypress automation framework and stay one step ahead in your career by earning a Cypress certification. Check out our Cypress 101 Certification.

YouTube

Watch this 3 hours of complete tutorial to learn the basics of Cypress and various Cypress commands with the Cypress testing at LambdaTest.

Run Cypress automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful