How to use isNotAlreadyRunTest method in Cypress

Best JavaScript code snippet using cypress

runner.js

Source:runner.js Github

copy

Full Screen

1/* eslint-disable prefer-rest-params */2/* globals Cypress */3const _ = require('lodash')4const moment = require('moment')5const Promise = require('bluebird')6const Pending = require('mocha/lib/pending')7const $Log = require('./log')8const $utils = require('./utils')9const $errUtils = require('./error_utils')10const $stackUtils = require('./stack_utils')11const mochaCtxKeysRe = /^(_runnable|test)$/12const betweenQuotesRe = /\"(.+?)\"/13const HOOKS = 'beforeAll beforeEach afterEach afterAll'.split(' ')14const TEST_BEFORE_RUN_EVENT = 'runner:test:before:run'15const TEST_AFTER_RUN_EVENT = 'runner:test:after:run'16const RUNNABLE_LOGS = 'routes agents commands'.split(' ')17const RUNNABLE_PROPS = 'id order title root hookName hookId err state failedFromHookId body speed type duration wallClockStartedAt wallClockDuration timings file'.split(' ')18const debug = require('debug')('cypress:driver:runner')19const fire = (event, runnable, Cypress) => {20 debug('fire: %o', { event })21 if (runnable._fired == null) {22 runnable._fired = {}23 }24 runnable._fired[event] = true25 // dont fire anything again if we are skipped26 if (runnable._ALREADY_RAN) {27 return28 }29 return Cypress.action(event, wrap(runnable), runnable)30}31const fired = (event, runnable) => {32 return !!(runnable._fired && runnable._fired[event])33}34const testBeforeRunAsync = (test, Cypress) => {35 return Promise.try(() => {36 if (!fired('runner:test:before:run:async', test)) {37 return fire('runner:test:before:run:async', test, Cypress)38 }39 })40}41const runnableAfterRunAsync = (runnable, Cypress) => {42 runnable.clearTimeout()43 return Promise.try(() => {44 if (!fired('runner:runnable:after:run:async', runnable)) {45 return fire('runner:runnable:after:run:async', runnable, Cypress)46 }47 })48}49const testAfterRun = (test, Cypress) => {50 test.clearTimeout()51 if (!fired(TEST_AFTER_RUN_EVENT, test)) {52 setWallClockDuration(test)53 fire(TEST_AFTER_RUN_EVENT, test, Cypress)54 // perf loop only through55 // a tests OWN properties and not56 // inherited properties from its shared ctx57 for (let key of Object.keys(test.ctx || {})) {58 const value = test.ctx[key]59 if (_.isObject(value) && !mochaCtxKeysRe.test(key)) {60 // nuke any object properties that come from61 // cy.as() aliases or anything set from 'this'62 // so we aggressively perform GC and prevent obj63 // ref's from building up64 test.ctx[key] = undefined65 }66 }67 // reset the fn to be empty function68 // for GC to be aggressive and prevent69 // closures from hold references70 test.fn = () => {}71 // prevent loop comprehension72 return null73 }74}75const setTestTimingsForHook = (test, hookName, obj) => {76 if (test.timings == null) {77 test.timings = {}78 }79 if (test.timings[hookName] == null) {80 test.timings[hookName] = []81 }82 return test.timings[hookName].push(obj)83}84const setTestTimings = (test, name, obj) => {85 if (test.timings == null) {86 test.timings = {}87 }88 test.timings[name] = obj89}90const setWallClockDuration = (test) => {91 return test.wallClockDuration = new Date() - test.wallClockStartedAt92}93// we need to optimize wrap by converting94// tests to an id-based object which prevents95// us from recursively iterating through every96// parent since we could just return the found test97const wrap = (runnable) => {98 return $utils.reduceProps(runnable, RUNNABLE_PROPS)99}100const wrapAll = (runnable) => {101 return _.extend(102 {},103 $utils.reduceProps(runnable, RUNNABLE_PROPS),104 $utils.reduceProps(runnable, RUNNABLE_LOGS),105 )106}107const getHookName = (hook) => {108 // find the name of the hook by parsing its109 // title and pulling out whats between the quotes110 const name = hook.title.match(betweenQuotesRe)111 return name && name[1]112}113const forceGc = (obj) => {114 // aggressively forces GC by purging115 // references to ctx, and removes callback116 // functions for closures117 for (let key of Object.keys(obj.ctx || {})) {118 obj.ctx[key] = undefined119 }120 if (obj.fn) {121 obj.fn = () => {}122 }123}124const eachHookInSuite = (suite, fn) => {125 for (let type of HOOKS) {126 for (let hook of suite[`_${type}`]) {127 fn(hook)128 }129 }130 // prevent loop comprehension131 return null132}133// iterates over a suite's tests (including nested suites)134// and will return as soon as the callback is true135const findTestInSuite = (suite, fn = _.identity) => {136 for (const test of suite.tests) {137 if (fn(test)) {138 return test139 }140 }141 for (suite of suite.suites) {142 const test = findTestInSuite(suite, fn)143 if (test) {144 return test145 }146 }147}148// same as findTestInSuite but iterates backwards149const findLastTestInSuite = (suite, fn = _.identity) => {150 for (let i = suite.suites.length - 1; i >= 0; i--) {151 const test = findLastTestInSuite(suite.suites[i], fn)152 if (test) {153 return test154 }155 }156 for (let i = suite.tests.length - 1; i >= 0; i--) {157 const test = suite.tests[i]158 if (fn(test)) {159 return test160 }161 }162}163const getAllSiblingTests = (suite, getTestById) => {164 const tests = []165 suite.eachTest((test) => {166 // iterate through each of our suites tests.167 // this will iterate through all nested tests168 // as well. and then we add it only if its169 // in our filtered tests array170 if (getTestById(test.id)) {171 return tests.push(test)172 }173 })174 return tests175}176function getTestIndexFromId (id) {177 return +id.slice(1)178}179const getTestFromHook = (hook) => {180 // if theres already a currentTest use that181 const test = hook.ctx.currentTest182 if (test) {183 return test184 }185}186// we have to see if this is the last suite amongst187// its siblings. but first we have to filter out188// suites which dont have a filtered test in them189const isLastSuite = (suite, tests) => {190 if (suite.root) {191 return false192 }193 // grab all of the suites from our filtered tests194 // including all of their ancestor suites!195 const suites = _.reduce(tests, (memo, test) => {196 let parent197 while ((parent = test.parent)) {198 memo.push(parent)199 test = parent200 }201 return memo202 }203 , [])204 // intersect them with our parent suites and see if the last one is us205 return _206 .chain(suites)207 .uniq()208 .intersection(suite.parent.suites)209 .last()210 .value() === suite211}212// we are the last test that will run in the suite213// if we're the last test in the tests array or214// if we failed from a hook and that hook was 'before'215// since then mocha skips the remaining tests in the suite216const lastTestThatWillRunInSuite = (test, tests) => {217 return isLastTest(test, tests) || (test.failedFromHookId && (test.hookName === 'before all'))218}219const isLastTest = (test, tests) => {220 return test === _.last(tests)221}222const isRootSuite = (suite) => {223 return suite && suite.root224}225const overrideRunnerHook = (Cypress, _runner, getTestById, getTest, setTest, getTests) => {226 // bail if our _runner doesnt have a hook.227 // useful in tests228 if (!_runner.hook) {229 return230 }231 // monkey patch the hook event so we can wrap232 // 'test:after:run' around all of233 // the hooks surrounding a test runnable234 const _runnerHook = _runner.hook235 _runner.hook = function (name, fn) {236 const allTests = getTests()237 const changeFnToRunAfterHooks = () => {238 const originalFn = fn239 const test = getTest()240 fn = function () {241 setTest(null)242 testAfterRun(test, Cypress)243 // and now invoke next(err)244 return originalFn.apply(window, arguments)245 }246 }247 switch (name) {248 case 'afterEach': {249 const t = getTest()250 // find all of the filtered _tests which share251 // the same parent suite as our current _test252 const tests = getAllSiblingTests(t.parent, getTestById)253 // make sure this test isnt the last test overall but also254 // isnt the last test in our filtered parent suite's tests array255 if (this.suite.root && (t !== _.last(allTests)) && (t !== _.last(tests))) {256 changeFnToRunAfterHooks()257 }258 break259 }260 case 'afterAll': {261 // find all of the filtered allTests which share262 // the same parent suite as our current _test263 const t = getTest()264 if (t) {265 const siblings = getAllSiblingTests(t.parent, getTestById)266 // 1. if we're the very last test in the entire allTests267 // we wait until the root suite fires268 // 2. else if we arent the last nested suite we fire if we're269 // the last test that will run270 if (271 (isRootSuite(this.suite) && isLastTest(t, allTests)) ||272 (!isLastSuite(this.suite, allTests) && lastTestThatWillRunInSuite(t, siblings))273 ) {274 changeFnToRunAfterHooks()275 }276 }277 break278 }279 default:280 break281 }282 return _runnerHook.call(this, name, fn)283 }284}285const getTestResults = (tests) => {286 return _.map(tests, (test) => {287 const obj = _.pick(test, 'id', 'duration', 'state')288 obj.title = test.originalTitle289 // TODO FIX THIS!290 if (!obj.state) {291 obj.state = 'skipped'292 }293 return obj294 })295}296const hasOnly = (suite) => {297 return (298 suite._onlyTests.length ||299 suite._onlySuites.length ||300 _.some(suite.suites, hasOnly)301 )302}303const normalizeAll = (suite, initialTests = {}, setTestsById, setTests, onRunnable, onLogsById, getTestId) => {304 let hasTests = false305 // only loop until we find the first test306 findTestInSuite(suite, (test) => {307 return hasTests = true308 })309 // if we dont have any tests then bail310 if (!hasTests) {311 return312 }313 // we are doing a super perf loop here where314 // we hand back a normalized object but also315 // create optimized lookups for the tests without316 // traversing through it multiple times317 const tests = {}318 const normalizedSuite = normalize(suite, tests, initialTests, onRunnable, onLogsById, getTestId)319 if (setTestsById) {320 // use callback here to hand back321 // the optimized tests322 setTestsById(tests)323 }324 if (setTests) {325 let i = 0326 const testsArr = _.map(tests, (test) => {327 test.order = i += 1328 return test329 })330 // same pattern here331 setTests(testsArr)332 }333 return normalizedSuite334}335const normalize = (runnable, tests, initialTests, onRunnable, onLogsById, getTestId) => {336 const normalizeRunnable = (runnable) => {337 let i338 runnable.id = getTestId()339 // tests have a type of 'test' whereas suites do not have a type property340 if (runnable.type == null) {341 runnable.type = 'suite'342 }343 if (onRunnable) {344 onRunnable(runnable)345 }346 // if we have a runnable in the initial state347 // then merge in existing properties into the runnable348 i = initialTests[runnable.id]349 if (i) {350 _.each(RUNNABLE_LOGS, (type) => {351 return _.each(i[type], onLogsById)352 })353 _.extend(runnable, i)354 }355 // reduce this runnable down to its props356 // and collections357 return wrapAll(runnable)358 }359 const push = (test) => {360 return tests[test.id] != null ? tests[test.id] : (tests[test.id] = test)361 }362 const normalizedRunnable = normalizeRunnable(runnable)363 if ((runnable.type !== 'suite') || !hasOnly(runnable)) {364 if (runnable.type === 'test') {365 push(runnable)366 }367 // recursively iterate and normalize all other _runnables368 _.each({ tests: runnable.tests, suites: runnable.suites }, (_runnables, type) => {369 if (runnable[type]) {370 return normalizedRunnable[type] = _.map(_runnables, (runnable) => {371 return normalize(runnable, tests, initialTests, onRunnable, onLogsById, getTestId)372 })373 }374 })375 return normalizedRunnable376 }377 // this follows how mocha filters onlys. its runner#filterOnly378 // is pretty much the same minus the normalization part379 const filterOnly = (normalizedSuite, suite) => {380 if (suite._onlyTests.length) {381 suite.tests = suite._onlyTests382 normalizedSuite.tests = _.map(suite._onlyTests, (test) => {383 const normalizedTest = normalizeRunnable(test, initialTests, onRunnable, onLogsById, getTestId)384 push(normalizedTest)385 return normalizedTest386 })387 suite.suites = []388 normalizedSuite.suites = []389 } else {390 suite.tests = []391 normalizedSuite.tests = []392 _.each(suite._onlySuites, (onlySuite) => {393 const normalizedOnlySuite = normalizeRunnable(onlySuite, initialTests, onRunnable, onLogsById, getTestId)394 if (hasOnly(onlySuite)) {395 return filterOnly(normalizedOnlySuite, onlySuite)396 }397 })398 suite.suites = _.filter(suite.suites, (childSuite) => {399 const normalizedChildSuite = normalizeRunnable(childSuite, initialTests, onRunnable, onLogsById, getTestId)400 return (suite._onlySuites.indexOf(childSuite) !== -1) || filterOnly(normalizedChildSuite, childSuite)401 })402 normalizedSuite.suites = _.map(suite.suites, (childSuite) => normalize(childSuite, tests, initialTests, onRunnable, onLogsById, getTestId))403 }404 return suite.tests.length || suite.suites.length405 }406 filterOnly(normalizedRunnable, runnable)407 return normalizedRunnable408}409const hookFailed = (hook, err, hookName, getTest, getTestFromHookOrFindTest) => {410 // NOTE: sometimes mocha will fail a hook without having emitted on('hook')411 // event, so this hook might not have currentTest set correctly412 // in which case we need to lookup the test413 const test = getTest() || getTestFromHookOrFindTest(hook)414 test.err = err415 test.state = 'failed'416 test.duration = hook.duration // TODO: nope (?)417 test.hookName = hookName // TODO: why are we doing this?418 test.failedFromHookId = hook.hookId419 if (hook.alreadyEmittedMocha) {420 test.alreadyEmittedMocha = true421 } else {422 return Cypress.action('runner:test:end', wrap(test))423 }424}425function getTestFromRunnable (runnable) {426 switch (runnable.type) {427 case 'hook':428 return getTestFromHook(runnable)429 case 'test':430 return runnable431 default: null432 }433}434const _runnerListeners = (_runner, Cypress, _emissions, getTestById, getTest, setTest, getHookId, getTestFromHookOrFindTest) => {435 _runner.on('start', () => {436 return Cypress.action('runner:start', {437 start: new Date(),438 })439 })440 _runner.on('end', () => {441 return Cypress.action('runner:end', {442 end: new Date(),443 })444 })445 _runner.on('suite', (suite) => {446 if (_emissions.started[suite.id]) {447 return448 }449 _emissions.started[suite.id] = true450 return Cypress.action('runner:suite:start', wrap(suite))451 })452 _runner.on('suite end', (suite) => {453 // cleanup our suite + its hooks454 forceGc(suite)455 eachHookInSuite(suite, forceGc)456 if (_emissions.ended[suite.id]) {457 return458 }459 _emissions.ended[suite.id] = true460 return Cypress.action('runner:suite:end', wrap(suite))461 })462 _runner.on('hook', (hook) => {463 if (hook.hookId == null) {464 hook.hookId = getHookId()465 }466 if (hook.hookName == null) {467 hook.hookName = getHookName(hook)468 }469 // mocha incorrectly sets currentTest on before/after all's.470 // if there is a nested suite with a before, then471 // currentTest will refer to the previous test run472 // and not our current473 // https://github.com/cypress-io/cypress/issues/1987474 if ((hook.hookName === 'before all' || hook.hookName === 'after all') && hook.ctx.currentTest) {475 delete hook.ctx.currentTest476 }477 // set the hook's id from the test because478 // hooks do not have their own id, their479 // commands need to grouped with the test480 // and we can only associate them by this id481 const test = getTest() || getTestFromHookOrFindTest(hook)482 if (!test) {483 // we couldn't find a test to run with this hook484 // probably because the entire suite has already completed485 // so return early and tell onRunnableRun to skip the test486 return487 }488 hook.id = test.id489 hook.ctx.currentTest = test490 // make sure we set this test as the current now491 // else its possible that our TEST_AFTER_RUN_EVENT492 // will never fire if this failed in a before hook493 setTest(test)494 return Cypress.action('runner:hook:start', wrap(hook))495 })496 _runner.on('hook end', (hook) => {497 return Cypress.action('runner:hook:end', wrap(hook))498 })499 _runner.on('test', (test) => {500 setTest(test)501 if (_emissions.started[test.id]) {502 return503 }504 _emissions.started[test.id] = true505 return Cypress.action('runner:test:start', wrap(test))506 })507 _runner.on('test end', (test) => {508 if (_emissions.ended[test.id]) {509 return510 }511 _emissions.ended[test.id] = true512 return Cypress.action('runner:test:end', wrap(test))513 })514 _runner.on('pass', (test) => {515 return Cypress.action('runner:pass', wrap(test))516 })517 // if a test is pending mocha will only518 // emit the pending event instead of the test519 // so we normalize the pending / test events520 _runner.on('pending', function (test) {521 // do nothing if our test is skipped522 if (test._ALREADY_RAN) {523 return524 }525 if (!fired(TEST_BEFORE_RUN_EVENT, test)) {526 fire(TEST_BEFORE_RUN_EVENT, test, Cypress)527 }528 test.state = 'pending'529 if (!test.alreadyEmittedMocha) {530 // do not double emit this event531 test.alreadyEmittedMocha = true532 Cypress.action('runner:pending', wrap(test))533 }534 this.emit('test', test)535 // if this is not the last test amongst its siblings536 // then go ahead and fire its test:after:run event537 // else this will not get called538 const tests = getAllSiblingTests(test.parent, getTestById)539 if (_.last(tests) !== test) {540 return fire(TEST_AFTER_RUN_EVENT, test, Cypress)541 }542 })543 return _runner.on('fail', (runnable, err) => {544 let hookName545 const isHook = runnable.type === 'hook'546 err.stack = $stackUtils.normalizedStack(err)547 if (isHook) {548 const parentTitle = runnable.parent.title549 hookName = getHookName(runnable)550 // append a friendly message to the error indicating551 // we're skipping the remaining tests in this suite552 err = $errUtils.appendErrMsg(553 err,554 $errUtils.errByPath('uncaught.error_in_hook', {555 parentTitle,556 hookName,557 }).message,558 )559 }560 // always set runnable err so we can tap into561 // taking a screenshot on error562 runnable.err = $errUtils.wrapErr(err)563 if (!runnable.alreadyEmittedMocha) {564 // do not double emit this event565 runnable.alreadyEmittedMocha = true566 Cypress.action('runner:fail', wrap(runnable), runnable.err)567 }568 // if we've already fired the test after run event569 // it means that this runnable likely failed due to570 // a double done(err) callback, and we need to fire571 // this again!572 if (fired(TEST_AFTER_RUN_EVENT, runnable)) {573 fire(TEST_AFTER_RUN_EVENT, runnable, Cypress)574 }575 if (isHook) {576 // if a hook fails (such as a before) then the test will never577 // get run and we'll need to make sure we set the test so that578 // the TEST_AFTER_RUN_EVENT fires correctly579 return hookFailed(runnable, runnable.err, hookName, getTest, getTestFromHookOrFindTest)580 }581 })582}583const create = (specWindow, mocha, Cypress, cy) => {584 let _id = 0585 let _hookId = 0586 let _uncaughtFn = null587 let _resumedAtTestIndex = null588 const _runner = mocha.getRunner()589 _runner.suite = mocha.getRootSuite()590 function isNotAlreadyRunTest (test) {591 return _resumedAtTestIndex == null || getTestIndexFromId(test.id) >= _resumedAtTestIndex592 }593 const getTestFromHookOrFindTest = (hook) => {594 const test = getTestFromHook(hook)595 if (test) {596 return test597 }598 const suite = hook.parent599 if (hook.hookName === 'after all') {600 return findLastTestInSuite(suite, isNotAlreadyRunTest)601 }602 if (hook.hookName === 'before all') {603 return findTestInSuite(suite, isNotAlreadyRunTest)604 }605 }606 const onScriptError = (err) => {607 // err will not be returned if cy can associate this608 // uncaught exception to an existing runnable609 if (!err) {610 return true611 }612 const todoMsg = () => {613 if (!Cypress.config('isTextTerminal')) {614 return 'Check your console for the stack trace or click this message to see where it originated from.'615 }616 }617 const appendMsg = _.chain([618 'Cypress could not associate this error to any specific test.',619 'We dynamically generated a new test to display this failure.',620 todoMsg(),621 ])622 .compact()623 .join('\n\n')624 .value()625 err = $errUtils.appendErrMsg(err, appendMsg)626 const throwErr = () => {627 throw err628 }629 // we could not associate this error630 // and shouldn't ever start our run631 _uncaughtFn = throwErr632 // return undefined so the browser does its default633 // uncaught exception behavior (logging to console)634 return undefined635 }636 specWindow.onerror = function () {637 const err = cy.onSpecWindowUncaughtException.apply(cy, arguments)638 return onScriptError(err)639 }640 // hold onto the _runnables for faster lookup later641 let _stopped = false642 let _test = null643 let _tests = []644 let _testsById = {}645 const _testsQueue = []646 const _testsQueueById = {}647 const _runnables = []648 const _logsById = {}649 let _emissions = {650 started: {},651 ended: {},652 }653 let _startTime = null654 // increment the id counter655 const getTestId = () => {656 return `r${_id += 1}`657 }658 const getHookId = () => {659 return `h${_hookId += 1}`660 }661 const setTestsById = (tbid) => {662 return _testsById = tbid663 }664 const setTests = (t) => {665 return _tests = t666 }667 const getTests = () => {668 return _tests669 }670 const onRunnable = (r) => {671 return _runnables.push(r)672 }673 const onLogsById = (l) => {674 return _logsById[l.id] = l675 }676 const getTest = () => {677 return _test678 }679 const setTest = (t) => {680 return _test = t681 }682 const getTestById = (id) => {683 // perf short circuit684 if (!id) {685 return686 }687 return _testsById[id]688 }689 overrideRunnerHook(Cypress, _runner, getTestById, getTest, setTest, getTests)690 return {691 onScriptError,692 normalizeAll (tests) {693 // if we have an uncaught error then slice out694 // all of the tests and suites and just generate695 // a single test since we received an uncaught696 // error prior to processing any of mocha's tests697 // which could have occurred in a separate support file698 if (_uncaughtFn) {699 _runner.suite.suites = []700 _runner.suite.tests = []701 // create a runnable to associate for the failure702 mocha.createRootTest('An uncaught error was detected outside of a test', _uncaughtFn)703 }704 return normalizeAll(705 _runner.suite,706 tests,707 setTestsById,708 setTests,709 onRunnable,710 onLogsById,711 getTestId,712 )713 },714 run (fn) {715 if (_startTime == null) {716 _startTime = moment().toJSON()717 }718 _runnerListeners(_runner, Cypress, _emissions, getTestById, getTest, setTest, getHookId, getTestFromHookOrFindTest)719 return _runner.run((failures) => {720 // if we happen to make it all the way through721 // the run, then just set _stopped to true here722 _stopped = true723 // TODO this functions is not correctly724 // synchronized with the 'end' event that725 // we manage because of uncaught hook errors726 if (fn) {727 return fn(failures, getTestResults(_tests))728 }729 })730 },731 onRunnableRun (runnableRun, runnable, args) {732 // extract out the next(fn) which mocha uses to733 // move to the next runnable - this will be our async seam734 const _next = args[0]735 const test = getTest() || getTestFromRunnable(runnable)736 // if there's no test, this is likely a rouge before/after hook737 // that should not have run, so skip this runnable738 if (!test) {739 return _next()740 }741 // closure for calculating the actual742 // runtime of a runnables fn exection duration743 // and also the run of the runnable:after:run:async event744 let lifecycleStart745 let wallClockStartedAt = null746 let wallClockEnd = null747 let fnDurationStart = null748 let fnDurationEnd = null749 let afterFnDurationStart = null750 let afterFnDurationEnd = null751 // when this is a hook, capture the real start752 // date so we can calculate our test's duration753 // including all of its hooks754 wallClockStartedAt = new Date()755 if (!test.wallClockStartedAt) {756 // if we don't have lifecycle timings yet757 lifecycleStart = wallClockStartedAt758 }759 if (test.wallClockStartedAt == null) {760 test.wallClockStartedAt = wallClockStartedAt761 }762 // if this isnt a hook, then the name is 'test'763 const hookName = runnable.type === 'hook' ? getHookName(runnable) : 'test'764 // if we haven't yet fired this event for this test765 // that means that we need to reset the previous state766 // of cy - since we now have a new 'test' and all of the767 // associated _runnables will share this state768 if (!fired(TEST_BEFORE_RUN_EVENT, test)) {769 fire(TEST_BEFORE_RUN_EVENT, test, Cypress)770 }771 const next = (err) => {772 // now set the duration of the after runnable run async event773 afterFnDurationEnd = (wallClockEnd = new Date())774 switch (runnable.type) {775 case 'hook':776 // reset runnable duration to include lifecycle777 // and afterFn timings purely for the mocha runner.778 // this is what it 'feels' like to the user779 runnable.duration = wallClockEnd - wallClockStartedAt780 setTestTimingsForHook(test, hookName, {781 hookId: runnable.hookId,782 fnDuration: fnDurationEnd - fnDurationStart,783 afterFnDuration: afterFnDurationEnd - afterFnDurationStart,784 })785 break786 case 'test':787 // if we are currently on a test then788 // recalculate its duration to be based789 // against that (purely for the mocha reporter)790 test.duration = wallClockEnd - test.wallClockStartedAt791 // but still preserve its actual function792 // body duration for timings793 setTestTimings(test, 'test', {794 fnDuration: fnDurationEnd - fnDurationStart,795 afterFnDuration: afterFnDurationEnd - afterFnDurationStart,796 })797 break798 default:799 break800 }801 return _next(err)802 }803 const onNext = (err) => {804 // when done with the function set that to end805 fnDurationEnd = new Date()806 // and also set the afterFnDuration to this same date807 afterFnDurationStart = fnDurationEnd808 // attach error right now809 // if we have one810 if (err) {811 if (err instanceof Pending) {812 err.isPending = true813 }814 runnable.err = $errUtils.wrapErr(err)815 }816 return runnableAfterRunAsync(runnable, Cypress)817 .then(() => {818 // once we complete callback with the819 // original err820 next(err)821 // return null here to signal to bluebird822 // that we did not forget to return a promise823 // because mocha internally does not return824 // the test.run(fn)825 return null826 }).catch((err) => {827 next(err)828 // return null here to signal to bluebird829 // that we did not forget to return a promise830 // because mocha internally does not return831 // the test.run(fn)832 return null833 })834 }835 // our runnable is about to run, so let cy know. this enables836 // us to always have a correct runnable set even when we are837 // running lifecycle events838 // and also get back a function result handler that we use as839 // an async seam840 cy.setRunnable(runnable, hookName)841 // TODO: handle promise timeouts here!842 // whenever any runnable is about to run843 // we figure out what test its associated to844 // if its a hook, and then we fire the845 // test:before:run:async action if its not846 // been fired before for this test847 return testBeforeRunAsync(test, Cypress)848 .catch((err) => {849 // TODO: if our async tasks fail850 // then allow us to cause the test851 // to fail here by blowing up its fn852 // callback853 const { fn } = runnable854 const restore = () => {855 return runnable.fn = fn856 }857 runnable.fn = () => {858 restore()859 throw err860 }861 }).finally(() => {862 if (lifecycleStart) {863 // capture how long the lifecycle took as part864 // of the overall wallClockDuration of our test865 setTestTimings(test, 'lifecycle', new Date() - lifecycleStart)866 }867 // capture the moment we're about to invoke868 // the runnable's callback function869 fnDurationStart = new Date()870 // call the original method with our871 // custom onNext function872 return runnableRun.call(runnable, onNext)873 })874 },875 getStartTime () {876 return _startTime877 },878 setStartTime (iso) {879 _startTime = iso880 },881 countByTestState (tests, state) {882 const count = _.filter(tests, (test, key) => {883 return test.state === state884 })885 return count.length886 },887 setNumLogs (num) {888 return $Log.setCounter(num)889 },890 getEmissions () {891 return _emissions892 },893 getTestsState () {894 const id = _test != null ? _test.id : undefined895 const tests = {}896 // bail if we dont have a current test897 if (!id) {898 return {}899 }900 // search through all of the tests901 // until we find the current test902 // and break then903 for (let test of _tests) {904 if (test.id === id) {905 break906 } else {907 test = wrapAll(test)908 _.each(RUNNABLE_LOGS, (type) => {909 let logs910 logs = test[type]911 if (logs) {912 test[type] = _.map(logs, $Log.toSerializedJSON)913 }914 })915 tests[test.id] = test916 }917 }918 return tests919 },920 stop () {921 if (_stopped) {922 return923 }924 _stopped = true925 // abort the run926 _runner.abort()927 // emit the final 'end' event928 // since our reporter depends on this event929 // and mocha may never fire this becuase our930 // runnable may never finish931 _runner.emit('end')932 // remove all the listeners933 // so no more events fire934 return _runner.removeAllListeners()935 },936 getDisplayPropsForLog: $Log.getDisplayProps,937 getConsolePropsForLogById (logId) {938 let attrs939 attrs = _logsById[logId]940 if (attrs) {941 return $Log.getConsoleProps(attrs)942 }943 },944 getSnapshotPropsForLogById (logId) {945 let attrs946 attrs = _logsById[logId]947 if (attrs) {948 return $Log.getSnapshotProps(attrs)949 }950 },951 getErrorByTestId (testId) {952 let test953 test = getTestById(testId)954 if (test) {955 return $errUtils.wrapErr(test.err)956 }957 },958 resumeAtTest (id, emissions = {}) {959 _resumedAtTestIndex = getTestIndexFromId(id)960 _emissions = emissions961 for (let test of _tests) {962 if (getTestIndexFromId(test.id) !== _resumedAtTestIndex) {963 test._ALREADY_RAN = true964 test.pending = true965 } else {966 // bail so we can stop now967 return968 }969 }970 },971 getResumedAtTestIndex () {972 return _resumedAtTestIndex973 },974 cleanupQueue (numTestsKeptInMemory) {975 const cleanup = (queue) => {976 if (queue.length > numTestsKeptInMemory) {977 const test = queue.shift()978 delete _testsQueueById[test.id]979 _.each(RUNNABLE_LOGS, (logs) => {980 return _.each(test[logs], (attrs) => {981 // we know our attrs have been cleaned982 // now, so lets store that983 attrs._hasBeenCleanedUp = true984 return $Log.reduceMemory(attrs)985 })986 })987 return cleanup(queue)988 }989 }990 return cleanup(_testsQueue)991 },992 addLog (attrs, isInteractive) {993 // we dont need to hold a log reference994 // to anything in memory when we're headless995 // because you cannot inspect any logs996 let existing997 if (!isInteractive) {998 return999 }1000 let test = getTestById(attrs.testId)1001 // bail if for whatever reason we1002 // cannot associate this log to a test1003 if (!test) {1004 return1005 }1006 // if this test isnt in the current queue1007 // then go ahead and add it1008 if (!_testsQueueById[test.id]) {1009 _testsQueueById[test.id] = true1010 _testsQueue.push(test)1011 }1012 existing = _logsById[attrs.id]1013 if (existing) {1014 // because log:state:changed may1015 // fire at a later time, its possible1016 // we've already cleaned up these attrs1017 // and in that case we don't want to do1018 // anything at all1019 if (existing._hasBeenCleanedUp) {1020 return1021 }1022 // mutate the existing object1023 return _.extend(existing, attrs)1024 }1025 _logsById[attrs.id] = attrs1026 const { testId, instrument } = attrs1027 test = getTestById(testId)1028 if (test) {1029 // pluralize the instrument1030 // as a property on the runnable1031 let name1032 const logs = test[name = `${instrument}s`] != null ? test[name] : (test[name] = [])1033 // else push it onto the logs1034 return logs.push(attrs)1035 }1036 },1037 }1038}1039module.exports = {1040 create,...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1describe('My First Test', function() {2 it('Does not do much!', function() {3 cy.contains('type').click()4 cy.url().should('include', '/commands/actions')5 cy.get('.action-email')6 .type('

