diff --git a/packages/driver/cypress/fixtures/isolated-runner-inner.html b/packages/driver/cypress/fixtures/isolated-runner-inner.html new file mode 100644 index 000000000000..895fc7379f48 --- /dev/null +++ b/packages/driver/cypress/fixtures/isolated-runner-inner.html @@ -0,0 +1,24 @@ + + + + Isolated Runner Fixture + + +
+ + +
+ + + + diff --git a/packages/driver/cypress/integration/commands/actions/click_spec.js b/packages/driver/cypress/integration/commands/actions/click_spec.js index 93ff851c272a..7fd96b610db7 100644 --- a/packages/driver/cypress/integration/commands/actions/click_spec.js +++ b/packages/driver/cypress/integration/commands/actions/click_spec.js @@ -3711,7 +3711,8 @@ describe('shadow dom', () => { it('does not hang when experimentalShadowDomSupport is false and clicking on custom element', () => { Cypress.config('experimentalShadowDomSupport', false) // needs some size or it's considered invisible and click will fail its prerequisites - cy.$$('#shadow-element-1').css({ padding: 2 }) + // so we make it display: block so its getClientRects() contains only a single + cy.$$('#shadow-element-1').css({ display: 'block' }) cy.get('#shadow-element-1').click() }) diff --git a/packages/driver/cypress/integration/commands/actions/type_spec.js b/packages/driver/cypress/integration/commands/actions/type_spec.js index 120c9b59b49d..d22a6642f258 100644 --- a/packages/driver/cypress/integration/commands/actions/type_spec.js +++ b/packages/driver/cypress/integration/commands/actions/type_spec.js @@ -3051,7 +3051,7 @@ describe('src/cy/commands/actions/type - #type', () => { const spyTableName = cy.spy(top.console, 'groupCollapsed') const spyTableData = cy.spy(top.console, 'table') - const commandLogEl = getCommandLogWithText('foo') + const commandLogEl = getCommandLogWithText('foo', 'message-text') const reactCommandInstance = findReactInstance(commandLogEl[0]) diff --git a/packages/driver/cypress/integration/e2e/dom_hitbox.spec.js b/packages/driver/cypress/integration/e2e/dom_hitbox.spec.js index a5d0fb2561c3..cd4fba07b7b6 100644 --- a/packages/driver/cypress/integration/e2e/dom_hitbox.spec.js +++ b/packages/driver/cypress/integration/e2e/dom_hitbox.spec.js @@ -127,7 +127,7 @@ const ensureCorrectHighlightPositions = (sel) => { const getAndPin = (sel) => { cy.get(sel) - clickCommandLog(sel) + clickCommandLog(sel, 'message-text') } const clickAndPin = (sel, ...args) => { diff --git a/packages/driver/cypress/support/utils.js b/packages/driver/cypress/support/utils.js index 149ecfd51a88..bd37b8414ca8 100644 --- a/packages/driver/cypress/support/utils.js +++ b/packages/driver/cypress/support/utils.js @@ -1,14 +1,12 @@ const { $, _, Promise } = Cypress -export const getCommandLogWithText = (text) => { +export const getCommandLogWithText = (command, type = 'method') => { // Open current test if not already open, so we can find the command log cy.$$('.runnable-active .runnable-wrapper:not(.is-open)', top.document).click() return cy - .$$(`.runnable-active .command-wrapper:contains(${text})`, top.document) - .parentsUntil('li') - .last() - .parent() + .$$(`.runnable-active .command-${type}:contains(${command})`, top.document) + .closest('.command') } export const findReactInstance = function (dom) { @@ -22,12 +20,11 @@ export const findReactInstance = function (dom) { : internalInstance.return.stateNode } -export const clickCommandLog = (sel) => { +export const clickCommandLog = (sel, type) => { return cy.wait(10) .then(() => { return withMutableReporterState(() => { - const commandLogEl = getCommandLogWithText(sel) - + const commandLogEl = getCommandLogWithText(sel, type) const reactCommandInstance = findReactInstance(commandLogEl[0]) if (!reactCommandInstance) { diff --git a/packages/driver/src/cy/errors.js b/packages/driver/src/cy/errors.js index 5f50742d4a3f..7d10e1bc6298 100644 --- a/packages/driver/src/cy/errors.js +++ b/packages/driver/src/cy/errors.js @@ -14,6 +14,8 @@ const create = (state, config, log) => { snapshot: true, error: err, consoleProps () { + if (!current) return + const obj = {} const prev = current.get('prev') diff --git a/packages/driver/src/dom/coordinates.js b/packages/driver/src/dom/coordinates.js index 2de5b81b6508..f115fcb8faba 100644 --- a/packages/driver/src/dom/coordinates.js +++ b/packages/driver/src/dom/coordinates.js @@ -1,3 +1,4 @@ +const _ = require('lodash') const $window = require('./window') const $elements = require('./elements') @@ -7,6 +8,13 @@ const getElementAtPointFromViewport = (doc, x, y) => { const isAutIframe = (win) => !$elements.getNativeProp(win.parent, 'frameElement') +const getFirstValidSizedRect = (el) => { + return _.find(el.getClientRects(), (rect) => { + // use the first rect that has a nonzero width and height + return rect.width && rect.height + }) || el.getBoundingClientRect() // otherwise fall back to the parent client rect +} + /** * @param {JQuery} $el */ @@ -32,7 +40,7 @@ const getElementPositioning = ($el) => { // returns a zero length DOMRectList in that case, which becomes undefined. // so we fallback to getBoundingClientRect() so that we get an actual DOMRect // with all properties 0'd out - const rect = [...el.getClientRects()].find((e) => e.width && e.height) || el.getBoundingClientRect() + const rect = getFirstValidSizedRect(el) // we want to return the coordinates from the autWindow to the element // which handles a situation in which the element is inside of a nested diff --git a/packages/runner/cypress/fixtures/errors/assertions_spec.js b/packages/runner/cypress/fixtures/errors/assertions_spec.js new file mode 100644 index 000000000000..5651e2c274ab --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/assertions_spec.js @@ -0,0 +1,15 @@ +import './setup' + +describe('assertion failures', () => { + it('with expect().', () => { + expect('actual').to.equal('expected') + }) + + it('with assert()', () => { + assert(false, 'should be true') + }) + + it('with assert.()', () => { + assert.equal('actual', 'expected') + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/commands_spec.js b/packages/runner/cypress/fixtures/errors/commands_spec.js new file mode 100644 index 000000000000..2f509a585350 --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/commands_spec.js @@ -0,0 +1,11 @@ +import './setup' + +describe('commands', { defaultCommandTimeout: 0 }, () => { + it('failure', () => { + cy.get('#does-not-exist') + }) + + it('chained failure', () => { + cy.get('body').find('#does-not-exist') + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/custom_commands_spec.js b/packages/runner/cypress/fixtures/errors/custom_commands_spec.js new file mode 100644 index 000000000000..919a23d68f6c --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/custom_commands_spec.js @@ -0,0 +1,27 @@ +import './setup' + +Cypress.Commands.add('failAssertion', () => { + expect('actual').to.equal('expected') +}) + +Cypress.Commands.add('failException', () => { + ({}).bar() +}) + +Cypress.Commands.add('failCommand', () => { + cy.get('#does-not-exist') +}) + +describe('custom commands', { defaultCommandTimeout: 0 }, () => { + it('assertion failure', () => { + cy.failAssertion() + }) + + it('exception', () => { + cy.failException() + }) + + it('command failure', () => { + cy.failCommand() + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/docs_url_spec.js b/packages/runner/cypress/fixtures/errors/docs_url_spec.js new file mode 100644 index 000000000000..f5806c950ec7 --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/docs_url_spec.js @@ -0,0 +1,13 @@ +import './setup' + +describe('docs url', () => { + it('displays as button in interactive mode', () => { + Cypress.config('isInteractive', true) + cy.viewport() + }) + + it('is text in error message in run mode', () => { + Cypress.config('isInteractive', false) + cy.viewport() + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/each_spec.js b/packages/runner/cypress/fixtures/errors/each_spec.js new file mode 100644 index 000000000000..e95785680ddf --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/each_spec.js @@ -0,0 +1,21 @@ +import './setup' + +describe('cy.each', { defaultCommandTimeout: 0 }, () => { + it('assertion failure', () => { + cy.wrap([1]).each(() => { + expect('actual').to.equal('expected') + }) + }) + + it('exception', () => { + cy.wrap([1]).each(() => { + ({}).bar() + }) + }) + + it('command failure', () => { + cy.wrap([1]).each(() => { + cy.get('#does-not-exist') + }) + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/events_spec.js b/packages/runner/cypress/fixtures/errors/events_spec.js new file mode 100644 index 000000000000..855dd08d5516 --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/events_spec.js @@ -0,0 +1,35 @@ +import './setup' + +describe('event handlers', { defaultCommandTimeout: 0 }, () => { + it('event assertion failure', () => { + cy.on('window:load', () => { + expect('actual').to.equal('expected') + }) + + cy.visit('http://localhost:1919') + }) + + it('event exception', () => { + cy.on('window:load', () => { + ({}).bar() + }) + + cy.visit('http://localhost:1919') + }) + + it('fail handler assertion failure', () => { + cy.on('fail', () => { + expect('actual').to.equal('expected') + }) + + cy.get('#does-not-exist') + }) + + it('fail handler exception', () => { + cy.on('fail', () => { + ({}).bar() + }) + + cy.get('#does-not-exist') + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/exceptions_spec.js b/packages/runner/cypress/fixtures/errors/exceptions_spec.js new file mode 100644 index 000000000000..ec61175d6b1c --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/exceptions_spec.js @@ -0,0 +1,13 @@ +import './setup' + +const outsideError = require('../../../../server/test/support/fixtures/projects/todos/throws-error') + +describe('exception failures', () => { + it('in spec file', () => { + ({}).bar() + }) + + it('in file outside project', () => { + outsideError() + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/readfile_spec.js b/packages/runner/cypress/fixtures/errors/readfile_spec.js new file mode 100644 index 000000000000..141e89113e3c --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/readfile_spec.js @@ -0,0 +1,7 @@ +import './setup' + +describe('cy.readFile', () => { + it('existence failure', () => { + cy.readFile('does-not-exist', { timeout: 0 }) + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/route_spec.js b/packages/runner/cypress/fixtures/errors/route_spec.js new file mode 100644 index 000000000000..889e2be067d2 --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/route_spec.js @@ -0,0 +1,85 @@ +import { abortXhr, sendXhr } from './setup' + +describe('cy.route', { defaultCommandTimeout: 0 }, () => { + it('callback assertion failure', () => { + cy.server().route(() => { + expect('actual').to.equal('expected') + }) + }) + + it('callback exception', () => { + cy.server().route(() => { + ({}).bar() + }) + }) + + it('command failure', () => { + cy.server().route(() => { + cy.get('#does-not-exist') + + return '/foo' + }) + }) + + it('onAbort assertion failure', () => { + cy.server().route({ + url: '/foo', + onAbort () { + expect('actual').to.equal('expected') + }, + }) + .window().then(abortXhr('/foo')) + }) + + it('onAbort exception', () => { + cy.server().route({ + url: '/foo', + onAbort () { + ({}).bar() + }, + }) + .window().then(abortXhr('/foo')) + }) + + it('onRequest assertion failure', () => { + cy.server().route({ + url: '/foo', + onRequest () { + expect('actual').to.equal('expected') + }, + }) + .window().then(sendXhr('/foo')) + }) + + it('onRequest exception', () => { + cy.server().route({ + url: '/foo', + onRequest () { + ({}).bar() + }, + }) + .window().then(sendXhr('/foo')) + }) + + it('onResponse assertion failure', () => { + cy.server().route({ + url: '/json-content-type', + onResponse () { + expect('actual').to.equal('expected') + }, + }) + .window().then(sendXhr('/json-content-type')) + .wait(10000) + }) + + it('onResponse exception', () => { + cy.server().route({ + url: '/json-content-type', + onResponse () { + ({}).bar() + }, + }) + .window().then(sendXhr('/json-content-type')) + .wait(10000) + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/server_spec.js b/packages/runner/cypress/fixtures/errors/server_spec.js new file mode 100644 index 000000000000..5f6677520e9f --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/server_spec.js @@ -0,0 +1,65 @@ +import { abortXhr, sendXhr } from './setup' + +describe('cy.server', { defaultCommandTimeout: 0 }, () => { + it('onAbort assertion failure', () => { + cy.server({ + onAbort () { + expect('actual').to.equal('expected') + }, + }) + .route('/foo') + .window().then(abortXhr('/foo')) + }) + + it('onAbort exception', () => { + cy.server({ + onAbort () { + ({}).bar() + }, + }) + .route('/foo') + .window().then(abortXhr('/foo')) + }) + + it('onRequest assertion failure', () => { + cy.server({ + onRequest () { + expect('actual').to.equal('expected') + }, + }) + .route('/foo') + .window().then(sendXhr('/foo')) + }) + + it('onRequest exception', () => { + cy.server({ + onRequest () { + ({}).bar() + }, + }) + .route('/foo') + .window().then(sendXhr('/foo')) + }) + + it('onResponse assertion failure', () => { + cy.server({ + onResponse () { + expect('actual').to.equal('expected') + }, + }) + .route('/json-content-type') + .window().then(sendXhr('/json-content-type')) + .wait(10000) + }) + + it('onResponse exception', () => { + cy.server({ + onResponse () { + ({}).bar() + }, + }) + .route('/json-content-type') + .window().then(sendXhr('/json-content-type')) + .wait(10000) + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/setup.js b/packages/runner/cypress/fixtures/errors/setup.js new file mode 100644 index 000000000000..f6d5fb0b83bc --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/setup.js @@ -0,0 +1,29 @@ +const testToRun = window.testToRun +const originalIt = window.it + +window.it = (title, ...args) => { + const itFn = title === testToRun ? originalIt : () => {} + + return itFn(title, ...args) +} + +window.it.only = () => { + throw new Error('Instead of putting .only in the spec-under-test, put it in the corresponding test in the parent spec (reporter.error.spec.js, etc)') +} + +// eslint-disable-next-line +export const sendXhr = (route) => (win) => { + const xhr = new win.XMLHttpRequest() + + xhr.open('GET', route) + xhr.send() + + return xhr +} + +// eslint-disable-next-line +export const abortXhr = (route) => (win) => { + const xhr = sendXhr(route)(win) + + xhr.abort() +} diff --git a/packages/runner/cypress/fixtures/errors/should_spec.js b/packages/runner/cypress/fixtures/errors/should_spec.js new file mode 100644 index 000000000000..5d7158333497 --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/should_spec.js @@ -0,0 +1,58 @@ +import './setup' + +describe('cy.should', { defaultCommandTimeout: 0 }, () => { + it('callback assertion failure', () => { + cy.wrap({}).should(() => { + expect('actual').to.equal('expected') + }) + }) + + it('callback exception', () => { + cy.wrap({}).should(() => { + ({}).bar() + }) + }) + + it('standard assertion failure', () => { + cy.wrap({}) + .should('have.property', 'foo') + }) + + it('after multiple', () => { + cy.wrap({ foo: 'foo' }).should('have.property', 'foo') + .should('equal', 'bar') + }) + + it('after multiple callbacks exception', () => { + cy.wrap({ foo: 'foo' }) + .should(() => { + expect(true).to.be.true + }) + .should(() => { + ({}).bar() + }) + }) + + it('after multiple callbacks assertion failure', () => { + cy.wrap({ foo: 'foo' }) + .should(() => { + expect(true).to.be.true + }) + .should(() => { + expect('actual').to.equal('expected') + }) + }) + + it('after callback success assertion failure', () => { + cy.wrap({}) + .should(() => { + expect(true).to.be.true + }) + .should('have.property', 'foo') + }) + + it('command failure after success', () => { + cy.wrap({ foo: 'foo' }).should('have.property', 'foo') + cy.get('#does-not-exist') + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/spread_spec.js b/packages/runner/cypress/fixtures/errors/spread_spec.js new file mode 100644 index 000000000000..b6dd1a51bf1c --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/spread_spec.js @@ -0,0 +1,21 @@ +import './setup' + +describe('cy.spread', { defaultCommandTimeout: 0 }, () => { + it('assertion failure', () => { + cy.wrap([1, 2, 3]).spread(() => { + expect('actual').to.equal('expected') + }) + }) + + it('exception', () => { + cy.wrap([1, 2, 3]).spread(() => { + ({}).bar() + }) + }) + + it('command failure', () => { + cy.wrap([1, 2, 3]).spread(() => { + cy.get('#does-not-exist') + }) + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/then_spec.js b/packages/runner/cypress/fixtures/errors/then_spec.js new file mode 100644 index 000000000000..00c45d4bb431 --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/then_spec.js @@ -0,0 +1,21 @@ +import './setup' + +describe('cy.then', { defaultCommandTimeout: 0 }, () => { + it('assertion failure', () => { + cy.wrap({}).then(() => { + expect('actual').to.equal('expected') + }) + }) + + it('exception', () => { + cy.wrap({}).then(() => { + ({}).bar() + }) + }) + + it('command failure', () => { + cy.wrap({}).then(() => { + cy.get('#does-not-exist') + }) + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/typescript_spec.ts b/packages/runner/cypress/fixtures/errors/typescript_spec.ts new file mode 100644 index 000000000000..285c310e6404 --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/typescript_spec.ts @@ -0,0 +1,21 @@ +import './setup' + +// simple example of typescript types +type Foo = { + something: string +} + +describe('typescript', { defaultCommandTimeout: 0 }, () => { + it('assertion failure', () => { + expect('actual').to.equal('expected') + }) + + it('exception', () => { + // @ts-ignore + ({}).bar() + }) + + it('command failure', () => { + cy.get('#does-not-exist') + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/uncaught_spec.js b/packages/runner/cypress/fixtures/errors/uncaught_spec.js new file mode 100644 index 000000000000..8edadd6daf9d --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/uncaught_spec.js @@ -0,0 +1,28 @@ +import './setup' + +describe('uncaught errors', { defaultCommandTimeout: 0 }, () => { + it('sync app exception', () => { + cy.visit('/index.html') + cy.get('.trigger-sync-error').click() + }) + + it('async app exception', () => { + cy.visit('/index.html') + cy.get('.trigger-async-error').click() + cy.wait(10000) + }) + + it('async exception', () => { + setTimeout(() => { + ({}).bar() + }) + + cy.wait(10000) + }) + + it('async exception with done', (done) => { + setTimeout(() => { + ({}).bar() + }) + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/unexpected_spec.js b/packages/runner/cypress/fixtures/errors/unexpected_spec.js new file mode 100644 index 000000000000..18f869f5b97c --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/unexpected_spec.js @@ -0,0 +1,30 @@ +import './setup' + +describe('unexpected errors', { defaultCommandTimeout: 0 }, () => { + let originalIsSpecialKeyword + let orignalCyExpect + + beforeEach(() => { + originalIsSpecialKeyword = Cypress.LocalStorage._isSpecialKeyword + orignalCyExpect = cy.expect + }) + + afterEach(() => { + Cypress.LocalStorage._isSpecialKeyword = originalIsSpecialKeyword + cy.expect = orignalCyExpect + }) + + it('Cypress method error', () => { + Cypress.LocalStorage.setStorages({ foo: 'foo' }) + + window.autWindow.eval(`Cypress.LocalStorage._isSpecialKeyword = () => { throw new Error('thrown in Cypress-LocalStorage-_isSpecialKeyword') }`) + + Cypress.LocalStorage.clear() + }) + + it('internal cy error', () => { + window.autWindow.eval(`cy.expect = () => { throw new Error('thrown in cy-expect') }`) + + cy.wrap({ foo: 'foo' }).should('have.property', 'foo') + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/validation_spec.js b/packages/runner/cypress/fixtures/errors/validation_spec.js new file mode 100644 index 000000000000..21d2e48ad3bc --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/validation_spec.js @@ -0,0 +1,15 @@ +import './setup' + +describe('validation errors', { defaultCommandTimeout: 0 }, () => { + it('from cypress', () => { + cy.viewport() + }) + + it('from chai expect', () => { + expect(true).to.be.nope + }) + + it('from chai assert', () => { + assert.deepInclude() + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/visit_spec.js b/packages/runner/cypress/fixtures/errors/visit_spec.js new file mode 100644 index 000000000000..f2b727082670 --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/visit_spec.js @@ -0,0 +1,35 @@ +import './setup' + +describe('cy.visit', () => { + it('onBeforeLoad assertion failure', () => { + cy.visit('/index.html', { + onBeforeLoad () { + expect('actual').to.equal('expected') + }, + }) + }) + + it('onBeforeLoad exception', () => { + cy.visit('/index.html', { + onBeforeLoad () { + ({}).bar() + }, + }) + }) + + it('onLoad assertion failure', () => { + cy.visit('/index.html', { + onLoad () { + expect('actual').to.equal('expected') + }, + }) + }) + + it('onLoad exception', () => { + cy.visit('/index.html', { + onLoad () { + ({}).bar() + }, + }) + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/within_spec.js b/packages/runner/cypress/fixtures/errors/within_spec.js new file mode 100644 index 000000000000..d7ca3f342351 --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/within_spec.js @@ -0,0 +1,21 @@ +import './setup' + +describe('cy.within', { defaultCommandTimeout: 0 }, () => { + it('assertion failure', () => { + cy.get('body').within(() => { + expect('actual').to.equal('expected') + }) + }) + + it('exception', () => { + cy.get('body').within(() => { + ({}).bar() + }) + }) + + it('command failure', () => { + cy.get('body').within(() => { + cy.get('#does-not-exist') + }) + }) +}) diff --git a/packages/runner/cypress/fixtures/errors/wrap_spec.js b/packages/runner/cypress/fixtures/errors/wrap_spec.js new file mode 100644 index 000000000000..f566fc572c27 --- /dev/null +++ b/packages/runner/cypress/fixtures/errors/wrap_spec.js @@ -0,0 +1,21 @@ +import './setup' + +describe('cy.wrap', { defaultCommandTimeout: 0 }, () => { + it('assertion failure', () => { + cy.wrap(() => { + expect('actual').to.equal('expected') + }).then((fn) => fn()) + }) + + it('exception', () => { + cy.wrap(() => { + ({}).bar() + }).then((fn) => fn()) + }) + + it('command failure', () => { + cy.wrap(() => { + cy.get('#does-not-exist') + }).then((fn) => fn()) + }) +}) diff --git a/packages/runner/cypress/integration/reporter.errors.spec.js b/packages/runner/cypress/integration/reporter.errors.spec.js new file mode 100644 index 000000000000..c5af93b1ffc1 --- /dev/null +++ b/packages/runner/cypress/integration/reporter.errors.spec.js @@ -0,0 +1,708 @@ +const helpers = require('../support/helpers') + +const _ = Cypress._ +const { runIsolatedCypress } = helpers.createCypress() + +export const verifyFailure = (options) => { + const { + hasCodeFrame = true, + verifyOpenInIde = true, + column, + codeFrameText, + message, + stack, + file, + win, + } = options + let { regex, line } = options + + regex = regex || new RegExp(`${file}:${line || '\\d+'}:${column}`) + + const testOpenInIde = () => { + expect(win.runnerWs.emit.withArgs('open:file').lastCall.args[1].file).to.include(file) + } + + win.runnerWs.emit.withArgs('get:user:editor') + .yields({ + preferredOpener: { + id: 'foo-editor', + name: 'Foo', + openerId: 'foo-editor', + isOther: false, + }, + }) + + win.runnerWs.emit.withArgs('open:file') + + cy.contains('View stack trace').click() + + _.each([].concat(message), (msg) => { + cy.get('.runnable-err-message') + .should('include.text', msg) + + cy.get('.runnable-err-stack-trace') + .should('not.include.text', msg) + }) + + cy.get('.runnable-err-stack-trace') + .invoke('text') + .should('match', regex) + + if (stack) { + _.each([].concat(stack), (stackLine) => { + cy.get('.runnable-err-stack-trace') + .should('include.text', stackLine) + }) + } + + cy.get('.runnable-err-stack-trace') + .should('not.include.text', '__stackReplacementMarker') + + if (verifyOpenInIde) { + cy.contains('.runnable-err-stack-trace .runnable-err-file-path a', file) + .click() + .should(() => { + testOpenInIde() + }) + } + + if (!hasCodeFrame) return + + cy + .get('.test-err-code-frame .runnable-err-file-path') + .invoke('text') + .should('match', regex) + + cy.get('.test-err-code-frame pre span').should('include.text', codeFrameText) + + if (verifyOpenInIde) { + cy.contains('.test-err-code-frame .runnable-err-file-path a', file) + .click() + .should(() => { + expect(win.runnerWs.emit.withArgs('open:file')).to.be.calledTwice + testOpenInIde() + }) + } +} + +const verifyInternalFailure = (props) => { + const { method } = props + + cy.get('.runnable-err-message') + .should('include.text', `thrown in ${method.replace(/\./g, '-')}`) + + cy.get('.runnable-err-stack-trace') + .should('include.text', method) + + cy.get('.test-err-code-frame') + .should('not.exist') +} + +// eslint-disable-next-line +const createVerifyTest = (modifier) => (title, props) => { + const verifyFn = props.verifyFn || verifyFailure + + ;(modifier ? it[modifier] : it)(title, () => { + return runIsolatedCypress(`cypress/fixtures/errors/${props.file}`, { + onBeforeRun ({ specWindow, win, autCypress }) { + specWindow.testToRun = title + specWindow.autWindow = win + specWindow.autCypress = autCypress + + if (props.onBeforeRun) { + props.onBeforeRun({ specWindow, win }) + } + }, + }) + .then(({ win }) => { + props.codeFrameText = props.codeFrameText || title + props.win = win + + verifyFn(props) + }) + }) +} + +const verify = { + it: createVerifyTest(), +} + +verify.it['only'] = createVerifyTest('only') +verify.it['skip'] = createVerifyTest('skip') + +describe('errors ui', () => { + describe('assertion failures', () => { + const file = 'assertions_spec.js' + + verify.it('with expect().', { + file, + column: 25, + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('with assert()', { + file, + column: '(5|12)', + message: `should be true`, + }) + + verify.it('with assert.()', { + file, + column: 12, + message: `expected 'actual' to equal 'expected'`, + }) + }) + + describe('exception failures', () => { + const file = 'exceptions_spec.js' + + verify.it('in spec file', { + file, + column: 10, + message: 'bar is not a function', + }) + + verify.it('in file outside project', { + file, + message: 'An outside error', + regex: /todos\/throws\-error\.js:5:9/, + codeFrameText: `thrownewError('An outside error')`, + verifyOpenInIde: false, + }) + }) + + describe('commands', () => { + const file = 'commands_spec.js' + + verify.it('failure', { + file, + column: 8, + message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', + }) + + verify.it('chained failure', { + file, + column: 20, + message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', + }) + }) + + describe('cy.then', () => { + const file = 'then_spec.js' + + verify.it('assertion failure', { + file, + column: 27, + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('exception', { + file, + column: 12, + message: 'bar is not a function', + }) + + verify.it('command failure', { + file, + column: 10, + message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', + }) + }) + + describe('cy.should', () => { + const file = 'should_spec.js' + + verify.it('callback assertion failure', { + file, + column: 27, + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('callback exception', { + file, + column: 12, + message: 'bar is not a function', + }) + + verify.it('standard assertion failure', { + file, + column: 6, + message: 'Timed out retrying: expected {} to have property \'foo\'', + }) + + verify.it('after multiple', { + file, + column: 6, + message: 'Timed out retrying: expected \'foo\' to equal \'bar\'', + }) + + verify.it('after multiple callbacks exception', { + file, + column: 12, + codeFrameText: '({}).bar()', + message: 'bar is not a function', + }) + + verify.it('after multiple callbacks assertion failure', { + file, + column: 27, + codeFrameText: '.should(()=>', + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('after callback success assertion failure', { + file, + column: 6, + codeFrameText: '.should(\'have.property', + message: `expected {} to have property 'foo'`, + }) + + verify.it('command failure after success', { + file, + column: 8, + message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', + }) + }) + + describe('cy.each', () => { + const file = 'each_spec.js' + + verify.it('assertion failure', { + file, + column: 27, + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('exception', { + file, + column: 12, + message: 'bar is not a function', + }) + + verify.it('command failure', { + file, + column: 10, + message: 'Expected to find element: #does-not-exist, but never found it', + }) + }) + + describe('cy.spread', () => { + const file = 'spread_spec.js' + + verify.it('assertion failure', { + file, + column: 27, + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('exception', { + file, + column: 12, + message: 'bar is not a function', + }) + + verify.it('command failure', { + file, + column: 10, + message: 'Expected to find element: #does-not-exist, but never found it', + }) + }) + + describe('cy.within', () => { + const file = 'within_spec.js' + + verify.it('assertion failure', { + file, + column: 27, + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('exception', { + file, + column: 12, + message: 'bar is not a function', + }) + + verify.it('command failure', { + file, + column: 10, + message: 'Expected to find element: #does-not-exist, but never found it', + }) + }) + + describe('cy.wrap', () => { + const file = 'wrap_spec.js' + + verify.it('assertion failure', { + file, + column: 27, + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('exception', { + file, + column: 12, + message: 'bar is not a function', + }) + + verify.it('command failure', { + file, + column: 10, + message: 'Expected to find element: #does-not-exist, but never found it', + }) + }) + + describe('cy.visit', () => { + const file = 'visit_spec.js' + + verify.it('onBeforeLoad assertion failure', { + file, + column: 29, + codeFrameText: 'onBeforeLoad', + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('onBeforeLoad exception', { + file, + column: 14, + codeFrameText: 'onBeforeLoad', + message: 'bar is not a function', + }) + + verify.it('onLoad assertion failure', { + file, + column: 29, + codeFrameText: 'onLoad', + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('onLoad exception', { + file, + column: 14, + codeFrameText: 'onLoad', + message: 'bar is not a function', + }) + }) + + describe('cy.route', () => { + const file = 'route_spec.js' + + verify.it('callback assertion failure', { + file, + column: 27, + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('callback exception', { + file, + column: 12, + message: 'bar is not a function', + }) + + verify.it('command failure', { + file, + column: 10, + message: 'Expected to find element: #does-not-exist, but never found it', + }) + + verify.it('onAbort assertion failure', { + file, + column: 29, + codeFrameText: 'onAbort', + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('onAbort exception', { + file, + column: 14, + codeFrameText: 'onAbort', + message: 'bar is not a function', + }) + + verify.it('onRequest assertion failure', { + file, + column: 29, + codeFrameText: 'onRequest', + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('onRequest exception', { + file, + column: 14, + codeFrameText: 'onRequest', + message: 'bar is not a function', + }) + + // FIXME: in isolated runner, the error ends up uncaught for + // some reason, which throws off the test + verify.it.skip('onResponse assertion failure', { + file, + column: 29, + codeFrameText: 'onResponse', + message: `expected 'actual' to equal 'expected'`, + }) + + // FIXME: in isolated runner, the error ends up uncaught for + // some reason, which throws off the test + verify.it.skip('onResponse exception', { + file, + column: 14, + codeFrameText: 'onResponse', + message: 'bar is not a function', + }) + }) + + describe('cy.server', () => { + const file = 'server_spec.js' + + verify.it('onAbort assertion failure', { + file, + column: 29, + codeFrameText: 'onAbort', + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('onAbort exception', { + file, + column: 14, + codeFrameText: 'onAbort', + message: 'bar is not a function', + }) + + verify.it('onRequest assertion failure', { + file, + column: 29, + codeFrameText: 'onRequest', + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('onRequest exception', { + file, + column: 14, + codeFrameText: 'onRequest', + message: 'bar is not a function', + }) + + // FIXME: in isolated runner, the error ends up uncaught for + // some reason, which throws off the test + verify.it.skip('onResponse assertion failure', { + file, + column: 29, + codeFrameText: 'onResponse', + message: `expected 'actual' to equal 'expected'`, + }) + + // FIXME: in isolated runner, the error ends up uncaught for + // some reason, which throws off the test + verify.it.skip('onResponse exception', { + file, + column: 14, + codeFrameText: 'onResponse', + message: 'bar is not a function', + }) + }) + + describe('cy.readFile', () => { + const file = 'readfile_spec.js' + + verify.it('existence failure', { + onBeforeRun ({ win }) { + win.runnerWs.emit.withArgs('backend:request', 'read:file') + .yields({ error: { code: 'ENOENT' } }) + }, + file, + column: 8, + message: 'failed because the file does not exist', + }) + }) + + describe('validation errors', () => { + const file = 'validation_spec.js' + + verify.it('from cypress', { + file, + column: 8, + message: 'can only accept a string preset or', + stack: ['throwErrBadArgs', 'From Your Spec Code:'], + }) + + verify.it('from chai expect', { + file, + column: '(5|12)', // different between chrome & firefox + message: 'Invalid Chai property: nope', + stack: ['proxyGetter', 'From Your Spec Code:'], + }) + + verify.it('from chai assert', { + file, + column: 12, + message: 'object tested must be an array', + }) + }) + + describe('event handlers', () => { + const file = 'events_spec.js' + + verify.it('event assertion failure', { + file, + column: 27, + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('event exception', { + file, + column: 12, + message: 'bar is not a function', + }) + + verify.it('fail handler assertion failure', { + file, + column: 27, + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('fail handler exception', { + file, + column: 12, + message: 'bar is not a function', + }) + }) + + describe('uncaught errors', () => { + const file = 'uncaught_spec.js' + + verify.it('sync app exception', { + file, + message: [ + 'The following error originated from your application code', + 'syncReference is not defined', + ], + regex: /localhost\:\d+\/fixtures\/isolated-runner-inner.html:\d+:\d+/, + hasCodeFrame: false, + verifyOpenInIde: false, + }) + + // FIXME: does not get caught and wrapped like it does in real cypress + verify.it.skip('async app exception', { + file, + message: [ + 'The following error originated from your application code', + 'asyncReference is not defined', + ], + regex: /localhost\:\d+\/fixtures\/isolated-runner-inner.html:\d+:\d+/, + hasCodeFrame: false, + verifyOpenInIde: false, + }) + + verify.it('async exception', { + file, + column: 12, + message: [ + 'bar is not a function', + 'The following error originated from your test code', + ], + }) + + verify.it('async exception with done', { + file, + column: 12, + message: [ + 'bar is not a function', + 'The following error originated from your test code', + ], + }) + }) + + describe('custom commands', () => { + const file = 'custom_commands_spec.js' + + verify.it('assertion failure', { + file, + column: 23, + message: `expected 'actual' to equal 'expected'`, + codeFrameText: `add('failAssertion'`, + }) + + verify.it('exception', { + file, + column: 8, + message: 'bar is not a function', + codeFrameText: `add('failException'`, + }) + + verify.it('command failure', { + file, + column: 6, + message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', + codeFrameText: `add('failCommand'`, + }) + }) + + describe('typescript', () => { + const file = 'typescript_spec.ts' + + verify.it('assertion failure', { + file, + column: 25, + message: `expected 'actual' to equal 'expected'`, + }) + + verify.it('exception', { + file, + column: 10, + message: 'bar is not a function', + }) + + verify.it('command failure', { + file, + column: 8, + message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', + }) + }) + + describe('docs url', () => { + const file = 'docs_url_spec.js' + const docsUrl = 'https://on.cypress.io/viewport' + + verify.it('displays as button in interactive mode', { + file, + verifyFn () { + cy + .get('.runnable-err-message') + .should('not.contain', docsUrl) + .contains('Learn more') + .should('have.attr', 'href', docsUrl) + }, + }) + + verify.it('is text in error message in run mode', { + file, + verifyFn () { + cy + .get('.runnable-err-message') + .should('contain', docsUrl) + .contains('Learn more') + .should('not.exist') + }, + }) + }) + + // cases where there is a bug in Cypress and we should show cypress internals + // instead of the invocation stack. we do this by monkey-patching internal + // methods to make them throw an error + describe('unexpected errors', () => { + const file = 'unexpected_spec.js' + + verify.it('Cypress method error', { + file, + verifyFn: verifyInternalFailure, + method: 'Cypress.LocalStorage._isSpecialKeyword', + }) + + verify.it('internal cy error', { + file, + verifyFn: verifyInternalFailure, + method: 'cy.expect', + }) + }) +}) diff --git a/packages/runner/cypress/plugins/index.js b/packages/runner/cypress/plugins/index.js index 62be55014b82..e5158967e1ca 100644 --- a/packages/runner/cypress/plugins/index.js +++ b/packages/runner/cypress/plugins/index.js @@ -5,9 +5,7 @@ const { getSnapshot, saveSnapshot } = require('./snapshot/snapshotPlugin') /** * @type {Cypress.PluginConfig} */ -module.exports = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config +module.exports = (on) => { on('task', { getSnapshot, saveSnapshot, diff --git a/packages/runner/cypress/support/helpers.js b/packages/runner/cypress/support/helpers.js index de6c2cc15285..26e178ac001f 100644 --- a/packages/runner/cypress/support/helpers.js +++ b/packages/runner/cypress/support/helpers.js @@ -91,13 +91,14 @@ function createCypress () { /** * Spawns an isolated Cypress runner as the AUT, with provided spec/fixture and optional state/config - * @param {()=>void | {[key:string]: any}} mochaTests + * @param {string | ()=>void | {[key:string]: any}} mochaTestsOrFile * @param {{state?: any, config?: any}} opts */ - const runIsolatedCypress = (mochaTests, opts = {}) => { + const runIsolatedCypress = (mochaTestsOrFile, opts = {}) => { _.defaultsDeep(opts, { state: {}, config: { video: false }, + onBeforeRun () {}, }) return cy.visit('/fixtures/isolated-runner.html#/tests/cypress/fixtures/empty_spec.js') @@ -193,7 +194,7 @@ function createCypress () { onInitializedListeners = [] autCypress.run((failed) => { - resolve({ failed, mochaStubs, autCypress }) + resolve({ failed, mochaStubs, autCypress, win }) }) } @@ -208,15 +209,22 @@ function createCypress () { cy.stub(autCypress, 'onSpecWindow').snapshot(enableStubSnapshots).callsFake((specWindow) => { autCypress.onSpecWindow.restore() + opts.onBeforeRun({ specWindow, win, autCypress }) + + const testsInOwnFile = _.isString(mochaTestsOrFile) + const relativeFile = testsInOwnFile ? mochaTestsOrFile : 'cypress/fixtures/empty_spec.js' + autCypress.onSpecWindow(specWindow, [ { - absolute: 'cypress/fixtures/empty_spec.js', - relative: 'cypress/fixtures/empty_spec.js', - relativeUrl: '/__cypress/tests?p=cypress/fixtures/empty_spec.js', + absolute: relativeFile, + relative: relativeFile, + relativeUrl: `/__cypress/tests?p=${relativeFile}`, }, ]) - generateMochaTestsForWin(specWindow, mochaTests) + if (testsInOwnFile) return + + generateMochaTestsForWin(specWindow, mochaTestsOrFile) specWindow.before = () => {} specWindow.beforeEach = () => {} specWindow.afterEach = () => {} @@ -241,7 +249,7 @@ function createCypress () { .yieldsAsync({ response: { isOkStatusCode: true, isHtml: true, - url: 'http://localhost:3500/fixtures/generic.html', + url: 'http://localhost:3500/fixtures/isolated-runner-inner.html', } }) .withArgs('set:runnables') @@ -278,7 +286,6 @@ function createCypress () { snapshotMochaEvents, onInitialized, getAutCypress, - } } diff --git a/packages/server/test/e2e/8_error_ui_spec.ts b/packages/server/test/e2e/8_error_ui_spec.ts index 43ac85af8da3..df2eafa9d363 100644 --- a/packages/server/test/e2e/8_error_ui_spec.ts +++ b/packages/server/test/e2e/8_error_ui_spec.ts @@ -1,65 +1,24 @@ -import bodyParser from 'body-parser' import e2e, { expect } from '../support/helpers/e2e' import Fixtures from '../support/helpers/fixtures' -const WEBPACK_PREPROCESSOR_PROJECTS = [ - 'webpack-preprocessor', - 'webpack-preprocessor-ts-loader', - 'webpack-preprocessor-ts-loader-compiler-options', - 'webpack-preprocessor-awesome-typescript-loader', -] - -const onServer = function (app) { - app.use(bodyParser.json()) - - return app.get('/response', (req, res) => res.json({ ok: true })) -} - -const VARIOUS_FAILURES_EXPECTED_FAILURES = 63 - const verifyPassedAndFailedAreSame = (expectedFailures) => { return ({ stdout }) => { const passes = stdout.match(/✓ ✓ VERIFY/g) - expect(passes.length, 'number of passes should equal the number of failures').to.equal(expectedFailures) + expect(passes?.length || 0, 'number of passes should equal the number of failures').to.equal(expectedFailures) } } describe('e2e error ui', function () { - e2e.setup({ - port: 1919, - onServer, - }) - - e2e.it('displays correct UI for errors', { - spec: 'various_failures_spec.js', - expectedExitCode: VARIOUS_FAILURES_EXPECTED_FAILURES, - timeout: 240000, // 4 minutes - noTypeScript: true, - onRun (exec) { - return exec().then(verifyPassedAndFailedAreSame(VARIOUS_FAILURES_EXPECTED_FAILURES)) - }, - }) - - e2e.it('displays correct UI for errors in custom commands', { - spec: 'various_failures_custom_commands_spec.js', - expectedExitCode: VARIOUS_FAILURES_EXPECTED_FAILURES, - timeout: 240000, // 4 minutes - noTypeScript: true, - onRun (exec) { - return exec().then(verifyPassedAndFailedAreSame(VARIOUS_FAILURES_EXPECTED_FAILURES)) - }, - }) - - e2e.it('displays correct UI for typescript errors', { - spec: 'various_failures_spec.ts', - expectedExitCode: 2, - onRun (exec) { - return exec().then(verifyPassedAndFailedAreSame(2)) - }, - }) - - WEBPACK_PREPROCESSOR_PROJECTS.forEach((project) => { + e2e.setup() + + ;[ + 'webpack-preprocessor', + 'webpack-preprocessor-ts-loader', + 'webpack-preprocessor-ts-loader-compiler-options', + 'webpack-preprocessor-awesome-typescript-loader', + ] + .forEach((project) => { e2e.it(`handles sourcemaps in webpack for project: ${project}`, { project: Fixtures.projectPath(project), spec: 'failing_spec.*', diff --git a/packages/server/test/support/fixtures/projects/e2e/cypress/integration/various_failures_custom_commands_spec.js b/packages/server/test/support/fixtures/projects/e2e/cypress/integration/various_failures_custom_commands_spec.js deleted file mode 100644 index 8dbd3b4ed10e..000000000000 --- a/packages/server/test/support/fixtures/projects/e2e/cypress/integration/various_failures_custom_commands_spec.js +++ /dev/null @@ -1,833 +0,0 @@ -/** - * See comment at top of various_failures_spec.js for more info - * This covers the same errors but inside custom commands - */ - -import outsideError from '../../../todos/throws-error' -import { setup, fail, verify, verifyInternalError } from '../support/util' - -setup({ - idePath: { - relative: 'cypress/support/commands.js', - absolute: /\/[^\/]+\/cypress\/support\/commands\.js/, - }, - verifyStackLineIsSpecFile: false, -}) - -context('assertion failures', function () { - describe('with expect().', function () { - fail(this, () => { - cy.failExpect() - }) - - verify(this, { - column: 23, - codeFrameText: 'add(\'failExpect\'', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('with assert()', function () { - fail(this, () => { - cy.failAssert() - }) - - verify(this, { - column: '(3|10)', // different between chrome & firefox - codeFrameText: 'add(\'failAssert\'', - message: 'should be true', - }) - }) - - describe('with assert.()', function () { - fail(this, () => { - cy.failAssertMethod() - }) - - verify(this, { - column: 10, - codeFrameText: 'add(\'failAssertMethod\'', - message: `expected 'actual' to equal 'expected'`, - }) - }) -}) - -context('exceptions', function () { - describe('in commands file', function () { - fail(this, () => { - cy.failException() - }) - - verify(this, { - column: 8, - codeFrameText: 'add(\'failException\'', - message: 'bar is not a function', - }) - }) - - describe('in file outside project', function () { - fail(this, () => { - outsideError() - }) - - verify(this, { - message: 'An outside error', - regex: /todos\/throws\-error\.js:5:9/, - codeFrameText: `thrownewError('An outside error')`, - verifyOpenInIde: false, - }) - }) -}) - -context('commands', function () { - describe('failure', function () { - fail(this, () => { - cy.failCommand() - }) - - verify(this, { - column: 6, - codeFrameText: 'add(\'failCommand\'', - message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', - }) - }) - - describe('chained failure', function () { - fail(this, () => { - cy.failChainedCommand() - }) - - verify(this, { - column: 18, - codeFrameText: 'add(\'failChainedCommand\'', - message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', - }) - }) -}) - -context('cy.then', function () { - describe('assertion failure', function () { - fail(this, () => { - cy.failThenAssertion() - }) - - verify(this, { - column: 25, - codeFrameText: 'add(\'failThenAssertion\'', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('exception', function () { - fail(this, () => { - cy.failThenException() - }) - - verify(this, { - column: 10, - codeFrameText: 'add(\'failThenException\'', - message: 'bar is not a function', - }) - }) - - describe('command failure', function () { - fail(this, () => { - cy.failThenCommandFailure() - }) - - verify(this, { - column: 8, - codeFrameText: 'add(\'failThenCommandFailure\'', - message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', - }) - }) -}) - -context('cy.should', function () { - describe('callback assertion failure', function () { - fail(this, () => { - cy.failShouldCallbackAssertion() - }) - - verify(this, { - column: 25, - codeFrameText: 'add(\'failShouldCallbackAssertion\'', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('callback exception', function () { - fail(this, () => { - cy.failShouldCallbackException() - }) - - verify(this, { - column: 10, - codeFrameText: 'add(\'failShouldCallbackException\'', - message: 'bar is not a function', - }) - }) - - describe('assertion failure', function () { - fail(this, () => { - cy.failShouldAssertion() - }) - - verify(this, { - column: 4, - codeFrameText: 'add(\'failShouldAssertion\'', - message: 'Timed out retrying: expected {} to have property \'foo\'', - }) - }) - - describe('after multiple', function () { - fail(this, () => { - cy.failAfterMultipleShoulds() - }) - - verify(this, { - column: 4, - codeFrameText: 'add(\'failAfterMultipleShoulds\'', - message: 'Timed out retrying: expected \'foo\' to equal \'bar\'', - }) - }) - - describe('after multiple callbacks exception', function () { - fail(this, () => { - cy.failAfterMultipleShouldCallbacksException() - }) - - verify(this, { - column: 10, - codeFrameText: '({}).bar()', - message: 'bar is not a function', - }) - }) - - describe('after multiple callbacks assertion failure', function () { - fail(this, () => { - cy.failAfterMultipleShouldCallbacksAssertion() - }) - - verify(this, { - column: 25, - codeFrameText: '.should(()=>', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('after callback success assertion failure', function () { - fail(this, () => { - cy.failAfterCallbackSuccessAssertion() - }) - - verify(this, { - column: 4, - codeFrameText: '.should(\'have.property\',\'foo\')', - message: `expected {} to have property 'foo'`, - }) - }) - - describe('command failure after success', function () { - fail(this, () => { - cy.failCommandAfterShouldSuccess() - }) - - verify(this, { - column: 6, - codeFrameText: 'add(\'failCommandAfterShouldSuccess\'', - message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', - }) - }) -}) - -context('cy.each', function () { - describe('assertion failure', function () { - fail(this, () => { - cy.failEachAssertion() - }) - - verify(this, { - column: 25, - codeFrameText: 'add(\'failEachAssertion\'', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('exception', function () { - fail(this, () => { - cy.failEachException() - }) - - verify(this, { - column: 10, - codeFrameText: 'add(\'failEachException\'', - message: 'bar is not a function', - }) - }) - - describe('command failure', function () { - fail(this, () => { - cy.failEachCommandFailure() - }) - - verify(this, { - column: 8, - codeFrameText: 'add(\'failEachCommandFailure\'', - message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', - }) - }) -}) - -context('cy.spread', function () { - describe('assertion failure', function () { - fail(this, () => { - cy.failSpreadAssertion() - }) - - verify(this, { - column: 25, - codeFrameText: 'add(\'failSpreadAssertion\'', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('exception', function () { - fail(this, () => { - cy.failSpreadException() - }) - - verify(this, { - column: 10, - codeFrameText: 'add(\'failSpreadException\'', - message: 'bar is not a function', - }) - }) - - describe('command failure', function () { - fail(this, () => { - cy.failSpreadCommandFailure() - }) - - verify(this, { - column: 8, - codeFrameText: 'add(\'failSpreadCommandFailure\'', - message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', - }) - }) -}) - -context('cy.within', function () { - describe('assertion failure', function () { - fail(this, () => { - cy.failWithinAssertion() - }) - - verify(this, { - column: 25, - codeFrameText: 'add(\'failWithinAssertion\'', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('exception', function () { - fail(this, () => { - cy.failWithinException() - }) - - verify(this, { - column: 10, - codeFrameText: 'add(\'failWithinException\'', - message: 'bar is not a function', - }) - }) - - describe('command failure', function () { - fail(this, () => { - cy.failWithinCommandFailure() - }) - - verify(this, { - column: 8, - codeFrameText: 'add(\'failWithinCommandFailure\'', - message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', - }) - }) -}) - -context('cy.wrap', function () { - describe('assertion failure', function () { - fail(this, () => { - cy.failWrapAssertion() - }) - - verify(this, { - column: 25, - codeFrameText: 'add(\'failWrapAssertion\'', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('exception', function () { - fail(this, () => { - cy.failWrapException() - }) - - verify(this, { - column: 10, - codeFrameText: 'add(\'failWrapException\'', - message: 'bar is not a function', - }) - }) - - describe('command failure', function () { - fail(this, () => { - cy.failWrapCommandFailure() - }) - - verify(this, { - column: 8, - codeFrameText: 'add(\'failWrapCommandFailure\'', - message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', - }) - }) -}) - -context('cy.visit', function () { - describe('onBeforeLoad assertion failure', function () { - fail(this, () => { - cy.failVisitBeforeLoadAssertion() - }) - - verify(this, { - column: 27, - codeFrameText: 'onBeforeLoad', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onBeforeLoad exception', function () { - fail(this, () => { - cy.failVisitBeforeLoadException() - }) - - verify(this, { - column: 12, - codeFrameText: 'onBeforeLoad', - message: 'bar is not a function', - }) - }) - - describe('onLoad assertion failure', function () { - fail(this, () => { - cy.failVisitLoadAssertion() - }) - - verify(this, { - column: 27, - codeFrameText: 'onLoad', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onLoad exception', function () { - fail(this, () => { - cy.failVisitLoadException() - }) - - verify(this, { - column: 12, - codeFrameText: 'onLoad', - message: 'bar is not a function', - }) - }) -}) - -context('cy.route', function () { - describe('callback assertion failure', function () { - fail(this, () => { - cy.failRouteCallbackAssertion() - }) - - verify(this, { - column: 25, - codeFrameText: 'failRouteCallbackAssertion', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('callback exception', function () { - fail(this, () => { - cy.failRouteCallbackException() - }) - - verify(this, { - column: 10, - codeFrameText: 'failRouteCallbackException', - message: 'bar is not a function', - }) - }) - - describe('command failure', function () { - fail(this, () => { - cy.failRouteCallbackCommandFailure() - }) - - verify(this, { - column: 8, - codeFrameText: 'failRouteCallbackCommandFailure', - message: 'Expected to find element: #does-not-exist, but never found it', - }) - }) - - describe('onAbort assertion failure', function (done) { - fail(this, () => { - cy.failRouteOnAbortAssertion() - }) - - verify(this, { - column: 27, - codeFrameText: 'onAbort', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onAbort exception', function (done) { - fail(this, () => { - cy.failRouteOnAbortException() - }) - - verify(this, { - column: 12, - codeFrameText: 'onAbort', - message: 'bar is not a function', - }) - }) - - describe('onRequest assertion failure', function (done) { - fail(this, () => { - cy.failRouteOnRequestAssertion() - }) - - verify(this, { - column: 27, - codeFrameText: 'onRequest', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onRequest exception', function (done) { - fail(this, () => { - cy.failRouteOnRequestException() - }) - - verify(this, { - column: 12, - codeFrameText: 'onRequest', - message: 'bar is not a function', - }) - }) - - describe('onResponse assertion failure', function (done) { - fail(this, () => { - cy.failRouteOnResponseAssertion() - }) - - verify(this, { - column: 27, - codeFrameText: 'onResponse', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onResponse exception', function (done) { - fail(this, () => { - cy.failRouteOnResponseException() - }) - - verify(this, { - column: 12, - codeFrameText: 'onResponse', - message: 'bar is not a function', - }) - }) -}) - -context('cy.server', function () { - describe('onAbort assertion failure', function (done) { - fail(this, () => { - cy.failServerOnAbortAssertion() - }) - - verify(this, { - column: 27, - codeFrameText: 'onAbort', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onAbort exception', function (done) { - fail(this, () => { - cy.failServerOnAbortException() - }) - - verify(this, { - column: 12, - codeFrameText: 'onAbort', - message: 'bar is not a function', - }) - }) - - describe('onRequest assertion failure', function (done) { - fail(this, () => { - cy.failServerOnRequestAssertion() - }) - - verify(this, { - column: 27, - codeFrameText: 'onRequest', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onRequest exception', function (done) { - fail(this, () => { - cy.failServerOnRequestException() - }) - - verify(this, { - column: 12, - codeFrameText: 'onRequest', - message: 'bar is not a function', - }) - }) - - describe('onResponse assertion failure', function (done) { - fail(this, () => { - cy.failServerOnResponseAssertion() - }) - - verify(this, { - column: 27, - codeFrameText: 'onResponse', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onResponse exception', function (done) { - fail(this, () => { - cy.failServerOnResponseException() - }) - - verify(this, { - column: 12, - codeFrameText: 'onResponse', - message: 'bar is not a function', - }) - }) -}) - -context('cy.readFile', function () { - describe('existence failure', function () { - fail(this, () => { - cy.failReadFileExistence() - }) - - verify(this, { - column: 6, - codeFrameText: 'failReadFileExistence', - message: 'failed because the file does not exist', - }) - }) -}) - -context('validation errors', function () { - describe('from cypress', function () { - fail(this, () => { - cy.cypressValidationError() - }) - - verify(this, { - column: 6, - codeFrameText: 'add(\'cypressValidationError\'', - message: 'can only accept a string preset or', - stack: ['throwErrBadArgs', 'From Your Spec Code:'], - }) - }) - - describe('from chai expect', function () { - fail(this, () => { - cy.chaiExpectValidationError() - }) - - verify(this, { - column: '(3|10)', // different between chrome & firefox - codeFrameText: 'add(\'chaiExpectValidationError\'', - message: 'Invalid Chai property: nope', - stack: ['proxyGetter', 'From Your Spec Code:'], - }) - }) - - describe('from chai assert', function () { - fail(this, () => { - cy.chaiAssertValidationError() - }) - - verify(this, { - column: 10, - codeFrameText: 'add(\'chaiAssertValidationError\'', - message: 'object tested must be an array', - }) - }) -}) - -context('event handlers', function () { - describe('event assertion failure', function () { - fail(this, () => { - cy.failEventHandlerAssertion() - }) - - verify(this, { - column: 25, - codeFrameText: 'failEventHandlerAssertion', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('event exception', function () { - fail(this, () => { - cy.failEventHandlerException() - }) - - verify(this, { - column: 10, - codeFrameText: 'failEventHandlerException', - message: 'bar is not a function', - }) - }) - - describe('fail handler assertion failure', function () { - fail(this, () => { - cy.failFailHandlerAssertion() - }) - - verify(this, { - column: 25, - codeFrameText: 'failFailHandlerAssertion', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('fail handler exception', function () { - fail(this, () => { - cy.failFailHandlerException() - }) - - verify(this, { - column: 10, - codeFrameText: 'failFailHandlerException', - message: 'bar is not a function', - }) - }) -}) - -context('uncaught errors', () => { - describe('sync app exception', function () { - fail(this, () => { - cy.failSyncAppException() - }) - - verify(this, { - message: [ - 'The following error originated from your application code', - 'qux is not defined', - ], - regex: /localhost\:\d+\/js_errors.html:\d+:\d+/, - hasCodeFrame: false, - verifyOpenInIde: false, - }) - }) - - describe('async app exception', function () { - fail(this, () => { - cy.failAsyncAppException() - }) - - verify(this, { - message: [ - 'The following error originated from your application code', - 'qax is not defined', - ], - regex: /localhost\:\d+\/js_errors.html:\d+:\d+/, - hasCodeFrame: false, - verifyOpenInIde: false, - }) - }) - - describe('async exception', function () { - fail(this, () => { - cy.failAsyncException() - }) - - verify(this, { - column: 10, - message: [ - 'bar is not a function', - 'The following error originated from your test code', - ], - codeFrameText: 'failAsyncException', - }) - }) - - describe('async exception with done', function () { - fail(this, (done) => { - cy.failAsyncException() - }) - - verify(this, { - column: 10, - message: [ - 'bar is not a function', - 'The following error originated from your test code', - ], - codeFrameText: 'failAsyncException', - }) - }) -}) - -// covering cases where there is a bug in Cypress and we shouldn't show -// the invocation stack. it should show the original stack even if it is -// thrown within a command -context('unexpected errors', () => { - describe('Cypress method error', function () { - const isJquery = Cypress.dom.isJquery - - beforeEach(() => { - Cypress.dom.isJquery = isJquery - }) - - fail(this, () => { - cy.failInternalCypressMethod() - }) - - verifyInternalError(this, { - method: 'Cypress.dom.isJquery', - }) - }) - - describe('cy method error', function () { - const cyExpect = cy.expect - - beforeEach(() => { - cy.expect = cyExpect - }) - - fail(this, () => { - cy.failInternalCyMethod() - }) - - verifyInternalError(this, { - method: 'cy.expect', - }) - }) -}) diff --git a/packages/server/test/support/fixtures/projects/e2e/cypress/integration/various_failures_spec.js b/packages/server/test/support/fixtures/projects/e2e/cypress/integration/various_failures_spec.js deleted file mode 100644 index 3e0721b3ce8a..000000000000 --- a/packages/server/test/support/fixtures/projects/e2e/cypress/integration/various_failures_spec.js +++ /dev/null @@ -1,981 +0,0 @@ -/** - * This tests various failure scenarios where an error and code frame is displayed - * It does this by having a test fail and then a subsequent test run that - * tests the appearance of the command log - * Because of this, the test order is important - * There should be the same number of failing tests as there are passing - * tests, because each failure has a verification test (e.g. 11 fail, 11 pass) - */ -import outsideError from '../../../todos/throws-error' -import { - fail, - verify, - verifyInternalError, - setup, - sendXhr, - abortXhr, -} from '../support/util' - -setup({ verifyStackLineIsSpecFile: true }) - -context('assertion failures', function () { - describe('with expect().', function () { - fail(this, () => { - expect('actual').to.equal('expected') - }) - - verify(this, { - column: 27, - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('with assert()', function () { - fail(this, () => { - assert(false, 'should be true') - }) - - verify(this, { - column: '(7|14)', // different between chrome & firefox - message: 'should be true', - }) - }) - - describe('with assert.()', function () { - fail(this, () => { - assert.equal('actual', 'expected') - }) - - verify(this, { - column: 14, - message: `expected 'actual' to equal 'expected'`, - }) - }) -}) - -context('exceptions', function () { - describe('in spec file', function () { - fail(this, () => { - ({}).bar() - }) - - verify(this, { - column: 12, - message: 'bar is not a function', - }) - }) - - describe('in file outside project', function () { - fail(this, () => { - outsideError() - }) - - verify(this, { - message: 'An outside error', - regex: /todos\/throws\-error\.js:5:9/, - codeFrameText: `thrownewError('An outside error')`, - verifyOpenInIde: false, - }) - }) -}) - -context('commands', function () { - describe('failure', function () { - fail(this, () => { - cy.get('#does-not-exist') - }) - - verify(this, { - column: 10, - message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', - }) - }) - - describe('chained failure', function () { - fail(this, () => { - cy.get('body').find('#does-not-exist') - }) - - verify(this, { - column: 22, - message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', - }) - }) -}) - -context('cy.then', function () { - describe('assertion failure', function () { - fail(this, () => { - cy.wrap({}).then(() => { - expect('actual').to.equal('expected') - }) - }) - - verify(this, { - column: 29, - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('exception', function () { - fail(this, () => { - cy.wrap({}).then(() => { - ({}).bar() - }) - }) - - verify(this, { - column: 14, - message: 'bar is not a function', - }) - }) - - describe('command failure', function () { - fail(this, () => { - cy.wrap({}).then(() => { - cy.get('#does-not-exist') - }) - }) - - verify(this, { - column: 12, - message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', - }) - }) -}) - -context('cy.should', function () { - describe('callback assertion failure', function () { - fail(this, () => { - cy.wrap({}).should(() => { - expect('actual').to.equal('expected') - }) - }) - - verify(this, { - column: 29, - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('callback exception', function () { - fail(this, () => { - cy.wrap({}).should(() => { - ({}).bar() - }) - }) - - verify(this, { - column: 14, - message: 'bar is not a function', - }) - }) - - describe('assertion failure', function () { - fail(this, () => { - cy.wrap({}) - .should('have.property', 'foo') - }) - - verify(this, { - column: 8, - message: 'Timed out retrying: expected {} to have property \'foo\'', - }) - }) - - describe('after multiple', function () { - fail(this, () => { - cy.wrap({ foo: 'foo' }).should('have.property', 'foo') - .should('equal', 'bar') - }) - - verify(this, { - column: 8, - message: 'Timed out retrying: expected \'foo\' to equal \'bar\'', - }) - }) - - describe('after multiple callbacks exception', function () { - fail(this, () => { - cy.wrap({ foo: 'foo' }) - .should(() => { - expect(true).to.be.true - }) - .should(() => { - ({}).bar() - }) - }) - - verify(this, { - column: 14, - codeFrameText: '({}).bar()', - message: 'bar is not a function', - }) - }) - - describe('after multiple callbacks assertion failure', function () { - fail(this, () => { - cy.wrap({ foo: 'foo' }) - .should(() => { - expect(true).to.be.true - }) - .should(() => { - expect('actual').to.equal('expected') - }) - }) - - verify(this, { - column: 29, - codeFrameText: '.should(()=>', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('after callback success assertion failure', function () { - fail(this, () => { - cy.wrap({}) - .should(() => { - expect(true).to.be.true - }) - .should('have.property', 'foo') - }) - - verify(this, { - column: 8, - codeFrameText: '.should(\'have.property', - message: `expected {} to have property 'foo'`, - }) - }) - - describe('command failure after success', function () { - fail(this, () => { - cy.wrap({ foo: 'foo' }).should('have.property', 'foo') - cy.get('#does-not-exist') - }) - - verify(this, { - column: 10, - message: 'Timed out retrying: Expected to find element: #does-not-exist, but never found it', - }) - }) -}) - -context('cy.each', function () { - describe('assertion failure', function () { - fail(this, () => { - cy.wrap([1]).each(() => { - expect('actual').to.equal('expected') - }) - }) - - verify(this, { - column: 29, - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('exception', function () { - fail(this, () => { - cy.wrap([1]).each(() => { - ({}).bar() - }) - }) - - verify(this, { - column: 14, - message: 'bar is not a function', - }) - }) - - describe('command failure', function () { - fail(this, () => { - cy.wrap([1]).each(() => { - cy.get('#does-not-exist') - }) - }) - - verify(this, { - column: 12, - message: 'Expected to find element: #does-not-exist, but never found it', - }) - }) -}) - -context('cy.spread', function () { - describe('assertion failure', function () { - fail(this, () => { - cy.wrap([1, 2, 3]).spread(() => { - expect('actual').to.equal('expected') - }) - }) - - verify(this, { - column: 29, - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('exception', function () { - fail(this, () => { - cy.wrap([1, 2, 3]).spread(() => { - ({}).bar() - }) - }) - - verify(this, { - column: 14, - message: 'bar is not a function', - }) - }) - - describe('command failure', function () { - fail(this, () => { - cy.wrap([1, 2, 3]).spread(() => { - cy.get('#does-not-exist') - }) - }) - - verify(this, { - column: 12, - message: 'Expected to find element: #does-not-exist, but never found it', - }) - }) -}) - -context('cy.within', function () { - describe('assertion failure', function () { - fail(this, () => { - cy.get('body').within(() => { - expect('actual').to.equal('expected') - }) - }) - - verify(this, { - column: 29, - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('exception', function () { - fail(this, () => { - cy.get('body').within(() => { - ({}).bar() - }) - }) - - verify(this, { - column: 14, - message: 'bar is not a function', - }) - }) - - describe('command failure', function () { - fail(this, () => { - cy.get('body').within(() => { - cy.get('#does-not-exist') - }) - }) - - verify(this, { - column: 12, - message: 'Expected to find element: #does-not-exist, but never found it', - }) - }) -}) - -context('cy.wrap', function () { - describe('assertion failure', function () { - fail(this, () => { - cy.wrap(() => { - expect('actual').to.equal('expected') - }).then((fn) => fn()) - }) - - verify(this, { - column: 29, - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('exception', function () { - fail(this, () => { - cy.wrap(() => { - ({}).bar() - }).then((fn) => fn()) - }) - - verify(this, { - column: 14, - message: 'bar is not a function', - }) - }) - - describe('command failure', function () { - fail(this, () => { - cy.wrap(() => { - cy.get('#does-not-exist') - }).then((fn) => fn()) - }) - - verify(this, { - column: 12, - message: 'Expected to find element: #does-not-exist, but never found it', - }) - }) -}) - -context('cy.visit', () => { - describe('onBeforeLoad assertion failure', function () { - fail(this, () => { - cy.visit('/index.html', { - onBeforeLoad () { - expect('actual').to.equal('expected') - }, - }) - }) - - verify(this, { - column: 31, - codeFrameText: 'onBeforeLoad', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onBeforeLoad exception', function () { - fail(this, () => { - cy.visit('/index.html', { - onBeforeLoad () { - ({}).bar() - }, - }) - }) - - verify(this, { - column: 16, - codeFrameText: 'onBeforeLoad', - message: 'bar is not a function', - }) - }) - - describe('onLoad assertion failure', function () { - fail(this, () => { - cy.visit('/index.html', { - onLoad () { - expect('actual').to.equal('expected') - }, - }) - }) - - verify(this, { - column: 31, - codeFrameText: 'onLoad', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onLoad exception', function () { - fail(this, () => { - cy.visit('/index.html', { - onLoad () { - ({}).bar() - }, - }) - }) - - verify(this, { - column: 16, - codeFrameText: 'onLoad', - message: 'bar is not a function', - }) - }) -}) - -context('cy.route', () => { - describe('callback assertion failure', function () { - fail(this, () => { - cy.server().route(() => { - expect('actual').to.equal('expected') - }) - }) - - verify(this, { - column: 29, - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('callback exception', function () { - fail(this, () => { - cy.server().route(() => { - ({}).bar() - }) - }) - - verify(this, { - column: 14, - message: 'bar is not a function', - }) - }) - - describe('command failure', function () { - fail(this, () => { - cy.server().route(() => { - cy.get('#does-not-exist') - - return '/foo' - }) - }) - - verify(this, { - column: 12, - message: 'Expected to find element: #does-not-exist, but never found it', - }) - }) - - describe('onAbort assertion failure', function (done) { - fail(this, () => { - cy.server().route({ - url: '/foo', - onAbort () { - expect('actual').to.equal('expected') - }, - }) - .window().then(abortXhr) - }) - - verify(this, { - column: 31, - codeFrameText: 'onAbort', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onAbort exception', function (done) { - fail(this, () => { - cy.server().route({ - url: '/foo', - onAbort () { - ({}).bar() - }, - }) - .window().then(abortXhr) - }) - - verify(this, { - column: 16, - codeFrameText: 'onAbort', - message: 'bar is not a function', - }) - }) - - describe('onRequest assertion failure', function (done) { - fail(this, () => { - cy.server().route({ - url: '/foo', - onRequest () { - expect('actual').to.equal('expected') - }, - }) - .window().then(sendXhr) - }) - - verify(this, { - column: 31, - codeFrameText: 'onRequest', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onRequest exception', function (done) { - fail(this, () => { - cy.server().route({ - url: '/foo', - onRequest () { - ({}).bar() - }, - }) - .window().then(sendXhr) - }) - - verify(this, { - column: 16, - codeFrameText: 'onRequest', - message: 'bar is not a function', - }) - }) - - describe('onResponse assertion failure', function (done) { - fail(this, () => { - cy.server().route({ - url: '/users', - onResponse () { - expect('actual').to.equal('expected') - }, - }) - .visit('/xhr.html').get('#fetch').click() - .wait(10000) - }) - - verify(this, { - column: 31, - codeFrameText: 'onResponse', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onResponse exception', function (done) { - fail(this, () => { - cy.server().route({ - url: '/users', - onResponse () { - ({}).bar() - }, - }) - .visit('/xhr.html').get('#fetch').click() - .wait(10000) - }) - - verify(this, { - column: 16, - codeFrameText: 'onResponse', - message: 'bar is not a function', - }) - }) -}) - -context('cy.server', () => { - describe('onAbort assertion failure', function (done) { - fail(this, () => { - cy.server({ - onAbort () { - expect('actual').to.equal('expected') - }, - }) - .route('/foo') - .window().then(abortXhr) - }) - - verify(this, { - column: 31, - codeFrameText: 'onAbort', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onAbort exception', function (done) { - fail(this, () => { - cy.server({ - onAbort () { - ({}).bar() - }, - }) - .route('/foo') - .window().then(abortXhr) - }) - - verify(this, { - column: 16, - codeFrameText: 'onAbort', - message: 'bar is not a function', - }) - }) - - describe('onRequest assertion failure', function (done) { - fail(this, () => { - cy.server({ - onRequest () { - expect('actual').to.equal('expected') - }, - }) - .route('/foo') - .window().then(sendXhr) - }) - - verify(this, { - column: 31, - codeFrameText: 'onRequest', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onRequest exception', function (done) { - fail(this, () => { - cy.server({ - onRequest () { - ({}).bar() - }, - }) - .route('/foo') - .window().then(sendXhr) - }) - - verify(this, { - column: 16, - codeFrameText: 'onRequest', - message: 'bar is not a function', - }) - }) - - describe('onResponse assertion failure', function (done) { - fail(this, () => { - cy.server({ - onResponse () { - expect('actual').to.equal('expected') - }, - }) - .route('/users') - .visit('/xhr.html').get('#fetch').click() - .wait(10000) - }) - - verify(this, { - column: 31, - codeFrameText: 'onResponse', - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('onResponse exception', function (done) { - fail(this, () => { - cy.server({ - onResponse () { - ({}).bar() - }, - }) - .route('/users') - .visit('/xhr.html').get('#fetch').click() - .wait(10000) - }) - - verify(this, { - column: 16, - codeFrameText: 'onResponse', - message: 'bar is not a function', - }) - }) -}) - -context('cy.readFile', function () { - describe('existence failure', function () { - fail(this, () => { - cy.readFile('does-not-exist', { timeout: 0 }) - }) - - verify(this, { - column: 10, - message: 'failed because the file does not exist', - }) - }) -}) - -context('validation errors', function () { - describe('from cypress', function () { - fail(this, () => { - cy.viewport() - }) - - verify(this, { - column: 10, - message: 'can only accept a string preset or', - stack: ['throwErrBadArgs', 'From Your Spec Code:'], - }) - }) - - describe('from chai expect', function () { - fail(this, () => { - expect(true).to.be.nope - }) - - verify(this, { - column: '(7|14)', // different between chrome & firefox - message: 'Invalid Chai property: nope', - stack: ['proxyGetter', 'From Your Spec Code:'], - }) - }) - - describe('from chai assert', function () { - fail(this, () => { - assert.deepInclude() - }) - - verify(this, { - column: 14, - message: 'object tested must be an array', - }) - }) -}) - -context('event handlers', function () { - describe('event assertion failure', function () { - fail(this, () => { - cy.on('window:load', () => { - expect('actual').to.equal('expected') - }) - - cy.visit('http://localhost:1919') - }) - - verify(this, { - column: 29, - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('event exception', function () { - fail(this, () => { - cy.on('window:load', () => { - ({}).bar() - }) - - cy.visit('http://localhost:1919') - }) - - verify(this, { - column: 14, - message: 'bar is not a function', - }) - }) - - describe('fail handler assertion failure', function () { - fail(this, () => { - cy.on('fail', () => { - expect('actual').to.equal('expected') - }) - - cy.get('#does-not-exist') - }) - - verify(this, { - column: 29, - message: `expected 'actual' to equal 'expected'`, - }) - }) - - describe('fail handler exception', function () { - fail(this, () => { - cy.on('fail', () => { - ({}).bar() - }) - - cy.get('#does-not-exist') - }) - - verify(this, { - column: 14, - message: 'bar is not a function', - }) - }) -}) - -context('uncaught errors', () => { - describe('sync app exception', function () { - fail(this, () => { - cy.visit('/js_errors.html') - cy.get('.sync-error').click() - }) - - verify(this, { - message: [ - 'The following error originated from your application code', - 'qux is not defined', - ], - regex: /localhost\:\d+\/js_errors.html:\d+:\d+/, - hasCodeFrame: false, - verifyOpenInIde: false, - }) - }) - - describe('async app exception', function () { - fail(this, () => { - cy.visit('/js_errors.html') - cy.get('.async-error').click() - cy.wait(10000) - }) - - verify(this, { - message: [ - 'The following error originated from your application code', - 'qax is not defined', - ], - regex: /localhost\:\d+\/js_errors.html:\d+:\d+/, - hasCodeFrame: false, - verifyOpenInIde: false, - }) - }) - - describe('async exception', function () { - fail(this, () => { - setTimeout(() => { - ({}).bar() - }) - - cy.wait(10000) - }) - - verify(this, { - column: 14, - message: [ - 'bar is not a function', - 'The following error originated from your test code', - ], - }) - }) - - describe('async exception with done', function () { - fail(this, (done) => { - setTimeout(() => { - ({}).bar() - }, 20) - }) - - verify(this, { - column: 14, - message: [ - 'bar is not a function', - 'The following error originated from your test code', - ], - codeFrameText: 'fail(this,(done)=>', - }) - }) -}) - -// covering cases where there is a bug in Cypress and we shouldn't show -// the invocation stack. it should show the original stack even if it is -// thrown within a command -context('unexpected errors', () => { - describe('Cypress method error', function () { - const isJquery = Cypress.dom.isJquery - - beforeEach(() => { - Cypress.dom.isJquery = isJquery - }) - - fail(this, () => { - top.window.eval(`Cypress.dom.isJquery = () => { throw new Error('thrown in CypressdomisJquery') }`) - - cy.get('body') - }) - - verifyInternalError(this, { - method: 'Cypress.dom.isJquery', - }) - }) - - describe('internal cy error', function () { - const cyExpect = cy.expect - - beforeEach(() => { - cy.expect = cyExpect - }) - - fail(this, () => { - top.window.eval(`cy.expect = () => { throw new Error('thrown in cyexpect') }`) - - cy.wrap({ foo: 'foo' }).should('have.property', 'foo') - }) - - verifyInternalError(this, { - method: 'cy.expect', - }) - }) -}) diff --git a/packages/server/test/support/fixtures/projects/e2e/cypress/integration/various_failures_spec.ts b/packages/server/test/support/fixtures/projects/e2e/cypress/integration/various_failures_spec.ts deleted file mode 100644 index 812b07f10215..000000000000 --- a/packages/server/test/support/fixtures/projects/e2e/cypress/integration/various_failures_spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * This tests various failure scenarios where an error and code frame is displayed - * It does this by having a test fail and then a subsequent test run that - * tests the appearance of the command log - * Because of this, the test order is important - * There should be the same number of failing tests as there are passing - * tests, because each failure has a verification test (e.g. 11 fail, 11 pass) - */ - -// simple example of typescript types -type Foo = { - something: string -} - -import { fail, setup, verify } from '../support/util' - -setup({ verifyStackLineIsSpecFile: true }) - -context('validation errors', function () { - describe('from cypress with docsUrl', function () { - beforeEach(() => { - Cypress.config('isInteractive', true) - }) - - fail(this, () => { - cy.viewport() - }) - - verify(this, { - line: 26, // this only has 1 test, so we can be specific - column: 10, - verifyDocsLearnMore: 'https://on.cypress.io/viewport', - message: 'can only accept a string preset or', - stack: ['throwErrBadArgs', 'From Your Spec Code:'], - }) - }) - - describe('from cypress without docsUrl', function () { - beforeEach(() => { - Cypress.config('isInteractive', false) - }) - - fail(this, () => { - cy.viewport() - }) - - verify(this, { - line: 44, // this only has 1 test, so we can be specific - column: 10, - verifyDocsContent: 'https://on.cypress.io/viewport', - message: 'can only accept a string preset or', - stack: ['throwErrBadArgs', 'From Your Spec Code:'], - }) - }) -}) diff --git a/packages/server/test/support/fixtures/projects/e2e/cypress/support/commands.js b/packages/server/test/support/fixtures/projects/e2e/cypress/support/commands.js index 69e0babb6f5b..e69de29bb2d1 100644 --- a/packages/server/test/support/fixtures/projects/e2e/cypress/support/commands.js +++ b/packages/server/test/support/fixtures/projects/e2e/cypress/support/commands.js @@ -1,425 +0,0 @@ -import { sendXhr, abortXhr } from './util' - -Cypress.Commands.add('failExpect', () => { - expect('actual').to.equal('expected') -}) - -Cypress.Commands.add('failAssert', () => { - assert(false, 'should be true') -}) - -Cypress.Commands.add('failAssertMethod', () => { - assert.equal('actual', 'expected') -}) - -Cypress.Commands.add('failException', () => { - ({}).bar() -}) - -Cypress.Commands.add('failCommand', () => { - cy.get('#does-not-exist') -}) - -Cypress.Commands.add('failChainedCommand', () => { - cy.get('body').find('#does-not-exist') -}) - -Cypress.Commands.add('failThenAssertion', () => { - cy.wrap({}).then(() => { - expect('actual').to.equal('expected') - }) -}) - -Cypress.Commands.add('failShouldCallbackAssertion', () => { - cy.wrap({}).should(() => { - expect('actual').to.equal('expected') - }) -}) - -Cypress.Commands.add('failThenException', () => { - cy.wrap({}).then(() => { - ({}).bar() - }) -}) - -Cypress.Commands.add('failThenCommandFailure', () => { - cy.wrap({}).then(() => { - cy.get('#does-not-exist') - }) -}) - -Cypress.Commands.add('failShouldCallbackException', () => { - cy.wrap({}).should(() => { - ({}).bar() - }) -}) - -Cypress.Commands.add('failShouldAssertion', () => { - cy.wrap({}) - .should('have.property', 'foo') -}) - -Cypress.Commands.add('failAfterMultipleShoulds', () => { - cy.wrap({ foo: 'foo' }).should('have.property', 'foo') - .should('equal', 'bar') -}) - -Cypress.Commands.add('failAfterMultipleShouldCallbacksException', () => { - cy.wrap({}) - .should(() => { - expect(true).to.be.true - }) - .should(() => { - ({}).bar() - }) -}) - -Cypress.Commands.add('failAfterMultipleShouldCallbacksAssertion', () => { - cy.wrap({}) - .should(() => { - expect(true).to.be.true - }) - .should(() => { - expect('actual').to.equal('expected') - }) -}) - -Cypress.Commands.add('failAfterCallbackSuccessAssertion', () => { - cy.wrap({}) - .should(() => { - expect(true).to.be.true - }) - .should('have.property', 'foo') -}) - -Cypress.Commands.add('failCommandAfterShouldSuccess', () => { - cy.wrap({ foo: 'foo' }).should('have.property', 'foo') - cy.get('#does-not-exist') -}) - -Cypress.Commands.add('failEachAssertion', () => { - cy.wrap([1]).each(() => { - expect('actual').to.equal('expected') - }) -}) - -Cypress.Commands.add('failEachException', () => { - cy.wrap([1]).each(() => { - ({}).bar() - }) -}) - -Cypress.Commands.add('failEachCommandFailure', () => { - cy.wrap([1]).each(() => { - cy.get('#does-not-exist') - }) -}) - -Cypress.Commands.add('failSpreadAssertion', () => { - cy.wrap([1, 2, 3]).spread(() => { - expect('actual').to.equal('expected') - }) -}) - -Cypress.Commands.add('failSpreadException', () => { - cy.wrap([1, 2, 3]).spread(() => { - ({}).bar() - }) -}) - -Cypress.Commands.add('failSpreadCommandFailure', () => { - cy.wrap([1, 2, 3]).spread(() => { - cy.get('#does-not-exist') - }) -}) - -Cypress.Commands.add('failWithinAssertion', () => { - cy.get('body').within(() => { - expect('actual').to.equal('expected') - }) -}) - -Cypress.Commands.add('failWithinException', () => { - cy.get('body').within(() => { - ({}).bar() - }) -}) - -Cypress.Commands.add('failWithinCommandFailure', () => { - cy.get('body').within(() => { - cy.get('#does-not-exist') - }) -}) - -Cypress.Commands.add('failWrapAssertion', () => { - cy.wrap(() => { - expect('actual').to.equal('expected') - }).then((fn) => fn()) -}) - -Cypress.Commands.add('failWrapException', () => { - cy.wrap(() => { - ({}).bar() - }).then((fn) => fn()) -}) - -Cypress.Commands.add('failWrapCommandFailure', () => { - cy.wrap(() => { - cy.get('#does-not-exist') - }).then((fn) => fn()) -}) - -Cypress.Commands.add('failVisitBeforeLoadAssertion', () => { - cy.visit('/index.html', { - onBeforeLoad () { - expect('actual').to.equal('expected') - }, - }) -}) - -Cypress.Commands.add('failVisitBeforeLoadException', () => { - cy.visit('/index.html', { - onBeforeLoad () { - ({}).bar() - }, - }) -}) - -Cypress.Commands.add('failVisitLoadAssertion', () => { - cy.visit('/index.html', { - onLoad () { - expect('actual').to.equal('expected') - }, - }) -}) - -Cypress.Commands.add('failVisitLoadException', () => { - cy.visit('/index.html', { - onLoad () { - ({}).bar() - }, - }) -}) - -Cypress.Commands.add('failRouteCallbackAssertion', () => { - cy.server().route(() => { - expect('actual').to.equal('expected') - }) -}) - -Cypress.Commands.add('failRouteCallbackException', () => { - cy.server().route(() => { - ({}).bar() - }) -}) - -Cypress.Commands.add('failRouteCallbackCommandFailure', () => { - cy.server().route(() => { - cy.get('#does-not-exist') - - return '/foo' - }) -}) - -Cypress.Commands.add('failRouteOnAbortAssertion', () => { - cy.server().route({ - url: '/foo', - onAbort () { - expect('actual').to.equal('expected') - }, - }) - .window().then(abortXhr) -}) - -Cypress.Commands.add('failRouteOnAbortException', () => { - cy.server().route({ - url: '/foo', - onAbort () { - ({}).bar() - }, - }) - .window().then(abortXhr) -}) - -Cypress.Commands.add('failRouteOnRequestAssertion', () => { - cy.server().route({ - url: '/foo', - onRequest () { - expect('actual').to.equal('expected') - }, - }) - .window().then(sendXhr) -}) - -Cypress.Commands.add('failRouteOnRequestException', () => { - cy.server().route({ - url: '/foo', - onRequest () { - ({}).bar() - }, - }) - .window().then(sendXhr) -}) - -Cypress.Commands.add('failRouteOnResponseAssertion', () => { - cy.server().route({ - url: '/users', - onResponse () { - expect('actual').to.equal('expected') - }, - }) - .visit('/xhr.html').get('#fetch').click() - .wait(10000) -}) - -Cypress.Commands.add('failRouteOnResponseException', () => { - cy.server().route({ - url: '/users', - onResponse () { - ({}).bar() - }, - }) - .visit('/xhr.html').get('#fetch').click() - .wait(10000) -}) - -Cypress.Commands.add('failServerOnAbortAssertion', () => { - cy.server({ - onAbort () { - expect('actual').to.equal('expected') - }, - }) - .route('/foo') - .window().then(abortXhr) -}) - -Cypress.Commands.add('failServerOnAbortException', () => { - cy.server({ - onAbort () { - ({}).bar() - }, - }) - .route('/foo') - .window().then(abortXhr) -}) - -Cypress.Commands.add('failServerOnRequestAssertion', () => { - cy.server({ - onRequest () { - expect('actual').to.equal('expected') - }, - }) - .route('/foo') - .window().then(sendXhr) -}) - -Cypress.Commands.add('failServerOnRequestException', () => { - cy.server({ - onRequest () { - ({}).bar() - }, - }) - .route('/foo') - .window().then(sendXhr) -}) - -Cypress.Commands.add('failServerOnResponseAssertion', () => { - cy.server({ - onResponse () { - expect('actual').to.equal('expected') - }, - }) - .route('/users') - .visit('/xhr.html').get('#fetch').click() - .wait(10000) -}) - -Cypress.Commands.add('failServerOnResponseException', () => { - cy.server({ - onResponse () { - ({}).bar() - }, - }) - .route('/users') - .visit('/xhr.html').get('#fetch').click() - .wait(10000) -}) - -Cypress.Commands.add('failReadFileExistence', () => { - cy.readFile('does-not-exist', { timeout: 0 }) -}) - -Cypress.Commands.add('cypressValidationError', () => { - cy.viewport() -}) - -Cypress.Commands.add('chaiExpectValidationError', () => { - expect(true).to.be.nope -}) - -Cypress.Commands.add('chaiAssertValidationError', () => { - assert.deepInclude() -}) - -Cypress.Commands.add('failEventHandlerAssertion', () => { - cy.on('window:load', () => { - expect('actual').to.equal('expected') - }) - - cy.visit('http://localhost:1919') -}) - -Cypress.Commands.add('failEventHandlerException', () => { - cy.on('window:load', () => { - ({}).bar() - }) - - cy.visit('http://localhost:1919') -}) - -Cypress.Commands.add('failFailHandlerAssertion', () => { - cy.on('fail', () => { - expect('actual').to.equal('expected') - }) - - cy.get('#does-not-exist') -}) - -Cypress.Commands.add('failFailHandlerException', () => { - cy.on('fail', () => { - ({}).bar() - }) - - cy.get('#does-not-exist') -}) - -Cypress.Commands.add('failSyncAppException', () => { - cy.visit('/js_errors.html') - cy.get('.sync-error').click() -}) - -Cypress.Commands.add('failAsyncAppException', () => { - cy.visit('/js_errors.html') - cy.get('.async-error').click() - cy.wait(10000) -}) - -Cypress.Commands.add('failAsyncException', () => { - setTimeout(() => { - ({}).bar() - }) - - cy.wait(10000) -}) - -Cypress.Commands.add('failInternalCypressMethod', () => { - top.window.eval(`Cypress.dom.isJquery = () => { throw new Error('thrown in CypressdomisJquery') }`) - - cy.get('body') -}) - -Cypress.Commands.add('failInternalCyMethod', () => { - top.window.eval(`cy.expect = () => { throw new Error('thrown in cyexpect') }`) - - cy.wrap({ foo: 'foo' }).should('have.property', 'foo') -}) diff --git a/packages/server/test/support/fixtures/projects/e2e/cypress/support/index.js b/packages/server/test/support/fixtures/projects/e2e/cypress/support/index.js index 7416345e6d45..27e017398261 100644 --- a/packages/server/test/support/fixtures/projects/e2e/cypress/support/index.js +++ b/packages/server/test/support/fixtures/projects/e2e/cypress/support/index.js @@ -1,5 +1,3 @@ -import './commands' - before(function () { if (Cypress.browser.family === 'chromium' && Cypress.browser.name !== 'electron') { return Cypress.automation('remote:debugger:protocol', { diff --git a/packages/server/test/support/fixtures/projects/e2e/cypress/support/util.js b/packages/server/test/support/fixtures/projects/e2e/cypress/support/util.js index e6da7bfb499a..c067a10e0b42 100644 --- a/packages/server/test/support/fixtures/projects/e2e/cypress/support/util.js +++ b/packages/server/test/support/fixtures/projects/e2e/cypress/support/util.js @@ -1,7 +1,6 @@ const { _ } = Cypress let count = 1 -let shouldVerifyStackLineIsSpecFile = true let openInIdePath = Cypress.spec // ensure title is unique since it's necessary for querying the UI @@ -12,76 +11,23 @@ const getTitle = (ctx) => { return `${parentTitle} ${ctx.title}`.trim() } -export const setup = ({ verifyStackLineIsSpecFile, idePath }) => { - shouldVerifyStackLineIsSpecFile = verifyStackLineIsSpecFile - if (idePath) openInIdePath = idePath -} - -// NOTE: use { defaultCommandTimeout: 0 } once per-test configuration is -// implemented (https://github.com/cypress-io/cypress/pull/5346) export const fail = (ctx, test) => { const title = `${count++}) ✗ FAIL - ${getTitle(ctx)}` - const withDone = test.length > 0 - - if (withDone) { - it(title, (done) => { - cy.timeout(0) // speed up failures to not retry since we know they should fail - - return test(done) - }) - return - } - - it(title, () => { - cy.timeout(0) // speed up failures to not retry since we know they should fail - - return test() - }) + it(title, { defaultCommandTimeout: 0 }, test) } export const verify = (ctx, options) => { const { - hasCodeFrame = true, - verifyOpenInIde = true, - verifyDocsContent, - verifyDocsLearnMore, + line, column, - codeFrameText, message, stack, } = options - let { regex, line } = options - - // if no specific line, just accept any number - line = line || '\\d+' - - // test only the column number because the line number is brittle - // since any changes to this file can affect it - if (!regex) { - regex = shouldVerifyStackLineIsSpecFile ? - new RegExp(`${Cypress.spec.relative}:${line}:${column}`) : - new RegExp(`cypress\/support\/commands\.js:${line}:${column}`) - } - - const testOpenInIde = (runnerWs) => { - if (_.isRegExp(openInIdePath.absolute)) { - expect(runnerWs.emit.withArgs('open:file').lastCall.args[1].file).to.match(openInIdePath.absolute) - } else { - expect(runnerWs.emit).to.be.calledWithMatch('open:file', { - file: openInIdePath.absolute, - }) - } - } - it(`✓ VERIFY`, function () { - const currTest = this.test - const currTestIndex = Cypress._.findIndex(ctx.tests, (test) => { - return test === currTest - }) - // find the previous test in the suite - const prevTest = ctx.tests[currTestIndex - 1] + const fileRegex = new RegExp(`${Cypress.spec.relative}:${line}:${column}`) + it(`✓ VERIFY`, function () { const runnerWs = window.top.runnerWs cy.stub(window.top.runnerWs, 'emit').callThrough().withArgs('get:user:editor') @@ -101,123 +47,52 @@ export const verify = (ctx, options) => { .contains(`FAIL - ${getTitle(ctx)}`) .closest('.runnable-wrapper') .within(() => { + cy.contains('View stack trace').click() + _.each([].concat(message), (msg) => { cy.get('.runnable-err-message') .should('include.text', msg) - // TODO: get this working. it currently fails in a bizarre way - // displayed stack trace should not include message - // cy.get('.runnable-err-stack-trace') - // .invoke('text') - // .should('not.include.text', msg) + cy.get('.runnable-err-stack-trace') + .should('not.include.text', msg) }) - cy.contains('View stack trace').click() - cy.get('.runnable-err-stack-trace') .invoke('text') - .should('match', regex) + .should('match', fileRegex) - if (stack) { - _.each([].concat(stack), (stackLine) => { - cy.get('.runnable-err-stack-trace') - .should('include.text', stackLine) - }) - } + _.each([].concat(stack), (stackLine) => { + cy.get('.runnable-err-stack-trace') + .should('include.text', stackLine) + }) cy.get('.runnable-err-stack-trace') .should('not.include.text', '__stackReplacementMarker') - const docsUrl = _.get(prevTest, 'err.docsUrl') - - if (verifyDocsLearnMore) { - expect(docsUrl).to.eq(verifyDocsLearnMore) - - // make sure Learn More is there - // and the docs url is not embedded - // in the error message - cy - .get('.runnable-err-message') - .should('not.contain', docsUrl) - .contains('Learn more') - .should('have.attr', 'href', docsUrl) - } - - if (verifyDocsContent) { - expect(docsUrl).to.be.undefined - - // verify that the docsUrl is - // embedded in the content, but - // that there's no Learn More link - cy - .get('.runnable-err-message') - .should('contain', verifyDocsContent) - .contains('Learn more') - .should('not.exist') - } - - if (verifyOpenInIde) { - cy.contains('.runnable-err-stack-trace .runnable-err-file-path', openInIdePath.relative) - .click() - .should(() => { - testOpenInIde(runnerWs) + cy.contains('.runnable-err-stack-trace .runnable-err-file-path', openInIdePath.relative) + .click() + .should(() => { + expect(runnerWs.emit).to.be.calledWithMatch('open:file', { + file: openInIdePath.absolute, }) - } - - if (!hasCodeFrame) return + }) cy .get('.test-err-code-frame .runnable-err-file-path') .invoke('text') - .should('match', regex) - - // most code frames will show `fail(this,()=>` as the 1st line but - // for some it's cut off due to the test code being more lines, - // so we prefer the `codeFrameText` - cy.get('.test-err-code-frame pre span').should('include.text', codeFrameText || 'fail(this,()=>') - - if (verifyOpenInIde) { - cy.contains('.test-err-code-frame .runnable-err-file-path', openInIdePath.relative) - .click() - .should(() => { - expect(runnerWs.emit.withArgs('open:file')).to.be.calledTwice - testOpenInIde(runnerWs) - }) - } - }) - }) -} - -export const verifyInternalError = (ctx, { method }) => { - it(`✓ VERIFY`, () => { - cy.wrap(Cypress.$(window.top.document.body)) - .find('.reporter') - .contains(`FAIL - ${getTitle(ctx)}`) - .closest('.runnable-wrapper') - .within(() => { - cy.get('.runnable-err-message') - .should('include.text', `thrown in ${method.replace(/\./g, '')}`) + .should('match', fileRegex) - cy.get('.runnable-err-stack-trace') - .should('include.text', method) + // code frames will show `fail(this,()=>` as the 1st line + cy.get('.test-err-code-frame pre span').should('include.text', 'fail(this,()=>') - cy.get('.test-err-code-frame') - .should('not.exist') + cy.contains('.test-err-code-frame .runnable-err-file-path', openInIdePath.relative) + .click() + .should(() => { + expect(runnerWs.emit.withArgs('open:file')).to.be.calledTwice + expect(runnerWs.emit).to.be.calledWithMatch('open:file', { + file: openInIdePath.absolute, + }) + }) }) }) } - -export const sendXhr = (win) => { - const xhr = new win.XMLHttpRequest() - - xhr.open('GET', '/foo') - xhr.send() - - return xhr -} - -export const abortXhr = (win) => { - const xhr = sendXhr(win) - - xhr.abort() -} diff --git a/packages/server/test/support/fixtures/projects/webpack-preprocessor-awesome-typescript-loader/cypress/integration/failing_spec.ts b/packages/server/test/support/fixtures/projects/webpack-preprocessor-awesome-typescript-loader/cypress/integration/failing_spec.ts index 364ccfed4c66..9f13d3578b48 100644 --- a/packages/server/test/support/fixtures/projects/webpack-preprocessor-awesome-typescript-loader/cypress/integration/failing_spec.ts +++ b/packages/server/test/support/fixtures/projects/webpack-preprocessor-awesome-typescript-loader/cypress/integration/failing_spec.ts @@ -1,10 +1,7 @@ /** - * This tests various failure scenarios where an error and code frame is displayed + * This tests the error UI for a certain webpack preprocessor setup. * It does this by having a test fail and then a subsequent test run that - * tests the appearance of the command log - * Because of this, the test order is important - * There should be the same number of failing tests as there are passing - * tests, because each failure has a verification test (e.g. 11 fail, 11 pass) + * verifies the appearance of the command log. */ // simple example of typescript types @@ -12,9 +9,7 @@ type Foo = { something: string } -import { fail, setup, verify } from '../../../e2e/cypress/support/util' - -setup({ verifyStackLineIsSpecFile: true }) +import { fail, verify } from '../../../e2e/cypress/support/util' context('validation errors', function () { beforeEach(() => { @@ -28,7 +23,7 @@ context('validation errors', function () { }) verify(this, { - line: 27, // this only has 1 test, so we can be specific + line: 22, column: 8, message: 'can only accept a string preset or', stack: ['throwErrBadArgs', 'From Your Spec Code:'], diff --git a/packages/server/test/support/fixtures/projects/webpack-preprocessor-ts-loader-compiler-options/cypress/integration/failing_spec.ts b/packages/server/test/support/fixtures/projects/webpack-preprocessor-ts-loader-compiler-options/cypress/integration/failing_spec.ts index 0ad966876553..f62e9b0a2a4e 100644 --- a/packages/server/test/support/fixtures/projects/webpack-preprocessor-ts-loader-compiler-options/cypress/integration/failing_spec.ts +++ b/packages/server/test/support/fixtures/projects/webpack-preprocessor-ts-loader-compiler-options/cypress/integration/failing_spec.ts @@ -1,12 +1,9 @@ /// /** - * This tests various failure scenarios where an error and code frame is displayed + * This tests the error UI for a certain webpack preprocessor setup. * It does this by having a test fail and then a subsequent test run that - * tests the appearance of the command log - * Because of this, the test order is important - * There should be the same number of failing tests as there are passing - * tests, because each failure has a verification test (e.g. 11 fail, 11 pass) + * verifies the appearance of the command log. */ // simple example of typescript types @@ -14,9 +11,7 @@ type Foo = { something: string } -import { fail, setup, verify } from '../../../e2e/cypress/support/util' - -setup({ verifyStackLineIsSpecFile: true }) +import { fail, verify } from '../../../e2e/cypress/support/util' context('validation errors', function () { beforeEach(() => { @@ -30,7 +25,7 @@ context('validation errors', function () { }) verify(this, { - line: 29, // this only has 1 test, so we can be specific + line: 24, column: 8, message: 'can only accept a string preset or', stack: ['throwErrBadArgs', 'From Your Spec Code:'], diff --git a/packages/server/test/support/fixtures/projects/webpack-preprocessor-ts-loader/cypress/integration/failing_spec.ts b/packages/server/test/support/fixtures/projects/webpack-preprocessor-ts-loader/cypress/integration/failing_spec.ts index 0ad966876553..f62e9b0a2a4e 100644 --- a/packages/server/test/support/fixtures/projects/webpack-preprocessor-ts-loader/cypress/integration/failing_spec.ts +++ b/packages/server/test/support/fixtures/projects/webpack-preprocessor-ts-loader/cypress/integration/failing_spec.ts @@ -1,12 +1,9 @@ /// /** - * This tests various failure scenarios where an error and code frame is displayed + * This tests the error UI for a certain webpack preprocessor setup. * It does this by having a test fail and then a subsequent test run that - * tests the appearance of the command log - * Because of this, the test order is important - * There should be the same number of failing tests as there are passing - * tests, because each failure has a verification test (e.g. 11 fail, 11 pass) + * verifies the appearance of the command log. */ // simple example of typescript types @@ -14,9 +11,7 @@ type Foo = { something: string } -import { fail, setup, verify } from '../../../e2e/cypress/support/util' - -setup({ verifyStackLineIsSpecFile: true }) +import { fail, verify } from '../../../e2e/cypress/support/util' context('validation errors', function () { beforeEach(() => { @@ -30,7 +25,7 @@ context('validation errors', function () { }) verify(this, { - line: 29, // this only has 1 test, so we can be specific + line: 24, column: 8, message: 'can only accept a string preset or', stack: ['throwErrBadArgs', 'From Your Spec Code:'], diff --git a/packages/server/test/support/fixtures/projects/webpack-preprocessor/cypress/integration/failing_spec.js b/packages/server/test/support/fixtures/projects/webpack-preprocessor/cypress/integration/failing_spec.js index e060e602851b..03efddb77617 100644 --- a/packages/server/test/support/fixtures/projects/webpack-preprocessor/cypress/integration/failing_spec.js +++ b/packages/server/test/support/fixtures/projects/webpack-preprocessor/cypress/integration/failing_spec.js @@ -1,15 +1,10 @@ /** - * This tests various failure scenarios where an error and code frame is displayed + * This tests the error UI for a certain webpack preprocessor setup. * It does this by having a test fail and then a subsequent test run that - * tests the appearance of the command log - * Because of this, the test order is important - * There should be the same number of failing tests as there are passing - * tests, because each failure has a verification test (e.g. 11 fail, 11 pass) + * verifies the appearance of the command log. */ -import { fail, setup, verify } from '../../../e2e/cypress/support/util' - -setup({ verifyStackLineIsSpecFile: true }) +import { fail, verify } from '../../../e2e/cypress/support/util' context('validation errors', function () { beforeEach(() => { @@ -21,7 +16,7 @@ context('validation errors', function () { }) verify(this, { - line: 20, // this only has 1 test, so we can be specific + line: 15, column: 8, message: 'can only accept a string preset or', stack: ['throwErrBadArgs', 'From Your Spec Code:'],