Best JavaScript code snippet using cypress
cy.js
Source:cy.js
...50}51const setRemoteIframeProps = ($autIframe, state) => {52 return state('$autIframe', $autIframe)53}54function __stackReplacementMarker(fn, ctx, args) {55 return fn.apply(ctx, args)56}57// We only set top.onerror once since we make it configurable:false58// but we update cy instance every run (page reload or rerun button)59let curCy = null60const setTopOnError = function (cy) {61 if (curCy) {62 curCy = cy63 return64 }65 curCy = cy66 const onTopError = function () {67 return curCy.onUncaughtException.apply(curCy, arguments)68 }69 top.onerror = onTopError70 // Prevent Mocha from setting top.onerror which would override our handler71 // Since the setter will change which event handler gets invoked, we make it a noop72 return Object.defineProperty(top, 'onerror', {73 set() {},74 get() {75 return onTopError76 },77 configurable: false,78 enumerable: true,79 })80}81const create = function (specWindow, Cypress, Cookies, state, config, log) {82 let stopped = false83 const commandFns = {}84 const isStopped = () => {85 return stopped86 }87 const onFinishAssertions = function () {88 return assertions.finishAssertions.apply(window, arguments)89 }90 const warnMixingPromisesAndCommands = function () {91 const title = state('runnable').fullTitle()92 $errUtils.warnByPath('miscellaneous.mixing_promises_and_commands', {93 args: { title },94 })95 }96 const $$ = function (selector, context) {97 if (context == null) {98 context = state('document')99 }100 return $dom.query(selector, context)101 }102 const queue = $CommandQueue.create()103 // $VideoRecorder.create(Cypress)104 const timeouts = $Timeouts.create(state)105 const stability = $Stability.create(Cypress, state)106 const retries = $Retries.create(107 Cypress,108 state,109 timeouts.timeout,110 timeouts.clearTimeout,111 stability.whenStable,112 onFinishAssertions113 )114 const assertions = $Assertions.create(state, queue, retries.retry)115 const jquery = $jQuery.create(state)116 const location = $Location.create(state)117 const focused = $Focused.create(state)118 const keyboard = $Keyboard.create(state)119 const mouse = $Mouse.create(state, keyboard, focused, Cypress)120 const timers = $Timers.create()121 const { expect } = $Chai.create(specWindow, state, assertions.assert)122 const xhrs = $Xhrs.create(state)123 const aliases = $Aliases.create(state)124 const errors = $Errors.create(state, config, log)125 const ensures = $Ensures.create(state, expect)126 const snapshots = $Snapshots.create($$, state)127 const isCy = (val) => {128 return val === cy || $utils.isInstanceOf(val, $Chainer)129 }130 const runnableCtx = function (name) {131 ensures.ensureRunnable(name)132 return state('runnable').ctx133 }134 const urlNavigationEvent = (event) => {135 return Cypress.action(136 'app:navigation:changed',137 `page navigation event (${event})`138 )139 }140 const contentWindowListeners = function (contentWindow) {141 return $Listeners.bindTo(contentWindow, {142 onError() {143 // use a function callback here instead of direct144 // reference so our users can override this function145 // if need be146 return cy.onUncaughtException.apply(cy, arguments)147 },148 onSubmit(e) {149 return Cypress.action('app:form:submitted', e)150 },151 onBeforeUnload(e) {152 stability.isStable(false, 'beforeunload')153 Cookies.setInitial()154 timers.reset()155 Cypress.action('app:window:before:unload', e)156 // return undefined so our beforeunload handler157 // doesnt trigger a confirmation dialog158 return undefined159 },160 onUnload(e) {161 return Cypress.action('app:window:unload', e)162 },163 onNavigation(...args) {164 return Cypress.action('app:navigation:changed', ...args)165 },166 onAlert(str) {167 return Cypress.action('app:window:alert', str)168 },169 onConfirm(str) {170 const results = Cypress.action('app:window:confirm', str)171 // return false if ANY results are false172 // else true173 const ret = !_.some(results, returnedFalse)174 Cypress.action('app:window:confirmed', str, ret)175 return ret176 },177 })178 }179 const wrapNativeMethods = function (contentWindow) {180 try {181 // return null to trick contentWindow into thinking182 // its not been iframed if modifyObstructiveCode is true183 if (config('modifyObstructiveCode')) {184 Object.defineProperty(contentWindow, 'frameElement', {185 get() {186 return null187 },188 })189 }190 contentWindow.HTMLElement.prototype.focus = function (focusOption) {191 return focused.interceptFocus(this, contentWindow, focusOption)192 }193 contentWindow.HTMLElement.prototype.blur = function () {194 return focused.interceptBlur(this)195 }196 contentWindow.SVGElement.prototype.focus = function (focusOption) {197 return focused.interceptFocus(this, contentWindow, focusOption)198 }199 contentWindow.SVGElement.prototype.blur = function () {200 return focused.interceptBlur(this)201 }202 contentWindow.HTMLInputElement.prototype.select = function () {203 return $selection.interceptSelect.call(this)204 }205 contentWindow.document.hasFocus = function () {206 return focused.documentHasFocus.call(this)207 }208 const cssModificationSpy = function (original, ...args) {209 snapshots.onCssModified(this.href)210 return original.apply(this, args)211 }212 const { insertRule } = contentWindow.CSSStyleSheet.prototype213 const { deleteRule } = contentWindow.CSSStyleSheet.prototype214 contentWindow.CSSStyleSheet.prototype.insertRule = _.wrap(215 insertRule,216 cssModificationSpy217 )218 contentWindow.CSSStyleSheet.prototype.deleteRule = _.wrap(219 deleteRule,220 cssModificationSpy221 )222 } catch (error) {} // eslint-disable-line no-empty223 }224 const enqueue = function (obj) {225 // if we have a nestedIndex it means we're processing226 // nested commands and need to splice them into the227 // index past the current index as opposed to228 // pushing them to the end we also dont want to229 // reset the run defer because splicing means we're230 // already in a run loop and dont want to create another!231 // we also reset the .next property to properly reference232 // our new obj233 // we had a bug that would bomb on custom commands when it was the234 // first command. this was due to nestedIndex being undefined at that235 // time. so we have to ensure to check that its any kind of number (even 0)236 // in order to know to splice into the existing array.237 let nestedIndex = state('nestedIndex')238 // if this is a number then we know239 // we're about to splice this into our commands240 // and need to reset next + increment the index241 if (_.isNumber(nestedIndex)) {242 state('nestedIndex', (nestedIndex += 1))243 }244 // we look at whether or not nestedIndex is a number, because if it245 // is then we need to splice inside of our commands, else just push246 // it onto the end of the queu247 const index = _.isNumber(nestedIndex) ? nestedIndex : queue.length248 queue.splice(index, 0, obj)249 return Cypress.action('cy:command:enqueued', obj)250 }251 const getCommandsUntilFirstParentOrValidSubject = function (252 command,253 memo = []254 ) {255 if (!command) {256 return null257 }258 // push these onto the beginning of the commands array259 memo.unshift(command)260 // break and return the memo261 if (262 command.get('type') === 'parent' ||263 $dom.isAttached(command.get('subject'))264 ) {265 return memo266 }267 return getCommandsUntilFirstParentOrValidSubject(command.get('prev'), memo)268 }269 const runCommand = function (command) {270 // bail here prior to creating a new promise271 // because we could have stopped / canceled272 // prior to ever making it through our first273 // command274 if (stopped) {275 return276 }277 state('current', command)278 state('chainerId', command.get('chainerId'))279 return stability280 .whenStable(() => {281 // TODO: handle this event282 // @trigger "invoke:start", command283 state('nestedIndex', state('index'))284 return command.get('args')285 })286 .all()287 .then((args) => {288 // store this if we enqueue new commands289 // to check for promise violations290 let ret291 let enqueuedCmd = null292 const commandEnqueued = (obj) => {293 return (enqueuedCmd = obj)294 }295 // only check for command enqueing when none296 // of our args are functions else commands297 // like cy.then or cy.each would always fail298 // since they return promises and queue more299 // new commands300 if (noArgsAreAFunction(args)) {301 Cypress.once('command:enqueued', commandEnqueued)302 }303 // run the command's fn with runnable's context304 try {305 ret = __stackReplacementMarker(command.get('fn'), state('ctx'), args)306 } catch (err) {307 throw err308 } finally {309 // always remove this listener310 Cypress.removeListener('command:enqueued', commandEnqueued)311 }312 state('commandIntermediateValue', ret)313 // we cannot pass our cypress instance or our chainer314 // back into bluebird else it will create a thenable315 // which is never resolved316 if (isCy(ret)) {317 return null318 }319 if (!(!enqueuedCmd || !isPromiseLike(ret))) {320 return $errUtils.throwErrByPath(321 'miscellaneous.command_returned_promise_and_commands',322 {323 args: {324 current: command.get('name'),325 called: enqueuedCmd.name,326 },327 }328 )329 }330 if (!(!enqueuedCmd || !!_.isUndefined(ret))) {331 // TODO: clean this up in the utility function332 // to conditionally stringify functions333 ret = _.isFunction(ret) ? ret.toString() : $utils.stringify(ret)334 // if we got a return value and we enqueued335 // a new command and we didn't return cy336 // or an undefined value then throw337 return $errUtils.throwErrByPath(338 'miscellaneous.returned_value_and_commands_from_custom_command',339 {340 args: {341 current: command.get('name'),342 returned: ret,343 },344 }345 )346 }347 return ret348 })349 .then((subject) => {350 state('commandIntermediateValue', undefined)351 // we may be given a regular array here so352 // we need to re-wrap the array in jquery353 // if that's the case if the first item354 // in this subject is a jquery element.355 // we want to do this because in 3.1.2 there356 // was a regression when wrapping an array of elements357 const firstSubject = $utils.unwrapFirst(subject)358 // if ret is a DOM element and its not an instance of our own jQuery359 if (360 subject &&361 $dom.isElement(firstSubject) &&362 !$utils.isInstanceOf(subject, $)363 ) {364 // set it back to our own jquery object365 // to prevent it from being passed downstream366 // TODO: enable turning this off367 // wrapSubjectsInJquery: false368 // which will just pass subjects downstream369 // without modifying them370 subject = $dom.wrap(subject)371 }372 command.set({ subject })373 // end / snapshot our logs374 // if they need it375 command.finishLogs()376 // reset the nestedIndex back to null377 state('nestedIndex', null)378 // also reset recentlyReady back to null379 state('recentlyReady', null)380 state('subject', subject)381 return subject382 })383 }384 const run = function () {385 const next = function () {386 // bail if we've been told to abort in case387 // an old command continues to run after388 if (stopped) {389 return390 }391 // start at 0 index if we dont have one392 let index = state('index') || state('index', 0)393 const command = queue.at(index)394 // if the command should be skipped395 // just bail and increment index396 // and set the subject397 // TODO DRY THIS LOGIC UP398 if (command && command.get('skip')) {399 // must set prev + next since other400 // operations depend on this state being correct401 command.set({ prev: queue.at(index - 1), next: queue.at(index + 1) })402 state('index', index + 1)403 state('subject', command.get('subject'))404 return next()405 }406 // if we're at the very end407 if (!command) {408 // trigger queue is almost finished409 Cypress.action('cy:command:queue:before:end')410 // we need to wait after all commands have411 // finished running if the application under412 // test is no longer stable because we cannot413 // move onto the next test until its finished414 return stability.whenStable(() => {415 Cypress.action('cy:command:queue:end')416 return null417 })418 }419 // store the previous timeout420 const prevTimeout = timeouts.timeout()421 // store the current runnable422 const runnable = state('runnable')423 Cypress.action('cy:command:start', command)424 return runCommand(command).then(() => {425 // each successful command invocation should426 // always reset the timeout for the current runnable427 // unless it already has a state. if it has a state428 // and we reset the timeout again, it will always429 // cause a timeout later no matter what. by this time430 // mocha expects the test to be done431 let fn432 if (!runnable.state) {433 timeouts.timeout(prevTimeout)434 }435 // mutate index by incrementing it436 // this allows us to keep the proper index437 // in between different hooks like before + beforeEach438 // else run will be called again and index would start439 // over at 0440 state('index', (index += 1))441 Cypress.action('cy:command:end', command)442 fn = state('onPaused')443 if (fn) {444 return new Promise((resolve) => {445 return fn(resolve)446 }).then(next)447 }448 return next()449 })450 }451 let inner = null452 // this ends up being the parent promise wrapper453 const promise = new Promise((resolve, reject) => {454 // bubble out the inner promise455 // we must use a resolve(null) here456 // so the outer promise is first defined457 // else this will kick off the 'next' call458 // too soon and end up running commands prior459 // to promise being defined460 inner = Promise.resolve(null).then(next).then(resolve).catch(reject)461 // can't use onCancel argument here because462 // its called asynchronously463 // when we manually reject our outer promise we464 // have to immediately cancel the inner one else465 // it won't be notified and its callbacks will466 // continue to be invoked467 // normally we don't have to do this because rejections468 // come from the inner promise and bubble out to our outer469 //470 // but when we manually reject the outer promise we471 // have to go in the opposite direction from outer -> inner472 const rejectOuterAndCancelInner = function (err) {473 inner.cancel()474 return reject(err)475 }476 state('resolve', resolve)477 state('reject', rejectOuterAndCancelInner)478 })479 .catch((err) => {480 // since this failed this means that a481 // specific command failed and we should482 // highlight it in red or insert a new command483 err.name = err.name || 'CypressError'484 errors.commandRunningFailed(err)485 return fail(err, state('runnable'))486 })487 .finally(cleanup)488 // cancel both promises489 const cancel = function () {490 promise.cancel()491 inner.cancel()492 // notify the world493 return Cypress.action('cy:canceled')494 }495 state('cancel', cancel)496 state('promise', promise)497 // return this outer bluebird promise498 return promise499 }500 const removeSubject = () => {501 return state('subject', undefined)502 }503 const pushSubjectAndValidate = function (name, args, firstCall, prevSubject) {504 if (firstCall) {505 // if we have a prevSubject then error506 // since we're invoking this improperly507 let needle508 if (509 prevSubject &&510 ((needle = 'optional'), ![].concat(prevSubject).includes(needle))511 ) {512 const stringifiedArg = $utils.stringifyActual(args[0])513 $errUtils.throwErrByPath(514 'miscellaneous.invoking_child_without_parent',515 {516 args: {517 cmd: name,518 args: _.isString(args[0])519 ? `\"${stringifiedArg}\"`520 : stringifiedArg,521 },522 }523 )524 }525 // else if this is the very first call526 // on the chainer then make the first527 // argument undefined (we have no subject)528 removeSubject()529 }530 const subject = state('subject')531 if (prevSubject) {532 // make sure our current subject is valid for533 // what we expect in this command534 ensures.ensureSubjectByType(subject, prevSubject, name)535 }536 args.unshift(subject)537 Cypress.action('cy:next:subject:prepared', subject, args)538 return args539 }540 const doneEarly = function () {541 stopped = true542 // we only need to worry about doneEarly when543 // it comes from a manual event such as stopping544 // Cypress or when we yield a (done) callback545 // and could arbitrarily call it whenever we want546 const p = state('promise')547 // if our outer promise is pending548 // then cancel outer and inner549 // and set canceled to be true550 if (p && p.isPending()) {551 state('canceled', true)552 state('cancel')()553 }554 return cleanup()555 }556 const cleanup = function () {557 // cleanup could be called during a 'stop' event which558 // could happen in between a runnable because they are async559 if (state('runnable')) {560 // make sure we reset the runnable's timeout now561 state('runnable').resetTimeout()562 }563 // if a command fails then after each commands564 // could also fail unless we clear this out565 state('commandIntermediateValue', undefined)566 // reset the nestedIndex back to null567 state('nestedIndex', null)568 // also reset recentlyReady back to null569 state('recentlyReady', null)570 // and forcibly move the index needle to the571 // end in case we have after / afterEach hooks572 // which need to run573 return state('index', queue.length)574 }575 const getUserInvocationStack = (err) => {576 const current = state('current')577 const currentAssertionCommand = current?.get('currentAssertionCommand')578 const withInvocationStack = currentAssertionCommand || current579 // user assertion errors (expect().to, etc) get their invocation stack580 // attached to the error thrown from chai581 // command errors and command assertion errors (default assertion or cy.should)582 // have the invocation stack attached to the current command583 let userInvocationStack = state('currentAssertionUserInvocationStack')584 // if there is no user invocation stack from an assertion or it is the default585 // assertion, meaning it came from a command (e.g. cy.get), prefer the586 // command's user invocation stack so the code frame points to the command.587 // `should` callbacks are tricky because the `currentAssertionUserInvocationStack`588 // points to the `cy.should`, but the error came from inside the callback,589 // so we need to prefer that.590 if (591 !userInvocationStack ||592 err.isDefaultAssertionErr ||593 (currentAssertionCommand && !current?.get('followedByShouldCallback'))594 ) {595 userInvocationStack = withInvocationStack?.get('userInvocationStack')596 }597 if (!userInvocationStack) return598 if (599 $errUtils.isCypressErr(err) ||600 $errUtils.isAssertionErr(err) ||601 $errUtils.isChaiValidationErr(err)602 ) {603 return userInvocationStack604 }605 }606 const fail = (err) => {607 let rets608 stopped = true609 err.stack = $stackUtils.normalizedStack(err)610 // err = $errUtils.enhanceStack({611 // err,612 // userInvocationStack: getUserInvocationStack(err),613 // projectRoot: config('projectRoot'),614 // })615 // err = $errUtils.processErr(err, config)616 // store the error on state now617 state('error', err)618 const finish = function (err) {619 // if we have an async done callback620 // we have an explicit (done) callback and621 // we aren't attached to the cypress command queue622 // promise chain and throwing the error would only623 // result in an unhandled rejection624 let d625 d = state('done')626 if (d) {627 // invoke it with err628 return d(err)629 }630 // else we're connected to the promise chain631 // and need to throw so this bubbles up632 throw err633 }634 // if we have a "fail" handler635 // 1. catch any errors it throws and fail the test636 // 2. otherwise swallow any errors637 // 3. but if the test is not ended with a done()638 // then it should fail639 // 4. and tests without a done will pass640 // if we dont have a "fail" handler641 // 1. callback with state("done") when async642 // 2. throw the error for the promise chain643 try {644 // collect all of the callbacks for 'fail'645 rets = Cypress.action('cy:fail', err, state('runnable'))646 } catch (cyFailErr) {647 // and if any of these throw synchronously immediately error648 cyFailErr.stack = $stackUtils.normalizedStack(cyFailErr)649 return finish(cyFailErr)650 }651 // bail if we had callbacks attached652 if (rets && rets.length) {653 return654 }655 // else figure out how to finisht this failure656 return finish(err)657 }658 const cy = {659 id: _.uniqueId('cy'),660 // synchrounous querying661 $$,662 state,663 // command queue instance664 queue,665 // errors sync methods666 fail,667 // chai expect sync methods668 expect,669 // is cy670 isCy,671 isStopped,672 // timeout sync methods673 timeout: timeouts.timeout,674 clearTimeout: timeouts.clearTimeout,675 // stability sync methods676 isStable: stability.isStable,677 whenStable: stability.whenStable,678 // xhr sync methods679 getRequestsByAlias: xhrs.getRequestsByAlias,680 getIndexedXhrByAlias: xhrs.getIndexedXhrByAlias,681 // alias sync methods682 getAlias: aliases.getAlias,683 addAlias: aliases.addAlias,684 validateAlias: aliases.validateAlias,685 getNextAlias: aliases.getNextAlias,686 aliasNotFoundFor: aliases.aliasNotFoundFor,687 getXhrTypeByAlias: aliases.getXhrTypeByAlias,688 // location sync methods689 getRemoteLocation: location.getRemoteLocation,690 // jquery sync methods691 getRemotejQueryInstance: jquery.getRemotejQueryInstance,692 // focused sync methods693 getFocused: focused.getFocused,694 needsFocus: focused.needsFocus,695 fireFocus: focused.fireFocus,696 fireBlur: focused.fireBlur,697 devices: {698 mouse,699 keyboard,700 },701 // timer sync methods702 pauseTimers: timers.pauseTimers,703 // snapshots sync methods704 createSnapshot: snapshots.createSnapshot,705 // retry sync methods706 retry: retries.retry,707 // assertions sync methods708 assert: assertions.assert,709 verifyUpcomingAssertions: assertions.verifyUpcomingAssertions,710 // ensure sync methods711 ensureWindow: ensures.ensureWindow,712 ensureElement: ensures.ensureElement,713 ensureDocument: ensures.ensureDocument,714 ensureAttached: ensures.ensureAttached,715 ensureExistence: ensures.ensureExistence,716 ensureElExistence: ensures.ensureElExistence,717 ensureElDoesNotHaveCSS: ensures.ensureElDoesNotHaveCSS,718 ensureVisibility: ensures.ensureVisibility,719 ensureDescendents: ensures.ensureDescendents,720 ensureNotReadonly: ensures.ensureNotReadonly,721 ensureNotDisabled: ensures.ensureNotDisabled,722 ensureValidPosition: ensures.ensureValidPosition,723 ensureScrollability: ensures.ensureScrollability,724 ensureElementIsNotAnimating: ensures.ensureElementIsNotAnimating,725 initialize($autIframe) {726 setRemoteIframeProps($autIframe, state)727 // dont need to worry about a try/catch here728 // because this is during initialize and its729 // impossible something is wrong here730 setWindowDocumentProps(getContentWindow($autIframe), state)731 // initially set the content window listeners too732 // so we can tap into all the normal flow of events733 // like before:unload, navigation events, etc734 contentWindowListeners(getContentWindow($autIframe))735 // the load event comes from the autIframe anytime any window736 // inside of it loads.737 // when this happens we need to check for cross origin errors738 // by trying to talk to the contentWindow document to see if739 // its accessible.740 // when we find ourselves in a cross origin situation, then our741 // proxy has not injected Cypress.action('window:before:load')742 // so Cypress.onBeforeAppWindowLoad() was never called743 return $autIframe.on('load', () => {744 // if setting these props failed745 // then we know we're in a cross origin failure746 let onpl747 let r748 try {749 setWindowDocumentProps(getContentWindow($autIframe), state)750 // we may need to update the url now751 urlNavigationEvent('load')752 // we normally DONT need to reapply contentWindow listeners753 // because they would have been automatically applied during754 // onBeforeAppWindowLoad, but in the case where we visited755 // about:blank in a visit, we do need these756 contentWindowListeners(getContentWindow($autIframe))757 Cypress.action('app:window:load', state('window'))758 // we are now stable again which is purposefully759 // the last event we call here, to give our event760 // listeners time to be invoked prior to moving on761 return stability.isStable(true, 'load')762 } catch (err) {763 let e = err764 // we failed setting the remote window props765 // which means we're in a cross domain failure766 // check first to see if you have a callback function767 // defined and let the page load change the error768 onpl = state('onPageLoadErr')769 if (onpl) {770 e = onpl(e)771 }772 // and now reject with it773 r = state('reject')774 if (r) {775 return r(e)776 }777 }778 })779 },780 stop() {781 // don't do anything if we've already stopped782 if (stopped) {783 return784 }785 return doneEarly()786 },787 reset() {788 stopped = false789 const s = state()790 const backup = {791 window: s.window,792 document: s.document,793 $autIframe: s.$autIframe,794 }795 // reset state back to empty object796 state.reset()797 // and then restore these backed up props798 state(backup)799 queue.reset()800 timers.reset()801 return cy.removeAllListeners()802 },803 addCommandSync(name, fn) {804 cy[name] = function () {805 return fn.apply(runnableCtx(name), arguments)806 }807 },808 addChainer(name, fn) {809 // add this function to our chainer class810 return $Chainer.add(name, fn)811 },812 addCommand({ name, fn, type, prevSubject }) {813 // TODO: prob don't need this anymore814 commandFns[name] = fn815 const wrap = function (firstCall) {816 fn = commandFns[name]817 const wrapped = wrapByType(fn, firstCall)818 wrapped.originalFn = fn819 return wrapped820 }821 const wrapByType = function (fn, firstCall) {822 if (type === 'parent') {823 return fn824 }825 // child, dual, assertion, utility command826 // pushes the previous subject into them827 // after verifying its of the correct type828 return function (...args) {829 // push the subject into the args830 args = pushSubjectAndValidate(name, args, firstCall, prevSubject)831 return fn.apply(runnableCtx(name), args)832 }833 }834 cy[name] = function (...args) {835 const userInvocationStack = $stackUtils.normalizedUserInvocationStack(836 new specWindow.Error('command invocation stack').stack837 )838 let ret839 // ensures.ensureRunnable(name)840 // this is the first call on cypress841 // so create a new chainer instance842 const chain = $Chainer.create(843 name,844 userInvocationStack,845 specWindow,846 args847 )848 // store the chain so we can access it later849 state('chain', chain)850 // if we are in the middle of a command851 // and its return value is a promise852 // that means we are attempting to invoke853 // a cypress command within another cypress854 // command and we should error855 ret = state('commandIntermediateValue')856 if (ret) {857 const current = state('current')858 // if this is a custom promise859 if (isPromiseLike(ret) && noArgsAreAFunction(current.get('args'))) {860 $errUtils.throwErrByPath(861 'miscellaneous.command_returned_promise_and_commands',862 {863 args: {864 current: current.get('name'),865 called: name,866 },867 }868 )869 }870 }871 // if we're the first call onto a cy872 // command, then kick off the run873 if (!state('promise')) {874 if (state('returnedCustomPromise')) {875 warnMixingPromisesAndCommands()876 }877 run()878 }879 return chain880 }881 return cy.addChainer(name, (chainer, userInvocationStack, args) => {882 const { firstCall, chainerId } = chainer883 // dont enqueue / inject any new commands if884 // onInjectCommand returns false885 const onInjectCommand = state('onInjectCommand')886 if (_.isFunction(onInjectCommand)) {887 if (onInjectCommand.call(cy, name, ...args) === false) {888 return889 }890 }891 enqueue({892 name,893 args,894 type,895 chainerId,896 userInvocationStack,897 fn: wrap(firstCall),898 })899 return true900 })901 },902 now(name, ...args) {903 return Promise.resolve(commandFns[name].apply(cy, args))904 },905 replayCommandsFrom(current) {906 // reset each chainerId to the907 // current value908 const chainerId = state('chainerId')909 const insert = function (command) {910 command.set('chainerId', chainerId)911 // clone the command to prevent912 // mutating its properties913 return enqueue(command.clone())914 }915 // - starting with the aliased command916 // - walk up to each prev command917 // - until you reach a parent command918 // - or until the subject is in the DOM919 // - from that command walk down inserting920 // every command which changed the subject921 // - coming upon an assertion should only be922 // inserted if the previous command should923 // be replayed924 const commands = getCommandsUntilFirstParentOrValidSubject(current)925 if (commands) {926 let initialCommand = commands.shift()927 const commandsToInsert = _.reduce(928 commands,929 (memo, command, index) => {930 let needle931 const push = () => {932 return memo.push(command)933 }934 if (!(command.get('type') !== 'assertion')) {935 // if we're an assertion and the prev command936 // is in the memo, then push this one937 if (((needle = command.get('prev')), memo.includes(needle))) {938 push()939 }940 } else if (941 !(command.get('subject') === initialCommand.get('subject'))942 ) {943 // when our subjects dont match then944 // reset the initialCommand to this command945 // so the next commands can compare against946 // this one to figure out the changing subjects947 initialCommand = command948 push()949 }950 return memo951 },952 [initialCommand]953 )954 for (let c of commandsToInsert) {955 insert(c)956 }957 }958 // prevent loop comprehension959 return null960 },961 onBeforeAppWindowLoad(contentWindow) {962 // we set window / document props before the window load event963 // so that we properly handle events coming from the application964 // from the time that happens BEFORE the load event occurs965 setWindowDocumentProps(contentWindow, state)966 urlNavigationEvent('before:load')967 contentWindowListeners(contentWindow)968 wrapNativeMethods(contentWindow)969 snapshots.onBeforeWindowLoad()970 return timers.wrap(contentWindow)971 },972 onSpecWindowUncaughtException() {973 // create the special uncaught exception err974 const err = errors.createUncaughtException('spec', arguments)975 const runnable = state('runnable')976 if (!runnable) return err977 try {978 fail(err)979 } catch (failErr) {980 const r = state('reject')981 if (r) {982 return r(err)983 }984 return failErr985 }986 },987 onUncaughtException() {988 let r989 const runnable = state('runnable')990 // don't do anything if we don't have a current runnable991 if (!runnable) {992 return993 }994 // create the special uncaught exception err995 const err = errors.createUncaughtException('app', arguments)996 const results = Cypress.action('app:uncaught:exception', err, runnable)997 // dont do anything if any of our uncaught:exception998 // listeners returned false999 if (_.some(results, returnedFalse)) {1000 return1001 }1002 // do all the normal fail stuff and promise cancelation1003 // but dont re-throw the error1004 r = state('reject')1005 if (r) {1006 r(err)1007 }1008 // per the onerror docs we need to return true here1009 // https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror1010 // When the function returns true, this prevents the firing of the default event handler.1011 return true1012 },1013 detachDom(...args) {1014 return snapshots.detachDom(...args)1015 },1016 getStyles(...args) {1017 return snapshots.getStyles(...args)1018 },1019 setRunnable(runnable, hookName) {1020 // when we're setting a new runnable1021 // prepare to run again!1022 stopped = false1023 // reset the promise again1024 state('promise', undefined)1025 state('hookName', hookName)1026 state('runnable', runnable)1027 state('ctx', runnable.ctx)1028 const { fn } = runnable1029 const restore = () => {1030 return (runnable.fn = fn)1031 }1032 runnable.fn = function () {1033 restore()1034 const timeout = config('defaultCommandTimeout')1035 // control timeouts on runnables ourselves1036 if (_.isFinite(timeout)) {1037 timeouts.timeout(timeout)1038 }1039 // store the current length of our queue1040 // before we invoke the runnable.fn1041 const currentLength = queue.length1042 try {1043 // if we have a fn.length that means we1044 // are accepting a done callback and need1045 // to change the semantics around how we1046 // attach the run queue1047 let done1048 if (fn.length) {1049 const originalDone = arguments[0]1050 arguments[0] = done = function (err) {1051 // TODO: handle no longer error1052 // when ended early1053 doneEarly()1054 originalDone(err)1055 // return null else we there are situations1056 // where returning a regular bluebird promise1057 // results in a warning about promise being created1058 // in a handler but not returned1059 return null1060 }1061 // store this done property1062 // for async tests1063 state('done', done)1064 }1065 let ret = __stackReplacementMarker(fn, this, arguments)1066 // if we returned a value from fn1067 // and enqueued some new commands1068 // and the value isnt currently cy1069 // or a promise1070 if (1071 ret &&1072 queue.length > currentLength &&1073 !isCy(ret) &&1074 !isPromiseLike(ret)1075 ) {1076 // TODO: clean this up in the utility function1077 // to conditionally stringify functions1078 ret = _.isFunction(ret) ? ret.toString() : $utils.stringify(ret)1079 $errUtils.throwErrByPath(...
stack_utils_spec.js
Source:stack_utils_spec.js
1const $stackUtils = require('@packages/driver/src/cypress/stack_utils')2const $sourceMapUtils = require('@packages/driver/src/cypress/source_map_utils')3describe('driver/src/cypress/stack_utils', () => {4 context('.replacedStack', () => {5 const message = 'Original error\n\nline 2'6 it('returns stack with original message', () => {7 const err = new Error(message)8 const newStack = 'at foo (path/to/file.js:1:1)\nat bar (path/to/file.js:2:2)'9 const stack = $stackUtils.replacedStack(err, newStack)10 expect(stack).to.equal(`Error: ${message}\n${newStack}`)11 })12 it('does not replace stack if error has no stack', () => {13 const err = new Error(message)14 err.stack = ''15 const stack = $stackUtils.replacedStack(err, 'new stack')16 expect(stack).to.equal('')17 })18 })19 context('.getCodeFrame', () => {20 let originalErr21 const sourceCode = `it('is a failing test', () => {22 cy.get('.not-there')23})\24`25 beforeEach(() => {26 originalErr = {27 parsedStack: [28 { message: 'Only a message' },29 {30 fileUrl: 'http://localhost:12345/__cypress/tests?p=cypress/integration/features/source_map_spec.js',31 absoluteFile: '/dev/app/cypress/integration/features/source_map_spec.js',32 relativeFile: 'cypress/integration/features/source_map_spec.js',33 line: 2,34 column: 5,35 },36 ],37 }38 })39 it('returns existing code frame if error already has one', () => {40 const existingCodeFrame = {}41 originalErr.codeFrame = existingCodeFrame42 expect($stackUtils.getCodeFrame(originalErr)).to.equal(existingCodeFrame)43 })44 it('returns undefined if there is no parsed stack', () => {45 originalErr.parsedStack = undefined46 expect($stackUtils.getCodeFrame(originalErr)).to.be.undefined47 })48 it('returns undefined if parsed stack is empty', () => {49 originalErr.parsedStack = []50 expect($stackUtils.getCodeFrame(originalErr)).to.be.undefined51 })52 it('returns undefined if there are only message lines', () => {53 originalErr.parsedStack = [{ message: 'Only a message' }]54 expect($stackUtils.getCodeFrame(originalErr)).to.be.undefined55 })56 it('returns code frame from first stack line', () => {57 cy.stub($sourceMapUtils, 'getSourceContents').returns(sourceCode)58 const codeFrame = $stackUtils.getCodeFrame(originalErr)59 expect(codeFrame).to.be.an('object')60 expect(codeFrame.frame).to.contain(` 1 | it('is a failing test', () => {`)61 expect(codeFrame.frame).to.contain(`> 2 | cy.get('.not-there'`)62 expect(codeFrame.frame).to.contain(` | ^`)63 expect(codeFrame.frame).to.contain(` 3 | }`)64 expect(codeFrame.absoluteFile).to.equal('/dev/app/cypress/integration/features/source_map_spec.js')65 expect(codeFrame.relativeFile).to.equal('cypress/integration/features/source_map_spec.js')66 expect(codeFrame.language).to.equal('js')67 expect(codeFrame.line).to.equal(2)68 expect(codeFrame.column).to.eq(5)69 })70 it('does not add code frame if stack does not yield one', () => {71 cy.stub($sourceMapUtils, 'getSourceContents').returns(null)72 expect($stackUtils.getCodeFrame(originalErr)).to.be.undefined73 })74 })75 context('.getSourceStack', () => {76 let generatedStack77 const projectRoot = '/dev/app'78 beforeEach(() => {79 cy.stub($sourceMapUtils, 'getSourcePosition').returns({80 file: 'some_other_file.ts',81 line: 2,82 column: 1,83 })84 $sourceMapUtils.getSourcePosition.onCall(1).returns({85 file: 'cypress/integration/features/source_map_spec.coffee',86 line: 4,87 column: 3,88 })89 generatedStack = `Error: spec iframe stack90 at foo.bar (http://localhost:1234/source_map_spec.js:12:4)91 at Context.<anonymous> (http://localhost:1234/tests?p=cypress/integration/features/source_map_spec.js:6:4)\92`93 })94 it('receives generated stack and returns object with source stack and parsed source stack', () => {95 const sourceStack = $stackUtils.getSourceStack(generatedStack, projectRoot)96 expect(sourceStack.sourceMapped).to.equal(`Error: spec iframe stack97 at foo.bar (some_other_file.ts:2:2)98 at Context.<anonymous> (cypress/integration/features/source_map_spec.coffee:4:4)\99`)100 expect(sourceStack.parsed).to.eql([101 {102 message: 'Error: spec iframe stack',103 whitespace: '',104 },105 {106 function: 'foo.bar',107 fileUrl: 'http://localhost:1234/source_map_spec.js',108 originalFile: 'some_other_file.ts',109 relativeFile: 'some_other_file.ts',110 absoluteFile: '/dev/app/some_other_file.ts',111 line: 2,112 column: 2,113 whitespace: ' ',114 },115 {116 function: 'Context.<anonymous>',117 fileUrl: 'http://localhost:1234/tests?p=cypress/integration/features/source_map_spec.js',118 originalFile: 'cypress/integration/features/source_map_spec.coffee',119 relativeFile: 'cypress/integration/features/source_map_spec.coffee',120 absoluteFile: '/dev/app/cypress/integration/features/source_map_spec.coffee',121 line: 4,122 column: 4,123 whitespace: ' ',124 },125 ])126 })127 it('works when first line is the error message', () => {128 const sourceStack = $stackUtils.getSourceStack(generatedStack, projectRoot)129 expect(sourceStack.sourceMapped).to.equal(`Error: spec iframe stack130 at foo.bar (some_other_file.ts:2:2)131 at Context.<anonymous> (cypress/integration/features/source_map_spec.coffee:4:4)\132`)133 })134 it('works when first line is not the error message', () => {135 generatedStack = generatedStack.split('\n').slice(1).join('\n')136 const sourceStack = $stackUtils.getSourceStack(generatedStack, projectRoot)137 expect(sourceStack.sourceMapped).to.equal(` at foo.bar (some_other_file.ts:2:2)138 at Context.<anonymous> (cypress/integration/features/source_map_spec.coffee:4:4)\139`)140 })141 it('works when first several lines are the error message', () => {142 generatedStack = `Some\nmore\nlines\n\n${generatedStack}`143 const sourceStack = $stackUtils.getSourceStack(generatedStack, projectRoot)144 expect(sourceStack.sourceMapped).to.equal(`Some145more146lines147Error: spec iframe stack148 at foo.bar (some_other_file.ts:2:2)149 at Context.<anonymous> (cypress/integration/features/source_map_spec.coffee:4:4)\150`)151 })152 it('strips webpack protocol from relativeFile and maintains it in originalFile', () => {153 $sourceMapUtils.getSourcePosition.returns({154 file: 'cypress:///some_other_file.ts',155 line: 2,156 column: 1,157 })158 $sourceMapUtils.getSourcePosition.onCall(1).returns({159 file: 'webpack:///cypress/integration/features/source_map_spec.coffee',160 line: 4,161 column: 3,162 })163 const sourceStack = $stackUtils.getSourceStack(generatedStack, projectRoot)164 expect(sourceStack.sourceMapped).to.equal(`Error: spec iframe stack165 at foo.bar (cypress:///some_other_file.ts:2:2)166 at Context.<anonymous> (webpack:///cypress/integration/features/source_map_spec.coffee:4:4)\167`)168 expect(sourceStack.parsed).to.eql([169 {170 message: 'Error: spec iframe stack',171 whitespace: '',172 },173 {174 function: 'foo.bar',175 fileUrl: 'http://localhost:1234/source_map_spec.js',176 originalFile: 'cypress:///some_other_file.ts',177 relativeFile: 'some_other_file.ts',178 absoluteFile: '/dev/app/some_other_file.ts',179 line: 2,180 column: 2,181 whitespace: ' ',182 },183 {184 function: 'Context.<anonymous>',185 fileUrl: 'http://localhost:1234/tests?p=cypress/integration/features/source_map_spec.js',186 originalFile: 'webpack:///cypress/integration/features/source_map_spec.coffee',187 relativeFile: 'cypress/integration/features/source_map_spec.coffee',188 absoluteFile: '/dev/app/cypress/integration/features/source_map_spec.coffee',189 line: 4,190 column: 4,191 whitespace: ' ',192 },193 ])194 })195 it('returns empty object if there\'s no stack', () => {196 expect($stackUtils.getSourceStack()).to.eql({})197 })198 })199 context('.stackWithUserInvocationStackSpliced', () => {200 let err201 let userInvocationStack202 beforeEach(() => {203 err = new Error(`\204original message205original message line 2206original message line 3`)207 err.stack = `\208Error: original message209original message line 2210original message line 3211 at originalStack1 (path/to/file:1:1)212 at originalStack2 (path/to/file:1:1)213 at __stackReplacementMarker (path/to/another:2:2)214 at originalStack4 (path/to/file:1:1)215 at originalStack5 (path/to/file:1:1)`216 userInvocationStack = `\217user invocation message218user invocation message line 2219user invocation message line 3220 at userStack1 (path/to/another:2:2)221 at userStack2 (path/to/another:2:2)`222 })223 it('appends replaces the user invocation wrapper and all lines below it with the user invocation stack', () => {224 const { stack } = $stackUtils.stackWithUserInvocationStackSpliced(err, userInvocationStack)225 expect(stack).to.equal(`\226Error: original message227original message line 2228original message line 3229 at originalStack1 (path/to/file:1:1)230 at originalStack2 (path/to/file:1:1)231From Your Spec Code:232 at userStack1 (path/to/another:2:2)233 at userStack2 (path/to/another:2:2)`)234 })235 it('returns the index of where the user invocation is in the stack', () => {236 const { index } = $stackUtils.stackWithUserInvocationStackSpliced(err, userInvocationStack)237 expect(index).to.equal(6)238 })239 it('appends at end when there is no stack replacement marker in the stack', () => {240 err.stack = err.stack.replace(' at __stackReplacementMarker (path/to/another:2:2)\n', '')241 const { stack } = $stackUtils.stackWithUserInvocationStackSpliced(err, userInvocationStack)242 expect(stack).to.equal(`\243Error: original message244original message line 2245original message line 3246 at originalStack1 (path/to/file:1:1)247 at originalStack2 (path/to/file:1:1)248 at originalStack4 (path/to/file:1:1)249 at originalStack5 (path/to/file:1:1)250From Your Spec Code:251 at userStack1 (path/to/another:2:2)252 at userStack2 (path/to/another:2:2)`)253 })254 })255 context('.stackWithoutMessage', () => {256 it('returns stack with the foremost message lines', () => {257 const stack = `\258message 1259message 2260 at stack1 (foo.js:1:1)261message 3262 at stack2 (bar.js:2:2)`263 const result = $stackUtils.stackWithoutMessage(stack)264 expect(result).to.equal(`\265 at stack1 (foo.js:1:1)266message 3267 at stack2 (bar.js:2:2)`)268 })269 })270 context('.normalizedUserInvocationStack', () => {271 it('removes message and cy[name] lines and normalizes indentation', () => {272 const stack = `\273message 1274message 2275 at addCommand/cy[name]@cypress:///cy.js:0:0276 at stack1 (foo.js:1:1)277 at stack2 (bar.js:2:2)`278 const result = $stackUtils.normalizedUserInvocationStack(stack)279 expect(result).to.equal(`\280 at stack1 (foo.js:1:1)281 at stack2 (bar.js:2:2)`)282 })283 })...
stack_utils.js
Source:stack_utils.js
1const _ = require('lodash')2const { codeFrameColumns } = require('@babel/code-frame')3const errorStackParser = require('error-stack-parser')4const path = require('path')5// const $sourceMapUtils = require('./source_map_utils')6const $utils = require('./utils')7const whitespaceRegex = /^(\s*)*/8const stackLineRegex = /^\s*(at )?.*@?\(?.*\:\d+\:\d+\)?$/9const customProtocolRegex = /^[^:\/]+:\/+/10const STACK_REPLACEMENT_MARKER = '__stackReplacementMarker'11// returns tuple of [message, stack]12const splitStack = (stack) => {13 const lines = stack.split('\n')14 return _.reduce(15 lines,16 (memo, line) => {17 if (memo.messageEnded || stackLineRegex.test(line)) {18 memo.messageEnded = true19 memo[1].push(line)20 } else {21 memo[0].push(line)22 }23 return memo24 },25 [[], []]26 )27}28const unsplitStack = (messageLines, stackLines) => {29 return _.castArray(messageLines).concat(stackLines).join('\n')30}31const getStackLines = (stack) => {32 const [, stackLines] = splitStack(stack)33 return stackLines34}35const stackWithoutMessage = (stack) => {36 return getStackLines(stack).join('\n')37}38const hasCrossFrameStacks = (specWindow) => {39 // get rid of the top lines since they naturally have different line:column40 const normalize = (stack) => {41 return stack.replace(/^.*\n/, '')42 }43 const topStack = normalize(new Error().stack)44 const specStack = normalize(new specWindow.Error().stack)45 return topStack === specStack46}47const stackWithContentAppended = (err, stack) => {48 const appendToStack = err.appendToStack49 if (!appendToStack || !appendToStack.content) return stack50 delete err.appendToStack51 // if the content is a stack trace, which is should be, then normalize the52 // indentation, then indent it a little further than the rest of the stack53 const normalizedContent = normalizeStackIndentation(appendToStack.content)54 const content = $utils.indent(normalizedContent, 2)55 return `${stack}\n\n${appendToStack.title}:\n${content}`56}57const stackWithLinesRemoved = (stack, cb) => {58 const [messageLines, stackLines] = splitStack(stack)59 const remainingStackLines = cb(stackLines)60 return unsplitStack(messageLines, remainingStackLines)61}62const stackWithLinesDroppedFromMarker = (stack, marker) => {63 return stackWithLinesRemoved(stack, (lines) => {64 // drop lines above the marker65 const withAboveMarkerRemoved = _.dropWhile(lines, (line) => {66 return !_.includes(line, marker)67 })68 // remove the first line because it includes the marker69 return withAboveMarkerRemoved.slice(1)70 })71}72const stackWithReplacementMarkerLineRemoved = (stack) => {73 return stackWithLinesRemoved(stack, (lines) => {74 return _.reject(lines, (line) => _.includes(line, STACK_REPLACEMENT_MARKER))75 })76}77const stackWithUserInvocationStackSpliced = (err, userInvocationStack) => {78 const stack = _.trim(err.stack, '\n') // trim newlines from end79 const [messageLines, stackLines] = splitStack(stack)80 const userInvocationStackWithoutMessage = stackWithoutMessage(81 userInvocationStack82 )83 let commandCallIndex = _.findIndex(stackLines, (line) => {84 return line.includes(STACK_REPLACEMENT_MARKER)85 })86 if (commandCallIndex < 0) {87 commandCallIndex = stackLines.length88 }89 stackLines.splice(commandCallIndex, stackLines.length, 'From Your Spec Code:')90 stackLines.push(userInvocationStackWithoutMessage)91 // the commandCallIndex is based off the stack without the message,92 // but the full stack includes the message + 'From Your Spec Code:',93 // so we adjust it94 return {95 stack: unsplitStack(messageLines, stackLines),96 index: commandCallIndex + messageLines.length + 1,97 }98}99const getLanguageFromExtension = (filePath) => {100 return (path.extname(filePath) || '').toLowerCase().replace('.', '') || null101}102const getCodeFrameFromSource = (103 sourceCode,104 { line, column, relativeFile, absoluteFile }105) => {106 if (!sourceCode) return107 const frame = codeFrameColumns(sourceCode, { start: { line, column } })108 if (!frame) return109 return {110 line,111 column,112 originalFile: relativeFile,113 relativeFile,114 absoluteFile,115 frame,116 language: getLanguageFromExtension(relativeFile),117 }118}119const getCodeFrameStackLine = (err, stackIndex) => {120 // if a specific index is not specified, use the first line with a file in it121 if (stackIndex == null)122 return _.find(err.parsedStack, (line) => !!line.fileUrl)123 return err.parsedStack[stackIndex]124}125const getCodeFrame = (err, stackIndex) => {126 if (err.codeFrame) return err.codeFrame127 const stackLine = getCodeFrameStackLine(err, stackIndex)128 if (!stackLine) return129 const { fileUrl, originalFile } = stackLine130 return getCodeFrameFromSource(131 $sourceMapUtils.getSourceContents(fileUrl, originalFile),132 stackLine133 )134}135const getWhitespace = (line) => {136 if (!line) return ''137 const [, whitespace] = line.match(whitespaceRegex) || []138 return whitespace || ''139}140const getSourceDetails = (generatedDetails) => {141 const sourceDetails = $sourceMapUtils.getSourcePosition(142 generatedDetails.file,143 generatedDetails144 )145 if (!sourceDetails) return generatedDetails146 const { line, column, file } = sourceDetails147 let fn = generatedDetails.function148 return {149 line,150 column,151 file,152 function: fn,153 }154}155const functionExtrasRegex = /(\/<|<\/<)$/156const cleanFunctionName = (functionName) => {157 if (!_.isString(functionName)) return '<unknown>'158 return _.trim(functionName.replace(functionExtrasRegex, ''))159}160const parseLine = (line) => {161 const isStackLine = stackLineRegex.test(line)162 if (!isStackLine) return163 const parsed = errorStackParser.parse({ stack: line })[0]164 if (!parsed) return165 return {166 line: parsed.lineNumber,167 column: parsed.columnNumber,168 file: parsed.fileName,169 function: cleanFunctionName(parsed.functionName),170 }171}172const stripCustomProtocol = (filePath) => {173 return filePath.replace(customProtocolRegex, '')174}175const getSourceDetailsForLine = (projectRoot, line) => {176 const whitespace = getWhitespace(line)177 const generatedDetails = parseLine(line)178 // if it couldn't be parsed, it's a message line179 if (!generatedDetails) {180 return {181 message: line,182 whitespace,183 }184 }185 const sourceDetails = getSourceDetails(generatedDetails)186 const originalFile = sourceDetails.file187 const relativeFile = stripCustomProtocol(originalFile)188 return {189 function: sourceDetails.function,190 fileUrl: generatedDetails.file,191 originalFile,192 relativeFile,193 absoluteFile: path.join(projectRoot, relativeFile),194 line: sourceDetails.line,195 // adding 1 to column makes more sense for code frame and opening in editor196 column: sourceDetails.column + 1,197 whitespace,198 }199}200const reconstructStack = (parsedStack) => {201 return _.map(parsedStack, (parsedLine) => {202 if (parsedLine.message != null) {203 return `${parsedLine.whitespace}${parsedLine.message}`204 }205 const { whitespace, originalFile, function: fn, line, column } = parsedLine206 return `${whitespace}at ${fn} (${207 originalFile || '<unknown>'208 }:${line}:${column})`209 }).join('\n')210}211const getSourceStack = (stack, projectRoot) => {212 if (!_.isString(stack)) return {}213 const getSourceDetailsWithStackUtil = _.partial(214 getSourceDetailsForLine,215 projectRoot216 )217 const parsed = _.map(stack.split('\n'), getSourceDetailsWithStackUtil)218 return {219 parsed,220 sourceMapped: reconstructStack(parsed),221 }222}223const normalizeStackIndentation = (stack) => {224 const [messageLines, stackLines] = splitStack(stack)225 const normalizedStackLines = _.map(stackLines, (line) => {226 if (stackLineRegex.test(line)) {227 // stack lines get indented 4 spaces228 return line.replace(whitespaceRegex, ' ')229 }230 // message lines don't get indented at all231 return line.replace(whitespaceRegex, '')232 })233 return unsplitStack(messageLines, normalizedStackLines)234}235const normalizedStack = (err) => {236 // Firefox errors do not include the name/message in the stack, whereas237 // Chromium-based errors do, so we normalize them so that the stack238 // always includes the name/message239 const errString = err.toString()240 const errStack = err.stack || ''241 // the stack has already been normalized and normalizing the indentation242 // again could mess up the whitespace243 if (errStack.includes(errString)) return err.stack244 const firstErrLine = errString.slice(0, errString.indexOf('\n'))245 const firstStackLine = errStack.slice(0, errStack.indexOf('\n'))246 const stackIncludesMsg = firstStackLine.includes(firstErrLine)247 if (!stackIncludesMsg) {248 return `${errString}\n${errStack}`249 }250 return normalizeStackIndentation(errStack)251}252const normalizedUserInvocationStack = (userInvocationStack) => {253 // Firefox user invocation stack includes a line at the top that looks like254 // addCommand/cy[name]@cypress:///../driver/src/cypress/cy.js:936:77 or255 // add/$Chainer.prototype[key] (cypress:///../driver/src/cypress/chainer.js:30:128)256 // whereas Chromium browsers have the user's line first257 const stackLines = getStackLines(userInvocationStack)258 const winnowedStackLines = _.reject(stackLines, (line) => {259 return line.includes('cy[name]') || line.includes('Chainer.prototype[key]')260 }).join('\n')261 return normalizeStackIndentation(winnowedStackLines)262}263const replacedStack = (err, newStack) => {264 // if err already lacks a stack or we've removed the stack265 // for some reason, keep it stackless266 if (!err.stack) return err.stack267 const errString = err.toString()268 const stackLines = getStackLines(newStack)269 return unsplitStack(errString, stackLines)270}271module.exports = {272 getCodeFrame,273 getSourceStack,274 getStackLines,275 hasCrossFrameStacks,276 normalizedStack,277 normalizedUserInvocationStack,278 replacedStack,279 stackWithContentAppended,280 stackWithLinesDroppedFromMarker,281 stackWithoutMessage,282 stackWithReplacementMarkerLineRemoved,283 stackWithUserInvocationStackSpliced,...
verify-failures.js
Source:verify-failures.js
1import _ from 'lodash'2import helpers from '../support/helpers'3const { runIsolatedCypress } = helpers.createCypress({4 config: { isTextTerminal: true, retries: 0 },5 visitUrl: 'http://localhost:3500/fixtures/isolated-runner-inner.html',6})7const verifyFailure = (options) => {8 const {9 hasCodeFrame = true,10 verifyOpenInIde = true,11 column,12 codeFrameText,13 originalMessage,14 message = [],15 notInMessage = [],16 command,17 stack,18 file,19 win,20 uncaught = false,21 uncaughtMessage,22 } = options23 let { regex, line } = options24 regex = regex || new RegExp(`${file}:${line || '\\d+'}:${column}`)25 const testOpenInIde = () => {26 cy.log('open in IDE works').then(() => {27 expect(win.runnerWs.emit.withArgs('open:file').lastCall.args[1].file).to.include(file)28 })29 }30 win.runnerWs.emit.withArgs('get:user:editor')31 .yields({32 preferredOpener: {33 id: 'foo-editor',34 name: 'Foo',35 openerId: 'foo-editor',36 isOther: false,37 },38 })39 win.runnerWs.emit.withArgs('open:file')40 cy.contains('View stack trace').click()41 const messageLines = [].concat(message)42 if (messageLines.length) {43 cy.log('message contains expected lines and stack does not include message')44 _.each(messageLines, (msg) => {45 cy.get('.runnable-err-message')46 .should('include.text', msg)47 cy.get('.runnable-err-stack-trace')48 .should('not.include.text', msg)49 })50 }51 if (originalMessage) {52 cy.get('.runnable-err-message')53 .should('include.text', originalMessage)54 }55 const notInMessageLines = [].concat(notInMessage)56 if (notInMessageLines.length) {57 cy.log('message does not contain the specified lines')58 _.each(notInMessageLines, (msg) => {59 cy.get('.runnable-err-message')60 .should('not.include.text', msg)61 })62 }63 cy.log('stack trace matches the specified pattern')64 cy.get('.runnable-err-stack-trace')65 .invoke('text')66 .should('match', regex)67 if (stack) {68 const stackLines = [].concat(stack)69 if (stackLines.length) {70 cy.log('stack contains the expected lines')71 }72 _.each(stackLines, (stackLine) => {73 cy.get('.runnable-err-stack-trace')74 .should('include.text', stackLine)75 })76 }77 cy.get('.runnable-err-stack-trace')78 .invoke('text')79 .should('not.include', '__stackReplacementMarker')80 .should((stackTrace) => {81 // if this stack trace has the 'From Your Spec Code' addendum,82 // it should only appear once83 const match = stackTrace.match(/From Your Spec Code/g)84 if (match && match.length) {85 expect(match.length, `'From Your Spec Code' should only be in the stack once, but found ${match.length} instances`).to.equal(1)86 }87 })88 if (verifyOpenInIde) {89 cy.contains('.runnable-err-stack-trace .runnable-err-file-path a', file)90 .click('left')91 .should(() => {92 testOpenInIde()93 })94 }95 if (command) {96 cy.log('the error is attributed to the correct command')97 cy98 .get('.command-state-failed')99 .first()100 .find('.command-method')101 .invoke('text')102 .should('equal', command)103 }104 if (uncaught) {105 cy.log('uncaught error has an associated log for the original error')106 cy.get('.command-name-uncaught-exception')107 .should('have.length', 1)108 .should('have.class', 'command-state-failed')109 .find('.command-message-text')110 .should('include.text', uncaughtMessage || originalMessage)111 } else {112 cy.log('"caught" error does not have an uncaught error log')113 cy.get('.command-name-uncaught-exception').should('not.exist')114 }115 if (!hasCodeFrame) return116 cy.log('code frame matches specified pattern')117 cy118 .get('.test-err-code-frame .runnable-err-file-path')119 .invoke('text')120 .should('match', regex)121 cy.get('.test-err-code-frame pre span').should('include.text', codeFrameText)122 if (verifyOpenInIde) {123 cy.contains('.test-err-code-frame .runnable-err-file-path a', file)124 .click()125 .should(() => {126 expect(win.runnerWs.emit.withArgs('open:file')).to.be.calledTwice127 testOpenInIde()128 })129 }130}131const createVerifyTest = (modifier) => {132 return (title, opts, props) => {133 if (!props) {134 props = opts135 opts = null136 }137 const verifyFn = props.verifyFn || verifyFailure138 const args = _.compact([title, opts, () => {139 return runIsolatedCypress(`cypress/fixtures/errors/${props.file}`, {140 visitUrl: props.visitUrl,141 onBeforeRun ({ specWindow, win, autCypress }) {142 specWindow.testToRun = title143 specWindow.autWindow = win144 specWindow.autCypress = autCypress145 if (props.onBeforeRun) {146 props.onBeforeRun({ specWindow, win })147 }148 },149 })150 .then(({ win }) => {151 props.codeFrameText = props.codeFrameText || title152 props.win = win153 verifyFn(props, verifyFailure)154 })155 }])156;(modifier ? it[modifier] : it)(...args)157 }158}159export const verify = {160 it: createVerifyTest(),161}162verify.it['only'] = createVerifyTest('only')163verify.it['skip'] = createVerifyTest('skip')164export const verifyInternalFailure = (props) => {165 const { method, stackMethod } = props166 cy.get('.runnable-err-message')167 .should('include.text', `thrown in ${method.replace(/\./g, '-')}`)168 cy.get('.runnable-err-stack-expander > .collapsible-header').click()169 cy.get('.runnable-err-stack-trace')170 .should('include.text', stackMethod || method)171 // this is an internal cypress error and we can only show code frames172 // from specs, so it should not show the code frame173 cy.get('.test-err-code-frame')174 .should('not.exist')...
util.js
Source:util.js
1const { _ } = Cypress2let count = 13let openInIdePath = Cypress.spec4// ensure title is unique since it's necessary for querying the UI5// in the verification step6const getTitle = (ctx) => {7 const parentTitle = ctx.parent && ctx.parent.title8 return `${parentTitle} ${ctx.title}`.trim()9}10export const fail = (ctx, test) => {11 const title = `${count++}) â FAIL - ${getTitle(ctx)}`12 it(title, { defaultCommandTimeout: 0 }, test)13}14export const verify = (ctx, options) => {15 const {16 line,17 column,18 message,19 stack,20 } = options21 const fileRegex = new RegExp(`${Cypress.spec.relative}:${line}:${column}`)22 it(`â VERIFY`, function () {23 const runnerWs = window.top.runnerWs24 cy.stub(window.top.runnerWs, 'emit').callThrough().withArgs('get:user:editor')25 .yields({26 preferredOpener: {27 id: 'foo-editor',28 name: 'Foo',29 openerId: 'foo-editor',30 isOther: false,31 },32 })33 window.top.runnerWs.emit.callThrough().withArgs('open:file')34 cy.wrap(Cypress.$(window.top.document.body))35 .find('.reporter')36 .contains(`FAIL - ${getTitle(ctx)}`)37 .closest('.collapsible')38 .within(() => {39 cy.contains('View stack trace').click()40 _.each([].concat(message), (msg) => {41 cy.get('.runnable-err-message')42 .should('include.text', msg)43 cy.get('.runnable-err-stack-trace')44 .should('not.include.text', msg)45 })46 cy.get('.runnable-err-stack-trace')47 .invoke('text')48 .should('match', fileRegex)49 _.each([].concat(stack), (stackLine) => {50 cy.get('.runnable-err-stack-trace')51 .should('include.text', stackLine)52 })53 cy.get('.runnable-err-stack-trace')54 .should('not.include.text', '__stackReplacementMarker')55 cy.contains('.runnable-err-stack-trace .runnable-err-file-path', openInIdePath.relative)56 .click()57 .should(() => {58 expect(runnerWs.emit).to.be.calledWithMatch('open:file', {59 file: openInIdePath.absolute,60 })61 })62 cy63 .get('.test-err-code-frame .runnable-err-file-path')64 .invoke('text')65 .should('match', fileRegex)66 // code frames will show `fail(this,()=>` as the 1st line67 cy.get('.test-err-code-frame pre span').should('include.text', 'fail(this,()=>')68 cy.contains('.test-err-code-frame .runnable-err-file-path span', openInIdePath.relative)69 .click()70 .should(() => {71 expect(runnerWs.emit.withArgs('open:file')).to.be.calledTwice72 expect(runnerWs.emit).to.be.calledWithMatch('open:file', {73 file: openInIdePath.absolute,74 })75 })76 })77 })...
Using AI Code Generation
1describe('Example', () => {2 it('should work', () => {3 cy.contains('type').click()4 cy.url().should('include', '/commands/actions')5 cy.get('.action-email')6 .type('
Using AI Code Generation
1cy.get("button").click();2cy.get("button").click();3cy.get("button").click();4cy.get("button").click();5cy.get("button").click();6cy.get("button").click();7cy.get("button").click();8cy.get("button").click();9cy.get("button").click();10cy.get("button").click();11cy.get("button").click();12cy.get("button").click();13cy.get("button").click();14cy.get("button").click();15cy.get("button").click();16cy.get("button").click();17cy.get("button").click();18cy.get("button").click();19cy.get("button").click();20cy.get("button").click();21cy.get("button").click();22cy.get("button").click();23cy.get("button").click();24cy.get("button").click();25cy.get("button").click();26cy.get("button").click();27cy.get("button").click();28cy.get("button").click();29cy.get("button").click();30cy.get("button").click();31cy.get("button").click();32cy.get("button").click();33cy.get("button").click();34cy.get("button").click();35cy.get("button").click();36cy.get("button").click();37cy.get("button").click();38cy.get("button").click();
Using AI Code Generation
1Cypress.Commands.overwrite('visit', (originalFn, url, options) => {2 cy.log('Overwriting visit command')3 return originalFn(url, options)4})5Cypress.Commands.overwrite('get', (originalFn, selector, options) => {6 cy.log('Overwriting get command')7 return originalFn(selector, options)8})9Cypress.Commands.overwrite('type', (originalFn, subject, text, options) => {10 cy.log('Overwriting type command')11 return originalFn(subject, text, options)12})13Cypress.Commands.overwrite('click', (originalFn, subject, options) => {14 cy.log('Overwriting click command')15 return originalFn(subject, options)16})17Cypress.Commands.overwrite('log', (originalFn, message, options) => {18 cy.log('Overwriting log command')19 return originalFn(message, options)20})21Cypress.Commands.overwrite('wrap', (originalFn, subject, options) => {22 cy.log('Overwriting wrap command')23 return originalFn(subject, options)24})25Cypress.Commands.overwrite('contains', (originalFn, subject, text, options) => {26 cy.log('Overwriting contains command')27 return originalFn(subject, text, options)28})29Cypress.Commands.overwrite('should', (originalFn, subject, assertion, ...args) => {30 cy.log('Overwriting should command')31 return originalFn(subject, assertion, ...args)32})33Cypress.Commands.overwrite('and', (originalFn, subject, assertion, ...args) => {34 cy.log('Overwriting and command')35 return originalFn(subject, assertion, ...args)36})37Cypress.Commands.overwrite('wrap', (originalFn, subject, options) => {38 cy.log('Overwriting wrap command')39 return originalFn(subject, options)40})41Cypress.Commands.overwrite('debug', (originalFn, subject, options) => {42 cy.log('Overwriting debug command')43 return originalFn(subject, options)44})45Cypress.Commands.overwrite('pause', (originalFn, subject, options) => {46 cy.log('Overwriting pause command')47 return originalFn(subject, options)48})49Cypress.Commands.overwrite('end', (originalFn, subject, options) => {50 cy.log('Overwriting end command')51 return originalFn(subject, options)52})53Cypress.Commands.overwrite('reload', (originalFn
Using AI Code Generation
1Cypress.Commands.add('__stackReplacementMarker', {2 fn: () => {3 return;4 }5});6Cypress.Commands.add('__stackReplacementMarker', {7 fn: () => {8 return;9 }10});11Cypress.Commands.add('__stackReplacementMarker', {12 fn: () => {13 return;14 }15});16Cypress.Commands.add('__stackReplacementMarker', {17 fn: () => {18 return;19 }20});21Cypress.Commands.add('__stackReplacementMarker', {22 fn: () => {23 return;24 }25});26Cypress.Commands.add('__stackReplacementMarker', {27 fn: () => {28 return;29 }30});31Cypress.Commands.add('__stackReplacementMarker', {32 fn: () => {33 return;34 }35});36Cypress.Commands.add('__stackReplacementMarker', {37 fn: () => {38 return;39 }40});41Cypress.Commands.add('__stackReplacementMarker', {42 fn: () => {43 return;44 }45});46Cypress.Commands.add('__stackReplacementMarker', {47 fn: () => {48 return;49 }50});51Cypress.Commands.add('__stackReplacementMarker', {52 fn: () => {53 return;54 }55});56Cypress.Commands.add('__stackReplacementMarker', {57 fn: () => {58 return;59 }60});61Cypress.Commands.add('__stackReplacement
Using AI Code Generation
1Cypress.on('uncaught:exception', (err, runnable) => {2 if (err.message.includes('something')) {3 }4})5Cypress.on('uncaught:exception', (err, runnable) => {6 if (err.message.includes('something')) {7 }8})9Cypress.on('uncaught:exception', (err, runnable) => {10 if (err.message.includes('something')) {11 }12})13Cypress.on('uncaught:exception', (err, runnable) => {14 if (err.message.includes('something')) {15 }16})17Cypress.on('uncaught:exception', (err, runnable) => {18 if (err.message.includes('something')) {19 }20})21Cypress.on('uncaught:exception', (err, runnable) => {22 if (err.message.includes('something')) {23 }24})25Cypress.on('uncaught:exception',
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.
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.
Watch this 3 hours of complete tutorial to learn the basics of Cypress and various Cypress commands with the Cypress testing at LambdaTest.
Get 100 minutes of automation test minutes FREE!!