Full Screen

Using AI Code Generation

copy

Full Screen

1describe('My First Test', () => {2 it('Does not do much!', () => {3 cy.contains('type').click()4 cy.url().should('include', '/commands/actions')5 cy.get('.action-email')6 .type('

Full Screen

Using AI Code Generation

copy

Full Screen

1describe('My First Test', function() {2 it('Does not do much!', function() {3 expect(true).to.equal(false)4 })5})6Cypress.Commands.add('isNotAlreadyRunTest', function() {7 const test = this.currentTest;8 const alreadyRun = Cypress._.get(test, 'parent.tests', [])9 .filter(t => t.state === 'passed')10 .map(t => t.title)11 .includes(test.title);12 if (alreadyRun) {13 Cypress.log({14 message: [`Test "${test.title}" already passed`],15 consoleProps: () => {16 return {17 };18 },19 });20 this.skip();21 }22});23describe('My Second Test', function() {24 it('Does not do much!', function() {25 expect(true).to.equal(false)26 })27})28describe('My Third Test', function() {29 it('Does not do much!', function() {30 expect(true).to.equal(false)31 })32})33describe('My Fourth Test', function() {34 it('Does not do much!', function() {35 expect(true).to.equal(false)36 })37})38describe('My Fifth Test', function() {39 it('Does not do much!', function() {40 expect(true).to.equal(false)41 })42})43describe('My Sixth Test', function() {44 it('Does not do much!', function() {45 expect(true).to.equal(false)46 })47})48describe('My Seventh Test', function() {49 it('Does not do much!', function() {50 expect(true).to.equal(false)51 })52})53describe('My Eighth Test', function() {54 it('Does not do much!', function() {55 expect(true).to.equal(false)56 })57})

Full Screen

Using AI Code Generation

copy

Full Screen

1it('is not already run test', () => {2 cy.isNotAlreadyRunTest()3})4it('is already run test', () => {5 cy.isAlreadyRunTest()6})7it('is run test', () => {8 cy.isRunTest()9})10it('is not run test', () => {11 cy.isNotRunTest()12})13it('is run test', () => {14 cy.isRunTest()15})16it('is not run test', () => {17 cy.isNotRunTest()18})19it('is run test', () => {20 cy.isRunTest()21})22it('is not run test', () => {23 cy.isNotRunTest()24})25it('is run test', () => {26 cy.isRunTest()27})28it('is not run test', () => {29 cy.isNotRunTest()30})31it('is run test', () => {32 cy.isRunTest()33})34it('is not run test', () => {35 cy.isNotRunTest()36})37it('is run test', () => {38 cy.isRunTest()

Full Screen

Using AI Code Generation

copy

Full Screen

1describe('Test', () => {2 it('test', () => {3 cy.isNotAlreadyRunTest('test').then((isNotAlreadyRunTest) => {4 if (isNotAlreadyRunTest) {5 }6 });7 });8});9Cypress.Commands.add('isNotAlreadyRunTest', (testName) => {10 return cy.window().then((win) => {11 if (win.Cypress._alreadyRunTests.indexOf(testName) === -1) {12 win.Cypress._alreadyRunTests.push(testName);13 return true;14 }15 return false;16 });17});18Cypress.on('window:before:load', (win) => {19 win.Cypress = win.Cypress || {};20 win.Cypress._alreadyRunTests = win.Cypress._alreadyRunTests || [];21});22Cypress.LocalStorage.clear();23cy.clearLocalStorage();

Full Screen

Using AI Code Generation

copy

Full Screen

1describe('My First Test', function() {2 it('Does not do much!', function() {3 cy.isNotAlreadyRunTest('My First Test')4 })5})6Cypress.Commands.add('isNotAlreadyRunTest', (testName) => {7 cy.window().then((win) => {8 if (win.sessionStorage.getItem(testName)) {9 cy.log('Test already run, skipping...')10 } else {11 win.sessionStorage.setItem(testName, true)12 }13 })14})

Full Screen

Using AI Code Generation

copy

Full Screen

1cy.isNotAlreadyRunTest('test').then((isNotAlreadyRun) => {2 if (isNotAlreadyRun) {3 }4})5Cypress.Commands.add('isNotAlreadyRunTest', (testName) => {6 return cy.window().then((win) => {7 if (!win.testRun) {8 win.testRun = [];9 }10 if (win.testRun.indexOf(testName) === -1) {11 win.testRun.push(testName);12 return true;13 }14 return false;15 });16});

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