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