From 0760a06340217da08fcdd42b235af8281d628729 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Fri, 20 Mar 2020 13:53:25 -0400
Subject: [PATCH 01/86] isolated runner changes
---
packages/driver/package.json | 1 +
packages/driver/src/cy/aliases.coffee | 14 +-
packages/driver/src/cy/assertions.js | 27 +-
packages/driver/src/cy/commands/agents.coffee | 8 +-
.../driver/src/cy/commands/navigation.coffee | 8 +-
packages/driver/src/cypress/cy.js | 11 +-
.../cypress/fixtures/isolated-runner.html | 20 +
.../cypress/integration/cypress/empty_spec.js | 0
.../integration/cypress/eventSnapshots.js | 54 +
.../integration/cypress/runner.spec.js | 1324 +++++++++++++++++
packages/driver/test/support/server.coffee | 2 +
packages/reporter/cypress.json | 2 +-
packages/server/test/unit/args_spec.coffee | 2 +-
.../server/test/unit/exceptions_spec.coffee | 2 +-
.../server/test/unit/path_helpers_spec.coffee | 6 +-
15 files changed, 1444 insertions(+), 37 deletions(-)
create mode 100644 packages/driver/test/cypress/fixtures/isolated-runner.html
create mode 100644 packages/driver/test/cypress/integration/cypress/empty_spec.js
create mode 100644 packages/driver/test/cypress/integration/cypress/eventSnapshots.js
create mode 100644 packages/driver/test/cypress/integration/cypress/runner.spec.js
diff --git a/packages/driver/package.json b/packages/driver/package.json
index dd2d0bcf23f1..a39270c6970a 100644
--- a/packages/driver/package.json
+++ b/packages/driver/package.json
@@ -56,6 +56,7 @@
"parse-domain": "bahmutov/parse-domain#fb60bd4",
"setimmediate": "1.0.5",
"sinon": "8.1.1",
+ "snap-shot-core": "^7.4.0",
"text-mask-addons": "3.8.0",
"underscore": "1.9.1",
"underscore.string": "3.3.5",
diff --git a/packages/driver/src/cy/aliases.coffee b/packages/driver/src/cy/aliases.coffee
index beb45779961b..abece6fdd0ac 100644
--- a/packages/driver/src/cy/aliases.coffee
+++ b/packages/driver/src/cy/aliases.coffee
@@ -32,13 +32,13 @@ validateAlias = (alias) ->
if alias in blacklist
$errUtils.throwErrByPath "as.reserved_word", { args: { alias } }
-create = (state) ->
+create = (cy) ->
addAlias = (ctx, aliasObj) ->
{ alias, subject } = aliasObj
- aliases = state("aliases") ? {}
+ aliases = cy.state("aliases") ? {}
aliases[alias] = aliasObj
- state("aliases", aliases)
+ cy.state("aliases", aliases)
remoteSubject = cy.getRemotejQueryInstance(subject)
@@ -46,13 +46,13 @@ create = (state) ->
ctx[alias] = remoteSubject ? subject
getNextAlias = ->
- next = state("current").get("next")
+ next = cy.state("current").get("next")
if next and next.get("name") is "as"
next.get("args")[0]
getAlias = (name, cmd, log) ->
- aliases = state("aliases") ? {}
+ aliases = cy.state("aliases") ? {}
## bail if the name doesnt reference an alias
return if not aliasRe.test(name)
@@ -64,7 +64,7 @@ create = (state) ->
return alias
getAvailableAliases = ->
- return [] if not aliases = state("aliases")
+ return [] if not aliases = cy.state("aliases")
_.keys(aliases)
@@ -80,7 +80,7 @@ create = (state) ->
args: { name, displayName }
}
- cmd ?= log and log.get("name") or state("current").get("name")
+ cmd ?= log and log.get("name") or cy.state("current").get("name")
displayName = aliasDisplayName(name)
errPath = if availableAliases.length
diff --git a/packages/driver/src/cy/assertions.js b/packages/driver/src/cy/assertions.js
index d18dacbb8257..f422103b327d 100644
--- a/packages/driver/src/cy/assertions.js
+++ b/packages/driver/src/cy/assertions.js
@@ -1,4 +1,3 @@
-/* global cy Cypress */
const _ = require('lodash')
const Promise = require('bluebird')
@@ -70,14 +69,14 @@ const parseValueActualAndExpected = (value, actual, expected) => {
return obj
}
-const create = function (state, queue, retryFn) {
+const create = function (Cypress, cy) {
const getUpcomingAssertions = () => {
- const index = state('index') + 1
+ const index = cy.state('index') + 1
const assertions = []
// grab the rest of the queue'd commands
- for (let cmd of queue.slice(index).get()) {
+ for (let cmd of cy.queue.slice(index).get()) {
// don't break on utilities, just skip over them
if (cmd.is('utility')) {
continue
@@ -112,7 +111,7 @@ const create = function (state, queue, retryFn) {
cmd.set('assertionIndex', 0)
return cmd.get('fn').originalFn.apply(
- state('ctx'),
+ cy.state('ctx'),
[subject].concat(cmd.get('args')),
)
})
@@ -135,7 +134,7 @@ const create = function (state, queue, retryFn) {
const verifyUpcomingAssertions = function (subject, options = {}, callbacks = {}) {
const cmds = getUpcomingAssertions()
- state('upcomingAssertions', cmds)
+ cy.state('upcomingAssertions', cmds)
// we're applying the default assertion in the
// case where there are no upcoming assertion commands
@@ -228,7 +227,7 @@ const create = function (state, queue, retryFn) {
}
if (_.isFunction(onRetry)) {
- return retryFn(onRetry, options)
+ return cy.retry(onRetry, options)
}
}
@@ -258,7 +257,7 @@ const create = function (state, queue, retryFn) {
}
// when we do immediately unbind this function
- state('onBeforeLog', null)
+ cy.state('onBeforeLog', null)
const insertNewLog = (log) => {
cmd.log(log)
@@ -341,7 +340,7 @@ const create = function (state, queue, retryFn) {
return insertNewLog(log)
}
- state('onBeforeLog', setCommandLog)
+ cy.state('onBeforeLog', setCommandLog)
// send verify=true as the last arg
return assertFn.apply(this, args.concat(true))
@@ -381,16 +380,16 @@ const create = function (state, queue, retryFn) {
}
const restore = () => {
- state('upcomingAssertions', [])
+ cy.state('upcomingAssertions', [])
// no matter what we need to
// restore the assert fn
- return state('overrideAssert', undefined)
+ return cy.state('overrideAssert', undefined)
}
// store this in case our test ends early
// and we reset between tests
- state('overrideAssert', overrideAssert)
+ cy.state('overrideAssert', overrideAssert)
return Promise
.reduce(fns, assertions, [subject])
@@ -459,7 +458,7 @@ const create = function (state, queue, retryFn) {
// if our current command is an assertion type
isAssertionType(current) ||
// are we currently verifying assertions?
- (state('upcomingAssertions') && state('upcomingAssertions').length > 0) ||
+ (cy.state('upcomingAssertions') && cy.state('upcomingAssertions').length > 0) ||
// did the function have arguments
functionHadArguments(current)
}
@@ -503,7 +502,7 @@ const create = function (state, queue, retryFn) {
const assert = function (...args) {
// if we've temporarily overriden assertions
// then just bail early with this function
- const fn = state('overrideAssert') || assertFn
+ const fn = cy.state('overrideAssert') || assertFn
return fn.apply(this, args)
}
diff --git a/packages/driver/src/cy/commands/agents.coffee b/packages/driver/src/cy/commands/agents.coffee
index d6d93b30360a..f77bb1e2a60c 100644
--- a/packages/driver/src/cy/commands/agents.coffee
+++ b/packages/driver/src/cy/commands/agents.coffee
@@ -50,7 +50,7 @@ onInvoke = (Cypress, obj, args) ->
error: obj.error
type: "parent"
end: true
- snapshot: true
+ snapshot: !agent._noSnapshot
event: true
consoleProps: ->
consoleObj = {}
@@ -187,6 +187,12 @@ module.exports = (Commands, Cypress, cy, state, config) ->
return agent
+ ## disable DOM snapshots during log for this agent
+ agent.snapshot = (bool = true) ->
+ agent._noSnapshot = !bool
+
+ return agent
+
agent.as = (alias) ->
cy.validateAlias(alias)
cy.addAlias(ctx, {
diff --git a/packages/driver/src/cy/commands/navigation.coffee b/packages/driver/src/cy/commands/navigation.coffee
index fbe2d8f35ebd..2921390b98f6 100644
--- a/packages/driver/src/cy/commands/navigation.coffee
+++ b/packages/driver/src/cy/commands/navigation.coffee
@@ -98,7 +98,7 @@ specifyFileByRelativePath = (url, log) ->
}
})
-aboutBlank = (win) ->
+aboutBlank = (cy, win) ->
new Promise (resolve) ->
cy.once("window:load", resolve)
@@ -173,7 +173,7 @@ formSubmitted = (Cypress, e) ->
}
})
-pageLoading = (bool, state) ->
+pageLoading = (bool, Cypress, state) ->
return if state("pageLoading") is bool
state("pageLoading", bool)
@@ -187,7 +187,7 @@ stabilityChanged = (Cypress, state, config, stable, event) ->
## if we're currently visiting about blank
## and becoming unstable for the first time
## notifiy that we're page loading
- pageLoading(true, state)
+ pageLoading(true, Cypress, state)
return
else
## else wait until after we finish visiting
@@ -195,7 +195,7 @@ stabilityChanged = (Cypress, state, config, stable, event) ->
return
## let the world know that the app is page:loading
- pageLoading(!stable, state)
+ pageLoading(!stable, Cypress, state)
## if we aren't becoming unstable
## then just return now
diff --git a/packages/driver/src/cypress/cy.js b/packages/driver/src/cypress/cy.js
index 453f62b9fb3b..7f43d595fb70 100644
--- a/packages/driver/src/cypress/cy.js
+++ b/packages/driver/src/cypress/cy.js
@@ -5,6 +5,7 @@ const Promise = require('bluebird')
const $dom = require('../dom')
const $utils = require('./utils')
+const $selection = require('../dom/selection')
const $errUtils = require('./error_utils')
const $Chai = require('../cy/chai')
const $Xhrs = require('../cy/xhrs')
@@ -24,7 +25,6 @@ const $Timers = require('../cy/timers')
const $Timeouts = require('../cy/timeouts')
const $Retries = require('../cy/retries')
const $Stability = require('../cy/stability')
-const $selection = require('../dom/selection')
const $Snapshots = require('../cy/snapshots')
const $CommandQueue = require('./command_queue')
const $VideoRecorder = require('../cy/video-recorder')
@@ -91,6 +91,7 @@ const setTopOnError = function (cy) {
}
const create = function (specWindow, Cypress, Cookies, state, config, log) {
+ let cy = {}
let stopped = false
const commandFns = {}
@@ -124,7 +125,7 @@ const create = function (specWindow, Cypress, Cookies, state, config, log) {
const timeouts = $Timeouts.create(state)
const stability = $Stability.create(Cypress, state)
const retries = $Retries.create(Cypress, state, timeouts.timeout, timeouts.clearTimeout, stability.whenStable, onFinishAssertions)
- const assertions = $Assertions.create(state, queue, retries.retry)
+ const assertions = $Assertions.create(Cypress, cy)
const jquery = $jQuery.create(state)
const location = $Location.create(state)
@@ -136,7 +137,7 @@ const create = function (specWindow, Cypress, Cookies, state, config, log) {
const { expect } = $Chai.create(specWindow, assertions.assert)
const xhrs = $Xhrs.create(state)
- const aliases = $Aliases.create(state)
+ const aliases = $Aliases.create(cy)
const errors = $Errors.create(state, config, log)
const ensures = $Ensures.create(state, expect)
@@ -734,7 +735,7 @@ const create = function (specWindow, Cypress, Cookies, state, config, log) {
return finish(err)
}
- const cy = {
+ _.extend(cy, {
id: _.uniqueId('cy'),
// synchrounous querying
@@ -1328,7 +1329,7 @@ const create = function (specWindow, Cypress, Cookies, state, config, log) {
}
}
},
- }
+ })
_.each(privateProps, (obj, key) => {
return Object.defineProperty(cy, key, {
diff --git a/packages/driver/test/cypress/fixtures/isolated-runner.html b/packages/driver/test/cypress/fixtures/isolated-runner.html
new file mode 100644
index 000000000000..2e4667eed586
--- /dev/null
+++ b/packages/driver/test/cypress/fixtures/isolated-runner.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/driver/test/cypress/integration/cypress/empty_spec.js b/packages/driver/test/cypress/integration/cypress/empty_spec.js
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/driver/test/cypress/integration/cypress/eventSnapshots.js b/packages/driver/test/cypress/integration/cypress/eventSnapshots.js
new file mode 100644
index 000000000000..fa89d2b4c8d1
--- /dev/null
+++ b/packages/driver/test/cypress/integration/cypress/eventSnapshots.js
@@ -0,0 +1,54 @@
+/**
+ * @param {T=} obj
+ * @return {{[key in keyof T]: {mocha:string, setRunnables:string}}}
+ * @template T,K
+ */
+function Enum (obj) {
+ const keysByValue = {}
+ // const EnumLookup = (value) => keysByValue.get(value)
+
+ for (const key of Object.keys(obj)) {
+ keysByValue[key] = {
+ mocha: `${key}.mocha`,
+ setRunnables: `${key}.setRunnables`,
+ }
+ }
+
+ // Return a function with all your enum properties attached.
+ // Calling the function with the value will return the key.
+ return Object.freeze(keysByValue)
+}
+
+const snapshots = Enum({
+ FAIL_IN_AFTEREACH: true,
+ FAIL_IN_AFTER: true,
+ FAIL_IN_BEFOREEACH: true,
+ FAIL_IN_BEFORE: true,
+ FAIL_IN_TEST: true,
+ PASS_IN_AFTEREACH: true,
+ PASS_IN_AFTER: true,
+ PASS_IN_BEFOREEACH: true,
+ PASS_IN_BEFORE: true,
+ PASS_IN_TEST: true,
+ RETRY_PASS_IN_AFTEREACH: true,
+ // RETRY_PASS_IN_AFTER: true,
+ RETRY_PASS_IN_BEFOREEACH: true,
+ // RETRY_PASS_IN_BEFORE: true,
+ RETRY_PASS_IN_TEST: true,
+ RETRY_FAIL_IN_AFTEREACH: true,
+ // RETRY_FAIL_IN_AFTER: true,
+ // RETRY_FAIL_IN_BEFOREEACH: true,
+ // RETRY_FAIL_IN_BEFORE: true,
+ // RETRY_FAIL_IN_TEST: true,
+ PASS_WITH_ONLY: true,
+ FAIL_WITH_ONLY: true,
+ RETRY_PASS_WITH_ONLY: true,
+ SIMPLE_SINGLE_TEST: true,
+ THREE_TESTS_WITH_RETRY: true,
+ THREE_TESTS_WITH_HOOKS: true,
+
+})
+
+module.exports = {
+ EventSnapshots: snapshots,
+}
diff --git a/packages/driver/test/cypress/integration/cypress/runner.spec.js b/packages/driver/test/cypress/integration/cypress/runner.spec.js
new file mode 100644
index 000000000000..fbc1333a1d45
--- /dev/null
+++ b/packages/driver/test/cypress/integration/cypress/runner.spec.js
@@ -0,0 +1,1324 @@
+/* eslint prefer-rest-params: "off", no-console: "off", arrow-body-style: "off"*/
+
+const { _ } = Cypress
+const helpers = require('../../support/helpers')
+
+const { registerInCypress, stringifyShort } = require('../../plugins/snapshot/command')
+
+const snapshots = require('./eventSnapshots').EventSnapshots
+
+const sinon = require('sinon')
+
+registerInCypress()
+/**
+ * @type {sinon.SinonMatch}
+ */
+const match = Cypress.sinon.match
+
+// const { defer } = helpers
+
+const backupCy = window.cy
+const backupCypress = window.Cypress
+
+backupCy.__original__ = true
+
+/**
+ * @type {sinon.SinonStub}
+ */
+let allStubs
+/**
+ * @type {sinon.SinonStub}
+ */
+let mochaStubs
+/**
+ * @type {sinon.SinonStub}
+ */
+let setRunnablesStub
+
+const snapshotEvents = (name) => {
+ expect(setRunnablesStub.args).to.matchSnapshot(setRunnablesCleanseMap, name.setRunnables)
+ expect(mochaStubs.args).to.matchSnapshot(mochaEventCleanseMap, name.mocha)
+}
+
+const simpleSingleTest = {
+ suites: { 'suite 1': { tests: [{ name: 'test 1' }] } },
+}
+
+const threeTestsWithHooks = {
+ suites: { 'suite 1': { hooks: ['before', 'beforeEach', 'afterEach', 'after'], tests: ['test 1', 'test 2', 'test 3'] } },
+}
+
+const threeTestsWithRetry = {
+ suites: {
+ 'suite 1': {
+ hooks: ['before', 'beforeEach', 'afterEach', 'after'],
+ tests: [
+ 'test 1',
+ { name: 'test 2', fail: 2 },
+ 'test 3',
+ ],
+ },
+ },
+}
+
+const enableStubSnapshots = false
+// const enableStubSnapshots = true
+
+const eventCleanseMap = {
+ snapshots: stringifyShort,
+ parent: stringifyShort,
+ tests: stringifyShort,
+ commands: stringifyShort,
+ err: stringifyShort,
+ body: '[body]',
+ wallClockStartedAt: match.date,
+ lifecycle: match.number,
+ fnDuration: match.number,
+ duration: match.number,
+ afterFnDuration: match.number,
+ wallClockDuration: match.number,
+ stack: match.string,
+ message: '[error message]',
+}
+
+const mochaEventCleanseMap = {
+ ...eventCleanseMap,
+ start: match.date,
+ end: match.date,
+}
+
+const setRunnablesCleanseMap = { ...eventCleanseMap, tests: _.identity }
+
+let autCypress
+
+let onBeforeRun
+
+const createCypress = (mochaTests, opts = {}) => {
+ _.defaults(opts, {
+ state: {},
+ config: {},
+ })
+
+ return cy.visit('/fixtures/isolated-runner.html#/tests/integration/cypress/empty_spec.js')
+ .then({ timeout: 60000 }, (win) => {
+ win.channel.destroy()
+
+ allStubs = cy.stub().snapshot(enableStubSnapshots)
+ mochaStubs = cy.stub().snapshot(enableStubSnapshots)
+ setRunnablesStub = cy.stub().snapshot(enableStubSnapshots)
+
+ return new Promise((resolve) => {
+ const runCypress = () => {
+ autCypress.run.restore()
+
+ const emit = autCypress.emit
+ const emitMap = autCypress.emitMap
+ const emitThen = autCypress.emitThen
+
+ cy.stub(autCypress, 'automation').snapshot(enableStubSnapshots)
+ .callThrough()
+ .withArgs('take:screenshot')
+ .resolves({
+ testAttemptIndex: 0,
+ path: '/path/to/screenshot',
+ size: 12,
+ dimensions: { width: 20, height: 20 },
+ multipart: false,
+ pixelRatio: 1,
+ takenAt: new Date().toISOString(),
+ name: 'name',
+ blackout: ['.foo'],
+ duration: 100,
+ })
+
+ cy.stub(autCypress, 'emit').snapshot(enableStubSnapshots).log(false)
+ .callsFake(function () {
+ const noLog = _.includes([
+ 'navigation:changed',
+ 'stability:changed',
+ 'window:load',
+ 'url:changed',
+ 'log:added',
+ 'page:loading',
+ 'window:unload',
+ 'newListener',
+ ], arguments[0])
+ const noCall = _.includes(['window:before:unload', 'mocha'], arguments[0])
+ const isMocha = _.includes(['mocha'], arguments[0])
+
+ isMocha && mochaStubs.apply(this, arguments)
+
+ noLog || allStubs.apply(this, ['emit'].concat([].slice.call(arguments)))
+
+ return noCall || emit.apply(this, arguments)
+ })
+
+ cy.stub(autCypress, 'emitMap').snapshot(enableStubSnapshots).log(false)
+ .callsFake(function () {
+ allStubs.apply(this, ['emitMap'].concat([].slice.call(arguments)))
+
+ return emitMap.apply(this, arguments)
+ })
+
+ cy.stub(autCypress, 'emitThen').snapshot(enableStubSnapshots).log(false)
+ .callsFake(function () {
+ allStubs.apply(this, ['emitThen'].concat([].slice.call(arguments)))
+
+ return emitThen.apply(this, arguments)
+ })
+
+ spyOn(autCypress.mocha.getRunner(), 'fail', (...args) => {
+ Cypress.log({
+ name: 'Runner Fail',
+ message: `${args[1]}`,
+ state: 'failed',
+ consoleProps: () => {
+ return {
+ Error: args[1],
+ }
+ },
+ })
+ })
+
+ cy.spy(cy.state('window').console, 'log').as('console_log')
+ cy.spy(cy.state('window').console, 'error').as('console_error')
+
+ onBeforeRun && onBeforeRun()
+ autCypress.run(resolve)
+ }
+
+ cy.spy(win.reporterBus, 'emit').snapshot(enableStubSnapshots).as('reporterBus')
+ cy.spy(win.localBus, 'emit').snapshot(enableStubSnapshots).as('localBus')
+
+ cy.stub(win.channel, 'emit').snapshot(enableStubSnapshots)
+ .withArgs('watch:test:file')
+ .callsFake(() => {
+ autCypress = win.Cypress
+
+ cy.stub(autCypress, 'onSpecWindow').snapshot(enableStubSnapshots).callsFake((specWindow) => {
+ autCypress.onSpecWindow.restore()
+
+ autCypress.onSpecWindow(specWindow)
+
+ helpers.generateMochaTestsForWin(specWindow, mochaTests)
+
+ specWindow.before = () => {}
+ specWindow.beforeEach = () => {}
+ specWindow.afterEach = () => {}
+ specWindow.after = () => {}
+ specWindow.describe = () => {}
+ })
+
+ cy.stub(autCypress, 'run').snapshot(enableStubSnapshots).callsFake(runCypress)
+ })
+ .withArgs('is:automation:client:connected')
+ .yieldsAsync(true)
+
+ .withArgs('get:existing:run:state')
+ .callsFake((evt, cb) => {
+ cb(opts.state)
+ })
+
+ .withArgs('backend:request', 'resolve:url')
+ .yieldsAsync({ response: {
+ isOkStatusCode: true,
+ isHtml: true,
+ url: 'http://localhost:3500/fixtures/generic.html',
+ } })
+
+ .withArgs('set:runnables')
+ .callsFake((...args) => {
+ setRunnablesStub(...args)
+ _.last(args)()
+ })
+
+ // .withArgs('preserve:run:state')
+ // .callsFake()
+
+ .withArgs('automation:request')
+ .yieldsAsync({ response: {} })
+
+ const c = _.extend({}, Cypress.config(), { isTextTerminal: true }, opts.config)
+
+ c.state = {}
+ // c.state = opts.state
+
+ cy.stub(win.channel, 'on').snapshot(enableStubSnapshots)
+
+ win.Runner.start(win.document.getElementById('app'), c)
+ })
+ })
+}
+
+describe('src/cypress/runner', () => {
+ describe('isolated test runner', () => {
+ beforeEach(async () => {
+ window.cy = backupCy
+ window.Cypress = backupCypress
+ })
+
+ describe('test events', function () {
+ it('simple 1 test', () => {
+ createCypress(simpleSingleTest)
+ .then(shouldHaveTestResults(1, 0))
+ })
+
+ it('simple 3 tests', function () {
+ createCypress({
+ suites: {
+ 'suite 1': { tests: ['test 1', 'test 2', 'test 3'] },
+ },
+ })
+ .then(shouldHaveTestResults(3, 0))
+ })
+
+ it('simple fail', function () {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ tests: [
+ {
+ name: 'test 1',
+ fail: true,
+ },
+ ],
+ },
+ },
+ })
+ .then(shouldHaveTestResults(0, 1))
+ .then(() => {
+ // render exactly one error
+ cy.get('pre:contains(AssertionError)').should('have.length', 1)
+ .should('have.class', 'test-error')
+ })
+ })
+
+ it('pass fail pass fail', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ tests: [
+ 'test 1',
+ {
+ name: 'test 2',
+ fail: true,
+ },
+ ],
+ },
+ 'suite 2': {
+ tests: [
+ 'test 1',
+ {
+ name: 'test 2',
+ fail: true,
+ },
+ ],
+ },
+ },
+ })
+ .then(shouldHaveTestResults(2, 2))
+ })
+
+ it('fail pass', function () {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ tests: [
+ {
+ name: 'test 1',
+ fail: true,
+ },
+ { name: 'test 2' },
+ ],
+ },
+ },
+ })
+ .then(shouldHaveTestResults(1, 1))
+ })
+
+ it('no tests', function () {
+ createCypress({})
+ .then(shouldHaveTestResults(0, 0))
+
+ cy.contains('No tests found in your file').should('be.visible')
+ cy.get('.error-message p').invoke('text').should('eq', 'We could not detect any tests in the above file. Write some tests and re-run.')
+ })
+
+ it('ends test before nested suite', function () {
+ createCypress({
+ suites: {
+ 'suite 1': { tests: ['test 1', 'test 2'],
+ suites: {
+ 'suite 1-1': {
+ tests: ['test 1'],
+ },
+ } },
+ },
+ }, { config: { numTestRetries: 1 } })
+ .then(shouldHaveTestResults(3, 0))
+ })
+
+ it('simple fail, catch cy.on(fail)', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ tests: [
+ {
+ name: 'test 1',
+ fn: () => {
+ console.log('test ran')
+ cy.on('fail', () => {
+ console.log('on:fail')
+
+ return false
+ })
+
+ console.log('added handler')
+ expect(false).ok
+ throw new Error('error in test')
+ },
+ eval: true,
+ },
+ ],
+ },
+ },
+ })
+ .then(shouldHaveTestResults(1, 0))
+ })
+
+ describe('hook failures', () => {
+ it('fail in [before]', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: [
+ {
+ type: 'before',
+ fail: true,
+ },
+ ],
+ tests: [{ name: 'test 1' }],
+ },
+ },
+ })
+ .then(shouldHaveTestResults(0, 1))
+ .then(() => {
+ cy.get('.test-error:visible').invoke('text').then((text) => expect(text).to.matchSnapshot())
+ })
+ .then(() => {
+ snapshotEvents(snapshots.FAIL_IN_BEFORE)
+ })
+ })
+
+ it('fail in [beforeEach]', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: [
+ {
+ type: 'beforeEach',
+ fail: true,
+ },
+ ],
+ tests: [{ name: 'test 1' }],
+ },
+ },
+ })
+ .then(shouldHaveTestResults(0, 1))
+ .then(() => {
+ snapshotEvents(snapshots.FAIL_IN_BEFOREEACH)
+ })
+ })
+
+ it('fail in [afterEach]', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: [
+ {
+ type: 'afterEach',
+ fail: true,
+ },
+ ],
+ tests: [{ name: 'test 1' }],
+ },
+ },
+ })
+ .then(shouldHaveTestResults(0, 1))
+ .then(() => {
+ snapshotEvents(snapshots.FAIL_IN_AFTEREACH)
+ })
+ })
+
+ it('fail in [after]', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: [
+ {
+ type: 'after',
+ fail: true,
+ },
+ ],
+ tests: ['test 1', 'test 2'],
+ },
+ },
+ })
+ .then(shouldHaveTestResults(1, 1))
+ .then(() => {
+ cy.get('.test-error:visible').invoke('text').then((text) => expect(text).to.matchSnapshot())
+ })
+ .then(() => {
+ snapshotEvents(snapshots.FAIL_IN_AFTER)
+ })
+ })
+ })
+
+ describe('test failures w/ hooks', () => {
+ it('fail with [before]', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: ['before'],
+ tests: [
+ {
+ name: 'test 1',
+ fail: true,
+ },
+ { name: 'test 2' },
+ ],
+ },
+ },
+ })
+ .then(shouldHaveTestResults(1, 1))
+ })
+
+ it('fail with [after]', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: [{ type: 'after' }],
+ tests: [{ name: 'test 1', fail: true }, 'test 2'],
+ },
+ },
+ })
+ .then(shouldHaveTestResults(1, 1))
+ })
+
+ it('fail with all hooks', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: ['before', 'beforeEach', 'afterEach', 'after'],
+ tests: [{ name: 'test 1', fail: true }],
+ },
+ },
+ })
+ .then(shouldHaveTestResults(0, 1))
+ })
+ })
+
+ describe('mocha grep', () => {
+ it('fail with [only]', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: ['before', 'beforeEach', 'afterEach', 'after'],
+ tests: [
+ { name: 'test 1', fail: true },
+ { name: 'test 2', fail: true, only: true },
+ { name: 'test 3', fail: true },
+ ],
+ },
+ },
+ })
+ .then(shouldHaveTestResults(0, 1))
+ .then(() => {
+ snapshotEvents(snapshots.FAIL_WITH_ONLY)
+ })
+ })
+
+ it('pass with [only]', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: ['before', 'beforeEach', 'afterEach', 'after'],
+ tests: [
+ { name: 'test 1' },
+ { name: 'test 2', only: true },
+ { name: 'test 3' },
+ ],
+ },
+ },
+ })
+ .then(shouldHaveTestResults(1, 0))
+ .then(() => {
+ snapshotEvents(snapshots.PASS_WITH_ONLY)
+ })
+ })
+ })
+
+ describe('retries', () => {
+ it('can set retry config', () => {
+ createCypress({}, { config: { numTestRetries: 1 } })
+ .then(() => {
+ expect(autCypress.config()).to.has.property('numTestRetries', 1)
+ })
+ })
+
+ describe('retry ui', () => {
+ beforeEach(() => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ tests: [
+ { name: 'test 1', fail: 1 },
+ { name: 'test 2', fail: 2 },
+ { name: 'test 3', fail: 1 },
+ ],
+ },
+ },
+ }, { config: { numTestRetries: 1, isTextTerminal: false, enableTestRetriesInOpenMode: true } })
+ .then(shouldHaveTestResults(2, 1))
+ })
+
+ it('empty', () => {})
+
+ it('can toggle failed attempt', () => {
+ cy.contains('.runnable-wrapper', 'test 3').click().within(() => {
+ cy.contains('AssertionError').should('not.be.visible')
+ cy.contains('Attempt 1').click()
+ cy.contains('AssertionError').should('be.visible')
+ cy.contains('Attempt 1').click().find('i:last').pseudo(':before').should('have.property', 'content', '""')
+ cy.contains('AssertionError').should('not.be.visible')
+ })
+ })
+
+ it('can view error for failed attempt', () => {
+ cy.contains('Attempt 1')
+ .click()
+ .closest('.attempt-item')
+ .contains('AssertionError')
+ .click()
+
+ cy.get('@console_log').should('be.calledWithMatch', 'Command')
+ })
+ })
+
+ it('simple retry', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ tests: [
+ { name: 'test 1',
+ fail: 1,
+ },
+ ],
+ },
+ },
+ }, { config: { numTestRetries: 1 } })
+ .then(shouldHaveTestResults(1, 0))
+ })
+
+ it('test retry with hooks', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: ['before', 'beforeEach', 'afterEach', 'after'],
+ tests: [{ name: 'test 1', fail: 1 }],
+ },
+ },
+ }, { config: { numTestRetries: 1 } })
+ .then(shouldHaveTestResults(1, 0))
+ .then(() => {
+ cy.contains('test')
+ cy.contains('after all')
+ })
+ })
+
+ it('test retry with [only]', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: ['before', 'beforeEach', 'afterEach', 'after'],
+ tests: [
+ { name: 'test 1' },
+ { name: 'test 2', fail: 1, only: true },
+ { name: 'test 3' },
+ ],
+ },
+ },
+ }, { config: { numTestRetries: 1 } })
+ .then(shouldHaveTestResults(1, 0))
+ })
+
+ it('test retry with many hooks', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: [
+ 'before',
+ 'beforeEach',
+ 'afterEach',
+ 'after',
+ ],
+ tests: [
+ { name: 'test 1' },
+ { name: 'test 2', fail: 1 },
+ { name: 'test 3' },
+ ],
+ },
+ },
+ }, { config: { numTestRetries: 1 } })
+ .then(shouldHaveTestResults(3, 0))
+ })
+
+ it('can retry from [beforeEach]', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: [
+ 'before',
+ 'beforeEach',
+ { type: 'beforeEach', fail: 1 },
+ 'beforeEach',
+ 'afterEach',
+ 'after',
+ ],
+ tests: [{ name: 'test 1' }],
+ },
+ },
+ }, { config: { numTestRetries: 1 } })
+ .then(shouldHaveTestResults(1, 0))
+ .then(() => {
+ cy.contains('Attempt 1').click()
+ cy.contains('AssertionError').click()
+ cy.get('@reporterBus').its('lastCall.args').should('contain', 'runner:console:log')
+ })
+ .then(() => {
+ snapshotEvents(snapshots.RETRY_PASS_IN_BEFOREEACH)
+ })
+ })
+
+ it('can retry from [afterEach]', () => {
+ createCypress({
+ hooks: [{ type: 'afterEach', fail: 1 }],
+ suites: {
+ 'suite 1': {
+ hooks: [
+ 'before',
+ 'beforeEach',
+ 'beforeEach',
+ 'afterEach',
+ 'after',
+ ],
+ tests: [{ name: 'test 1' }, 'test 2', 'test 3'],
+ },
+ 'suite 2': {
+ hooks: [{ type: 'afterEach', fail: 2 }],
+ tests: ['test 1'],
+ },
+ 'suite 3': {
+ tests: ['test 1'],
+ },
+ },
+ }, { config: { numTestRetries: 2 } })
+ .then(shouldHaveTestResults(5, 0))
+ .then(() => {
+ cy.contains('test 1')
+ cy.contains('Attempt 1').click()
+ cy.contains('AssertionError').click()
+ cy.get('@reporterBus').its('lastCall.args').should('contain', 'runner:console:log')
+ })
+ .then(() => {
+ snapshotEvents(snapshots.RETRY_PASS_IN_AFTEREACH)
+ })
+ })
+
+ it('cant retry from [before]', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: [
+ { type: 'before', fail: 1 },
+ 'beforeEach',
+ 'beforeEach',
+ 'afterEach',
+ 'afterEach',
+ 'after',
+ ],
+ tests: [{ name: 'test 1' }],
+ },
+ },
+ }, { config: { numTestRetries: 1, isTextTerminal: false, enableTestRetriesInOpenMode: true } })
+ .then(shouldHaveTestResults(0, 1))
+ .then(() => {
+ // cy.contains('Attempt 1').click()
+ cy.contains('Although you have test retries')
+ cy.contains('AssertionError').click()
+ cy.get('@console_log').its('lastCall').should('be.calledWithMatch', 'Error')
+ })
+ })
+
+ it('cant retry from [after]', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: [
+ 'before',
+ 'beforeEach',
+ 'beforeEach',
+ 'afterEach',
+ 'afterEach',
+ { type: 'after', fail: 1 },
+ ],
+ tests: [{ name: 'test 1' }],
+ },
+ },
+ }, { config: { numTestRetries: 1, isTextTerminal: false, enableTestRetriesInOpenMode: true } })
+ .then(shouldHaveTestResults(0, 1))
+ .then(() => {
+ cy.contains('Although you have test retries')
+ cy.contains('AssertionError').click()
+ cy.get('@console_log').its('lastCall').should('be.calledWithMatch', 'Error')
+ })
+ })
+
+ describe('can configure retries', () => {
+ const getAttemptTag = (sel) => {
+ return cy.get(`.runnable-wrapper:contains${sel} .attempt-tag`)
+ }
+
+ it('via config value', () => {
+ createCypress({
+ suites: {
+ 'suite 1': () => {
+ Cypress.config('numTestRetries', 0)
+ it('no retry', () => assert(false))
+ Cypress.config('numTestRetries', 1)
+ it('1 retry', () => assert(false))
+ Cypress.config('numTestRetries', 2)
+ it('2 retries', () => assert(false))
+ Cypress.config('isTextTerminal', false)
+ it('open mode, no retry', () => assert(false))
+ Cypress.config('enableTestRetriesInOpenMode', true)
+ it('open mode, 2 retries', () => assert(false))
+ },
+ },
+ })
+ .then(shouldHaveTestResults(0, 5))
+ .then(() => {
+ getAttemptTag('(no retry):first').should('not.be.visible')
+ getAttemptTag('(1 retry)').should('have.length', 2)
+ getAttemptTag('(2 retries):first').should('have.length', 3)
+ getAttemptTag('(open mode, no retry)').should('not.be.visible')
+ getAttemptTag('(open mode, 2 retries)').should('have.length', 3)
+ })
+ })
+
+ it('throws when set via this.retries in test', () => {
+ createCypress({
+ suites: {
+ 'suite 1' () {
+ it('test 1', function () {
+ this.retries(0)
+ })
+ },
+ },
+ })
+ .then(shouldHaveTestResults(0, 1))
+ .then(() => {
+ cy.get('.test-error').should('contain', 'numTestRetries')
+ })
+ })
+
+ it('throws when set via this.retries in hook', () => {
+ createCypress({
+ suites: {
+ 'suite 1' () {
+ beforeEach(function () {
+ this.retries(0)
+ })
+
+ it('foo', () => {})
+ },
+ },
+ })
+ .then(shouldHaveTestResults(0, 1))
+ .then(() => {
+ cy.get('.test-error').should('contain', 'numTestRetries')
+ })
+ })
+
+ it('throws when set via this.retries in suite', () => {
+ createCypress({
+ suites: {
+ 'suite 1' () {
+ this.retries(0)
+ it('test 1', function () {
+ })
+ },
+ },
+ })
+ .then(shouldHaveTestResults(0, 1))
+ .then(() => {
+ cy.get('.test-error').should('contain', 'numTestRetries')
+ })
+ })
+ })
+ })
+ })
+
+ describe('save/reload state', () => {
+ describe('serialize / load from state', () => {
+ const serializeState = () => {
+ return getRunState(autCypress)
+ }
+
+ const loadStateFromSnapshot = (cypressConfig, name) => {
+ cy.task('getSnapshot', {
+ file: Cypress.spec.name,
+ exactSpecName: name,
+ })
+ .then((state) => {
+ cypressConfig[1].state = state
+ })
+ }
+
+ describe('hooks', () => {
+ let realState
+ const stub1 = sinon.stub()
+ const stub2 = sinon.stub()
+ const stub3 = sinon.stub().callsFake(() => realState = serializeState())
+ let cypressConfig = [
+ {
+ suites: {
+ 'suite 1': {
+ hooks: [
+ 'before',
+ 'beforeEach',
+ 'afterEach',
+ 'after',
+ ],
+ tests: [{ name: 'test 1', fn: stub1 }],
+ },
+ 'suite 2': {
+ tests: [
+ { name: 'test 1', fn: stub2 },
+ { name: 'test 2', fn: stub3 },
+ 'test 3',
+ ],
+ },
+ },
+ }, { config: { numTestRetries: 1 } },
+ ]
+
+ it('serialize state', () => {
+ createCypress(...cypressConfig)
+ .then(shouldHaveTestResults(4, 0))
+ .then(() => {
+ expect(realState).to.matchSnapshot(cleanseRunStateMap, 'serialize state - hooks')
+ })
+ })
+
+ it('load state', () => {
+ loadStateFromSnapshot(cypressConfig, 'serialize state - hooks')
+
+ createCypress(...cypressConfig)
+ .then(shouldHaveTestResults(4, 0))
+ .then(() => {
+ expect(stub1).to.calledOnce
+ expect(stub2).to.calledOnce
+ expect(stub3).to.calledTwice
+ })
+ })
+ })
+
+ describe('retries', () => {
+ let realState
+
+ let runCount = 0
+ const failThenSerialize = () => {
+ if (!runCount++) {
+ assert(false, 'stub 3 fail')
+ }
+
+ assert(true, 'stub 3 pass')
+
+ return realState = serializeState()
+ }
+
+ let runCount2 = 0
+ const failOnce = () => {
+ if (!runCount2++) {
+ assert(false, 'stub 2 fail')
+ }
+
+ assert(true, 'stub 2 pass')
+ }
+
+ const stub1 = sinon.stub()
+ const stub2 = sinon.stub().callsFake(failOnce)
+ const stub3 = sinon.stub().callsFake(failThenSerialize)
+
+ let cypressConfig = [
+ {
+ suites: {
+ 'suite 1': {
+ hooks: [
+ 'before',
+ 'beforeEach',
+ 'afterEach',
+ 'after',
+ ],
+ tests: [{ name: 'test 1', fn: stub1 }],
+ },
+ 'suite 2': {
+ tests: [
+ { name: 'test 1', fn: stub2 },
+ { name: 'test 2', fn: stub3 },
+ 'test 3',
+ ],
+ },
+ },
+ }, { config: { numTestRetries: 1 } },
+ ]
+
+ it('serialize state', () => {
+ createCypress(...cypressConfig)
+ .then(shouldHaveTestResults(4, 0))
+ .then(() => {
+ expect(realState).to.matchSnapshot(cleanseRunStateMap, 'serialize state - retries')
+ })
+ })
+
+ it('load state', () => {
+ loadStateFromSnapshot(cypressConfig, 'serialize state - retries')
+ createCypress(...cypressConfig)
+ .then(shouldHaveTestResults(4, 0))
+ .then(() => {
+ expect(stub1).to.calledOnce
+ expect(stub2).to.calledTwice
+ expect(stub3).calledThrice
+ })
+ })
+ })
+ })
+ })
+
+ describe('other specs', () => {
+ it('simple failing hook spec', () => {
+ const mochaTests = {
+ suites: {
+ 'simple failing hook spec': {
+ suites: {
+ 'beforeEach hooks': {
+ hooks: [{ type: 'beforeEach', fail: true }],
+ tests: ['never gets here'],
+ },
+ 'pending': {
+ tests: [{ name: 'is pending', pending: true }],
+ },
+ 'afterEach hooks': {
+ hooks: [{ type: 'afterEach', fail: true }],
+ tests: ['fails this', 'does not run this'],
+ },
+ 'after hooks': {
+ hooks: [{ type: 'after', fail: true }]
+ , tests: ['runs this', 'fails on this'],
+ },
+ },
+ },
+
+ },
+ }
+
+ createCypress(mochaTests)
+ .then(shouldHaveTestResults(1, 3))
+ .then(() => {
+ cy.contains('.test', 'never gets here').should('have.class', 'runnable-failed')
+ cy.contains('.command', 'beforeEach').should('have.class', 'command-state-failed')
+ cy.contains('.attempt-error', 'AssertionError: beforeEach').scrollIntoView().should('be.visible')
+
+ cy.contains('.test', 'is pending').should('have.class', 'runnable-pending')
+
+ cy.contains('.test', 'fails this').should('have.class', 'runnable-failed')
+ cy.contains('.command', 'afterEach').should('have.class', 'command-state-failed')
+ cy.contains('.attempt-error', 'AssertionError: afterEach').should('be.visible')
+
+ cy.contains('.test', 'does not run this').should('have.class', 'runnable-processing')
+
+ cy.contains('.test', 'runs this').should('have.class', 'runnable-passed')
+
+ cy.contains('.test', 'fails on this').should('have.class', 'runnable-failed')
+ cy.contains('.command', 'after').should('have.class', 'command-state-failed')
+ cy.contains('.attempt-error', 'AssertionError: after').should('be.visible')
+ })
+ })
+
+ it('async timeout spec', () => {
+ createCypress({
+ suites: {
+ 'async': {
+ tests: [
+ { name: 'bar fails',
+ // eslint-disable-next-line
+ fn (done) {
+ this.timeout(100)
+ cy.on('fail', function () {})
+ // eslint-disable-next-line
+ foo.bar()
+ },
+ eval: true,
+ },
+ ],
+ },
+ },
+ })
+ .then(shouldHaveTestResults(0, 1))
+ })
+
+ it('mocha suite:end fire before test:pass event', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ suites: {
+ 'suite 1-1': {
+ tests: ['test 1', 'test 2'],
+ },
+ },
+ },
+ },
+ }).then(() => {
+ const getOrderFired = (eventProps) => {
+ const event = _.find(mochaStubs.args, eventProps)
+
+ expect(event).ok
+
+ return _.indexOf(mochaStubs.args, event)
+ }
+
+ expect(getOrderFired({ 1: 'pass', 2: { title: 'test 2' } }))
+ .to.be.lt(getOrderFired({ 1: 'suite end', 2: { title: 'suite 1-1' } }))
+ })
+ })
+
+ describe('screenshots', () => {
+ let onAfterScreenshotListener
+
+ beforeEach(() => {
+ onBeforeRun = () => {
+ autCypress.Screenshot.onAfterScreenshot = cy.stub()
+ onAfterScreenshotListener = cy.stub()
+ autCypress.on('after:screenshot', onAfterScreenshotListener)
+ }
+ })
+
+ it('screenshot during each failed attempt', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ tests: [
+ {
+ name: 'test 1',
+ fn: () => {
+ assert(false, 'some error')
+ },
+ eval: true,
+ },
+ ],
+ },
+ },
+ }, { config: { numTestRetries: 2 } })
+ .then(() => {
+ // sent to server
+ expect(autCypress.automation.withArgs('take:screenshot')).calledThrice
+ expect(autCypress.automation.withArgs('take:screenshot').args).matchDeep([
+ { 1: { testAttemptIndex: 0 } },
+ { 1: { testAttemptIndex: 1 } },
+ { 1: { testAttemptIndex: 2 } },
+ ])
+
+ // on('after:screenshot')
+ expect(onAfterScreenshotListener.args[0][0]).to.matchDeep({ testAttemptIndex: 0 })
+ expect(onAfterScreenshotListener.args[1][0]).to.matchDeep({ testAttemptIndex: 1 })
+
+ // Screenshot.onAfterScreenshot
+ expect(autCypress.Screenshot.onAfterScreenshot.args[0]).to.matchSnapshot(
+ { '^.0': stringifyShort, 'test': stringifyShort, takenAt: match.string },
+ )
+ })
+ })
+
+ it('retry screenshot in test body', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ tests: [
+ {
+ name: 'test 1',
+ fn: () => {
+ cy.screenshot()
+ cy.then(() => assert(false))
+ },
+ eval: true,
+ },
+ ],
+ },
+ },
+ }, { config: { numTestRetries: 1 } })
+ .then(() => {
+ expect(autCypress.automation.withArgs('take:screenshot')).callCount(4)
+ expect(autCypress.automation.withArgs('take:screenshot').args).matchDeep([
+ { 1: { testAttemptIndex: 0 } },
+ { 1: { testAttemptIndex: 0 } },
+ { 1: { testAttemptIndex: 1 } },
+ { 1: { testAttemptIndex: 1 } },
+ ])
+
+ expect(autCypress.automation.withArgs('take:screenshot').args[0]).matchSnapshot({ startTime: match.string, testAttemptIndex: match(0) })
+ expect(onAfterScreenshotListener.args[0][0]).to.matchSnapshot({ testAttemptIndex: match(0) })
+ expect(onAfterScreenshotListener.args[2][0]).to.matchDeep({ testAttemptIndex: 1 })
+ expect(autCypress.Screenshot.onAfterScreenshot.args[0]).to.matchSnapshot(
+ { '^.0': stringifyShort, 'test': stringifyShort, takenAt: match.string },
+ )
+ })
+ })
+
+ it('retry screenshot in hook', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: [
+ {
+ type: 'beforeEach',
+ fn: () => {
+ cy.screenshot()
+ cy.then(() => assert(false))
+ },
+ eval: true,
+ },
+ ],
+ tests: [
+ {
+ name: 'test 1',
+ },
+ ],
+ },
+ },
+ }, { config: { numTestRetries: 1 } })
+ .then(() => {
+ expect(autCypress.automation.withArgs('take:screenshot')).callCount(4)
+ expect(autCypress.automation.withArgs('take:screenshot').args).matchDeep([
+ { 1: { testAttemptIndex: 0 } },
+ { 1: { testAttemptIndex: 0 } },
+ { 1: { testAttemptIndex: 1 } },
+ { 1: { testAttemptIndex: 1 } },
+ ])
+
+ expect(onAfterScreenshotListener.args[0][0]).matchDeep({ testAttemptIndex: 0 })
+ expect(onAfterScreenshotListener.args[3][0]).matchDeep({ testAttemptIndex: 1 })
+ })
+ })
+ })
+ })
+
+ describe('mocha events', () => {
+ it('simple single test', () => {
+ createCypress(simpleSingleTest)
+ .then(() => {
+ snapshotEvents(snapshots.SIMPLE_SINGLE_TEST)
+ })
+ })
+
+ it('simple three tests', () => {
+ createCypress(threeTestsWithHooks)
+ .then(() => {
+ snapshotEvents(snapshots.THREE_TESTS_WITH_HOOKS)
+ })
+ })
+
+ it('three tests with retry', () => {
+ createCypress(threeTestsWithRetry, { config: {
+ numTestRetries: 2,
+ } })
+ .then(() => {
+ snapshotEvents(snapshots.THREE_TESTS_WITH_RETRY)
+ })
+ })
+ })
+ })
+})
+
+const getRunState = (Cypress) => {
+ // cypress normally accesses `id` via a closure
+ const currentRunnable = Cypress.cy.state('runnable')
+ // const currentTest = currentRunnable && getTestFromRunnable(currentRunnable)
+ // const currentId = currentTest && currentTest.id
+
+ const currentId = currentRunnable && currentRunnable.id
+
+ const s = {
+ currentId,
+ tests: Cypress.getTestsState(),
+ startTime: Cypress.getStartTime(),
+ emissions: Cypress.getEmissions(),
+ }
+
+ s.passed = Cypress.countByTestState(s.tests, 'passed')
+ s.failed = Cypress.countByTestState(s.tests, 'failed')
+ s.pending = Cypress.countByTestState(s.tests, 'pending')
+ s.numLogs = Cypress.Log.countLogsByTests(s.tests)
+
+ return _.cloneDeep(s)
+}
+
+const cleanseRunStateMap = {
+ wallClockStartedAt: new Date(0),
+ wallClockDuration: 1,
+ fnDuration: 1,
+ afterFnDuration: 1,
+ lifecycle: 1,
+ duration: 1,
+ startTime: new Date(0),
+ 'err.stack': '[err stack]',
+}
+
+// const formatEvents = (stub) => {
+// return _.flatMap(stub.args, (args) => {
+// args = args.slice(1)
+// if (['mocha', 'automation:request', 'log:changed'].includes(args[0])) {
+// return []
+// }
+
+// let ret = [args[0]]
+
+// if (args[1] != null) {
+// ret = ret.concat([args[1]])
+// }
+
+// return [ret]
+// })
+// }
+
+const shouldHaveTestResults = (passed, failed) => {
+ return (exitCode) => {
+ expect(exitCode, 'resolve with failure count').eq(exitCode)
+ passed = passed || '--'
+ failed = failed || '--'
+ cy.get('header .passed .num').should('have.text', `${passed}`)
+ cy.get('header .failed .num').should('have.text', `${failed}`)
+ }
+}
+
+const spyOn = (obj, prop, fn) => {
+ const _fn = obj[prop]
+
+ obj[prop] = function () {
+ fn.apply(this, arguments)
+
+ const ret = _fn.apply(this, arguments)
+
+ return ret
+ }
+}
diff --git a/packages/driver/test/support/server.coffee b/packages/driver/test/support/server.coffee
index 2ba2721df1a0..8e92c50aa6e8 100644
--- a/packages/driver/test/support/server.coffee
+++ b/packages/driver/test/support/server.coffee
@@ -42,6 +42,8 @@ niv.install("react-dom@15.6.1")
.then ->
res.send "timeout"
+ app.use "/isolated-runner", express.static(path.join(__dirname, '../../../runner/dist'))
+
app.get "/node_modules/*", (req, res) ->
res.sendFile(path.join("node_modules", req.params[0]), {
root: path.join(__dirname, "../..")
diff --git a/packages/reporter/cypress.json b/packages/reporter/cypress.json
index 40950ebabc22..bb4635187cac 100644
--- a/packages/reporter/cypress.json
+++ b/packages/reporter/cypress.json
@@ -3,5 +3,5 @@
"viewportWidth": 400,
"viewportHeight": 450,
"supportFile": false,
- "pluginFile": false
+ "pluginsFile": false
}
diff --git a/packages/server/test/unit/args_spec.coffee b/packages/server/test/unit/args_spec.coffee
index b28a72685024..22aa56c322e1 100644
--- a/packages/server/test/unit/args_spec.coffee
+++ b/packages/server/test/unit/args_spec.coffee
@@ -388,7 +388,7 @@ describe "lib/util/args", ->
sinon.stub(os, "platform").returns("win32")
process.env.HTTP_PROXY = override
options = @setup()
- expect(getWindowsProxyUtil.getWindowsProxy).to.not.beCalled
+ expect(getWindowsProxyUtil.getWindowsProxy).to.not.called
expect(options.proxySource).to.be.undefined
expect(options.proxyServer).to.be.undefined
expect(options.proxyBypassList).to.be.undefined
diff --git a/packages/server/test/unit/exceptions_spec.coffee b/packages/server/test/unit/exceptions_spec.coffee
index bdb92d983e08..a697490f5ff2 100644
--- a/packages/server/test/unit/exceptions_spec.coffee
+++ b/packages/server/test/unit/exceptions_spec.coffee
@@ -24,7 +24,7 @@ describe "lib/exceptions", ->
sinon.stub(user, "get").resolves({})
exception.getAuthToken().then (authToken) ->
- expect(authToken).to.be.undinefed
+ expect(authToken).to.be.undefined
context ".getErr", ->
it "returns an object literal", ->
diff --git a/packages/server/test/unit/path_helpers_spec.coffee b/packages/server/test/unit/path_helpers_spec.coffee
index d3a9659abc58..83790b7814f6 100644
--- a/packages/server/test/unit/path_helpers_spec.coffee
+++ b/packages/server/test/unit/path_helpers_spec.coffee
@@ -7,10 +7,10 @@ describe "lib/util/path_helpers", ->
check = path_helpers.checkIfResolveChangedRootFolder
it "ignores non-absolute paths", ->
- expect(check('foo/index.js', 'foo')).to.be.false()
+ expect(check('foo/index.js', 'foo')).to.be.false
it "handles paths that do not switch", ->
- expect(check('/foo/index.js', '/foo')).to.be.false()
+ expect(check('/foo/index.js', '/foo')).to.be.false
it "detects path switch", ->
- expect(check('/private/foo/index.js', '/foo')).to.be.true()
+ expect(check('/private/foo/index.js', '/foo')).to.be.true
From 491b2e86727483b6028b5ea75fbf1ae2fe3a17c9 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 23 Mar 2020 16:17:55 -0400
Subject: [PATCH 02/86] more fixes for isolated runner, get runner.spec passing
---
.../driver/src/cy/commands/navigation.coffee | 2 +-
packages/driver/src/cypress/cy.js | 33 +--
.../integration/cypress/runner.spec.js | 233 +++++++++---------
packages/driver/test/cypress/plugins/index.js | 6 +
.../test/cypress/support/helpers.coffee | 13 -
packages/driver/test/cypress/support/index.js | 2 +
packages/runner/src/lib/event-manager.js | 6 +
yarn.lock | 26 +-
8 files changed, 182 insertions(+), 139 deletions(-)
delete mode 100644 packages/driver/test/cypress/support/helpers.coffee
diff --git a/packages/driver/src/cy/commands/navigation.coffee b/packages/driver/src/cy/commands/navigation.coffee
index 2921390b98f6..2f2b0f344666 100644
--- a/packages/driver/src/cy/commands/navigation.coffee
+++ b/packages/driver/src/cy/commands/navigation.coffee
@@ -825,7 +825,7 @@ module.exports = (Commands, Cypress, cy, state, config) ->
hasVisitedAboutBlank = true
currentlyVisitingAboutBlank = true
- aboutBlank(win)
+ aboutBlank(cy, win)
.then ->
currentlyVisitingAboutBlank = false
diff --git a/packages/driver/src/cypress/cy.js b/packages/driver/src/cypress/cy.js
index 7f43d595fb70..45215e94e2ed 100644
--- a/packages/driver/src/cypress/cy.js
+++ b/packages/driver/src/cypress/cy.js
@@ -72,22 +72,27 @@ const setTopOnError = function (cy) {
curCy = cy
- const onTopError = function () {
- return curCy.onUncaughtException.apply(curCy, arguments)
- }
+ // prevent overriding top.onerror twice when loading more than one
+ // instance of test runner.
+ if (!top.__cypress__onerror) {
+ top.__cypress__onerror = true
+ const onTopError = function () {
+ return curCy.onUncaughtException.apply(curCy, arguments)
+ }
- top.onerror = onTopError
+ top.onerror = onTopError
- // Prevent Mocha from setting top.onerror which would override our handler
- // Since the setter will change which event handler gets invoked, we make it a noop
- return Object.defineProperty(top, 'onerror', {
- set () {},
- get () {
- return onTopError
- },
- configurable: false,
- enumerable: true,
- })
+ // Prevent Mocha from setting top.onerror which would override our handler
+ // Since the setter will change which event handler gets invoked, we make it a noop
+ return Object.defineProperty(top, 'onerror', {
+ set () {},
+ get () {
+ return onTopError
+ },
+ configurable: false,
+ enumerable: true,
+ })
+ }
}
const create = function (specWindow, Cypress, Cookies, state, config, log) {
diff --git a/packages/driver/test/cypress/integration/cypress/runner.spec.js b/packages/driver/test/cypress/integration/cypress/runner.spec.js
index fbc1333a1d45..daa3d94a1131 100644
--- a/packages/driver/test/cypress/integration/cypress/runner.spec.js
+++ b/packages/driver/test/cypress/integration/cypress/runner.spec.js
@@ -117,9 +117,12 @@ const createCypress = (mochaTests, opts = {}) => {
cy.stub(autCypress, 'automation').snapshot(enableStubSnapshots)
.callThrough()
+ .withArgs('clear:cookies')
+ .resolves({
+ foo: 'bar',
+ })
.withArgs('take:screenshot')
.resolves({
- testAttemptIndex: 0,
path: '/path/to/screenshot',
size: 12,
dimensions: { width: 20, height: 20 },
@@ -146,7 +149,9 @@ const createCypress = (mochaTests, opts = {}) => {
const noCall = _.includes(['window:before:unload', 'mocha'], arguments[0])
const isMocha = _.includes(['mocha'], arguments[0])
- isMocha && mochaStubs.apply(this, arguments)
+ if (isMocha) {
+ mochaStubs.apply(this, arguments)
+ }
noLog || allStubs.apply(this, ['emit'].concat([].slice.call(arguments)))
@@ -219,6 +224,9 @@ const createCypress = (mochaTests, opts = {}) => {
cb(opts.state)
})
+ .withArgs('backend:request', 'reset:server:state')
+ .yieldsAsync({})
+
.withArgs('backend:request', 'resolve:url')
.yieldsAsync({ response: {
isOkStatusCode: true,
@@ -245,14 +253,14 @@ const createCypress = (mochaTests, opts = {}) => {
cy.stub(win.channel, 'on').snapshot(enableStubSnapshots)
- win.Runner.start(win.document.getElementById('app'), c)
+ win.Runner.start(win.document.getElementById('app'), window.btoa(JSON.stringify(c)))
})
})
}
describe('src/cypress/runner', () => {
describe('isolated test runner', () => {
- beforeEach(async () => {
+ beforeEach(() => {
window.cy = backupCy
window.Cypress = backupCypress
})
@@ -288,8 +296,7 @@ describe('src/cypress/runner', () => {
.then(shouldHaveTestResults(0, 1))
.then(() => {
// render exactly one error
- cy.get('pre:contains(AssertionError)').should('have.length', 1)
- .should('have.class', 'test-error')
+ cy.get('.runnable-err:contains(AssertionError)').should('have.length', 1)
})
})
@@ -403,7 +410,7 @@ describe('src/cypress/runner', () => {
})
.then(shouldHaveTestResults(0, 1))
.then(() => {
- cy.get('.test-error:visible').invoke('text').then((text) => expect(text).to.matchSnapshot())
+ cy.get('.runnable-err:visible').invoke('text').should('contain', 'Because this error occurred during a before all hook')
})
.then(() => {
snapshotEvents(snapshots.FAIL_IN_BEFORE)
@@ -466,7 +473,7 @@ describe('src/cypress/runner', () => {
})
.then(shouldHaveTestResults(1, 1))
.then(() => {
- cy.get('.test-error:visible').invoke('text').then((text) => expect(text).to.matchSnapshot())
+ cy.get('.runnable-err:visible').invoke('text').should('contain', 'Because this error occurred during a after all hook')
})
.then(() => {
snapshotEvents(snapshots.FAIL_IN_AFTER)
@@ -558,7 +565,8 @@ describe('src/cypress/runner', () => {
})
})
- describe('retries', () => {
+ // NOTE: for test-retries
+ describe.skip('retries', () => {
it('can set retry config', () => {
createCypress({}, { config: { numTestRetries: 1 } })
.then(() => {
@@ -784,7 +792,8 @@ describe('src/cypress/runner', () => {
})
})
- describe('can configure retries', () => {
+ // NOTE: for test-retries
+ describe.skip('can configure retries', () => {
const getAttemptTag = (sel) => {
return cy.get(`.runnable-wrapper:contains${sel} .attempt-tag`)
}
@@ -828,7 +837,7 @@ describe('src/cypress/runner', () => {
})
.then(shouldHaveTestResults(0, 1))
.then(() => {
- cy.get('.test-error').should('contain', 'numTestRetries')
+ cy.get('.runnable-err').should('contain', 'numTestRetries')
})
})
@@ -846,7 +855,7 @@ describe('src/cypress/runner', () => {
})
.then(shouldHaveTestResults(0, 1))
.then(() => {
- cy.get('.test-error').should('contain', 'numTestRetries')
+ cy.get('.runnable-err').should('contain', 'numTestRetries')
})
})
@@ -862,7 +871,7 @@ describe('src/cypress/runner', () => {
})
.then(shouldHaveTestResults(0, 1))
.then(() => {
- cy.get('.test-error').should('contain', 'numTestRetries')
+ cy.get('.runnable-err').should('contain', 'numTestRetries')
})
})
})
@@ -934,7 +943,8 @@ describe('src/cypress/runner', () => {
})
})
- describe('retries', () => {
+ // NOTE: for test-retries
+ describe.skip('retries', () => {
let realState
let runCount = 0
@@ -1038,13 +1048,13 @@ describe('src/cypress/runner', () => {
.then(() => {
cy.contains('.test', 'never gets here').should('have.class', 'runnable-failed')
cy.contains('.command', 'beforeEach').should('have.class', 'command-state-failed')
- cy.contains('.attempt-error', 'AssertionError: beforeEach').scrollIntoView().should('be.visible')
+ cy.contains('.runnable-err', 'AssertionError: beforeEach').scrollIntoView().should('be.visible').then((v) => console.log(v.text()))
cy.contains('.test', 'is pending').should('have.class', 'runnable-pending')
cy.contains('.test', 'fails this').should('have.class', 'runnable-failed')
cy.contains('.command', 'afterEach').should('have.class', 'command-state-failed')
- cy.contains('.attempt-error', 'AssertionError: afterEach').should('be.visible')
+ cy.contains('.runnable-err', 'AssertionError: afterEach').should('be.visible')
cy.contains('.test', 'does not run this').should('have.class', 'runnable-processing')
@@ -1052,7 +1062,7 @@ describe('src/cypress/runner', () => {
cy.contains('.test', 'fails on this').should('have.class', 'runnable-failed')
cy.contains('.command', 'after').should('have.class', 'command-state-failed')
- cy.contains('.attempt-error', 'AssertionError: after').should('be.visible')
+ cy.contains('.runnable-err', 'AssertionError: after').should('be.visible')
})
})
@@ -1114,110 +1124,113 @@ describe('src/cypress/runner', () => {
}
})
- it('screenshot during each failed attempt', () => {
- createCypress({
- suites: {
- 'suite 1': {
- tests: [
- {
- name: 'test 1',
- fn: () => {
- assert(false, 'some error')
+ // NOTE: for test-retries
+ describe.skip('retries', () => {
+ it('screenshot during each failed attempt', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ tests: [
+ {
+ name: 'test 1',
+ fn: () => {
+ assert(false, 'some error')
+ },
+ eval: true,
},
- eval: true,
- },
- ],
+ ],
+ },
},
- },
- }, { config: { numTestRetries: 2 } })
- .then(() => {
+ }, { config: { numTestRetries: 2 } })
+ .then(() => {
// sent to server
- expect(autCypress.automation.withArgs('take:screenshot')).calledThrice
- expect(autCypress.automation.withArgs('take:screenshot').args).matchDeep([
- { 1: { testAttemptIndex: 0 } },
- { 1: { testAttemptIndex: 1 } },
- { 1: { testAttemptIndex: 2 } },
- ])
-
- // on('after:screenshot')
- expect(onAfterScreenshotListener.args[0][0]).to.matchDeep({ testAttemptIndex: 0 })
- expect(onAfterScreenshotListener.args[1][0]).to.matchDeep({ testAttemptIndex: 1 })
-
- // Screenshot.onAfterScreenshot
- expect(autCypress.Screenshot.onAfterScreenshot.args[0]).to.matchSnapshot(
- { '^.0': stringifyShort, 'test': stringifyShort, takenAt: match.string },
- )
+ expect(autCypress.automation.withArgs('take:screenshot')).calledThrice
+ expect(autCypress.automation.withArgs('take:screenshot').args).matchDeep([
+ { 1: { testAttemptIndex: 0 } },
+ { 1: { testAttemptIndex: 1 } },
+ { 1: { testAttemptIndex: 2 } },
+ ])
+
+ // on('after:screenshot')
+ expect(onAfterScreenshotListener.args[0][0]).to.matchDeep({ testAttemptIndex: 0 })
+ expect(onAfterScreenshotListener.args[1][0]).to.matchDeep({ testAttemptIndex: 1 })
+
+ // Screenshot.onAfterScreenshot
+ expect(autCypress.Screenshot.onAfterScreenshot.args[0]).to.matchSnapshot(
+ { '^.0': stringifyShort, 'test': stringifyShort, takenAt: match.string },
+ )
+ })
})
- })
- it('retry screenshot in test body', () => {
- createCypress({
- suites: {
- 'suite 1': {
- tests: [
- {
- name: 'test 1',
- fn: () => {
- cy.screenshot()
- cy.then(() => assert(false))
+ it('retry screenshot in test body', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ tests: [
+ {
+ name: 'test 1',
+ fn: () => {
+ cy.screenshot()
+ cy.then(() => assert(false))
+ },
+ eval: true,
},
- eval: true,
- },
- ],
+ ],
+ },
},
- },
- }, { config: { numTestRetries: 1 } })
- .then(() => {
- expect(autCypress.automation.withArgs('take:screenshot')).callCount(4)
- expect(autCypress.automation.withArgs('take:screenshot').args).matchDeep([
- { 1: { testAttemptIndex: 0 } },
- { 1: { testAttemptIndex: 0 } },
- { 1: { testAttemptIndex: 1 } },
- { 1: { testAttemptIndex: 1 } },
- ])
-
- expect(autCypress.automation.withArgs('take:screenshot').args[0]).matchSnapshot({ startTime: match.string, testAttemptIndex: match(0) })
- expect(onAfterScreenshotListener.args[0][0]).to.matchSnapshot({ testAttemptIndex: match(0) })
- expect(onAfterScreenshotListener.args[2][0]).to.matchDeep({ testAttemptIndex: 1 })
- expect(autCypress.Screenshot.onAfterScreenshot.args[0]).to.matchSnapshot(
- { '^.0': stringifyShort, 'test': stringifyShort, takenAt: match.string },
- )
+ }, { config: { numTestRetries: 1 } })
+ .then(() => {
+ expect(autCypress.automation.withArgs('take:screenshot')).callCount(4)
+ expect(autCypress.automation.withArgs('take:screenshot').args).matchDeep([
+ { 1: { testAttemptIndex: 0 } },
+ { 1: { testAttemptIndex: 0 } },
+ { 1: { testAttemptIndex: 1 } },
+ { 1: { testAttemptIndex: 1 } },
+ ])
+
+ expect(autCypress.automation.withArgs('take:screenshot').args[0]).matchSnapshot({ startTime: match.string, testAttemptIndex: match(0) })
+ expect(onAfterScreenshotListener.args[0][0]).to.matchSnapshot({ testAttemptIndex: match(0) })
+ expect(onAfterScreenshotListener.args[2][0]).to.matchDeep({ testAttemptIndex: 1 })
+ expect(autCypress.Screenshot.onAfterScreenshot.args[0]).to.matchSnapshot(
+ { '^.0': stringifyShort, 'test': stringifyShort, takenAt: match.string },
+ )
+ })
})
- })
- it('retry screenshot in hook', () => {
- createCypress({
- suites: {
- 'suite 1': {
- hooks: [
- {
- type: 'beforeEach',
- fn: () => {
- cy.screenshot()
- cy.then(() => assert(false))
+ it('retry screenshot in hook', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ hooks: [
+ {
+ type: 'beforeEach',
+ fn: () => {
+ cy.screenshot()
+ cy.then(() => assert(false))
+ },
+ eval: true,
},
- eval: true,
- },
- ],
- tests: [
- {
- name: 'test 1',
- },
- ],
+ ],
+ tests: [
+ {
+ name: 'test 1',
+ },
+ ],
+ },
},
- },
- }, { config: { numTestRetries: 1 } })
- .then(() => {
- expect(autCypress.automation.withArgs('take:screenshot')).callCount(4)
- expect(autCypress.automation.withArgs('take:screenshot').args).matchDeep([
- { 1: { testAttemptIndex: 0 } },
- { 1: { testAttemptIndex: 0 } },
- { 1: { testAttemptIndex: 1 } },
- { 1: { testAttemptIndex: 1 } },
- ])
-
- expect(onAfterScreenshotListener.args[0][0]).matchDeep({ testAttemptIndex: 0 })
- expect(onAfterScreenshotListener.args[3][0]).matchDeep({ testAttemptIndex: 1 })
+ }, { config: { numTestRetries: 1 } })
+ .then(() => {
+ expect(autCypress.automation.withArgs('take:screenshot')).callCount(4)
+ expect(autCypress.automation.withArgs('take:screenshot').args).matchDeep([
+ { 1: { testAttemptIndex: 0 } },
+ { 1: { testAttemptIndex: 0 } },
+ { 1: { testAttemptIndex: 1 } },
+ { 1: { testAttemptIndex: 1 } },
+ ])
+
+ expect(onAfterScreenshotListener.args[0][0]).matchDeep({ testAttemptIndex: 0 })
+ expect(onAfterScreenshotListener.args[3][0]).matchDeep({ testAttemptIndex: 1 })
+ })
})
})
})
diff --git a/packages/driver/test/cypress/plugins/index.js b/packages/driver/test/cypress/plugins/index.js
index b0a17637cffb..ac0e1df25453 100644
--- a/packages/driver/test/cypress/plugins/index.js
+++ b/packages/driver/test/cypress/plugins/index.js
@@ -5,6 +5,7 @@ const _ = require('lodash')
const path = require('path')
const fs = require('fs-extra')
const Promise = require('bluebird')
+const { getSnapshot, saveSnapshot } = require('./snapshot')
const webpack = require('@cypress/webpack-preprocessor')
process.env.NO_LIVERELOAD = '1'
@@ -33,5 +34,10 @@ module.exports = (on) => {
return null
},
+
+ getSnapshot,
+
+ saveSnapshot,
+
})
}
diff --git a/packages/driver/test/cypress/support/helpers.coffee b/packages/driver/test/cypress/support/helpers.coffee
deleted file mode 100644
index d686ba6b2252..000000000000
--- a/packages/driver/test/cypress/support/helpers.coffee
+++ /dev/null
@@ -1,13 +0,0 @@
-_ = Cypress._
-
-getFirstSubjectByName = (name) ->
- cy.queue.find({name: name}).get("subject")
-
-getQueueNames = ->
- _.map(cy.queue, "name")
-
-module.exports = {
- getQueueNames
-
- getFirstSubjectByName
-}
diff --git a/packages/driver/test/cypress/support/index.js b/packages/driver/test/cypress/support/index.js
index c08934668746..ce36e97d38df 100644
--- a/packages/driver/test/cypress/support/index.js
+++ b/packages/driver/test/cypress/support/index.js
@@ -16,3 +16,5 @@
// Alternatively you can use CommonJS syntax:
// require("./commands")
require('./defaults')
+
+require('./commands')
diff --git a/packages/runner/src/lib/event-manager.js b/packages/runner/src/lib/event-manager.js
index ac86f839aa44..b65af47797c4 100644
--- a/packages/runner/src/lib/event-manager.js
+++ b/packages/runner/src/lib/event-manager.js
@@ -30,6 +30,12 @@ const socketRerunEvents = 'runner:restart watched:file:changed'.split(' ')
const localBus = new EventEmitter()
const reporterBus = new EventEmitter()
+if (window.Cypress) {
+ window.channel = ws
+ window.reporterBus = reporterBus
+ window.localBus = localBus
+}
+
let Cypress
const eventManager = {
diff --git a/yarn.lock b/yarn.lock
index 1c90dc2e1d5f..674f0f8f000b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -18919,6 +18919,11 @@ plur@^3.0.0:
dependencies:
irregular-plurals "^2.0.0"
+pluralize@7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
+ integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==
+
pluralize@8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
@@ -21676,6 +21681,25 @@ snap-shot-core@10.2.0:
quote "0.4.0"
ramda "0.26.1"
+snap-shot-core@^7.4.0:
+ version "7.4.0"
+ resolved "https://registry.yarnpkg.com/snap-shot-core/-/snap-shot-core-7.4.0.tgz#b3215fbb3e3bbc734706a56feff175040bb06254"
+ integrity sha512-rSRDbrfvYFp7dwtSJhFw+SUT4tS1tKSty7RsW9f9erP2fFyn7VIfYe9cgmseZytSMovB0I9WxATwKhGAsbYBCQ==
+ dependencies:
+ arg "4.1.0"
+ check-more-types "2.24.0"
+ common-tags "1.8.0"
+ debug "4.1.1"
+ escape-quotes "1.0.2"
+ folktale "2.3.2"
+ is-ci "2.0.0"
+ jsesc "2.5.2"
+ lazy-ass "1.6.0"
+ mkdirp "0.5.1"
+ pluralize "7.0.0"
+ quote "0.4.0"
+ ramda "0.26.1"
+
snap-shot-it@7.9.1:
version "7.9.1"
resolved "https://registry.yarnpkg.com/snap-shot-it/-/snap-shot-it-7.9.1.tgz#a5787d5b4fe354aa258cdd5254f3e85f66093281"
@@ -21766,7 +21790,7 @@ socket.io-circular-parser@cypress-io/socket.io-circular-parser#unpatched-has-bin
circular-json "0.5.9"
component-emitter "1.2.1"
debug "~4.1.0"
- has-binary2 "~1.0.2"
+ has-binary2 cypress-io/has-binary#8580a33df21e8b36a43f57872a82c60829636a92
socket.io-client@2.3.0:
version "2.3.0"
From b648a8a73158f23e6e82e011121380d1fc25c7d8 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 23 Mar 2020 16:18:24 -0400
Subject: [PATCH 03/86] fix prev commit add untracked files
---
.../__snapshots__/runner.spec.js.snapshot.js | 3515 +++++++++++++++++
.../plugins/snapshot/command/index.d.ts | 15 +
.../cypress/plugins/snapshot/command/index.js | 591 +++
.../test/cypress/plugins/snapshot/index.js | 56 +
.../driver/test/cypress/support/commands.js | 7 +
.../driver/test/cypress/support/helpers.js | 153 +
6 files changed, 4337 insertions(+)
create mode 100644 packages/driver/test/__snapshots__/runner.spec.js.snapshot.js
create mode 100644 packages/driver/test/cypress/plugins/snapshot/command/index.d.ts
create mode 100644 packages/driver/test/cypress/plugins/snapshot/command/index.js
create mode 100644 packages/driver/test/cypress/plugins/snapshot/index.js
create mode 100644 packages/driver/test/cypress/support/commands.js
create mode 100644 packages/driver/test/cypress/support/helpers.js
diff --git a/packages/driver/test/__snapshots__/runner.spec.js.snapshot.js b/packages/driver/test/__snapshots__/runner.spec.js.snapshot.js
new file mode 100644
index 000000000000..73928cc5acb5
--- /dev/null
+++ b/packages/driver/test/__snapshots__/runner.spec.js.snapshot.js
@@ -0,0 +1,3515 @@
+exports['FAIL_IN_AFTER.mocha'] = [
+ [
+ "mocha",
+ "start",
+ {
+ "start": "match.date"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "pass",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test end",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test:after:run",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "wallClockDuration": "match.number",
+ "timings": {
+ "lifecycle": "match.number",
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test",
+ {
+ "id": "r4",
+ "order": 2,
+ "title": "test 2",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "pass",
+ {
+ "id": "r4",
+ "order": 2,
+ "title": "test 2",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test end",
+ {
+ "id": "r4",
+ "order": 2,
+ "title": "test 2",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r4",
+ "title": "\"after all\" hook",
+ "hookName": "after all",
+ "hookId": "h1",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "fail",
+ {
+ "id": "r4",
+ "title": "\"after all\" hook for \"test 2\"",
+ "hookName": "after all",
+ "hookId": "h1",
+ "err": "{Object 6}",
+ "state": "failed",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ },
+ {
+ "message": "[error message]",
+ "name": "AssertionError",
+ "stack": "match.string",
+ "actual": null,
+ "showDiff": false
+ }
+ ],
+ [
+ "mocha",
+ "test:after:run",
+ {
+ "id": "r4",
+ "order": 2,
+ "title": "test 2",
+ "hookName": "after all",
+ "err": "{Object 6}",
+ "state": "failed",
+ "failedFromHookId": "h1",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "wallClockDuration": "match.number",
+ "timings": {
+ "lifecycle": "match.number",
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "end",
+ {
+ "end": "match.date"
+ }
+ ]
+]
+
+exports['FAIL_IN_AFTER.setRunnables'] = [
+ [
+ "set:runnables",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js",
+ "tests": [],
+ "suites": [
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null,
+ "tests": [
+ {
+ "id": "r3",
+ "title": "test 1",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ },
+ {
+ "id": "r4",
+ "title": "test 2",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ "suites": []
+ }
+ ]
+ },
+ "[Function run]"
+ ]
+]
+
+exports['FAIL_IN_AFTEREACH.mocha'] = [
+ [
+ "mocha",
+ "start",
+ {
+ "start": "match.date"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "pass",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test end",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r3",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h1",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "fail",
+ {
+ "id": "r3",
+ "title": "\"after each\" hook for \"test 1\"",
+ "hookName": "after each",
+ "hookId": "h1",
+ "err": "{Object 6}",
+ "state": "failed",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ },
+ {
+ "message": "[error message]",
+ "name": "AssertionError",
+ "stack": "match.string",
+ "actual": null,
+ "showDiff": false
+ }
+ ],
+ [
+ "mocha",
+ "test:after:run",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "hookName": "after each",
+ "err": "{Object 6}",
+ "state": "failed",
+ "failedFromHookId": "h1",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "wallClockDuration": "match.number",
+ "timings": {
+ "lifecycle": "match.number",
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "end",
+ {
+ "end": "match.date"
+ }
+ ]
+]
+
+exports['FAIL_IN_AFTEREACH.setRunnables'] = [
+ [
+ "set:runnables",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js",
+ "tests": [],
+ "suites": [
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null,
+ "tests": [
+ {
+ "id": "r3",
+ "title": "test 1",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ "suites": []
+ }
+ ]
+ },
+ "[Function run]"
+ ]
+]
+
+exports['FAIL_IN_BEFORE.mocha'] = [
+ [
+ "mocha",
+ "start",
+ {
+ "start": "match.date"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r3",
+ "title": "\"before all\" hook",
+ "hookName": "before all",
+ "hookId": "h1",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "fail",
+ {
+ "id": "r3",
+ "title": "\"before all\" hook for \"test 1\"",
+ "hookName": "before all",
+ "hookId": "h1",
+ "err": "{Object 6}",
+ "state": "failed",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ },
+ {
+ "message": "[error message]",
+ "name": "AssertionError",
+ "stack": "match.string",
+ "actual": null,
+ "showDiff": false
+ }
+ ],
+ [
+ "mocha",
+ "test:after:run",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "hookName": "before all",
+ "err": "{Object 6}",
+ "state": "failed",
+ "failedFromHookId": "h1",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "wallClockDuration": "match.number",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "end",
+ {
+ "end": "match.date"
+ }
+ ]
+]
+
+exports['FAIL_IN_BEFORE.setRunnables'] = [
+ [
+ "set:runnables",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js",
+ "tests": [],
+ "suites": [
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null,
+ "tests": [
+ {
+ "id": "r3",
+ "title": "test 1",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ "suites": []
+ }
+ ]
+ },
+ "[Function run]"
+ ]
+]
+
+exports['FAIL_IN_BEFOREEACH.mocha'] = [
+ [
+ "mocha",
+ "start",
+ {
+ "start": "match.date"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r3",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h1",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "fail",
+ {
+ "id": "r3",
+ "title": "\"before each\" hook for \"test 1\"",
+ "hookName": "before each",
+ "hookId": "h1",
+ "err": "{Object 6}",
+ "state": "failed",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ },
+ {
+ "message": "[error message]",
+ "name": "AssertionError",
+ "stack": "match.string",
+ "actual": null,
+ "showDiff": false
+ }
+ ],
+ [
+ "mocha",
+ "test:after:run",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "hookName": "before each",
+ "err": "{Object 6}",
+ "state": "failed",
+ "failedFromHookId": "h1",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "wallClockDuration": "match.number",
+ "timings": {
+ "lifecycle": "match.number",
+ "before each": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "end",
+ {
+ "end": "match.date"
+ }
+ ]
+]
+
+exports['FAIL_IN_BEFOREEACH.setRunnables'] = [
+ [
+ "set:runnables",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js",
+ "tests": [],
+ "suites": [
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null,
+ "tests": [
+ {
+ "id": "r3",
+ "title": "test 1",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ "suites": []
+ }
+ ]
+ },
+ "[Function run]"
+ ]
+]
+
+exports['FAIL_WITH_ONLY.mocha'] = [
+ [
+ "mocha",
+ "start",
+ {
+ "start": "match.date"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r4",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r5",
+ "title": "\"before all\" hook",
+ "hookName": "before all",
+ "hookId": "h1",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r5",
+ "title": "\"before all\" hook",
+ "hookName": "before all",
+ "hookId": "h1",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test",
+ {
+ "id": "r5",
+ "title": "test 2",
+ "body": "[body]",
+ "type": "test",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "after all": [
+ {
+ "hookId": "h4",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r5",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r5",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "fail",
+ {
+ "id": "r5",
+ "title": "test 2",
+ "err": "{Object 6}",
+ "state": "failed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "after all": [
+ {
+ "hookId": "h4",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ },
+ {
+ "message": "[error message]",
+ "name": "AssertionError",
+ "stack": "match.string",
+ "actual": null,
+ "showDiff": false
+ }
+ ],
+ [
+ "mocha",
+ "test end",
+ {
+ "id": "r5",
+ "title": "test 2",
+ "err": "{Object 6}",
+ "state": "failed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "after all": [
+ {
+ "hookId": "h4",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r5",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r5",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r5",
+ "title": "\"after all\" hook",
+ "hookName": "after all",
+ "hookId": "h4",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r5",
+ "title": "\"after all\" hook",
+ "hookName": "after all",
+ "hookId": "h4",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test:after:run",
+ {
+ "id": "r5",
+ "title": "test 2",
+ "err": "{Object 6}",
+ "state": "failed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "wallClockDuration": "match.number",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "after all": [
+ {
+ "hookId": "h4",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r4",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "end",
+ {
+ "end": "match.date"
+ }
+ ]
+]
+
+exports['FAIL_WITH_ONLY.setRunnables'] = [
+ [
+ "set:runnables",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js",
+ "tests": [],
+ "suites": [
+ {
+ "id": "r4",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null,
+ "tests": [
+ {
+ "id": "r5",
+ "title": "test 2",
+ "body": "[body]",
+ "type": "test",
+ "file": null,
+ "order": 2
+ }
+ ],
+ "suites": []
+ }
+ ]
+ },
+ "[Function run]"
+ ]
+]
+
+exports['PASS_WITH_ONLY.mocha'] = [
+ [
+ "mocha",
+ "start",
+ {
+ "start": "match.date"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r4",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r5",
+ "title": "\"before all\" hook",
+ "hookName": "before all",
+ "hookId": "h1",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r5",
+ "title": "\"before all\" hook",
+ "hookName": "before all",
+ "hookId": "h1",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test",
+ {
+ "id": "r5",
+ "title": "test 2",
+ "body": "[body]",
+ "type": "test",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "after all": [
+ {
+ "hookId": "h4",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r5",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r5",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "pass",
+ {
+ "id": "r5",
+ "title": "test 2",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "after all": [
+ {
+ "hookId": "h4",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test end",
+ {
+ "id": "r5",
+ "title": "test 2",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "after all": [
+ {
+ "hookId": "h4",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r5",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r5",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r5",
+ "title": "\"after all\" hook",
+ "hookName": "after all",
+ "hookId": "h4",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r5",
+ "title": "\"after all\" hook",
+ "hookName": "after all",
+ "hookId": "h4",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test:after:run",
+ {
+ "id": "r5",
+ "title": "test 2",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "wallClockDuration": "match.number",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "after all": [
+ {
+ "hookId": "h4",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r4",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "end",
+ {
+ "end": "match.date"
+ }
+ ]
+]
+
+exports['PASS_WITH_ONLY.setRunnables'] = [
+ [
+ "set:runnables",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js",
+ "tests": [],
+ "suites": [
+ {
+ "id": "r4",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null,
+ "tests": [
+ {
+ "id": "r5",
+ "title": "test 2",
+ "body": "[body]",
+ "type": "test",
+ "file": null,
+ "order": 2
+ }
+ ],
+ "suites": []
+ }
+ ]
+ },
+ "[Function run]"
+ ]
+]
+
+exports['SIMPLE_SINGLE_TEST.mocha'] = [
+ [
+ "mocha",
+ "start",
+ {
+ "start": "match.date"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "pass",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test end",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test:after:run",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "wallClockDuration": "match.number",
+ "timings": {
+ "lifecycle": "match.number",
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "end",
+ {
+ "end": "match.date"
+ }
+ ]
+]
+
+exports['SIMPLE_SINGLE_TEST.setRunnables'] = [
+ [
+ "set:runnables",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js",
+ "tests": [],
+ "suites": [
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null,
+ "tests": [
+ {
+ "id": "r3",
+ "title": "test 1",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ "suites": []
+ }
+ ]
+ },
+ "[Function run]"
+ ]
+]
+
+exports['THREE_TESTS_WITH_HOOKS.mocha'] = [
+ [
+ "mocha",
+ "start",
+ {
+ "start": "match.date"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r3",
+ "title": "\"before all\" hook",
+ "hookName": "before all",
+ "hookId": "h1",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r3",
+ "title": "\"before all\" hook",
+ "hookName": "before all",
+ "hookId": "h1",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "body": "[body]",
+ "type": "test",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r3",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r3",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "pass",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test end",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r3",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r3",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test:after:run",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "wallClockDuration": "match.number",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test",
+ {
+ "id": "r4",
+ "order": 2,
+ "title": "test 2",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r4",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r4",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "pass",
+ {
+ "id": "r4",
+ "order": 2,
+ "title": "test 2",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test end",
+ {
+ "id": "r4",
+ "order": 2,
+ "title": "test 2",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r4",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r4",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test:after:run",
+ {
+ "id": "r4",
+ "order": 2,
+ "title": "test 2",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "wallClockDuration": "match.number",
+ "timings": {
+ "lifecycle": "match.number",
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test",
+ {
+ "id": "r5",
+ "order": 3,
+ "title": "test 3",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r5",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r5",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "pass",
+ {
+ "id": "r5",
+ "order": 3,
+ "title": "test 3",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "after all": [
+ {
+ "hookId": "h4",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test end",
+ {
+ "id": "r5",
+ "order": 3,
+ "title": "test 3",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "after all": [
+ {
+ "hookId": "h4",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r5",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r5",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r5",
+ "title": "\"after all\" hook",
+ "hookName": "after all",
+ "hookId": "h4",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r5",
+ "title": "\"after all\" hook",
+ "hookName": "after all",
+ "hookId": "h4",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test:after:run",
+ {
+ "id": "r5",
+ "order": 3,
+ "title": "test 3",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "wallClockDuration": "match.number",
+ "timings": {
+ "lifecycle": "match.number",
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "after all": [
+ {
+ "hookId": "h4",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "end",
+ {
+ "end": "match.date"
+ }
+ ]
+]
+
+exports['THREE_TESTS_WITH_HOOKS.setRunnables'] = [
+ [
+ "set:runnables",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js",
+ "tests": [],
+ "suites": [
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null,
+ "tests": [
+ {
+ "id": "r3",
+ "title": "test 1",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ },
+ {
+ "id": "r4",
+ "title": "test 2",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ },
+ {
+ "id": "r5",
+ "title": "test 3",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ "suites": []
+ }
+ ]
+ },
+ "[Function run]"
+ ]
+]
+
+exports['THREE_TESTS_WITH_RETRY.mocha'] = [
+ [
+ "mocha",
+ "start",
+ {
+ "start": "match.date"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "suite",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r3",
+ "title": "\"before all\" hook",
+ "hookName": "before all",
+ "hookId": "h1",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r3",
+ "title": "\"before all\" hook",
+ "hookName": "before all",
+ "hookId": "h1",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "body": "[body]",
+ "type": "test",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r3",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r3",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "pass",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test end",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r3",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r3",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test:after:run",
+ {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "wallClockDuration": "match.number",
+ "timings": {
+ "lifecycle": "match.number",
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test",
+ {
+ "id": "r4",
+ "order": 2,
+ "title": "test 2",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r4",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r4",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "fail",
+ {
+ "id": "r4",
+ "order": 2,
+ "title": "test 2",
+ "err": "{Object 6}",
+ "state": "failed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ },
+ {
+ "message": "[error message]",
+ "name": "AssertionError",
+ "stack": "match.string",
+ "actual": null,
+ "showDiff": false
+ }
+ ],
+ [
+ "mocha",
+ "test end",
+ {
+ "id": "r4",
+ "order": 2,
+ "title": "test 2",
+ "err": "{Object 6}",
+ "state": "failed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r4",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r4",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test:after:run",
+ {
+ "id": "r4",
+ "order": 2,
+ "title": "test 2",
+ "err": "{Object 6}",
+ "state": "failed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "wallClockDuration": "match.number",
+ "timings": {
+ "lifecycle": "match.number",
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test",
+ {
+ "id": "r5",
+ "order": 3,
+ "title": "test 3",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r5",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r5",
+ "title": "\"before each\" hook",
+ "hookName": "before each",
+ "hookId": "h2",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "pass",
+ {
+ "id": "r5",
+ "order": 3,
+ "title": "test 3",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "after all": [
+ {
+ "hookId": "h4",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test end",
+ {
+ "id": "r5",
+ "order": 3,
+ "title": "test 3",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "timings": {
+ "lifecycle": "match.number",
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "after all": [
+ {
+ "hookId": "h4",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r5",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r5",
+ "title": "\"after each\" hook",
+ "hookName": "after each",
+ "hookId": "h3",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook",
+ {
+ "id": "r5",
+ "title": "\"after all\" hook",
+ "hookName": "after all",
+ "hookId": "h4",
+ "body": "[body]",
+ "type": "hook",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "hook end",
+ {
+ "id": "r5",
+ "title": "\"after all\" hook",
+ "hookName": "after all",
+ "hookId": "h4",
+ "body": "[body]",
+ "type": "hook",
+ "duration": "match.number",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "test:after:run",
+ {
+ "id": "r5",
+ "order": 3,
+ "title": "test 3",
+ "state": "passed",
+ "body": "[body]",
+ "type": "test",
+ "duration": "match.number",
+ "wallClockStartedAt": "match.date",
+ "wallClockDuration": "match.number",
+ "timings": {
+ "lifecycle": "match.number",
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "test": {
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ],
+ "after all": [
+ {
+ "hookId": "h4",
+ "fnDuration": "match.number",
+ "afterFnDuration": "match.number"
+ }
+ ]
+ },
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js"
+ }
+ ],
+ [
+ "mocha",
+ "end",
+ {
+ "end": "match.date"
+ }
+ ]
+]
+
+exports['THREE_TESTS_WITH_RETRY.setRunnables'] = [
+ [
+ "set:runnables",
+ {
+ "id": "r1",
+ "title": "",
+ "root": true,
+ "type": "suite",
+ "file": "cypress/integration/cypress/runner.spec.js",
+ "tests": [],
+ "suites": [
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null,
+ "tests": [
+ {
+ "id": "r3",
+ "title": "test 1",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ },
+ {
+ "id": "r4",
+ "title": "test 2",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ },
+ {
+ "id": "r5",
+ "title": "test 3",
+ "body": "[body]",
+ "type": "test",
+ "file": null
+ }
+ ],
+ "suites": []
+ }
+ ]
+ },
+ "[Function run]"
+ ]
+]
+
+exports['serialize state - hooks'] = {
+ "currentId": "r6",
+ "tests": {
+ "r3": {
+ "id": "r3",
+ "order": 1,
+ "title": "test 1",
+ "state": "passed",
+ "body": "stub",
+ "type": "test",
+ "duration": 1,
+ "wallClockStartedAt": "1970-01-01T00:00:00.000Z",
+ "wallClockDuration": 1,
+ "timings": {
+ "lifecycle": 1,
+ "before all": [
+ {
+ "hookId": "h1",
+ "fnDuration": 1,
+ "afterFnDuration": 1
+ }
+ ],
+ "before each": [
+ {
+ "hookId": "h2",
+ "fnDuration": 1,
+ "afterFnDuration": 1
+ }
+ ],
+ "test": {
+ "fnDuration": 1,
+ "afterFnDuration": 1
+ },
+ "after each": [
+ {
+ "hookId": "h3",
+ "fnDuration": 1,
+ "afterFnDuration": 1
+ }
+ ],
+ "after all": [
+ {
+ "hookId": "h4",
+ "fnDuration": 1,
+ "afterFnDuration": 1
+ }
+ ]
+ },
+ "file": null
+ },
+ "r5": {
+ "id": "r5",
+ "order": 2,
+ "title": "test 1",
+ "state": "passed",
+ "body": "stub",
+ "type": "test",
+ "duration": 1,
+ "wallClockStartedAt": "1970-01-01T00:00:00.000Z",
+ "wallClockDuration": 1,
+ "timings": {
+ "lifecycle": 1,
+ "test": {
+ "fnDuration": 1,
+ "afterFnDuration": 1
+ }
+ },
+ "file": null
+ }
+ },
+ "startTime": "1970-01-01T00:00:00.000Z",
+ "emissions": {
+ "started": {
+ "r1": true,
+ "r2": true,
+ "r3": true,
+ "r4": true,
+ "r5": true,
+ "r6": true
+ },
+ "ended": {
+ "r3": true,
+ "r2": true,
+ "r5": true
+ }
+ },
+ "passed": 2,
+ "failed": 0,
+ "pending": 0,
+ "numLogs": 0
+}
+
+exports['src/cypress/runner isolated test runner test events hook failures fail in [after] #1'] = `
+AssertionErrorafter
+
+Because this error occurred during a after all hook we are skipping the remaining tests in the current suite: suite 1View stack traceAssertionError: after
+
+Because this error occurred during a \`after all\` hook we are skipping the remaining tests in the current suite: \`suite 1\`
+ at fn (cypress:///../driver/src/cy/chai.js:411:19)
+ at Context.runnable.fn (cypress:///../driver/src/cypress/cy.js:1192:24)
+ at eval (cypress:///../driver/src/cypress/runner.js:1141:28)
+ at PassThroughHandlerContext.finallyHandler (cypress:///./node_modules/bluebird/js/release/finally.js:56:23)
+ at PassThroughHandlerContext.tryCatcher (cypress:///./node_modules/bluebird/js/release/util.js:16:23)
+ at Promise._settlePromiseFromHandler (cypress:///./node_modules/bluebird/js/release/promise.js:512:31)
+ at Promise._settlePromise (cypress:///./node_modules/bluebird/js/release/promise.js:569:18)
+ at Promise._settlePromise0 (cypress:///./node_modules/bluebird/js/release/promise.js:614:10)
+ at Promise._settlePromises (cypress:///./node_modules/bluebird/js/release/promise.js:693:18)
+ at Async._drainQueue (cypress:///./node_modules/bluebird/js/release/async.js:133:16)
+ at Async._drainQueues (cypress:///./node_modules/bluebird/js/release/async.js:143:10)
+ at Async.drainQueues (cypress:///./node_modules/bluebird/js/release/async.js:17:14)
+`
diff --git a/packages/driver/test/cypress/plugins/snapshot/command/index.d.ts b/packages/driver/test/cypress/plugins/snapshot/command/index.d.ts
new file mode 100644
index 000000000000..ddbb4be46ce7
--- /dev/null
+++ b/packages/driver/test/cypress/plugins/snapshot/command/index.d.ts
@@ -0,0 +1,15 @@
+///
+
+declare namespace Chai {
+ interface Assertion {
+ matchSnapshot: {
+ (name?: string)
+ (replacers: object)
+ (replacers: object, name?: string)
+ }
+ matchDeep: {
+ (replacers: object, expected: object)
+ (expected: object)
+ }
+ }
+}
diff --git a/packages/driver/test/cypress/plugins/snapshot/command/index.js b/packages/driver/test/cypress/plugins/snapshot/command/index.js
new file mode 100644
index 000000000000..5a7c547f3fb5
--- /dev/null
+++ b/packages/driver/test/cypress/plugins/snapshot/command/index.js
@@ -0,0 +1,591 @@
+let _ = require('lodash')
+const Debug = require('debug')
+const chalk = require('chalk')
+const stripAnsi = require('strip-ansi')
+const { stripIndent } = require('common-tags')
+let sinon = require('sinon')
+
+const debug = Debug('plugin:snapshot')
+
+// window.localStorage.debug = 'spec* plugin:snapshot'
+// Debug.enable('plugin:snapshot')
+
+const registerInCypress = () => {
+ _ = Cypress._
+ sinon = Cypress.sinon
+ const $ = Cypress.$
+
+ chai = window.chai
+
+ let snapshotIndex = {}
+
+ const matchDeepCypress = function (...args) {
+ const exp = args[1] || args[0]
+
+ try {
+ const res = matchDeep.apply(this, [args[0], args[1], { Cypress, expectedOnly: true }])
+
+ Cypress.log({
+ name: 'assert',
+ message: `Expected **${chai.util.objDisplay(res.act)}** to deep match: **${chai.util.objDisplay(exp)}**`,
+ state: 'passed',
+ consoleProps: () => {
+ return {
+ Actual: res.act,
+ }
+ },
+ })
+ } catch (e) {
+ Cypress.log({
+ name: 'assert',
+ message: `Expected **${chai.util.objDisplay(e.act)}** to deep match: **${chai.util.objDisplay(args[1] || args[0])}**`,
+ state: 'failed',
+ consoleProps: () => {
+ return {
+ Actual: e.act,
+ Expected: exp,
+ }
+ },
+ })
+
+ throw e
+ }
+ }
+
+ const matchSnapshotCypress = function (m, snapshotName) {
+ const ctx = this
+ const testName = Cypress.mocha.getRunner().test.fullTitle()
+ const file = Cypress.spec.name
+
+ return cy.then(() => {
+ snapshotIndex[testName] = (snapshotIndex[testName] || 1)
+ const exactSpecName = snapshotName || `${testName} #${snapshotIndex[testName]}`
+
+ return cy.task('getSnapshot', {
+ file,
+ exactSpecName,
+ }, { log: false })
+ .then((exp) => {
+ try {
+ snapshotIndex[testName] = snapshotIndex[testName] + 1
+ const res = matchDeep.call(ctx, m, exp, { message: 'to match snapshot', Cypress, isSnapshot: true, sinon })
+
+ Cypress.log({
+ name: 'assert',
+ message: `snapshot matched: **${exactSpecName}**`,
+ state: 'passed',
+ consoleProps: () => {
+ return {
+ Actual: res.act,
+ }
+ },
+ })
+ } catch (e) {
+ if (Cypress.env('SNAPSHOT_UPDATE') && !e.failedMatcher && e.act) {
+ Cypress.log({
+ name: 'assert',
+ message: `snapshot updated: **${exactSpecName}**`,
+ state: 'passed',
+ consoleProps: () => {
+ return {
+ Expected: exp,
+ Actual: e.act,
+ }
+ },
+ })
+
+ return cy.task('saveSnapshot', {
+ file,
+ what: e.act,
+ exactSpecName,
+ }, { log: false })
+ }
+
+ Cypress.log({
+ name: 'assert',
+ message: `**snapshot failed match**: ${exactSpecName}`,
+ state: 'failed',
+ consoleProps: () => {
+ return {
+ Expected: exp,
+ Actual: e.act,
+ }
+ },
+ })
+
+ throw e
+ }
+ })
+ })
+ }
+
+ chai.Assertion.addMethod('matchDeep', matchDeepCypress)
+ chai.Assertion.addMethod('matchSnapshot', matchSnapshotCypress)
+
+ after(() => {
+ snapshotIndex = {}
+ })
+
+ before(() => {
+ const btn = addButton('toggle-snapshot-update', '', () => {
+ const prev = Cypress.env('SNAPSHOT_UPDATE')
+
+ Cypress.env('SNAPSHOT_UPDATE', !prev)
+ updateText()
+ })
+ const btnIcon = btn.children().first()
+ const updateText = () => {
+ return btnIcon.text(Cypress.env('SNAPSHOT_UPDATE') ? 'snapshot\nupdate\non' : 'snapshot\nupdate\noff')
+ .css({ 'font-size': '10px', 'line-height': '0.9' })
+ .html(btnIcon.html().replace(/\n/g, '
'))
+ }
+
+ updateText()
+ })
+
+ const addButton = (name, faClass, fn) => {
+ $(`#${name}`, window.top.document).remove()
+
+ const btn = $(``, window.top.document)
+ const container = $(
+ '.toggle-auto-scrolling.auto-scrolling-enabled',
+ window.top.document,
+ ).closest('.controls')
+
+ container.prepend(btn)
+
+ btn.on('click', fn)
+
+ return btn
+ }
+}
+
+// // unfortunate, but sinon uses isPrototype of, which will not work for
+// // two different sinon versions
+// match.isMatcher = (obj) => {
+// return _.isFunction(_.get(obj, 'test')) &&
+// _.isString(_.get(obj, 'message')) &&
+// _.isFunction(_.get(obj, 'and')) &&
+// _.isFunction(_.get(obj, 'or'))
+// }
+
+const matcherStringToObj = (mes) => {
+ const res = mes.replace(/typeOf\("(\w+)"\)/, '$1')
+
+ const ret = {}
+
+ ret.toString = () => {
+ return `${res}`
+ }
+
+ ret.toJSON = () => {
+ return `match.${res}`
+ }
+
+ return ret
+}
+
+const matchDeep = function (matchers, exp, optsArg) {
+ let m = matchers
+
+ if (exp === undefined) {
+ exp = m
+ m = {}
+ }
+
+ // debug(optsArg)
+
+ const opts = _.defaults(optsArg, {
+ message: 'to match',
+ Cypress: false,
+ diff: true,
+ expectedOnly: false,
+ sinon: null,
+ })
+
+ if (!opts.sinon) {
+ opts.sinon = sinon
+ }
+
+ const match = opts.sinon.match
+ const isAnsi = !opts.Cypress
+
+ const act = this._obj
+
+ debug('matchDeep:actual:', act)
+
+ m = _.map(m, (val, key) => {
+ return [key.split('.'), val]
+ })
+
+ const diffStr = withMatchers(m, match, opts.expectedOnly)(exp, act)
+
+ if (diffStr.changed) {
+ let e = _.extend(new Error(), { act: diffStr.act, failedMatcher: diffStr.opts.failedMatcher })
+
+ e.message = isAnsi ? `\n${diffStr.text}` : stripAnsi(diffStr.text)
+
+ if (_.isString(act)) {
+ e.message = `\n${stripIndent`
+ SnapshotError: Failed to match snapshot
+ Expected:\n---\n${printVar(exp)}\n---
+ Actual:\n---\n${printVar(diffStr.act)}\n---
+ `}`
+ }
+
+ throw e
+ }
+
+ return diffStr
+}
+
+let typeColors = {
+ modified: chalk.yellow,
+ added: chalk.green,
+ removed: chalk.red,
+ normal: chalk.gray,
+ failed: chalk.redBright,
+}
+
+let options = {
+ indent: 2,
+ indentChar: ' ',
+ newLineChar: '\n',
+ wrap: function wrap (type, text) {
+ return typeColors[type](text)
+ },
+}
+
+let indent = ''
+
+for (let i = 0; i < options.indent; i++) {
+ indent += options.indentChar
+}
+
+function isObject (obj) {
+ return typeof obj === 'object' && obj && getType(obj) !== 'RegExp'
+ // return typeof obj === 'object' && obj && !Array.isArray(obj)
+}
+
+function printVar (variable) {
+ switch (getType(variable)) {
+ case 'Null':
+ return variable
+ case 'Undefined':
+ return variable
+ case 'Boolean':
+ return variable
+ case 'Number':
+ return variable
+ case 'Function':
+ return `[Function${variable.name ? ` ${variable.name}` : ''}]`
+
+ // return variable.toString().replace(/\{.+\}/, '{}')
+
+ case 'Array':
+ case 'Object':
+
+ if (variable.toJSON) {
+ return variable.toJSON()
+ }
+
+ return stringifyShort(variable)
+
+ case 'String':
+ return `${variable}`
+
+ default: return `${variable}`
+ }
+}
+
+function indentSubItem (text) {
+ return text.split(options.newLineChar).map(function onMap (line, index) {
+ if (index === 0) {
+ return line
+ }
+
+ return indent + line
+ }).join(options.newLineChar)
+}
+
+function getType (obj) {
+ return Object.prototype.toString.call(obj).split('[object ').join('').slice(0, -1)
+}
+function keyChanged (key, text) {
+ return `${indent + key}: ${indentSubItem(text)}${options.newLineChar}`
+}
+
+function keyRemoved (key, variable) {
+ return options.wrap('removed', `- ${key}: ${printVar(variable)}`) + options.newLineChar
+}
+
+function keyAdded (key, variable) {
+ return options.wrap('added', `+ ${key}: ${printVar(variable)}`) + options.newLineChar
+}
+
+function parseMatcher (obj, match) {
+ if (match.isMatcher(obj)) {
+ return obj
+ }
+
+ let parseObj = (_.isString(obj) && obj) || (obj && obj.toJSON && obj.toJSON())
+
+ if (parseObj) {
+ const parsed = /match\.(.*)/.exec(parseObj)
+
+ if (parsed) {
+ // debug('parsed matcher from string:', parsed[1])
+
+ return match[parsed[1]]
+ }
+
+ return obj
+ }
+
+ return obj
+}
+
+function setReplacement (act, val, path) {
+ if (_.isFunction(val)) {
+ return val(act, path)
+ }
+
+ return val
+}
+
+const withMatchers = (matchers, match, expectedOnly = false) => {
+ const getReplacementFor = (path = [], m) => {
+ for (let rep of m) {
+ const wildCards = _.keys(_.pickBy(rep[0], (value) => {
+ return value === '*'
+ }))
+
+ const _path = _.map(path, (value, key) => {
+ if (_.includes(wildCards, `${key}`)) {
+ return '*'
+ }
+
+ return value
+ })
+
+ const matched = _path.join('.').endsWith(rep[0].join('.'))
+
+ // (_.last(_path) === _.last(val[0]))
+ // && _.isEqual(_.intersection(_path, val[0]), val[0])
+
+ if (matched) {
+ return rep[1]
+ }
+ }
+
+ return no_replacement
+ }
+
+ const testValue = (matcher, value) => {
+ // if (match.isMatcher(value)) {
+ // if (value.toString() === matcher.toString()) {
+ // return true
+ // }
+ // }
+
+ if (matcher.test(value)) {
+ return true
+ }
+
+ // addErr(new Error(`replace matcher failed: ${genError(newPath, matcher.toString(), value)}`))
+
+ return false
+ }
+
+ const no_replacement = {}
+
+ const diff = (exp, act, path = ['^'], optsArg) => {
+ const opts = _.defaults({}, optsArg, {
+ expectedOnly,
+ })
+
+ if (path.length > 15) {
+ throw new Error(`exceeded max depth on ${path.slice(0, 4)} ... ${path.slice(-4)}`)
+ }
+
+ // console.log(act)
+ let text = ''
+ let changed = false
+ let itemDiff
+ let keys
+ let subOutput = ''
+
+ let replacement = getReplacementFor(path, matchers)
+
+ // console.log(path)
+
+ if (replacement !== no_replacement) {
+ if (match.isMatcher(replacement)) {
+ if (testValue(replacement, act)) {
+ act = matcherStringToObj(replacement.message).toJSON()
+ } else {
+ opts.failedMatcher = true
+ if (!_.isFunction(act)) {
+ act = _.clone(act)
+ }
+
+ exp = replacement
+ }
+ } else {
+ act = setReplacement(act, replacement, path)
+ }
+ } else {
+ if (!_.isFunction(act) && !_.isFunction(_.get(act, 'toJSON'))) {
+ act = _.clone(act)
+ }
+
+ exp = parseMatcher(exp, match)
+ if (match.isMatcher(exp)) {
+ if (testValue(exp, act)) {
+ act = matcherStringToObj(exp.message).toJSON()
+
+ return {
+ text: '',
+ changed: false,
+ act,
+ }
+ }
+
+ return {
+ text: options.wrap('failed', `${chalk.green(printVar(act))} ⛔ ${matcherStringToObj(exp.message).toJSON()}`),
+ changed: true,
+ act,
+ }
+ }
+ }
+
+ if (_.isFunction(_.get(act, 'toJSON'))) {
+ act = act.toJSON()
+ }
+
+ if (isObject(exp) && isObject(act) && !match.isMatcher(exp)) {
+ keys = _.keysIn(exp)
+ let actObj = _.extend({}, act)
+ let key
+
+ keys.sort()
+ for (let i = 0; i < keys.length; i++) {
+ key = keys[i]
+ const isUndef = exp[key] === undefined
+
+ if (_.hasIn(act, key) || isUndef) {
+ itemDiff = diff(exp[key], act[key], path.concat([key]))
+ _.defaults(opts, itemDiff.opts)
+ act[key] = itemDiff.act
+ if (itemDiff.changed) {
+ subOutput += keyChanged(key, itemDiff.text)
+ changed = true
+ }
+ } else {
+ subOutput += keyRemoved(key, exp[key])
+ changed = true
+ }
+
+ delete actObj[key]
+ }
+
+ let addedKeys = _.keysIn(actObj)
+
+ if (!opts.expectedOnly) {
+ for (let i = 0; i < addedKeys.length; i++) {
+ const key = addedKeys[i]
+ const val = act[key]
+
+ const addDiff = diff(val, val, path.concat([key]))
+
+ _.defaults(opts, addDiff.opts)
+
+ act[key] = addDiff.act
+ if (act[key] === undefined) continue
+
+ if (opts.failedMatcher) {
+ subOutput += addDiff.text
+ } else {
+ subOutput += keyAdded(key, act[key])
+ }
+
+ changed = true
+ }
+ }
+
+ if (changed) {
+ let renderBracket = false
+
+ if (_.isArray(act) && _.isArray(exp)) {
+ renderBracket = true
+ }
+
+ const _O = renderBracket ? '[' : '{'
+ const _C = renderBracket ? ']' : '}'
+
+ text = options.wrap('normal', `${_O}${options.newLineChar}${subOutput}${_C}`)
+ }
+ } else if (match.isMatcher(exp)) {
+ debug('is matcher')
+ if (!testValue(exp, act)) {
+ text = options.wrap('failed', `${chalk.green(printVar(act))} ⛔ ${matcherStringToObj(exp.message).toJSON()}`)
+ changed = true
+ }
+ } else if (isObject(act)) {
+ debug('only act is obj')
+
+ const addDiff = diff({}, act, path, { expectedOnly: false })
+
+ _.defaults(opts, addDiff.opts)
+
+ return _.extend({},
+ addDiff, {
+ changed: true,
+ text: options.wrap('removed', `${printVar(exp)}\n${options.wrap('added', addDiff.text)}`),
+ })
+ } else {
+ debug('neither is obj')
+ exp = printVar(exp)
+ act = printVar(act)
+
+ if (exp !== act) {
+ text = options.wrap('modified', `${exp} ➡️ ${act}`)
+ changed = true
+ }
+ }
+
+ return {
+ changed,
+ text,
+ act,
+ opts,
+ }
+ }
+
+ return diff
+}
+
+const stringifyShort = (obj) => {
+ const constructorName = _.get(obj, 'constructor.name')
+
+ if (constructorName && !_.includes(['Object', 'Array'], constructorName)) {
+ return `{${constructorName}}`
+ }
+
+ if (_.isArray(obj)) {
+ return `[Array ${obj.length}]`
+ }
+
+ if (_.isObject(obj)) {
+ return `{Object ${Object.keys(obj).length}}`
+ }
+
+ return obj
+}
+
+module.exports = {
+ registerInCypress,
+ matchDeep,
+ stringifyShort,
+
+}
diff --git a/packages/driver/test/cypress/plugins/snapshot/index.js b/packages/driver/test/cypress/plugins/snapshot/index.js
new file mode 100644
index 000000000000..2b627d61afad
--- /dev/null
+++ b/packages/driver/test/cypress/plugins/snapshot/index.js
@@ -0,0 +1,56 @@
+const snapshotCore = require('snap-shot-core')
+const _ = require('lodash')
+
+/**
+ *
+ * @param {{what: any, file: string, exactSpecName: string, store?: Function compare?: Function}}
+ */
+const getSnapshot = (opts) => {
+ let result = null
+
+ opts = _.defaults(opts, {
+ what: 'aaaaa',
+ })
+
+ opts = _.assign(opts, {
+ compare: ({ expected }) => {
+ result = expected
+ throw new Error('bail')
+ },
+ opts: {
+ update: false,
+ ci: true,
+ },
+ })
+
+ try {
+ snapshotCore.core(
+ _.extend(
+ {},
+ opts,
+ ),
+ )
+ } catch (e) {
+ null
+ }
+
+ return result
+}
+
+const saveSnapshot = (opts) => {
+ opts = _.defaults(opts, {
+ })
+
+ return snapshotCore.core(_.extend({},
+ opts,
+ {
+ opts: {
+ update: true,
+ },
+ }))
+}
+
+module.exports = {
+ saveSnapshot,
+ getSnapshot,
+}
diff --git a/packages/driver/test/cypress/support/commands.js b/packages/driver/test/cypress/support/commands.js
new file mode 100644
index 000000000000..d7e09b0f7cbb
--- /dev/null
+++ b/packages/driver/test/cypress/support/commands.js
@@ -0,0 +1,7 @@
+Cypress.Commands.addAll({ prevSubject: 'element' }, {
+ pseudo (subject, pseudoSel) {
+ const win = Cypress.dom.getWindowByElement(subject.get(0))
+
+ cy.wrap(win.getComputedStyle(subject.get(0), pseudoSel))
+ },
+})
diff --git a/packages/driver/test/cypress/support/helpers.js b/packages/driver/test/cypress/support/helpers.js
new file mode 100644
index 000000000000..5a0f342be8e8
--- /dev/null
+++ b/packages/driver/test/cypress/support/helpers.js
@@ -0,0 +1,153 @@
+const { _ } = Cypress
+const debug = require('debug')('spec')
+
+const getFirstSubjectByName = (name) => {
+ return cy.queue.find({ name }).get('subject')
+}
+
+const getQueueNames = () => {
+ return _.map(cy.queue, 'name')
+}
+
+const createHooks = (win, hooks = []) => {
+ _.each(hooks, (hook) => {
+ if (_.isString(hook)) {
+ hook = { type: hook }
+ }
+
+ let { type, fail, fn } = hook
+
+ if (fn) {
+ if (hook.eval) {
+ const fnStr = fn.toString()
+
+ const newFn = function () {
+ return win.eval(`(${fnStr})`).call(this)
+ }
+
+ Object.defineProperty(newFn, 'length', { value: fn.length })
+ fn = newFn
+ }
+
+ return win[type](fn)
+ }
+
+ if (fail) {
+ const numFailures = fail
+
+ return win[type](() => {
+ if (_.isNumber(fail) && fail-- <= 0) {
+ debug(`hook pass after (${numFailures}) failures: ${type}`)
+ win.assert(true, type)
+
+ return
+ }
+
+ debug(`hook fail: ${type}`)
+
+ win.assert(false, type)
+
+ throw new Error(`hook failed: ${type}`)
+ })
+ }
+
+ return win[type](() => {
+ win.assert(true, type)
+ debug(`hook pass: ${type}`)
+ })
+ })
+}
+
+const createTests = (win, tests = []) => {
+ _.each(tests, (test) => {
+ if (_.isString(test)) {
+ test = { name: test }
+ }
+
+ let { name, pending, fail, fn, only } = test
+
+ let it = win.it
+
+ if (only) {
+ it = it['only']
+ }
+
+ if (fn) {
+ if (test.eval) {
+ const fnStr = fn.toString()
+
+ const newFn = function () {
+ return win.eval(`(${fnStr})`).call(this)
+ }
+
+ Object.defineProperty(newFn, 'length', { value: fn.length })
+ fn = newFn
+ }
+
+ return it(name, fn)
+ }
+
+ if (pending) {
+ return it(name)
+ }
+
+ if (fail) {
+ return it(name, () => {
+ if (_.isNumber(fail) && fail-- === 0) {
+ debug(`test pass after retry: ${name}`)
+ win.assert(true, name)
+
+ return
+ }
+
+ debug(`test fail: ${name}`)
+ win.assert(false, name)
+
+ throw new Error(`test fail: ${name}`)
+ })
+ }
+
+ return it(name, () => {
+ debug(`test pass: ${name}`)
+ win.assert(true, name)
+ })
+ })
+}
+
+const createSuites = (win, suites = {}) => {
+ _.each(suites, (obj, suiteName) => {
+ let fn = () => {
+ createHooks(win, obj.hooks)
+ createTests(win, obj.tests)
+ createSuites(win, obj.suites)
+ }
+
+ if (_.isFunction(obj)) {
+ fn = evalFn(win, obj)
+ }
+
+ win.describe(suiteName, fn)
+ })
+}
+
+const generateMochaTestsForWin = (win, obj) => {
+ createHooks(win, obj.hooks)
+ createTests(win, obj.tests)
+ createSuites(win, obj.suites)
+}
+
+// window.localStorage.debug = 'spec*'
+
+module.exports = {
+ getQueueNames,
+
+ getFirstSubjectByName,
+
+ generateMochaTestsForWin,
+}
+
+const evalFn = (win, fn) => {
+ return function () {
+ return win.eval(`(${fn.toString()})`).call(this)
+ }
+}
From 70923b4d5d2f50a98b770e9651d94689c32a344e Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 23 Mar 2020 16:31:21 -0400
Subject: [PATCH 04/86] add server reporter.spec.js for testing mocha output
---
.../reporter.spec.js.snapshot.js | 367 ++++++++++++++++++
packages/server/test/matchDeep.js | 158 ++++++++
packages/server/test/support/helpers/utils.js | 91 +++++
packages/server/test/unit/reporter.spec.js | 146 +++++++
4 files changed, 762 insertions(+)
create mode 100644 packages/server/__snapshots__/reporter.spec.js.snapshot.js
create mode 100644 packages/server/test/matchDeep.js
create mode 100644 packages/server/test/support/helpers/utils.js
create mode 100644 packages/server/test/unit/reporter.spec.js
diff --git a/packages/server/__snapshots__/reporter.spec.js.snapshot.js b/packages/server/__snapshots__/reporter.spec.js.snapshot.js
new file mode 100644
index 000000000000..540d099a727d
--- /dev/null
+++ b/packages/server/__snapshots__/reporter.spec.js.snapshot.js
@@ -0,0 +1,367 @@
+exports['fail in [afterEach] reporter results'] = {
+ "stats": {
+ "suites": 1,
+ "tests": 1,
+ "passes": 0,
+ "pending": 0,
+ "skipped": 0,
+ "failures": 1,
+ "wallClockStartedAt": "1970-01-01T00:00:00.000Z",
+ "wallClockEndedAt": "1970-01-01T00:00:00.000Z",
+ "wallClockDuration": 0
+ },
+ "reporter": "spec",
+ "reporterStats": {
+ "suites": 1,
+ "tests": 1,
+ "passes": 1,
+ "pending": 0,
+ "failures": 1,
+ "start": "match.date",
+ "end": "match.date",
+ "duration": "match.number"
+ },
+ "hooks": [
+ {
+ "hookId": "h1",
+ "hookName": "after each",
+ "title": [
+ "\"after each\" hook"
+ ],
+ "body": "[body]"
+ }
+ ],
+ "tests": [
+ {
+ "testId": "r3",
+ "title": [
+ "suite 1",
+ "test 1"
+ ],
+ "state": "failed",
+ "body": "[body]",
+ "stack": null,
+ "error": null,
+ "timings": {
+ "lifecycle": 1,
+ "test": {
+ "fnDuration": 1,
+ "afterFnDuration": 1
+ },
+ "after each": [
+ {
+ "hookId": "h1",
+ "fnDuration": 1,
+ "afterFnDuration": 1
+ }
+ ]
+ },
+ "failedFromHookId": "h1",
+ "wallClockStartedAt": "1970-01-01T00:00:00.000Z",
+ "wallClockDuration": 1,
+ "videoTimestamp": null
+ }
+ ]
+}
+
+exports['fail in [afterEach] runner emit'] = [
+ [
+ "start",
+ null
+ ],
+ [
+ "suite",
+ "{Suite}"
+ ],
+ [
+ "suite",
+ "{Suite}"
+ ],
+ [
+ "test",
+ "{Test}"
+ ],
+ [
+ "pass",
+ "{Test}"
+ ],
+ [
+ "test end",
+ "{Test}"
+ ],
+ [
+ "hook",
+ "{Object 57}"
+ ],
+ [
+ "fail",
+ "{Object 57}",
+ "{Object 6}"
+ ],
+ [
+ "test:after:run",
+ "{Test}"
+ ],
+ [
+ "suite end",
+ "{Suite}"
+ ],
+ [
+ "suite end",
+ "{Suite}"
+ ],
+ [
+ "end",
+ null
+ ]
+]
+
+exports['fail in [afterEach] stdout'] = `
+
+
+ suite 1
+ \u2713 test 1
+ 1) "after each" hook for "test 1"
+
+
+ 1 passing
+ 1 failing
+
+ 1) suite 1 "after each" hook for "test 1":
+
+
+
+
+
+
+`
+
+exports['fail in [beforeEach] reporter results'] = {
+ "stats": {
+ "suites": 1,
+ "tests": 1,
+ "passes": 0,
+ "pending": 0,
+ "skipped": 0,
+ "failures": 1,
+ "wallClockStartedAt": "1970-01-01T00:00:00.000Z",
+ "wallClockEndedAt": "1970-01-01T00:00:00.000Z",
+ "wallClockDuration": 0
+ },
+ "reporter": "spec",
+ "reporterStats": {
+ "suites": 1,
+ "tests": 0,
+ "passes": 0,
+ "pending": 0,
+ "failures": 1,
+ "start": "match.date",
+ "end": "match.date",
+ "duration": "match.number"
+ },
+ "hooks": [
+ {
+ "hookId": "h1",
+ "hookName": "before each",
+ "title": [
+ "\"before each\" hook"
+ ],
+ "body": "[body]"
+ }
+ ],
+ "tests": [
+ {
+ "testId": "r3",
+ "title": [
+ "suite 1",
+ "test 1"
+ ],
+ "state": "failed",
+ "body": "[body]",
+ "stack": null,
+ "error": null,
+ "timings": {
+ "lifecycle": 1,
+ "before each": [
+ {
+ "hookId": "h1",
+ "fnDuration": 1,
+ "afterFnDuration": 1
+ }
+ ]
+ },
+ "failedFromHookId": "h1",
+ "wallClockStartedAt": "1970-01-01T00:00:00.000Z",
+ "wallClockDuration": 1,
+ "videoTimestamp": null
+ }
+ ]
+}
+
+exports['fail in [beforeEach] runner emit'] = [
+ [
+ "start",
+ null
+ ],
+ [
+ "suite",
+ "{Suite}"
+ ],
+ [
+ "suite",
+ "{Suite}"
+ ],
+ [
+ "test",
+ "{Test}"
+ ],
+ [
+ "hook",
+ "{Object 54}"
+ ],
+ [
+ "fail",
+ "{Object 54}",
+ "{Object 6}"
+ ],
+ [
+ "test:after:run",
+ "{Test}"
+ ],
+ [
+ "suite end",
+ "{Suite}"
+ ],
+ [
+ "suite end",
+ "{Suite}"
+ ],
+ [
+ "end",
+ null
+ ]
+]
+
+exports['fail in [beforeEach] stdout'] = `
+
+
+ suite 1
+ 1) "before each" hook for "test 1"
+
+
+ 0 passing
+ 1 failing
+
+ 1) suite 1 "before each" hook for "test 1":
+
+
+
+
+
+
+`
+
+exports['simple_single_test reporter results'] = {
+ "stats": {
+ "suites": 1,
+ "tests": 1,
+ "passes": 1,
+ "pending": 0,
+ "skipped": 0,
+ "failures": 0,
+ "wallClockStartedAt": "1970-01-01T00:00:00.000Z",
+ "wallClockEndedAt": "1970-01-01T00:00:00.000Z",
+ "wallClockDuration": 0
+ },
+ "reporter": "spec",
+ "reporterStats": {
+ "suites": 1,
+ "tests": 1,
+ "passes": 1,
+ "pending": 0,
+ "failures": 0,
+ "start": "match.date",
+ "end": "match.date",
+ "duration": "match.number"
+ },
+ "hooks": [],
+ "tests": [
+ {
+ "testId": "r3",
+ "title": [
+ "suite 1",
+ "test 1"
+ ],
+ "state": "passed",
+ "body": "[body]",
+ "stack": null,
+ "error": null,
+ "timings": {
+ "lifecycle": 1,
+ "test": {
+ "fnDuration": 1,
+ "afterFnDuration": 1
+ }
+ },
+ "failedFromHookId": null,
+ "wallClockStartedAt": "1970-01-01T00:00:00.000Z",
+ "wallClockDuration": 1,
+ "videoTimestamp": null
+ }
+ ]
+}
+
+exports['simple_single_test runner emit'] = [
+ [
+ "start",
+ null
+ ],
+ [
+ "suite",
+ "{Suite}"
+ ],
+ [
+ "suite",
+ "{Suite}"
+ ],
+ [
+ "test",
+ "{Test}"
+ ],
+ [
+ "pass",
+ "{Test}"
+ ],
+ [
+ "test end",
+ "{Test}"
+ ],
+ [
+ "test:after:run",
+ "{Test}"
+ ],
+ [
+ "suite end",
+ "{Suite}"
+ ],
+ [
+ "suite end",
+ "{Suite}"
+ ],
+ [
+ "end",
+ null
+ ]
+]
+
+exports['simple_single_test stdout'] = `
+
+
+ suite 1
+ \u2713 test 1
+
+
+ 1 passing
+
+
+`
diff --git a/packages/server/test/matchDeep.js b/packages/server/test/matchDeep.js
new file mode 100644
index 000000000000..5795ec33f102
--- /dev/null
+++ b/packages/server/test/matchDeep.js
@@ -0,0 +1,158 @@
+const { matchDeep } = require('../../driver/test/cypress/plugins/snapshot/command')
+const { getSnapshot, saveSnapshot } = require('../../driver/test/cypress/plugins/snapshot')
+const chai = require('chai')
+const _ = require('lodash')
+const sinon = require('sinon')
+// const Debug = require('debug')
+// const debug = Debug('plugin:snapshot')
+
+/** @type {Mocha.ITest} */
+let currentTest
+
+const registerInMocha = () => {
+ let snapshotIndex = {}
+
+ global.beforeEach(function () {
+ snapshotIndex = {}
+ if (this.currentTest) {
+ currentTest = this.currentTest
+ }
+ })
+
+ const matchSnapshot = function (m, snapshotName) {
+ const ctx = this
+ const testName = currentTest.fullTitle()
+ const file = currentTest.file
+
+ snapshotIndex[testName] = (snapshotIndex[testName] || 0) + 1
+ const exactSpecName = snapshotName || `${testName} #${snapshotIndex[testName]}`
+
+ const exp = getSnapshot({
+ file,
+ exactSpecName,
+ })
+
+ try {
+ matchDeep.call(ctx, m, exp, { message: 'to match snapshot', chai, setGlobalSnapshot: _.noop, sinon })
+ } catch (e) {
+ if (_.has(e, 'act')) {
+ if (process.env['SNAPSHOT_UPDATE']) {
+ saveSnapshot({
+ file,
+ exactSpecName,
+ what: e.act,
+ })
+
+ return
+ }
+ }
+
+ throw e
+ }
+ }
+
+ const matchDeepMocha = function (...args) {
+ let ret
+ let act
+
+ try {
+ ret = matchDeep.apply(this, [args[0], args[1], { chai, setGlobalSnapshot: _.noop, sinon, expectedOnly: true }])
+ act = ret.act
+ } catch (e) {
+ if (e.act) {
+ act = e.act
+ }
+
+ throw e
+ } finally {
+ if (this.__flags.debug) {
+ // eslint-disable-next-line
+ console.info(act)
+ }
+ }
+
+ return ret
+ }
+
+ chai.Assertion.addMethod('matchSnapshot', matchSnapshot)
+ chai.Assertion.addMethod('matchDeep', matchDeepMocha)
+
+ chai.Assertion.addProperty('debug', function () {
+ this.__flags.debug = true
+ // debug(this)
+ })
+}
+
+const stringifyShort = (obj) => {
+ const constructorName = _.get(obj, 'constructor.name')
+
+ if (constructorName && !_.includes(['Object'], constructorName)) {
+ return `{${constructorName}}`
+ }
+
+ if (_.isArray(obj)) {
+ return `[Array ${obj.length}]`
+ }
+
+ if (_.isObject(obj)) {
+ return `{Object ${Object.keys(obj).length}}`
+ }
+
+ return obj
+}
+const parseMatcher = (matcher) => {
+ const regex = /match\.(.*)/
+
+ if (_.isString(matcher)) {
+ const parsed = regex.exec(matcher)
+
+ if (parsed) {
+ return parsed[1]
+ }
+ }
+}
+
+const parseSnapshot = (s) => {
+ s = _.cloneDeep(s)
+ const recurse = (_obj) => {
+ _.each(_obj, (value, key) => {
+ const matcherType = parseMatcher(value)
+
+ if (matcherType) {
+ const replacement = getFake(matcherType)
+
+ _obj[key] = replacement
+
+ return
+ }
+
+ if (_.isObjectLike(value)) {
+ return recurse(value)
+ }
+ })
+ }
+
+ recurse(s)
+
+ return s
+}
+const getFake = (matcherType) => {
+ if (matcherType === 'number') {
+ return 1
+ }
+
+ if (matcherType === 'date') {
+ return new Date(0)
+ }
+
+ if (matcherType === 'string') {
+ return 'foobar'
+ }
+}
+
+module.exports = {
+ registerInMocha,
+ stringifyShort,
+ parseSnapshot,
+
+}
diff --git a/packages/server/test/support/helpers/utils.js b/packages/server/test/support/helpers/utils.js
new file mode 100644
index 000000000000..88550dddcac7
--- /dev/null
+++ b/packages/server/test/support/helpers/utils.js
@@ -0,0 +1,91 @@
+/* eslint-disable prefer-rest-params */
+const _write = process.stdout.write
+const _ = require('lodash')
+const stripAnsi = require('strip-ansi')
+const debug = require('debug')('utils')
+const chalk = require('chalk')
+
+const spyStdout = (obj, props) => {
+ return spyOn(obj, props, () => stdout.capture(), (ret) => {
+ if (ret && ret.isPending) {
+ return ret.tap(() => {
+ stdout.restore()
+ })
+ }
+
+ stdout.restore()
+
+ return
+ })
+}
+
+const stdout = {
+ capture () {
+ const logs = []
+
+ const write = process.stdout.write
+
+ process.stdout.write = function (str) {
+ logs.push(str)
+
+ const args = (args) => {
+ debug.extend('stdout')(...args)
+
+ return _.map(_.map(args, stripAnsi), (v) => _.isString(v) && chalk.rgb(160, 100, 160)(v))
+ }
+
+ write.apply(this, args(arguments))
+ }
+
+ return {
+ data: logs,
+
+ toString: () => {
+ return stripAnsi(logs.join(''))
+ },
+ }
+ },
+
+ restore () {
+ process.stdout.write = _write
+ },
+}
+
+const spyOn = (obj, props, fn, fn2) => {
+ if (_.isString(props)) {
+ props = [props]
+ }
+
+ const rets = _.map(props, (prop) => {
+ const _fn = obj[prop]
+
+ const stub = obj[prop] = sinon.stub()
+ .callsFake(function () {
+ fn && fn.apply(this, arguments)
+
+ let ret = _fn.apply(this, arguments)
+
+ if (fn2) {
+ const fn2_ret = fn2.apply(this, [ret])
+
+ if (!_.isUndefined(fn2_ret)) {
+ ret = fn2_ret
+ }
+ }
+
+ return ret
+ })
+
+ return stub
+ })
+
+ if (rets.length === 1) return rets[0]
+
+ return rets
+}
+
+module.exports = {
+ spyOn,
+ stdout,
+ spyStdout,
+}
diff --git a/packages/server/test/unit/reporter.spec.js b/packages/server/test/unit/reporter.spec.js
new file mode 100644
index 000000000000..6d06456f38a6
--- /dev/null
+++ b/packages/server/test/unit/reporter.spec.js
@@ -0,0 +1,146 @@
+// process.env.SNAPSHOT_UPDATE = 1
+require('../spec_helper.coffee')
+const Reporter = require('../../lib/reporter')
+const _ = require('lodash')
+const sinon = require('sinon')
+const Debug = require('debug')
+const debug = Debug('spec:retries')
+const { spyOn, stdout } = require('../support/helpers/utils')
+const { registerInMocha, parseSnapshot, stringifyShort } = require('../matchDeep')
+
+registerInMocha()
+
+const { match } = sinon
+
+const events = require('../../../driver/test/__snapshots__/runner.spec.js.snapshot')
+const { EventSnapshots } = require('../../../driver/test/cypress/integration/cypress/eventSnapshots')
+
+let currentReporter
+let currentStubs
+
+/** @param {typeof EventSnapshots.FAIL_IN_AFTER} snapshotName */
+const getSnapshot = (snapshotName) => {
+ return _.mapValues(snapshotName, (v) => parseSnapshot(events[v]))
+}
+
+let stdoutStub
+
+function createReporter ({ setRunnables, mocha }) {
+ stdoutStub = stdout.capture()
+
+ const reporter = Reporter()
+
+ currentReporter = reporter
+
+ const runnables = parseSnapshot(setRunnables)[0][1]
+ const mochaEvents = parseSnapshot(mocha)
+
+ // const runnables = setRunnables[0][1]
+
+ reporter.setRunnables(runnables)
+
+ const stubs = {}
+
+ currentStubs = stubs
+
+ stubs.reporterEmit = spyOn(reporter, 'emit', debug.extend('reporter:emit'))
+ stubs.runnerEmit = spyOn(reporter.runner, 'emit', debug.extend('runner:emit'))
+
+ _.each(mochaEvents, (event) => {
+ reporter.emit(...event.slice(1, 3))
+ })
+
+ stdout.restore()
+
+ return { stubs, reporter }
+}
+
+module.exports = {
+ createReporter,
+}
+
+describe('reporter retries', () => {
+ const snapshot = (name) => {
+ if (!name) throw new Error('snapshot name cannot be empty')
+
+ /*
+ expect(currentReporter.runnables).to.matchSnapshot({
+ parent: stringifyShort,
+ 'addListener': undefined,
+ 'clearTimeout': undefined,
+ 'clone': undefined,
+ 'currentRetry': undefined,
+ 'emit': undefined,
+ 'enableTimeouts': undefined,
+ 'eventNames': undefined,
+ 'fullTitle': undefined,
+ 'getMaxListeners': undefined,
+ 'globals': undefined,
+ 'inspect': undefined,
+ 'listenerCount': undefined,
+ 'listeners': undefined,
+ 'on': undefined,
+ 'once': undefined,
+ 'prependListener': undefined,
+ 'prependOnceListener': undefined,
+ 'removeAllListeners': undefined,
+ 'removeListener': undefined,
+ 'resetTimeout': undefined,
+ 'retries': undefined,
+ 'run': undefined,
+ 'setMaxListeners': undefined,
+ 'skip': undefined,
+ 'slow': undefined,
+ 'timeout': undefined,
+ 'titlePath': undefined,
+ 'addSuite': undefined,
+ 'addTest': undefined,
+ 'afterAll': undefined,
+ 'afterEach': undefined,
+ 'bail': undefined,
+ 'beforeAll': undefined,
+ 'beforeEach': undefined,
+ 'eachTest': undefined,
+ 'total': undefined,
+ 'speed': undefined,
+ }, `${name} runnables`)
+ */
+ expect(currentStubs.runnerEmit.args).to.matchSnapshot(runnerEmitCleanseMap, `${name} runner emit`)
+ expect(currentReporter.results()).to.matchSnapshot({
+ 'reporterStats.end': match.date,
+ 'reporterStats.start': match.date,
+ 'reporterStats.duration': match.number,
+ }, `${name} reporter results`)
+
+ expect(stdoutStub.toString())
+
+ expect(stdoutStub.toString())
+ .matchSnapshot({ '^': (v) => v.replace(/\(\d+ms\)/g, '') }, `${name} stdout`)
+ }
+
+ afterEach(() => {
+ stdout.restore()
+ })
+
+ it('simple single test', () => {
+ createReporter(getSnapshot(EventSnapshots.SIMPLE_SINGLE_TEST))
+ snapshot('simple_single_test')
+ })
+
+ it('fail [afterEach]', () => {
+ createReporter(getSnapshot(EventSnapshots.FAIL_IN_AFTEREACH))
+
+ snapshot('fail in [afterEach]')
+ })
+
+ it('fail [beforeEach]', () => {
+ createReporter(getSnapshot(EventSnapshots.FAIL_IN_BEFOREEACH))
+
+ snapshot('fail in [beforeEach]')
+ })
+})
+
+const runnerEmitCleanseMap = {
+ '^.*.1': stringifyShort,
+ parent: stringifyShort,
+}
From ca9a1465a29c05067d00277ce4c48ba56b4c52d4 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 23 Mar 2020 16:48:05 -0400
Subject: [PATCH 05/86] fix has-binary yarn.lock
---
yarn.lock | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/yarn.lock b/yarn.lock
index 674f0f8f000b..eec7cf63db72 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -21790,7 +21790,7 @@ socket.io-circular-parser@cypress-io/socket.io-circular-parser#unpatched-has-bin
circular-json "0.5.9"
component-emitter "1.2.1"
debug "~4.1.0"
- has-binary2 cypress-io/has-binary#8580a33df21e8b36a43f57872a82c60829636a92
+ has-binary2 "~1.0.2"
socket.io-client@2.3.0:
version "2.3.0"
From 61613201f91a4b779896b414fc491d6c3602a2b8 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 23 Mar 2020 17:17:57 -0400
Subject: [PATCH 06/86] fix spec expected error message
---
.../driver/test/cypress/integration/commands/task_spec.coffee | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/driver/test/cypress/integration/commands/task_spec.coffee b/packages/driver/test/cypress/integration/commands/task_spec.coffee
index 4ae08cf5430e..7c443db5693b 100644
--- a/packages/driver/test/cypress/integration/commands/task_spec.coffee
+++ b/packages/driver/test/cypress/integration/commands/task_spec.coffee
@@ -164,7 +164,7 @@ describe "src/cy/commands/task", ->
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
- expect(err.message).to.eq("`cy.task('bar')` failed with the following error:\n\nThe task 'bar' was not handled in the plugins file. The following tasks are registered: return:arg, wait, create:long:file\n\nFix this in your plugins file here:\n#{Cypress.config('pluginsFile')}\n\nhttps://on.cypress.io/api/task")
+ expect(err.message).to.eq("`cy.task('bar')` failed with the following error:\n\nThe task 'bar' was not handled in the plugins file. The following tasks are registered: return:arg, wait, create:long:file, getSnapshot, saveSnapshot\n\nFix this in your plugins file here:\n#{Cypress.config('pluginsFile')}\n\nhttps://on.cypress.io/api/task")
done()
cy.task("bar")
From 8fcbf78fac85b9f059003e4d29078c48b2bbdd5c Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 24 Mar 2020 16:48:22 -0400
Subject: [PATCH 07/86] cleanup snapshot utility - use chai assert fn, add test
to runner.spec, pin dep
---
packages/driver/package.json | 2 +-
packages/driver/src/cypress/cy.js | 37 +++++-----
.../__snapshots__/runner.spec.js.snapshot.js | 29 ++++++++
.../integration/cypress/runner.spec.js | 53 +++++++------
.../cypress/plugins/snapshot/command/index.js | 74 ++++++-------------
packages/server/test/unit/reporter.spec.js | 42 -----------
yarn.lock | 2 +-
7 files changed, 107 insertions(+), 132 deletions(-)
diff --git a/packages/driver/package.json b/packages/driver/package.json
index a39270c6970a..c0582572d08e 100644
--- a/packages/driver/package.json
+++ b/packages/driver/package.json
@@ -56,7 +56,7 @@
"parse-domain": "bahmutov/parse-domain#fb60bd4",
"setimmediate": "1.0.5",
"sinon": "8.1.1",
- "snap-shot-core": "^7.4.0",
+ "snap-shot-core": "7.4.0",
"text-mask-addons": "3.8.0",
"underscore": "1.9.1",
"underscore.string": "3.3.5",
diff --git a/packages/driver/src/cypress/cy.js b/packages/driver/src/cypress/cy.js
index 45215e94e2ed..0cf79ec14311 100644
--- a/packages/driver/src/cypress/cy.js
+++ b/packages/driver/src/cypress/cy.js
@@ -74,25 +74,28 @@ const setTopOnError = function (cy) {
// prevent overriding top.onerror twice when loading more than one
// instance of test runner.
- if (!top.__cypress__onerror) {
- top.__cypress__onerror = true
- const onTopError = function () {
- return curCy.onUncaughtException.apply(curCy, arguments)
- }
-
- top.onerror = onTopError
+ if (top.onerror && top.onerror.isCypressHandler) {
+ return
+ }
- // Prevent Mocha from setting top.onerror which would override our handler
- // Since the setter will change which event handler gets invoked, we make it a noop
- return Object.defineProperty(top, 'onerror', {
- set () {},
- get () {
- return onTopError
- },
- configurable: false,
- enumerable: true,
- })
+ const onTopError = function () {
+ return curCy.onUncaughtException.apply(curCy, arguments)
}
+
+ onTopError.isCypressHandler = true
+
+ top.onerror = onTopError
+
+ // Prevent Mocha from setting top.onerror which would override our handler
+ // Since the setter will change which event handler gets invoked, we make it a noop
+ return Object.defineProperty(top, 'onerror', {
+ set () {},
+ get () {
+ return onTopError
+ },
+ configurable: false,
+ enumerable: true,
+ })
}
const create = function (specWindow, Cypress, Cookies, state, config, log) {
diff --git a/packages/driver/test/__snapshots__/runner.spec.js.snapshot.js b/packages/driver/test/__snapshots__/runner.spec.js.snapshot.js
index 73928cc5acb5..6d7939c05b1f 100644
--- a/packages/driver/test/__snapshots__/runner.spec.js.snapshot.js
+++ b/packages/driver/test/__snapshots__/runner.spec.js.snapshot.js
@@ -3494,6 +3494,35 @@ exports['serialize state - hooks'] = {
"numLogs": 0
}
+exports['src/cypress/runner isolated test runner other specs screenshots screenshot after failed test #1'] = [
+ [
+ "take:screenshot",
+ {
+ "titles": [
+ "suite 1",
+ "test 1"
+ ],
+ "testId": "r3",
+ "simple": true,
+ "testFailure": true,
+ "capture": "runner",
+ "clip": {
+ "x": 0,
+ "y": 0,
+ "width": 1000,
+ "height": 660
+ },
+ "viewport": {
+ "width": 1000,
+ "height": 660
+ },
+ "scaled": true,
+ "blackout": [],
+ "startTime": "1970-01-01T00:00:00.000Z"
+ }
+ ]
+]
+
exports['src/cypress/runner isolated test runner test events hook failures fail in [after] #1'] = `
AssertionErrorafter
diff --git a/packages/driver/test/cypress/integration/cypress/runner.spec.js b/packages/driver/test/cypress/integration/cypress/runner.spec.js
index daa3d94a1131..378cf7f6110d 100644
--- a/packages/driver/test/cypress/integration/cypress/runner.spec.js
+++ b/packages/driver/test/cypress/integration/cypress/runner.spec.js
@@ -1124,6 +1124,38 @@ describe('src/cypress/runner', () => {
}
})
+ it('screenshot after failed test', () => {
+ createCypress({
+ suites: {
+ 'suite 1': {
+ tests: [
+ {
+ name: 'test 1',
+ fn: () => {
+ assert(false, 'some error')
+ },
+ eval: true,
+ },
+ ],
+ },
+ },
+ })
+ .then(() => {
+ // sent to server
+ expect(autCypress.automation.withArgs('take:screenshot').args).to.matchSnapshot(cleanseRunStateMap)
+
+ //// on('after:screenshot')
+ // TODO: for some reason snapshot is not properly saved
+ // expect(onAfterScreenshotListener.args).to.matchSnapshot(cleanseRunStateMap)
+
+ //// Screenshot.onAfterScreenshot
+ // TODO: for some reason snapshot is not properly saved
+ // expect(autCypress.Screenshot.onAfterScreenshot.args).to.matchSnapshot(
+ // { '^.0.0': stringifyShort, 'test': stringifyShort, takenAt: match.string },
+ // )
+ })
+ })
+
// NOTE: for test-retries
describe.skip('retries', () => {
it('screenshot during each failed attempt', () => {
@@ -1264,11 +1296,7 @@ describe('src/cypress/runner', () => {
})
const getRunState = (Cypress) => {
- // cypress normally accesses `id` via a closure
const currentRunnable = Cypress.cy.state('runnable')
- // const currentTest = currentRunnable && getTestFromRunnable(currentRunnable)
- // const currentId = currentTest && currentTest.id
-
const currentId = currentRunnable && currentRunnable.id
const s = {
@@ -1297,23 +1325,6 @@ const cleanseRunStateMap = {
'err.stack': '[err stack]',
}
-// const formatEvents = (stub) => {
-// return _.flatMap(stub.args, (args) => {
-// args = args.slice(1)
-// if (['mocha', 'automation:request', 'log:changed'].includes(args[0])) {
-// return []
-// }
-
-// let ret = [args[0]]
-
-// if (args[1] != null) {
-// ret = ret.concat([args[1]])
-// }
-
-// return [ret]
-// })
-// }
-
const shouldHaveTestResults = (passed, failed) => {
return (exitCode) => {
expect(exitCode, 'resolve with failure count').eq(exitCode)
diff --git a/packages/driver/test/cypress/plugins/snapshot/command/index.js b/packages/driver/test/cypress/plugins/snapshot/command/index.js
index 5a7c547f3fb5..a43d0f5414bf 100644
--- a/packages/driver/test/cypress/plugins/snapshot/command/index.js
+++ b/packages/driver/test/cypress/plugins/snapshot/command/index.js
@@ -10,6 +10,16 @@ const debug = Debug('plugin:snapshot')
// window.localStorage.debug = 'spec* plugin:snapshot'
// Debug.enable('plugin:snapshot')
+// prints nice assertion error in command log with modified error message
+function throwErr (e, message, exp, ctx) {
+ try {
+ ctx.assert(false, message, 'sdf', exp, e.act, true)
+ } catch (err) {
+ err.message += `\n\n**- expected + actual:**\n${e.message}`
+ throw err
+ }
+}
+
const registerInCypress = () => {
_ = Cypress._
sinon = Cypress.sinon
@@ -21,10 +31,12 @@ const registerInCypress = () => {
const matchDeepCypress = function (...args) {
const exp = args[1] || args[0]
+ const ctx = this
try {
const res = matchDeep.apply(this, [args[0], args[1], { Cypress, expectedOnly: true }])
+ ctx.assert(true, `Expected **${chai.util.objDisplay(res.act)}** to deep match: **${chai.util.objDisplay(exp)}**`)
Cypress.log({
name: 'assert',
message: `Expected **${chai.util.objDisplay(res.act)}** to deep match: **${chai.util.objDisplay(exp)}**`,
@@ -36,19 +48,12 @@ const registerInCypress = () => {
},
})
} catch (e) {
- Cypress.log({
- name: 'assert',
- message: `Expected **${chai.util.objDisplay(e.act)}** to deep match: **${chai.util.objDisplay(args[1] || args[0])}**`,
- state: 'failed',
- consoleProps: () => {
- return {
- Actual: e.act,
- Expected: exp,
- }
- },
- })
-
- throw e
+ throwErr(
+ e,
+ `Expected **${chai.util.objDisplay(e.act)}** to deep match: **${chai.util.objDisplay(args[1] || args[0])}**`,
+ exp,
+ ctx,
+ )
}
}
@@ -65,34 +70,15 @@ const registerInCypress = () => {
file,
exactSpecName,
}, { log: false })
- .then((exp) => {
+ .then(function (exp) {
try {
snapshotIndex[testName] = snapshotIndex[testName] + 1
const res = matchDeep.call(ctx, m, exp, { message: 'to match snapshot', Cypress, isSnapshot: true, sinon })
- Cypress.log({
- name: 'assert',
- message: `snapshot matched: **${exactSpecName}**`,
- state: 'passed',
- consoleProps: () => {
- return {
- Actual: res.act,
- }
- },
- })
+ ctx.assert(true, `snapshot matched: **${exactSpecName}**`, res.act)
} catch (e) {
if (Cypress.env('SNAPSHOT_UPDATE') && !e.failedMatcher && e.act) {
- Cypress.log({
- name: 'assert',
- message: `snapshot updated: **${exactSpecName}**`,
- state: 'passed',
- consoleProps: () => {
- return {
- Expected: exp,
- Actual: e.act,
- }
- },
- })
+ ctx.assert(true, `snapshot updated: **${exactSpecName}**`, 'dsf', exp, e.act)
return cy.task('saveSnapshot', {
file,
@@ -101,19 +87,7 @@ const registerInCypress = () => {
}, { log: false })
}
- Cypress.log({
- name: 'assert',
- message: `**snapshot failed match**: ${exactSpecName}`,
- state: 'failed',
- consoleProps: () => {
- return {
- Expected: exp,
- Actual: e.act,
- }
- },
- })
-
- throw e
+ throwErr(e, `**snapshot failed match**: ${exactSpecName}`, exp, ctx)
}
})
})
@@ -316,11 +290,11 @@ function keyChanged (key, text) {
}
function keyRemoved (key, variable) {
- return options.wrap('removed', `- ${key}: ${printVar(variable)}`) + options.newLineChar
+ return options.wrap('removed', `**- ${key}: ${printVar(variable)}**`) + options.newLineChar
}
function keyAdded (key, variable) {
- return options.wrap('added', `+ ${key}: ${printVar(variable)}`) + options.newLineChar
+ return options.wrap('added', `**+ ${key}: ${printVar(variable)}**`) + options.newLineChar
}
function parseMatcher (obj, match) {
diff --git a/packages/server/test/unit/reporter.spec.js b/packages/server/test/unit/reporter.spec.js
index 6d06456f38a6..6035b4ef5234 100644
--- a/packages/server/test/unit/reporter.spec.js
+++ b/packages/server/test/unit/reporter.spec.js
@@ -63,48 +63,6 @@ describe('reporter retries', () => {
const snapshot = (name) => {
if (!name) throw new Error('snapshot name cannot be empty')
- /*
- expect(currentReporter.runnables).to.matchSnapshot({
- parent: stringifyShort,
- 'addListener': undefined,
- 'clearTimeout': undefined,
- 'clone': undefined,
- 'currentRetry': undefined,
- 'emit': undefined,
- 'enableTimeouts': undefined,
- 'eventNames': undefined,
- 'fullTitle': undefined,
- 'getMaxListeners': undefined,
- 'globals': undefined,
- 'inspect': undefined,
- 'listenerCount': undefined,
- 'listeners': undefined,
- 'on': undefined,
- 'once': undefined,
- 'prependListener': undefined,
- 'prependOnceListener': undefined,
- 'removeAllListeners': undefined,
- 'removeListener': undefined,
- 'resetTimeout': undefined,
- 'retries': undefined,
- 'run': undefined,
- 'setMaxListeners': undefined,
- 'skip': undefined,
- 'slow': undefined,
- 'timeout': undefined,
- 'titlePath': undefined,
- 'addSuite': undefined,
- 'addTest': undefined,
- 'afterAll': undefined,
- 'afterEach': undefined,
- 'bail': undefined,
- 'beforeAll': undefined,
- 'beforeEach': undefined,
- 'eachTest': undefined,
- 'total': undefined,
- 'speed': undefined,
- }, `${name} runnables`)
- */
expect(currentStubs.runnerEmit.args).to.matchSnapshot(runnerEmitCleanseMap, `${name} runner emit`)
expect(currentReporter.results()).to.matchSnapshot({
'reporterStats.end': match.date,
diff --git a/yarn.lock b/yarn.lock
index eec7cf63db72..b977ba0e705b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -21681,7 +21681,7 @@ snap-shot-core@10.2.0:
quote "0.4.0"
ramda "0.26.1"
-snap-shot-core@^7.4.0:
+snap-shot-core@7.4.0:
version "7.4.0"
resolved "https://registry.yarnpkg.com/snap-shot-core/-/snap-shot-core-7.4.0.tgz#b3215fbb3e3bbc734706a56feff175040bb06254"
integrity sha512-rSRDbrfvYFp7dwtSJhFw+SUT4tS1tKSty7RsW9f9erP2fFyn7VIfYe9cgmseZytSMovB0I9WxATwKhGAsbYBCQ==
From 2ba4fe9821e42e6c12466a1a86b34013157536c9 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 24 Mar 2020 16:51:09 -0400
Subject: [PATCH 08/86] remove test-retries related tests
---
.../integration/cypress/runner.spec.js | 493 ------------------
1 file changed, 493 deletions(-)
diff --git a/packages/driver/test/cypress/integration/cypress/runner.spec.js b/packages/driver/test/cypress/integration/cypress/runner.spec.js
index 378cf7f6110d..a490dceeba92 100644
--- a/packages/driver/test/cypress/integration/cypress/runner.spec.js
+++ b/packages/driver/test/cypress/integration/cypress/runner.spec.js
@@ -564,318 +564,6 @@ describe('src/cypress/runner', () => {
})
})
})
-
- // NOTE: for test-retries
- describe.skip('retries', () => {
- it('can set retry config', () => {
- createCypress({}, { config: { numTestRetries: 1 } })
- .then(() => {
- expect(autCypress.config()).to.has.property('numTestRetries', 1)
- })
- })
-
- describe('retry ui', () => {
- beforeEach(() => {
- createCypress({
- suites: {
- 'suite 1': {
- tests: [
- { name: 'test 1', fail: 1 },
- { name: 'test 2', fail: 2 },
- { name: 'test 3', fail: 1 },
- ],
- },
- },
- }, { config: { numTestRetries: 1, isTextTerminal: false, enableTestRetriesInOpenMode: true } })
- .then(shouldHaveTestResults(2, 1))
- })
-
- it('empty', () => {})
-
- it('can toggle failed attempt', () => {
- cy.contains('.runnable-wrapper', 'test 3').click().within(() => {
- cy.contains('AssertionError').should('not.be.visible')
- cy.contains('Attempt 1').click()
- cy.contains('AssertionError').should('be.visible')
- cy.contains('Attempt 1').click().find('i:last').pseudo(':before').should('have.property', 'content', '""')
- cy.contains('AssertionError').should('not.be.visible')
- })
- })
-
- it('can view error for failed attempt', () => {
- cy.contains('Attempt 1')
- .click()
- .closest('.attempt-item')
- .contains('AssertionError')
- .click()
-
- cy.get('@console_log').should('be.calledWithMatch', 'Command')
- })
- })
-
- it('simple retry', () => {
- createCypress({
- suites: {
- 'suite 1': {
- tests: [
- { name: 'test 1',
- fail: 1,
- },
- ],
- },
- },
- }, { config: { numTestRetries: 1 } })
- .then(shouldHaveTestResults(1, 0))
- })
-
- it('test retry with hooks', () => {
- createCypress({
- suites: {
- 'suite 1': {
- hooks: ['before', 'beforeEach', 'afterEach', 'after'],
- tests: [{ name: 'test 1', fail: 1 }],
- },
- },
- }, { config: { numTestRetries: 1 } })
- .then(shouldHaveTestResults(1, 0))
- .then(() => {
- cy.contains('test')
- cy.contains('after all')
- })
- })
-
- it('test retry with [only]', () => {
- createCypress({
- suites: {
- 'suite 1': {
- hooks: ['before', 'beforeEach', 'afterEach', 'after'],
- tests: [
- { name: 'test 1' },
- { name: 'test 2', fail: 1, only: true },
- { name: 'test 3' },
- ],
- },
- },
- }, { config: { numTestRetries: 1 } })
- .then(shouldHaveTestResults(1, 0))
- })
-
- it('test retry with many hooks', () => {
- createCypress({
- suites: {
- 'suite 1': {
- hooks: [
- 'before',
- 'beforeEach',
- 'afterEach',
- 'after',
- ],
- tests: [
- { name: 'test 1' },
- { name: 'test 2', fail: 1 },
- { name: 'test 3' },
- ],
- },
- },
- }, { config: { numTestRetries: 1 } })
- .then(shouldHaveTestResults(3, 0))
- })
-
- it('can retry from [beforeEach]', () => {
- createCypress({
- suites: {
- 'suite 1': {
- hooks: [
- 'before',
- 'beforeEach',
- { type: 'beforeEach', fail: 1 },
- 'beforeEach',
- 'afterEach',
- 'after',
- ],
- tests: [{ name: 'test 1' }],
- },
- },
- }, { config: { numTestRetries: 1 } })
- .then(shouldHaveTestResults(1, 0))
- .then(() => {
- cy.contains('Attempt 1').click()
- cy.contains('AssertionError').click()
- cy.get('@reporterBus').its('lastCall.args').should('contain', 'runner:console:log')
- })
- .then(() => {
- snapshotEvents(snapshots.RETRY_PASS_IN_BEFOREEACH)
- })
- })
-
- it('can retry from [afterEach]', () => {
- createCypress({
- hooks: [{ type: 'afterEach', fail: 1 }],
- suites: {
- 'suite 1': {
- hooks: [
- 'before',
- 'beforeEach',
- 'beforeEach',
- 'afterEach',
- 'after',
- ],
- tests: [{ name: 'test 1' }, 'test 2', 'test 3'],
- },
- 'suite 2': {
- hooks: [{ type: 'afterEach', fail: 2 }],
- tests: ['test 1'],
- },
- 'suite 3': {
- tests: ['test 1'],
- },
- },
- }, { config: { numTestRetries: 2 } })
- .then(shouldHaveTestResults(5, 0))
- .then(() => {
- cy.contains('test 1')
- cy.contains('Attempt 1').click()
- cy.contains('AssertionError').click()
- cy.get('@reporterBus').its('lastCall.args').should('contain', 'runner:console:log')
- })
- .then(() => {
- snapshotEvents(snapshots.RETRY_PASS_IN_AFTEREACH)
- })
- })
-
- it('cant retry from [before]', () => {
- createCypress({
- suites: {
- 'suite 1': {
- hooks: [
- { type: 'before', fail: 1 },
- 'beforeEach',
- 'beforeEach',
- 'afterEach',
- 'afterEach',
- 'after',
- ],
- tests: [{ name: 'test 1' }],
- },
- },
- }, { config: { numTestRetries: 1, isTextTerminal: false, enableTestRetriesInOpenMode: true } })
- .then(shouldHaveTestResults(0, 1))
- .then(() => {
- // cy.contains('Attempt 1').click()
- cy.contains('Although you have test retries')
- cy.contains('AssertionError').click()
- cy.get('@console_log').its('lastCall').should('be.calledWithMatch', 'Error')
- })
- })
-
- it('cant retry from [after]', () => {
- createCypress({
- suites: {
- 'suite 1': {
- hooks: [
- 'before',
- 'beforeEach',
- 'beforeEach',
- 'afterEach',
- 'afterEach',
- { type: 'after', fail: 1 },
- ],
- tests: [{ name: 'test 1' }],
- },
- },
- }, { config: { numTestRetries: 1, isTextTerminal: false, enableTestRetriesInOpenMode: true } })
- .then(shouldHaveTestResults(0, 1))
- .then(() => {
- cy.contains('Although you have test retries')
- cy.contains('AssertionError').click()
- cy.get('@console_log').its('lastCall').should('be.calledWithMatch', 'Error')
- })
- })
-
- // NOTE: for test-retries
- describe.skip('can configure retries', () => {
- const getAttemptTag = (sel) => {
- return cy.get(`.runnable-wrapper:contains${sel} .attempt-tag`)
- }
-
- it('via config value', () => {
- createCypress({
- suites: {
- 'suite 1': () => {
- Cypress.config('numTestRetries', 0)
- it('no retry', () => assert(false))
- Cypress.config('numTestRetries', 1)
- it('1 retry', () => assert(false))
- Cypress.config('numTestRetries', 2)
- it('2 retries', () => assert(false))
- Cypress.config('isTextTerminal', false)
- it('open mode, no retry', () => assert(false))
- Cypress.config('enableTestRetriesInOpenMode', true)
- it('open mode, 2 retries', () => assert(false))
- },
- },
- })
- .then(shouldHaveTestResults(0, 5))
- .then(() => {
- getAttemptTag('(no retry):first').should('not.be.visible')
- getAttemptTag('(1 retry)').should('have.length', 2)
- getAttemptTag('(2 retries):first').should('have.length', 3)
- getAttemptTag('(open mode, no retry)').should('not.be.visible')
- getAttemptTag('(open mode, 2 retries)').should('have.length', 3)
- })
- })
-
- it('throws when set via this.retries in test', () => {
- createCypress({
- suites: {
- 'suite 1' () {
- it('test 1', function () {
- this.retries(0)
- })
- },
- },
- })
- .then(shouldHaveTestResults(0, 1))
- .then(() => {
- cy.get('.runnable-err').should('contain', 'numTestRetries')
- })
- })
-
- it('throws when set via this.retries in hook', () => {
- createCypress({
- suites: {
- 'suite 1' () {
- beforeEach(function () {
- this.retries(0)
- })
-
- it('foo', () => {})
- },
- },
- })
- .then(shouldHaveTestResults(0, 1))
- .then(() => {
- cy.get('.runnable-err').should('contain', 'numTestRetries')
- })
- })
-
- it('throws when set via this.retries in suite', () => {
- createCypress({
- suites: {
- 'suite 1' () {
- this.retries(0)
- it('test 1', function () {
- })
- },
- },
- })
- .then(shouldHaveTestResults(0, 1))
- .then(() => {
- cy.get('.runnable-err').should('contain', 'numTestRetries')
- })
- })
- })
- })
})
describe('save/reload state', () => {
@@ -942,77 +630,6 @@ describe('src/cypress/runner', () => {
})
})
})
-
- // NOTE: for test-retries
- describe.skip('retries', () => {
- let realState
-
- let runCount = 0
- const failThenSerialize = () => {
- if (!runCount++) {
- assert(false, 'stub 3 fail')
- }
-
- assert(true, 'stub 3 pass')
-
- return realState = serializeState()
- }
-
- let runCount2 = 0
- const failOnce = () => {
- if (!runCount2++) {
- assert(false, 'stub 2 fail')
- }
-
- assert(true, 'stub 2 pass')
- }
-
- const stub1 = sinon.stub()
- const stub2 = sinon.stub().callsFake(failOnce)
- const stub3 = sinon.stub().callsFake(failThenSerialize)
-
- let cypressConfig = [
- {
- suites: {
- 'suite 1': {
- hooks: [
- 'before',
- 'beforeEach',
- 'afterEach',
- 'after',
- ],
- tests: [{ name: 'test 1', fn: stub1 }],
- },
- 'suite 2': {
- tests: [
- { name: 'test 1', fn: stub2 },
- { name: 'test 2', fn: stub3 },
- 'test 3',
- ],
- },
- },
- }, { config: { numTestRetries: 1 } },
- ]
-
- it('serialize state', () => {
- createCypress(...cypressConfig)
- .then(shouldHaveTestResults(4, 0))
- .then(() => {
- expect(realState).to.matchSnapshot(cleanseRunStateMap, 'serialize state - retries')
- })
- })
-
- it('load state', () => {
- loadStateFromSnapshot(cypressConfig, 'serialize state - retries')
- createCypress(...cypressConfig)
- .then(shouldHaveTestResults(4, 0))
- .then(() => {
- expect(stub1).to.calledOnce
- expect(stub2).to.calledTwice
- expect(stub3).calledThrice
- })
- })
- })
})
})
@@ -1155,116 +772,6 @@ describe('src/cypress/runner', () => {
// )
})
})
-
- // NOTE: for test-retries
- describe.skip('retries', () => {
- it('screenshot during each failed attempt', () => {
- createCypress({
- suites: {
- 'suite 1': {
- tests: [
- {
- name: 'test 1',
- fn: () => {
- assert(false, 'some error')
- },
- eval: true,
- },
- ],
- },
- },
- }, { config: { numTestRetries: 2 } })
- .then(() => {
- // sent to server
- expect(autCypress.automation.withArgs('take:screenshot')).calledThrice
- expect(autCypress.automation.withArgs('take:screenshot').args).matchDeep([
- { 1: { testAttemptIndex: 0 } },
- { 1: { testAttemptIndex: 1 } },
- { 1: { testAttemptIndex: 2 } },
- ])
-
- // on('after:screenshot')
- expect(onAfterScreenshotListener.args[0][0]).to.matchDeep({ testAttemptIndex: 0 })
- expect(onAfterScreenshotListener.args[1][0]).to.matchDeep({ testAttemptIndex: 1 })
-
- // Screenshot.onAfterScreenshot
- expect(autCypress.Screenshot.onAfterScreenshot.args[0]).to.matchSnapshot(
- { '^.0': stringifyShort, 'test': stringifyShort, takenAt: match.string },
- )
- })
- })
-
- it('retry screenshot in test body', () => {
- createCypress({
- suites: {
- 'suite 1': {
- tests: [
- {
- name: 'test 1',
- fn: () => {
- cy.screenshot()
- cy.then(() => assert(false))
- },
- eval: true,
- },
- ],
- },
- },
- }, { config: { numTestRetries: 1 } })
- .then(() => {
- expect(autCypress.automation.withArgs('take:screenshot')).callCount(4)
- expect(autCypress.automation.withArgs('take:screenshot').args).matchDeep([
- { 1: { testAttemptIndex: 0 } },
- { 1: { testAttemptIndex: 0 } },
- { 1: { testAttemptIndex: 1 } },
- { 1: { testAttemptIndex: 1 } },
- ])
-
- expect(autCypress.automation.withArgs('take:screenshot').args[0]).matchSnapshot({ startTime: match.string, testAttemptIndex: match(0) })
- expect(onAfterScreenshotListener.args[0][0]).to.matchSnapshot({ testAttemptIndex: match(0) })
- expect(onAfterScreenshotListener.args[2][0]).to.matchDeep({ testAttemptIndex: 1 })
- expect(autCypress.Screenshot.onAfterScreenshot.args[0]).to.matchSnapshot(
- { '^.0': stringifyShort, 'test': stringifyShort, takenAt: match.string },
- )
- })
- })
-
- it('retry screenshot in hook', () => {
- createCypress({
- suites: {
- 'suite 1': {
- hooks: [
- {
- type: 'beforeEach',
- fn: () => {
- cy.screenshot()
- cy.then(() => assert(false))
- },
- eval: true,
- },
- ],
- tests: [
- {
- name: 'test 1',
- },
- ],
- },
- },
- }, { config: { numTestRetries: 1 } })
- .then(() => {
- expect(autCypress.automation.withArgs('take:screenshot')).callCount(4)
- expect(autCypress.automation.withArgs('take:screenshot').args).matchDeep([
- { 1: { testAttemptIndex: 0 } },
- { 1: { testAttemptIndex: 0 } },
- { 1: { testAttemptIndex: 1 } },
- { 1: { testAttemptIndex: 1 } },
- ])
-
- expect(onAfterScreenshotListener.args[0][0]).matchDeep({ testAttemptIndex: 0 })
- expect(onAfterScreenshotListener.args[3][0]).matchDeep({ testAttemptIndex: 1 })
- })
- })
- })
})
})
From 13f1d01f4bd20e065781f380300629685338ed30 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Thu, 26 Mar 2020 11:32:28 -0400
Subject: [PATCH 09/86] small cleanup diff utility
---
.../integration/cypress/runner.spec.js | 22 -------------------
.../cypress/plugins/snapshot/command/index.js | 2 +-
2 files changed, 1 insertion(+), 23 deletions(-)
diff --git a/packages/driver/test/cypress/integration/cypress/runner.spec.js b/packages/driver/test/cypress/integration/cypress/runner.spec.js
index a490dceeba92..e021b51d2546 100644
--- a/packages/driver/test/cypress/integration/cypress/runner.spec.js
+++ b/packages/driver/test/cypress/integration/cypress/runner.spec.js
@@ -48,19 +48,6 @@ const threeTestsWithHooks = {
suites: { 'suite 1': { hooks: ['before', 'beforeEach', 'afterEach', 'after'], tests: ['test 1', 'test 2', 'test 3'] } },
}
-const threeTestsWithRetry = {
- suites: {
- 'suite 1': {
- hooks: ['before', 'beforeEach', 'afterEach', 'after'],
- tests: [
- 'test 1',
- { name: 'test 2', fail: 2 },
- 'test 3',
- ],
- },
- },
-}
-
const enableStubSnapshots = false
// const enableStubSnapshots = true
@@ -789,15 +776,6 @@ describe('src/cypress/runner', () => {
snapshotEvents(snapshots.THREE_TESTS_WITH_HOOKS)
})
})
-
- it('three tests with retry', () => {
- createCypress(threeTestsWithRetry, { config: {
- numTestRetries: 2,
- } })
- .then(() => {
- snapshotEvents(snapshots.THREE_TESTS_WITH_RETRY)
- })
- })
})
})
})
diff --git a/packages/driver/test/cypress/plugins/snapshot/command/index.js b/packages/driver/test/cypress/plugins/snapshot/command/index.js
index a43d0f5414bf..c3f0626aee99 100644
--- a/packages/driver/test/cypress/plugins/snapshot/command/index.js
+++ b/packages/driver/test/cypress/plugins/snapshot/command/index.js
@@ -523,7 +523,7 @@ const withMatchers = (matchers, match, expectedOnly = false) => {
act = printVar(act)
if (exp !== act) {
- text = options.wrap('modified', `${exp} ➡️ ${act}`)
+ text = options.wrap('modified', `**${exp} ⮕ ${act}**`)
changed = true
}
}
From 363c8980bb565c532c8c8ec67752660174f69d5b Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Thu, 26 Mar 2020 12:20:06 -0400
Subject: [PATCH 10/86] move runner.spec to packages/runner
---
circle.yml | 56 +++++++++++++++++--
packages/driver/package.json | 1 -
.../integration/commands/task_spec.coffee | 2 +-
packages/driver/test/cypress/plugins/index.js | 6 --
.../driver/test/cypress/support/commands.js | 7 ---
.../test/cypress/support/helpers.coffee | 13 +++++
packages/driver/test/cypress/support/index.js | 2 -
packages/runner/package.json | 3 +
packages/runner/test/.eslintrc.json | 5 --
.../__snapshots__/runner.spec.js.snapshot.js | 48 ++++++++--------
packages/runner/test/cypress.json | 7 +++
packages/runner/test/cypress/.eslintrc.json | 14 +++++
.../test/cypress/fixtures}/empty_spec.js | 0
.../test/cypress/integration}/runner.spec.js | 11 ++--
packages/runner/test/cypress/plugins/index.js | 17 ++++++
.../plugins/snapshot/command/index.d.ts | 0
.../cypress/plugins/snapshot/command/index.js | 1 -
.../test/cypress/plugins/snapshot/index.js | 0
.../test/cypress/support}/eventSnapshots.js | 0
.../test/cypress/support/helpers.js | 14 -----
packages/runner/test/cypress/support/index.js | 1 +
packages/server/test/matchDeep.js | 4 +-
packages/server/test/unit/reporter.spec.js | 4 +-
23 files changed, 142 insertions(+), 74 deletions(-)
delete mode 100644 packages/driver/test/cypress/support/commands.js
create mode 100644 packages/driver/test/cypress/support/helpers.coffee
delete mode 100644 packages/runner/test/.eslintrc.json
rename packages/{driver => runner}/test/__snapshots__/runner.spec.js.snapshot.js (98%)
create mode 100644 packages/runner/test/cypress.json
create mode 100644 packages/runner/test/cypress/.eslintrc.json
rename packages/{driver/test/cypress/integration/cypress => runner/test/cypress/fixtures}/empty_spec.js (100%)
rename packages/{driver/test/cypress/integration/cypress => runner/test/cypress/integration}/runner.spec.js (98%)
create mode 100644 packages/runner/test/cypress/plugins/index.js
rename packages/{driver => runner}/test/cypress/plugins/snapshot/command/index.d.ts (100%)
rename packages/{driver => runner}/test/cypress/plugins/snapshot/command/index.js (99%)
rename packages/{driver => runner}/test/cypress/plugins/snapshot/index.js (100%)
rename packages/{driver/test/cypress/integration/cypress => runner/test/cypress/support}/eventSnapshots.js (100%)
rename packages/{driver => runner}/test/cypress/support/helpers.js (91%)
create mode 100644 packages/runner/test/cypress/support/index.js
diff --git a/circle.yml b/circle.yml
index 6c4545dff840..b56292fb6849 100644
--- a/circle.yml
+++ b/circle.yml
@@ -351,8 +351,8 @@ jobs:
- store-npm-logs
"server-performance-tests":
- <<: *defaults
- steps:
+ <<: *defaults
+ steps:
- attach_workspace:
at: ~/
- run:
@@ -533,6 +533,48 @@ jobs:
browser: firefox
chunk: 8
+ "runner-integration-tests-chrome":
+ <<: *defaults
+ steps:
+ - attach_workspace:
+ at: ~/
+ - run:
+ command: yarn workspace @packages/driver start
+ background: true
+ - run:
+ command: yarn wait-on http://localhost:3500
+ - run:
+ command: |
+ CYPRESS_KONFIG_ENV=production \
+ CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \
+ yarn workspace @packages/runner cypress:run --record --parallel --group runner-integration-chrome --browser chrome
+ - store_test_results:
+ path: /tmp/cypress
+ - store_artifacts:
+ path: /tmp/artifacts
+ - store-npm-logs
+
+ "runner-integration-tests-firefox":
+ <<: *defaults
+ steps:
+ - attach_workspace:
+ at: ~/
+ - run:
+ command: yarn workspace @packages/driver start
+ background: true
+ - run:
+ command: yarn wait-on http://localhost:3500
+ - run:
+ command: |
+ CYPRESS_KONFIG_ENV=production \
+ CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \
+ yarn workspace @packages/runner cypress:run --record --parallel --group runner-integration-chrome --browser firefox
+ - store_test_results:
+ path: /tmp/cypress
+ - store_artifacts:
+ path: /tmp/artifacts
+ - store-npm-logs
+
"driver-integration-tests-chrome":
<<: *defaults
parallelism: 5
@@ -626,7 +668,7 @@ jobs:
- attach_workspace:
at: ~/
- run:
- command: yarn workspace @packages/reporter build-for-tests
+ command: yarn workspace @packages/reporter build-for-tests
- run:
command: |
CYPRESS_KONFIG_ENV=production \
@@ -1044,7 +1086,6 @@ jobs:
command: "npm run cypress:run"
wait-on: "http://localhost:2222"
-
"test-binary-against-realworld-firefox":
<<: *defaults
steps:
@@ -1224,6 +1265,13 @@ linux-workflow: &linux-workflow
- driver-integration-tests-firefox:
requires:
- build
+ - runner-integration-tests-chrome:
+ requires:
+ - build
+ - runner-integration-tests-firefox:
+ requires:
+ - build
+
## TODO: add these back in when flaky tests are fixed
# - driver-integration-tests-electron:
# requires:
diff --git a/packages/driver/package.json b/packages/driver/package.json
index c0582572d08e..dd2d0bcf23f1 100644
--- a/packages/driver/package.json
+++ b/packages/driver/package.json
@@ -56,7 +56,6 @@
"parse-domain": "bahmutov/parse-domain#fb60bd4",
"setimmediate": "1.0.5",
"sinon": "8.1.1",
- "snap-shot-core": "7.4.0",
"text-mask-addons": "3.8.0",
"underscore": "1.9.1",
"underscore.string": "3.3.5",
diff --git a/packages/driver/test/cypress/integration/commands/task_spec.coffee b/packages/driver/test/cypress/integration/commands/task_spec.coffee
index 7c443db5693b..4ae08cf5430e 100644
--- a/packages/driver/test/cypress/integration/commands/task_spec.coffee
+++ b/packages/driver/test/cypress/integration/commands/task_spec.coffee
@@ -164,7 +164,7 @@ describe "src/cy/commands/task", ->
expect(lastLog.get("error")).to.eq(err)
expect(lastLog.get("state")).to.eq("failed")
- expect(err.message).to.eq("`cy.task('bar')` failed with the following error:\n\nThe task 'bar' was not handled in the plugins file. The following tasks are registered: return:arg, wait, create:long:file, getSnapshot, saveSnapshot\n\nFix this in your plugins file here:\n#{Cypress.config('pluginsFile')}\n\nhttps://on.cypress.io/api/task")
+ expect(err.message).to.eq("`cy.task('bar')` failed with the following error:\n\nThe task 'bar' was not handled in the plugins file. The following tasks are registered: return:arg, wait, create:long:file\n\nFix this in your plugins file here:\n#{Cypress.config('pluginsFile')}\n\nhttps://on.cypress.io/api/task")
done()
cy.task("bar")
diff --git a/packages/driver/test/cypress/plugins/index.js b/packages/driver/test/cypress/plugins/index.js
index ac0e1df25453..b0a17637cffb 100644
--- a/packages/driver/test/cypress/plugins/index.js
+++ b/packages/driver/test/cypress/plugins/index.js
@@ -5,7 +5,6 @@ const _ = require('lodash')
const path = require('path')
const fs = require('fs-extra')
const Promise = require('bluebird')
-const { getSnapshot, saveSnapshot } = require('./snapshot')
const webpack = require('@cypress/webpack-preprocessor')
process.env.NO_LIVERELOAD = '1'
@@ -34,10 +33,5 @@ module.exports = (on) => {
return null
},
-
- getSnapshot,
-
- saveSnapshot,
-
})
}
diff --git a/packages/driver/test/cypress/support/commands.js b/packages/driver/test/cypress/support/commands.js
deleted file mode 100644
index d7e09b0f7cbb..000000000000
--- a/packages/driver/test/cypress/support/commands.js
+++ /dev/null
@@ -1,7 +0,0 @@
-Cypress.Commands.addAll({ prevSubject: 'element' }, {
- pseudo (subject, pseudoSel) {
- const win = Cypress.dom.getWindowByElement(subject.get(0))
-
- cy.wrap(win.getComputedStyle(subject.get(0), pseudoSel))
- },
-})
diff --git a/packages/driver/test/cypress/support/helpers.coffee b/packages/driver/test/cypress/support/helpers.coffee
new file mode 100644
index 000000000000..d686ba6b2252
--- /dev/null
+++ b/packages/driver/test/cypress/support/helpers.coffee
@@ -0,0 +1,13 @@
+_ = Cypress._
+
+getFirstSubjectByName = (name) ->
+ cy.queue.find({name: name}).get("subject")
+
+getQueueNames = ->
+ _.map(cy.queue, "name")
+
+module.exports = {
+ getQueueNames
+
+ getFirstSubjectByName
+}
diff --git a/packages/driver/test/cypress/support/index.js b/packages/driver/test/cypress/support/index.js
index ce36e97d38df..c08934668746 100644
--- a/packages/driver/test/cypress/support/index.js
+++ b/packages/driver/test/cypress/support/index.js
@@ -16,5 +16,3 @@
// Alternatively you can use CommonJS syntax:
// require("./commands")
require('./defaults')
-
-require('./commands')
diff --git a/packages/runner/package.json b/packages/runner/package.json
index a61d0e5e373a..f778fe20e615 100644
--- a/packages/runner/package.json
+++ b/packages/runner/package.json
@@ -7,6 +7,8 @@
"build": "webpack",
"build-prod": "cross-env NODE_ENV=production yarn build",
"clean-deps": "rm -rf node_modules",
+ "cypress:open": "node ../../scripts/cypress open --project ./test",
+ "cypress:run": "node ../../scripts/cypress run --project ./test",
"postinstall": "echo '@packages/runner needs: yarn build'",
"test": "yarn test-unit",
"test-debug": "yarn test-unit --inspect-brk=5566",
@@ -40,6 +42,7 @@
"react-input-autosize": "2.2.2",
"sinon": "7.5.0",
"sinon-chai": "3.3.0",
+ "snap-shot-core": "7.4.0",
"webpack": "4.35.3",
"webpack-cli": "3.3.2"
},
diff --git a/packages/runner/test/.eslintrc.json b/packages/runner/test/.eslintrc.json
deleted file mode 100644
index e19bbb381891..000000000000
--- a/packages/runner/test/.eslintrc.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "extends": [
- "../src/.eslintrc"
- ]
-}
diff --git a/packages/driver/test/__snapshots__/runner.spec.js.snapshot.js b/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js
similarity index 98%
rename from packages/driver/test/__snapshots__/runner.spec.js.snapshot.js
rename to packages/runner/test/__snapshots__/runner.spec.js.snapshot.js
index 6d7939c05b1f..ebc907535492 100644
--- a/packages/driver/test/__snapshots__/runner.spec.js.snapshot.js
+++ b/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js
@@ -14,7 +14,7 @@ exports['FAIL_IN_AFTER.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -265,7 +265,7 @@ exports['FAIL_IN_AFTER.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -285,7 +285,7 @@ exports['FAIL_IN_AFTER.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js",
+ "file": "cypress/integration/runner.spec.js",
"tests": [],
"suites": [
{
@@ -334,7 +334,7 @@ exports['FAIL_IN_AFTEREACH.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -506,7 +506,7 @@ exports['FAIL_IN_AFTEREACH.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -526,7 +526,7 @@ exports['FAIL_IN_AFTEREACH.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js",
+ "file": "cypress/integration/runner.spec.js",
"tests": [],
"suites": [
{
@@ -568,7 +568,7 @@ exports['FAIL_IN_BEFORE.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -666,7 +666,7 @@ exports['FAIL_IN_BEFORE.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -686,7 +686,7 @@ exports['FAIL_IN_BEFORE.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js",
+ "file": "cypress/integration/runner.spec.js",
"tests": [],
"suites": [
{
@@ -728,7 +728,7 @@ exports['FAIL_IN_BEFOREEACH.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -838,7 +838,7 @@ exports['FAIL_IN_BEFOREEACH.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -858,7 +858,7 @@ exports['FAIL_IN_BEFOREEACH.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js",
+ "file": "cypress/integration/runner.spec.js",
"tests": [],
"suites": [
{
@@ -900,7 +900,7 @@ exports['FAIL_WITH_ONLY.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -1246,7 +1246,7 @@ exports['FAIL_WITH_ONLY.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -1266,7 +1266,7 @@ exports['FAIL_WITH_ONLY.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js",
+ "file": "cypress/integration/runner.spec.js",
"tests": [],
"suites": [
{
@@ -1309,7 +1309,7 @@ exports['PASS_WITH_ONLY.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -1645,7 +1645,7 @@ exports['PASS_WITH_ONLY.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -1665,7 +1665,7 @@ exports['PASS_WITH_ONLY.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js",
+ "file": "cypress/integration/runner.spec.js",
"tests": [],
"suites": [
{
@@ -1708,7 +1708,7 @@ exports['SIMPLE_SINGLE_TEST.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -1820,7 +1820,7 @@ exports['SIMPLE_SINGLE_TEST.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -1840,7 +1840,7 @@ exports['SIMPLE_SINGLE_TEST.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js",
+ "file": "cypress/integration/runner.spec.js",
"tests": [],
"suites": [
{
@@ -1882,7 +1882,7 @@ exports['THREE_TESTS_WITH_HOOKS.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -2569,7 +2569,7 @@ exports['THREE_TESTS_WITH_HOOKS.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js"
+ "file": "cypress/integration/runner.spec.js"
}
],
[
@@ -2589,7 +2589,7 @@ exports['THREE_TESTS_WITH_HOOKS.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/cypress/runner.spec.js",
+ "file": "cypress/integration/runner.spec.js",
"tests": [],
"suites": [
{
diff --git a/packages/runner/test/cypress.json b/packages/runner/test/cypress.json
new file mode 100644
index 000000000000..80fc69f223e1
--- /dev/null
+++ b/packages/runner/test/cypress.json
@@ -0,0 +1,7 @@
+{
+ "projectId": "ypt4pf",
+ "baseUrl": "http://localhost:3500",
+ "hosts": {
+ "*.foobar.com": "127.0.0.1"
+ }
+}
diff --git a/packages/runner/test/cypress/.eslintrc.json b/packages/runner/test/cypress/.eslintrc.json
new file mode 100644
index 000000000000..1d43eafa7f5e
--- /dev/null
+++ b/packages/runner/test/cypress/.eslintrc.json
@@ -0,0 +1,14 @@
+{
+ "extends": [
+ "plugin:@cypress/dev/tests"
+ ],
+ "plugins": [
+ "cypress"
+ ],
+ "env": {
+ "cypress/globals": true
+ },
+ "rules": {
+ "mocha/no-global-tests": "off"
+ }
+}
diff --git a/packages/driver/test/cypress/integration/cypress/empty_spec.js b/packages/runner/test/cypress/fixtures/empty_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/empty_spec.js
rename to packages/runner/test/cypress/fixtures/empty_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/runner.spec.js b/packages/runner/test/cypress/integration/runner.spec.js
similarity index 98%
rename from packages/driver/test/cypress/integration/cypress/runner.spec.js
rename to packages/runner/test/cypress/integration/runner.spec.js
index e021b51d2546..6d5dcbe2e97d 100644
--- a/packages/driver/test/cypress/integration/cypress/runner.spec.js
+++ b/packages/runner/test/cypress/integration/runner.spec.js
@@ -1,15 +1,16 @@
/* eslint prefer-rest-params: "off", no-console: "off", arrow-body-style: "off"*/
const { _ } = Cypress
-const helpers = require('../../support/helpers')
+const helpers = require('../support/helpers')
-const { registerInCypress, stringifyShort } = require('../../plugins/snapshot/command')
+const snapshotPlugin = require('../plugins/snapshot/command')
-const snapshots = require('./eventSnapshots').EventSnapshots
+const snapshots = require('../support/eventSnapshots').EventSnapshots
const sinon = require('sinon')
-registerInCypress()
+snapshotPlugin.registerInCypress()
+const { stringifyShort } = snapshotPlugin
/**
* @type {sinon.SinonMatch}
*/
@@ -86,7 +87,7 @@ const createCypress = (mochaTests, opts = {}) => {
config: {},
})
- return cy.visit('/fixtures/isolated-runner.html#/tests/integration/cypress/empty_spec.js')
+ return cy.visit('/fixtures/isolated-runner.html#/tests/cypress/fixtures/empty_spec.js')
.then({ timeout: 60000 }, (win) => {
win.channel.destroy()
diff --git a/packages/runner/test/cypress/plugins/index.js b/packages/runner/test/cypress/plugins/index.js
new file mode 100644
index 000000000000..3269befd540e
--- /dev/null
+++ b/packages/runner/test/cypress/plugins/index.js
@@ -0,0 +1,17 @@
+///
+
+const { getSnapshot, saveSnapshot } = require('./snapshot')
+
+/**
+ * @type {Cypress.PluginConfig}
+ */
+module.exports = (on, config) => {
+ // `on` is used to hook into various events Cypress emits
+ // `config` is the resolved Cypress config
+ on('task', {
+
+ getSnapshot,
+
+ saveSnapshot,
+ })
+}
diff --git a/packages/driver/test/cypress/plugins/snapshot/command/index.d.ts b/packages/runner/test/cypress/plugins/snapshot/command/index.d.ts
similarity index 100%
rename from packages/driver/test/cypress/plugins/snapshot/command/index.d.ts
rename to packages/runner/test/cypress/plugins/snapshot/command/index.d.ts
diff --git a/packages/driver/test/cypress/plugins/snapshot/command/index.js b/packages/runner/test/cypress/plugins/snapshot/command/index.js
similarity index 99%
rename from packages/driver/test/cypress/plugins/snapshot/command/index.js
rename to packages/runner/test/cypress/plugins/snapshot/command/index.js
index c3f0626aee99..a0d8aa070bf1 100644
--- a/packages/driver/test/cypress/plugins/snapshot/command/index.js
+++ b/packages/runner/test/cypress/plugins/snapshot/command/index.js
@@ -561,5 +561,4 @@ module.exports = {
registerInCypress,
matchDeep,
stringifyShort,
-
}
diff --git a/packages/driver/test/cypress/plugins/snapshot/index.js b/packages/runner/test/cypress/plugins/snapshot/index.js
similarity index 100%
rename from packages/driver/test/cypress/plugins/snapshot/index.js
rename to packages/runner/test/cypress/plugins/snapshot/index.js
diff --git a/packages/driver/test/cypress/integration/cypress/eventSnapshots.js b/packages/runner/test/cypress/support/eventSnapshots.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/eventSnapshots.js
rename to packages/runner/test/cypress/support/eventSnapshots.js
diff --git a/packages/driver/test/cypress/support/helpers.js b/packages/runner/test/cypress/support/helpers.js
similarity index 91%
rename from packages/driver/test/cypress/support/helpers.js
rename to packages/runner/test/cypress/support/helpers.js
index 5a0f342be8e8..3f0668e76bbe 100644
--- a/packages/driver/test/cypress/support/helpers.js
+++ b/packages/runner/test/cypress/support/helpers.js
@@ -1,14 +1,6 @@
const { _ } = Cypress
const debug = require('debug')('spec')
-const getFirstSubjectByName = (name) => {
- return cy.queue.find({ name }).get('subject')
-}
-
-const getQueueNames = () => {
- return _.map(cy.queue, 'name')
-}
-
const createHooks = (win, hooks = []) => {
_.each(hooks, (hook) => {
if (_.isString(hook)) {
@@ -136,13 +128,7 @@ const generateMochaTestsForWin = (win, obj) => {
createSuites(win, obj.suites)
}
-// window.localStorage.debug = 'spec*'
-
module.exports = {
- getQueueNames,
-
- getFirstSubjectByName,
-
generateMochaTestsForWin,
}
diff --git a/packages/runner/test/cypress/support/index.js b/packages/runner/test/cypress/support/index.js
new file mode 100644
index 000000000000..43c03b759d9b
--- /dev/null
+++ b/packages/runner/test/cypress/support/index.js
@@ -0,0 +1 @@
+import './commands'
diff --git a/packages/server/test/matchDeep.js b/packages/server/test/matchDeep.js
index 5795ec33f102..7c30916f9381 100644
--- a/packages/server/test/matchDeep.js
+++ b/packages/server/test/matchDeep.js
@@ -1,5 +1,5 @@
-const { matchDeep } = require('../../driver/test/cypress/plugins/snapshot/command')
-const { getSnapshot, saveSnapshot } = require('../../driver/test/cypress/plugins/snapshot')
+const { matchDeep } = require('../../runner/test/cypress/plugins/snapshot/command')
+const { getSnapshot, saveSnapshot } = require('../../runner/test/cypress/plugins/snapshot')
const chai = require('chai')
const _ = require('lodash')
const sinon = require('sinon')
diff --git a/packages/server/test/unit/reporter.spec.js b/packages/server/test/unit/reporter.spec.js
index 6035b4ef5234..6eff5baac8b2 100644
--- a/packages/server/test/unit/reporter.spec.js
+++ b/packages/server/test/unit/reporter.spec.js
@@ -12,8 +12,8 @@ registerInMocha()
const { match } = sinon
-const events = require('../../../driver/test/__snapshots__/runner.spec.js.snapshot')
-const { EventSnapshots } = require('../../../driver/test/cypress/integration/cypress/eventSnapshots')
+const events = require('../../../runner/test/__snapshots__/runner.spec.js.snapshot')
+const { EventSnapshots } = require('../../../runner/test/cypress/support/eventSnapshots')
let currentReporter
let currentStubs
From 4c5a7578bcdf2d4f6420bcb3ca403a40d83a653b Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Fri, 27 Mar 2020 12:52:38 -0400
Subject: [PATCH 11/86] fix requiring missing file
---
packages/runner/test/cypress/support/index.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/packages/runner/test/cypress/support/index.js b/packages/runner/test/cypress/support/index.js
index 43c03b759d9b..e69de29bb2d1 100644
--- a/packages/runner/test/cypress/support/index.js
+++ b/packages/runner/test/cypress/support/index.js
@@ -1 +0,0 @@
-import './commands'
From 8031a43fb143b9ddb2f2d62ef5f1bf86042b02bf Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Fri, 27 Mar 2020 14:06:00 -0400
Subject: [PATCH 12/86] fix circle.yml runner-integration-tests
---
circle.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/circle.yml b/circle.yml
index b56292fb6849..2eff36f3873d 100644
--- a/circle.yml
+++ b/circle.yml
@@ -547,7 +547,7 @@ jobs:
command: |
CYPRESS_KONFIG_ENV=production \
CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \
- yarn workspace @packages/runner cypress:run --record --parallel --group runner-integration-chrome --browser chrome
+ yarn workspace @packages/runner cypress:run --record --group runner-integration-chrome --browser chrome
- store_test_results:
path: /tmp/cypress
- store_artifacts:
From 9c794a828c49a86fff770205f7cfc6c22060d579 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 31 Mar 2020 14:00:47 -0400
Subject: [PATCH 13/86] update readme
---
packages/runner/README.md | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/packages/runner/README.md b/packages/runner/README.md
index cd503bcec818..0bfb59156523 100644
--- a/packages/runner/README.md
+++ b/packages/runner/README.md
@@ -39,6 +39,19 @@ yarn lerna run build-prod --scope @packages/runner --stream
## Testing
+
+### Node Unit Tests
```bash
yarn lerna run test --scope @packages/runner --stream
```
+
+
+### Cypress
+
+Run Cypress tests found in `test/cypress/integration`.
+
+```bash
+yarn lerna run cypress:open --scope @packages/runner --stream
+```
+
+You'll want to run `yarn lerna run watch --scope @packages/runner --stream` to get changes to the main Cypress reporter while testing.
From b4d895ec8bc55c3bd4647d3b701a0a178a00345c Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Wed, 1 Apr 2020 13:45:35 -0400
Subject: [PATCH 14/86] finish refactor of helpers for isolated
runner-integration tests
---
.../test/cypress/integration/runner.spec.js | 321 +++---------------
.../cypress/plugins/snapshot/command/index.js | 2 +-
.../runner/test/cypress/support/helpers.js | 268 ++++++++++++++-
3 files changed, 305 insertions(+), 286 deletions(-)
diff --git a/packages/runner/test/cypress/integration/runner.spec.js b/packages/runner/test/cypress/integration/runner.spec.js
index 6d5dcbe2e97d..ae564e03ce36 100644
--- a/packages/runner/test/cypress/integration/runner.spec.js
+++ b/packages/runner/test/cypress/integration/runner.spec.js
@@ -1,46 +1,15 @@
-/* eslint prefer-rest-params: "off", no-console: "off", arrow-body-style: "off"*/
-
const { _ } = Cypress
const helpers = require('../support/helpers')
-const snapshotPlugin = require('../plugins/snapshot/command')
-
const snapshots = require('../support/eventSnapshots').EventSnapshots
const sinon = require('sinon')
-snapshotPlugin.registerInCypress()
-const { stringifyShort } = snapshotPlugin
-/**
- * @type {sinon.SinonMatch}
- */
-const match = Cypress.sinon.match
-
-// const { defer } = helpers
-
const backupCy = window.cy
const backupCypress = window.Cypress
backupCy.__original__ = true
-/**
- * @type {sinon.SinonStub}
- */
-let allStubs
-/**
- * @type {sinon.SinonStub}
- */
-let mochaStubs
-/**
- * @type {sinon.SinonStub}
- */
-let setRunnablesStub
-
-const snapshotEvents = (name) => {
- expect(setRunnablesStub.args).to.matchSnapshot(setRunnablesCleanseMap, name.setRunnables)
- expect(mochaStubs.args).to.matchSnapshot(mochaEventCleanseMap, name.mocha)
-}
-
const simpleSingleTest = {
suites: { 'suite 1': { tests: [{ name: 'test 1' }] } },
}
@@ -49,202 +18,7 @@ const threeTestsWithHooks = {
suites: { 'suite 1': { hooks: ['before', 'beforeEach', 'afterEach', 'after'], tests: ['test 1', 'test 2', 'test 3'] } },
}
-const enableStubSnapshots = false
-// const enableStubSnapshots = true
-
-const eventCleanseMap = {
- snapshots: stringifyShort,
- parent: stringifyShort,
- tests: stringifyShort,
- commands: stringifyShort,
- err: stringifyShort,
- body: '[body]',
- wallClockStartedAt: match.date,
- lifecycle: match.number,
- fnDuration: match.number,
- duration: match.number,
- afterFnDuration: match.number,
- wallClockDuration: match.number,
- stack: match.string,
- message: '[error message]',
-}
-
-const mochaEventCleanseMap = {
- ...eventCleanseMap,
- start: match.date,
- end: match.date,
-}
-
-const setRunnablesCleanseMap = { ...eventCleanseMap, tests: _.identity }
-
-let autCypress
-
-let onBeforeRun
-
-const createCypress = (mochaTests, opts = {}) => {
- _.defaults(opts, {
- state: {},
- config: {},
- })
-
- return cy.visit('/fixtures/isolated-runner.html#/tests/cypress/fixtures/empty_spec.js')
- .then({ timeout: 60000 }, (win) => {
- win.channel.destroy()
-
- allStubs = cy.stub().snapshot(enableStubSnapshots)
- mochaStubs = cy.stub().snapshot(enableStubSnapshots)
- setRunnablesStub = cy.stub().snapshot(enableStubSnapshots)
-
- return new Promise((resolve) => {
- const runCypress = () => {
- autCypress.run.restore()
-
- const emit = autCypress.emit
- const emitMap = autCypress.emitMap
- const emitThen = autCypress.emitThen
-
- cy.stub(autCypress, 'automation').snapshot(enableStubSnapshots)
- .callThrough()
- .withArgs('clear:cookies')
- .resolves({
- foo: 'bar',
- })
- .withArgs('take:screenshot')
- .resolves({
- path: '/path/to/screenshot',
- size: 12,
- dimensions: { width: 20, height: 20 },
- multipart: false,
- pixelRatio: 1,
- takenAt: new Date().toISOString(),
- name: 'name',
- blackout: ['.foo'],
- duration: 100,
- })
-
- cy.stub(autCypress, 'emit').snapshot(enableStubSnapshots).log(false)
- .callsFake(function () {
- const noLog = _.includes([
- 'navigation:changed',
- 'stability:changed',
- 'window:load',
- 'url:changed',
- 'log:added',
- 'page:loading',
- 'window:unload',
- 'newListener',
- ], arguments[0])
- const noCall = _.includes(['window:before:unload', 'mocha'], arguments[0])
- const isMocha = _.includes(['mocha'], arguments[0])
-
- if (isMocha) {
- mochaStubs.apply(this, arguments)
- }
-
- noLog || allStubs.apply(this, ['emit'].concat([].slice.call(arguments)))
-
- return noCall || emit.apply(this, arguments)
- })
-
- cy.stub(autCypress, 'emitMap').snapshot(enableStubSnapshots).log(false)
- .callsFake(function () {
- allStubs.apply(this, ['emitMap'].concat([].slice.call(arguments)))
-
- return emitMap.apply(this, arguments)
- })
-
- cy.stub(autCypress, 'emitThen').snapshot(enableStubSnapshots).log(false)
- .callsFake(function () {
- allStubs.apply(this, ['emitThen'].concat([].slice.call(arguments)))
-
- return emitThen.apply(this, arguments)
- })
-
- spyOn(autCypress.mocha.getRunner(), 'fail', (...args) => {
- Cypress.log({
- name: 'Runner Fail',
- message: `${args[1]}`,
- state: 'failed',
- consoleProps: () => {
- return {
- Error: args[1],
- }
- },
- })
- })
-
- cy.spy(cy.state('window').console, 'log').as('console_log')
- cy.spy(cy.state('window').console, 'error').as('console_error')
-
- onBeforeRun && onBeforeRun()
- autCypress.run(resolve)
- }
-
- cy.spy(win.reporterBus, 'emit').snapshot(enableStubSnapshots).as('reporterBus')
- cy.spy(win.localBus, 'emit').snapshot(enableStubSnapshots).as('localBus')
-
- cy.stub(win.channel, 'emit').snapshot(enableStubSnapshots)
- .withArgs('watch:test:file')
- .callsFake(() => {
- autCypress = win.Cypress
-
- cy.stub(autCypress, 'onSpecWindow').snapshot(enableStubSnapshots).callsFake((specWindow) => {
- autCypress.onSpecWindow.restore()
-
- autCypress.onSpecWindow(specWindow)
-
- helpers.generateMochaTestsForWin(specWindow, mochaTests)
-
- specWindow.before = () => {}
- specWindow.beforeEach = () => {}
- specWindow.afterEach = () => {}
- specWindow.after = () => {}
- specWindow.describe = () => {}
- })
-
- cy.stub(autCypress, 'run').snapshot(enableStubSnapshots).callsFake(runCypress)
- })
- .withArgs('is:automation:client:connected')
- .yieldsAsync(true)
-
- .withArgs('get:existing:run:state')
- .callsFake((evt, cb) => {
- cb(opts.state)
- })
-
- .withArgs('backend:request', 'reset:server:state')
- .yieldsAsync({})
-
- .withArgs('backend:request', 'resolve:url')
- .yieldsAsync({ response: {
- isOkStatusCode: true,
- isHtml: true,
- url: 'http://localhost:3500/fixtures/generic.html',
- } })
-
- .withArgs('set:runnables')
- .callsFake((...args) => {
- setRunnablesStub(...args)
- _.last(args)()
- })
-
- // .withArgs('preserve:run:state')
- // .callsFake()
-
- .withArgs('automation:request')
- .yieldsAsync({ response: {} })
-
- const c = _.extend({}, Cypress.config(), { isTextTerminal: true }, opts.config)
-
- c.state = {}
- // c.state = opts.state
-
- cy.stub(win.channel, 'on').snapshot(enableStubSnapshots)
-
- win.Runner.start(win.document.getElementById('app'), window.btoa(JSON.stringify(c)))
- })
- })
-}
+const { visit, snapshotEvents, onInitialized, getAutCypress } = helpers.createCypress()
describe('src/cypress/runner', () => {
describe('isolated test runner', () => {
@@ -255,12 +29,12 @@ describe('src/cypress/runner', () => {
describe('test events', function () {
it('simple 1 test', () => {
- createCypress(simpleSingleTest)
+ visit(simpleSingleTest)
.then(shouldHaveTestResults(1, 0))
})
it('simple 3 tests', function () {
- createCypress({
+ visit({
suites: {
'suite 1': { tests: ['test 1', 'test 2', 'test 3'] },
},
@@ -269,7 +43,7 @@ describe('src/cypress/runner', () => {
})
it('simple fail', function () {
- createCypress({
+ visit({
suites: {
'suite 1': {
tests: [
@@ -289,7 +63,7 @@ describe('src/cypress/runner', () => {
})
it('pass fail pass fail', () => {
- createCypress({
+ visit({
suites: {
'suite 1': {
tests: [
@@ -315,7 +89,7 @@ describe('src/cypress/runner', () => {
})
it('fail pass', function () {
- createCypress({
+ visit({
suites: {
'suite 1': {
tests: [
@@ -332,7 +106,7 @@ describe('src/cypress/runner', () => {
})
it('no tests', function () {
- createCypress({})
+ visit({})
.then(shouldHaveTestResults(0, 0))
cy.contains('No tests found in your file').should('be.visible')
@@ -340,7 +114,7 @@ describe('src/cypress/runner', () => {
})
it('ends test before nested suite', function () {
- createCypress({
+ visit({
suites: {
'suite 1': { tests: ['test 1', 'test 2'],
suites: {
@@ -354,21 +128,17 @@ describe('src/cypress/runner', () => {
})
it('simple fail, catch cy.on(fail)', () => {
- createCypress({
+ visit({
suites: {
'suite 1': {
tests: [
{
name: 'test 1',
fn: () => {
- console.log('test ran')
cy.on('fail', () => {
- console.log('on:fail')
-
return false
})
- console.log('added handler')
expect(false).ok
throw new Error('error in test')
},
@@ -383,7 +153,7 @@ describe('src/cypress/runner', () => {
describe('hook failures', () => {
it('fail in [before]', () => {
- createCypress({
+ visit({
suites: {
'suite 1': {
hooks: [
@@ -406,7 +176,7 @@ describe('src/cypress/runner', () => {
})
it('fail in [beforeEach]', () => {
- createCypress({
+ visit({
suites: {
'suite 1': {
hooks: [
@@ -426,7 +196,7 @@ describe('src/cypress/runner', () => {
})
it('fail in [afterEach]', () => {
- createCypress({
+ visit({
suites: {
'suite 1': {
hooks: [
@@ -446,7 +216,7 @@ describe('src/cypress/runner', () => {
})
it('fail in [after]', () => {
- createCypress({
+ visit({
suites: {
'suite 1': {
hooks: [
@@ -461,6 +231,7 @@ describe('src/cypress/runner', () => {
})
.then(shouldHaveTestResults(1, 1))
.then(() => {
+ expect('foo').contain('f')
cy.get('.runnable-err:visible').invoke('text').should('contain', 'Because this error occurred during a after all hook')
})
.then(() => {
@@ -471,7 +242,7 @@ describe('src/cypress/runner', () => {
describe('test failures w/ hooks', () => {
it('fail with [before]', () => {
- createCypress({
+ visit({
suites: {
'suite 1': {
hooks: ['before'],
@@ -489,7 +260,7 @@ describe('src/cypress/runner', () => {
})
it('fail with [after]', () => {
- createCypress({
+ visit({
suites: {
'suite 1': {
hooks: [{ type: 'after' }],
@@ -501,7 +272,7 @@ describe('src/cypress/runner', () => {
})
it('fail with all hooks', () => {
- createCypress({
+ visit({
suites: {
'suite 1': {
hooks: ['before', 'beforeEach', 'afterEach', 'after'],
@@ -515,7 +286,7 @@ describe('src/cypress/runner', () => {
describe('mocha grep', () => {
it('fail with [only]', () => {
- createCypress({
+ visit({
suites: {
'suite 1': {
hooks: ['before', 'beforeEach', 'afterEach', 'after'],
@@ -534,7 +305,7 @@ describe('src/cypress/runner', () => {
})
it('pass with [only]', () => {
- createCypress({
+ visit({
suites: {
'suite 1': {
hooks: ['before', 'beforeEach', 'afterEach', 'after'],
@@ -557,7 +328,7 @@ describe('src/cypress/runner', () => {
describe('save/reload state', () => {
describe('serialize / load from state', () => {
const serializeState = () => {
- return getRunState(autCypress)
+ return getRunState(getAutCypress())
}
const loadStateFromSnapshot = (cypressConfig, name) => {
@@ -599,7 +370,7 @@ describe('src/cypress/runner', () => {
]
it('serialize state', () => {
- createCypress(...cypressConfig)
+ visit(...cypressConfig)
.then(shouldHaveTestResults(4, 0))
.then(() => {
expect(realState).to.matchSnapshot(cleanseRunStateMap, 'serialize state - hooks')
@@ -609,7 +380,7 @@ describe('src/cypress/runner', () => {
it('load state', () => {
loadStateFromSnapshot(cypressConfig, 'serialize state - hooks')
- createCypress(...cypressConfig)
+ visit(...cypressConfig)
.then(shouldHaveTestResults(4, 0))
.then(() => {
expect(stub1).to.calledOnce
@@ -648,12 +419,12 @@ describe('src/cypress/runner', () => {
},
}
- createCypress(mochaTests)
+ visit(mochaTests)
.then(shouldHaveTestResults(1, 3))
.then(() => {
cy.contains('.test', 'never gets here').should('have.class', 'runnable-failed')
cy.contains('.command', 'beforeEach').should('have.class', 'command-state-failed')
- cy.contains('.runnable-err', 'AssertionError: beforeEach').scrollIntoView().should('be.visible').then((v) => console.log(v.text()))
+ cy.contains('.runnable-err', 'AssertionError: beforeEach').scrollIntoView().should('be.visible')
cy.contains('.test', 'is pending').should('have.class', 'runnable-pending')
@@ -672,7 +443,7 @@ describe('src/cypress/runner', () => {
})
it('async timeout spec', () => {
- createCypress({
+ visit({
suites: {
'async': {
tests: [
@@ -694,7 +465,7 @@ describe('src/cypress/runner', () => {
})
it('mocha suite:end fire before test:pass event', () => {
- createCypress({
+ visit({
suites: {
'suite 1': {
suites: {
@@ -704,7 +475,7 @@ describe('src/cypress/runner', () => {
},
},
},
- }).then(() => {
+ }).then(({ mochaStubs }) => {
const getOrderFired = (eventProps) => {
const event = _.find(mochaStubs.args, eventProps)
@@ -722,15 +493,15 @@ describe('src/cypress/runner', () => {
let onAfterScreenshotListener
beforeEach(() => {
- onBeforeRun = () => {
+ onInitialized((autCypress) => {
autCypress.Screenshot.onAfterScreenshot = cy.stub()
onAfterScreenshotListener = cy.stub()
autCypress.on('after:screenshot', onAfterScreenshotListener)
- }
+ })
})
it('screenshot after failed test', () => {
- createCypress({
+ visit({
suites: {
'suite 1': {
tests: [
@@ -745,7 +516,7 @@ describe('src/cypress/runner', () => {
},
},
})
- .then(() => {
+ .then(({ autCypress }) => {
// sent to server
expect(autCypress.automation.withArgs('take:screenshot').args).to.matchSnapshot(cleanseRunStateMap)
@@ -765,14 +536,14 @@ describe('src/cypress/runner', () => {
describe('mocha events', () => {
it('simple single test', () => {
- createCypress(simpleSingleTest)
+ visit(simpleSingleTest)
.then(() => {
snapshotEvents(snapshots.SIMPLE_SINGLE_TEST)
})
})
it('simple three tests', () => {
- createCypress(threeTestsWithHooks)
+ visit(threeTestsWithHooks)
.then(() => {
snapshotEvents(snapshots.THREE_TESTS_WITH_HOOKS)
})
@@ -811,24 +582,12 @@ const cleanseRunStateMap = {
'err.stack': '[err stack]',
}
-const shouldHaveTestResults = (passed, failed) => {
- return (exitCode) => {
- expect(exitCode, 'resolve with failure count').eq(exitCode)
- passed = passed || '--'
- failed = failed || '--'
- cy.get('header .passed .num').should('have.text', `${passed}`)
- cy.get('header .failed .num').should('have.text', `${failed}`)
- }
-}
-
-const spyOn = (obj, prop, fn) => {
- const _fn = obj[prop]
-
- obj[prop] = function () {
- fn.apply(this, arguments)
-
- const ret = _fn.apply(this, arguments)
-
- return ret
+const shouldHaveTestResults = (expPassed, expFailed) => {
+ return ({ failed }) => {
+ expect(failed, 'resolve with failure count').eq(failed)
+ expPassed = expPassed || '--'
+ expFailed = expFailed || '--'
+ cy.get('header .passed .num').should('have.text', `${expPassed}`)
+ cy.get('header .failed .num').should('have.text', `${expFailed}`)
}
}
diff --git a/packages/runner/test/cypress/plugins/snapshot/command/index.js b/packages/runner/test/cypress/plugins/snapshot/command/index.js
index a0d8aa070bf1..7cb7efc1dfa6 100644
--- a/packages/runner/test/cypress/plugins/snapshot/command/index.js
+++ b/packages/runner/test/cypress/plugins/snapshot/command/index.js
@@ -87,7 +87,7 @@ const registerInCypress = () => {
}, { log: false })
}
- throwErr(e, `**snapshot failed match**: ${exactSpecName}`, exp, ctx)
+ throwErr(e, `**snapshot failed to match**: ${exactSpecName}`, exp, ctx)
}
})
})
diff --git a/packages/runner/test/cypress/support/helpers.js b/packages/runner/test/cypress/support/helpers.js
index 3f0668e76bbe..eb199e645863 100644
--- a/packages/runner/test/cypress/support/helpers.js
+++ b/packages/runner/test/cypress/support/helpers.js
@@ -1,5 +1,264 @@
+/* eslint prefer-rest-params: "off", no-console: "off", arrow-body-style: "off"*/
+
const { _ } = Cypress
const debug = require('debug')('spec')
+const snapshotPlugin = require('../plugins/snapshot/command')
+
+/**
+ * @type {sinon.SinonMatch}
+ */
+const match = Cypress.sinon.match
+
+const { stringifyShort } = snapshotPlugin
+const eventCleanseMap = {
+ snapshots: stringifyShort,
+ parent: stringifyShort,
+ tests: stringifyShort,
+ commands: stringifyShort,
+ err: stringifyShort,
+ body: '[body]',
+ wallClockStartedAt: match.date,
+ lifecycle: match.number,
+ fnDuration: match.number,
+ duration: match.number,
+ afterFnDuration: match.number,
+ wallClockDuration: match.number,
+ stack: match.string,
+ message: '[error message]',
+}
+
+const mochaEventCleanseMap = {
+ ...eventCleanseMap,
+ start: match.date,
+ end: match.date,
+}
+
+const setRunnablesCleanseMap = { ...eventCleanseMap, tests: _.identity }
+
+const spyOn = (obj, prop, fn) => {
+ const _fn = obj[prop]
+
+ obj[prop] = function () {
+ fn.apply(this, arguments)
+
+ const ret = _fn.apply(this, arguments)
+
+ return ret
+ }
+}
+
+function createCypress () {
+ /**
+ * @type {sinon.SinonStub}
+ */
+ let allStubs
+ /**
+ * @type {sinon.SinonStub}
+ */
+ let mochaStubs
+ /**
+ * @type {sinon.SinonStub}
+ */
+ let setRunnablesStub
+
+ const enableStubSnapshots = false
+ // const enableStubSnapshots = true
+
+ let autCypress
+
+ const getAutCypress = () => autCypress
+
+ const snapshotEvents = (name) => {
+ expect(setRunnablesStub.args).to.matchSnapshot(setRunnablesCleanseMap, name.setRunnables)
+ expect(mochaStubs.args).to.matchSnapshot(mochaEventCleanseMap, name.mocha)
+ }
+
+ snapshotPlugin.registerInCypress()
+
+ let onInitializedListeners = []
+
+ const onInitialized = function (fn) {
+ console.log(fn)
+ onInitializedListeners.push(fn)
+ }
+
+ const visit = (mochaTests, opts = {}) => {
+ _.defaults(opts, {
+ state: {},
+ config: {},
+ })
+
+ return cy.visit('/fixtures/isolated-runner.html#/tests/cypress/fixtures/empty_spec.js')
+ .then({ timeout: 60000 }, (win) => {
+ win.channel.destroy()
+
+ allStubs = cy.stub().snapshot(enableStubSnapshots)
+ mochaStubs = cy.stub().snapshot(enableStubSnapshots)
+ setRunnablesStub = cy.stub().snapshot(enableStubSnapshots)
+
+ return new Promise((resolve) => {
+ const runCypress = () => {
+ autCypress.run.restore()
+
+ const emit = autCypress.emit
+ const emitMap = autCypress.emitMap
+ const emitThen = autCypress.emitThen
+
+ cy.stub(autCypress, 'automation').snapshot(enableStubSnapshots)
+ .callThrough()
+ .withArgs('clear:cookies')
+ .resolves({
+ foo: 'bar',
+ })
+ .withArgs('take:screenshot')
+ .resolves({
+ path: '/path/to/screenshot',
+ size: 12,
+ dimensions: { width: 20, height: 20 },
+ multipart: false,
+ pixelRatio: 1,
+ takenAt: new Date().toISOString(),
+ name: 'name',
+ blackout: ['.foo'],
+ duration: 100,
+ })
+
+ cy.stub(autCypress, 'emit').snapshot(enableStubSnapshots).log(false)
+ .callsFake(function () {
+ const noLog = _.includes([
+ 'navigation:changed',
+ 'stability:changed',
+ 'window:load',
+ 'url:changed',
+ 'log:added',
+ 'page:loading',
+ 'window:unload',
+ 'newListener',
+ ], arguments[0])
+ const noCall = _.includes(['window:before:unload', 'mocha'], arguments[0])
+ const isMocha = _.includes(['mocha'], arguments[0])
+
+ if (isMocha) {
+ mochaStubs.apply(this, arguments)
+ }
+
+ noLog || allStubs.apply(this, ['emit'].concat([].slice.call(arguments)))
+
+ return noCall || emit.apply(this, arguments)
+ })
+
+ cy.stub(autCypress, 'emitMap').snapshot(enableStubSnapshots).log(false)
+ .callsFake(function () {
+ allStubs.apply(this, ['emitMap'].concat([].slice.call(arguments)))
+
+ return emitMap.apply(this, arguments)
+ })
+
+ cy.stub(autCypress, 'emitThen').snapshot(enableStubSnapshots).log(false)
+ .callsFake(function () {
+ allStubs.apply(this, ['emitThen'].concat([].slice.call(arguments)))
+
+ return emitThen.apply(this, arguments)
+ })
+
+ spyOn(autCypress.mocha.getRunner(), 'fail', (...args) => {
+ Cypress.log({
+ name: 'Runner Fail',
+ message: `${args[1]}`,
+ state: 'failed',
+ consoleProps: () => {
+ return {
+ Error: args[1],
+ }
+ },
+ })
+ })
+
+ cy.spy(cy.state('window').console, 'log').as('console_log')
+ cy.spy(cy.state('window').console, 'error').as('console_error')
+
+ console.log(onInitializedListeners)
+ onInitializedListeners.forEach((fn) => fn(autCypress))
+ onInitializedListeners = []
+
+ autCypress.run((failed) => {
+ resolve({ failed, mochaStubs, autCypress })
+ })
+ }
+
+ cy.spy(win.reporterBus, 'emit').snapshot(enableStubSnapshots).as('reporterBus')
+ cy.spy(win.localBus, 'emit').snapshot(enableStubSnapshots).as('localBus')
+
+ cy.stub(win.channel, 'emit').snapshot(enableStubSnapshots)
+ .withArgs('watch:test:file')
+ .callsFake(() => {
+ autCypress = win.Cypress
+
+ cy.stub(autCypress, 'onSpecWindow').snapshot(enableStubSnapshots).callsFake((specWindow) => {
+ autCypress.onSpecWindow.restore()
+
+ autCypress.onSpecWindow(specWindow)
+
+ generateMochaTestsForWin(specWindow, mochaTests)
+
+ specWindow.before = () => {}
+ specWindow.beforeEach = () => {}
+ specWindow.afterEach = () => {}
+ specWindow.after = () => {}
+ specWindow.describe = () => {}
+ })
+
+ cy.stub(autCypress, 'run').snapshot(enableStubSnapshots).callsFake(runCypress)
+ })
+ .withArgs('is:automation:client:connected')
+ .yieldsAsync(true)
+
+ .withArgs('get:existing:run:state')
+ .callsFake((evt, cb) => {
+ cb(opts.state)
+ })
+
+ .withArgs('backend:request', 'reset:server:state')
+ .yieldsAsync({})
+
+ .withArgs('backend:request', 'resolve:url')
+ .yieldsAsync({ response: {
+ isOkStatusCode: true,
+ isHtml: true,
+ url: 'http://localhost:3500/fixtures/generic.html',
+ } })
+
+ .withArgs('set:runnables')
+ .callsFake((...args) => {
+ setRunnablesStub(...args)
+ _.last(args)()
+ })
+
+ // .withArgs('preserve:run:state')
+ // .callsFake()
+
+ .withArgs('automation:request')
+ .yieldsAsync({ response: {} })
+
+ const c = _.extend({}, Cypress.config(), { isTextTerminal: true }, opts.config)
+
+ c.state = {}
+ // c.state = opts.state
+
+ cy.stub(win.channel, 'on').snapshot(enableStubSnapshots)
+
+ win.Runner.start(win.document.getElementById('app'), window.btoa(JSON.stringify(c)))
+ })
+ })
+ }
+
+ return {
+ visit,
+ snapshotEvents,
+ onInitialized,
+ getAutCypress,
+ }
+}
const createHooks = (win, hooks = []) => {
_.each(hooks, (hook) => {
@@ -128,12 +387,13 @@ const generateMochaTestsForWin = (win, obj) => {
createSuites(win, obj.suites)
}
-module.exports = {
- generateMochaTestsForWin,
-}
-
const evalFn = (win, fn) => {
return function () {
return win.eval(`(${fn.toString()})`).call(this)
}
}
+
+module.exports = {
+ generateMochaTestsForWin,
+ createCypress,
+}
From b2b9449fcc1056fdcd577a2795e3940de76ad211 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Fri, 3 Apr 2020 15:21:56 -0400
Subject: [PATCH 15/86] restore runner/test/eslintrc
---
packages/runner/test/.eslintrc.json | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 packages/runner/test/.eslintrc.json
diff --git a/packages/runner/test/.eslintrc.json b/packages/runner/test/.eslintrc.json
new file mode 100644
index 000000000000..e19bbb381891
--- /dev/null
+++ b/packages/runner/test/.eslintrc.json
@@ -0,0 +1,5 @@
+{
+ "extends": [
+ "../src/.eslintrc"
+ ]
+}
From 62d14b807d560faf49c4c95111a28e83c4e7f115 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 6 Apr 2020 11:24:07 -0400
Subject: [PATCH 16/86] fix .eslintignore
---
.eslintignore | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.eslintignore b/.eslintignore
index c56cfb71cf3f..2b50319ccec5 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -27,6 +27,8 @@ packages/server/lib/scaffold/support/index.js
packages/server/lib/scaffold/support/commands.js
packages/server/test/support/fixtures/projects/e2e/cypress/integration/stdout_exit_early_failing_spec.js
+**/.projects
+**/*.d.ts
**/package-lock.json
**/tsconfig.json
**/.vscode
From e1520916b7f32f036b0e9ad4378422044a70f2fc Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Thu, 9 Apr 2020 17:17:11 -0400
Subject: [PATCH 17/86] temp 04/09/20 [skip ci]
---
packages/runner/src/lib/logger.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/runner/src/lib/logger.js b/packages/runner/src/lib/logger.js
index a52ee1cacc13..51a02cc82fc9 100644
--- a/packages/runner/src/lib/logger.js
+++ b/packages/runner/src/lib/logger.js
@@ -12,7 +12,7 @@ export default {
},
clearLog () {
- if (console.clear) console.clear()
+ // if (console.clear) console.clear()
},
logFormatted (consoleProps) {
From 45c8b597465de0612dc23902ab505c7a3ab52707 Mon Sep 17 00:00:00 2001
From: Jennifer Shehane
Date: Thu, 16 Apr 2020 15:43:07 +0630
Subject: [PATCH 18/86] Update readme to reflect how to start server for
cypress tests
---
packages/runner/README.md | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/packages/runner/README.md b/packages/runner/README.md
index 0bfb59156523..348bbae0fe5f 100644
--- a/packages/runner/README.md
+++ b/packages/runner/README.md
@@ -39,19 +39,28 @@ yarn lerna run build-prod --scope @packages/runner --stream
## Testing
-
### Node Unit Tests
+
```bash
yarn lerna run test --scope @packages/runner --stream
```
-
### Cypress
-Run Cypress tests found in `test/cypress/integration`.
+You'll need to start the server from the [`driver`](../driver) package in order to get Cypress running.
+
+```bash
+yarn lerna run start --scope @packages/driver --stream
+```
+
+Then you can run Cypress tests found in [`test/cypress/integration`](./test/cypress/integration).
```bash
yarn lerna run cypress:open --scope @packages/runner --stream
```
-You'll want to run `yarn lerna run watch --scope @packages/runner --stream` to get changes to the main Cypress reporter while testing.
+To see changes to the reporter while testing you'll want to run:
+
+```bash
+yarn lerna run watch --scope @packages/runner --stream
+```
From 2dd7b4122aa0310d5aa7ce2e944ccaa870ef3745 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 27 Apr 2020 11:53:02 -0400
Subject: [PATCH 19/86] try 2: fix rerun before/after hooks
---
packages/driver/src/cypress/runner.js | 52 +++++--
.../integration/cypress/runner_spec.js | 19 +++
.../server/__snapshots__/3_issue_1987.ts.js | 132 ++++++++++++++++++
packages/server/test/e2e/3_issue_1987.ts | 29 ++++
.../server/test/integration/cypress_spec.js | 2 +-
.../projects/hooks-after-rerun/cypress.json | 1 +
.../cypress/integration/afterhooks.spec.js | 85 +++++++++++
.../beforehook-and-test-navigation.js | 13 ++
.../cypress/plugins/index.js | 33 +++++
.../cypress/support/commands.js | 25 ++++
.../cypress/support/index.js | 20 +++
yarn.lock | 37 +----
12 files changed, 404 insertions(+), 44 deletions(-)
create mode 100644 packages/server/__snapshots__/3_issue_1987.ts.js
create mode 100644 packages/server/test/e2e/3_issue_1987.ts
create mode 100644 packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress.json
create mode 100644 packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/afterhooks.spec.js
create mode 100644 packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/beforehook-and-test-navigation.js
create mode 100644 packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
create mode 100644 packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/commands.js
create mode 100644 packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js
diff --git a/packages/driver/src/cypress/runner.js b/packages/driver/src/cypress/runner.js
index 98e6b9da690b..d786c642c5d0 100644
--- a/packages/driver/src/cypress/runner.js
+++ b/packages/driver/src/cypress/runner.js
@@ -251,6 +251,18 @@ const getTestFromHook = function (hook, suite, getTestById) {
return test
}
+ if (hook.hookName === 'after all') {
+ const siblings = getAllSiblingTests(suite, getTestById)
+
+ return _.last(siblings)
+ }
+
+ if (hook.hookName === 'before all') {
+ const siblings = getAllSiblingTests(suite, getTestById)
+
+ return _.first(siblings)
+ }
+
// if we have a hook id then attempt
// to find the test by its id
if (hook != null ? hook.id : undefined) {
@@ -394,7 +406,7 @@ const overrideRunnerHook = function (Cypress, _runner, getTestById, getTest, set
// the last test that will run
if (
(isRootSuite(this.suite) && isLastTest(t, allTests)) ||
- (isRootSuite(this.suite.parent) && lastTestThatWillRunInSuite(t, siblings)) ||
+ (isRootSuite(this.suite.parent) && !this.suite.parent._afterAll.length && lastTestThatWillRunInSuite(t, siblings)) ||
(!isLastSuite(this.suite, allTests) && lastTestThatWillRunInSuite(t, siblings))
) {
changeFnToRunAfterHooks()
@@ -577,7 +589,7 @@ const hookFailed = function (hook, err, hookName, getTestById, getTest) {
// finds the test by returning the first test from
// the parent or looping through the suites until
// it finds the first test
- const test = getTest() || getTestFromHook(hook, hook.parent, getTestById)
+ const test = getTestFromHook(hook, hook.parent, getTestById)
test.err = err
test.state = 'failed'
@@ -642,7 +654,7 @@ const _runnerListeners = function (_runner, Cypress, _emissions, getTestById, ge
// if there is a nested suite with a before, then
// currentTest will refer to the previous test run
// and not our current
- if ((hook.hookName === 'before all') && hook.ctx.currentTest) {
+ if ((hook.hookName === 'before all' || hook.hookName === 'after all') && hook.ctx.currentTest) {
delete hook.ctx.currentTest
}
@@ -650,7 +662,7 @@ const _runnerListeners = function (_runner, Cypress, _emissions, getTestById, ge
// hooks do not have their own id, their
// commands need to grouped with the test
// and we can only associate them by this id
- const test = getTest() || getTestFromHook(hook, hook.parent, getTestById)
+ const test = getTestFromHook(hook, hook.parent, getTestById)
hook.id = test.id
hook.ctx.currentTest = test
@@ -777,6 +789,10 @@ const _runnerListeners = function (_runner, Cypress, _emissions, getTestById, ge
})
}
+function getOrderFromId (id) {
+ return +id.slice(1)
+}
+
const create = function (specWindow, mocha, Cypress, cy) {
let _id = 0
let _hookId = 0
@@ -889,6 +905,14 @@ const create = function (specWindow, mocha, Cypress, cy) {
return _testsById[id]
}
+ function hasTestAlreadyRun (test) {
+ if (Cypress._RESUMED_AT_TEST) {
+ return getOrderFromId(test.id) < getOrderFromId(Cypress._RESUMED_AT_TEST)
+ }
+
+ return false
+ }
+
overrideRunnerHook(Cypress, _runner, getTestById, getTest, setTest, getTests)
return {
@@ -951,7 +975,7 @@ const create = function (specWindow, mocha, Cypress, cy) {
switch (runnable.type) {
case 'hook':
- test = getTest() || getTestFromHook(runnable, runnable.parent, getTestById)
+ test = getTestFromHook(runnable, runnable.parent, getTestById)
break
case 'test':
@@ -962,6 +986,17 @@ const create = function (specWindow, mocha, Cypress, cy) {
break
}
+ // if this isnt a hook, then the name is 'test'
+ const hookName = runnable.type === 'hook' ? getHookName(runnable) : 'test'
+
+ // extract out the next(fn) which mocha uses to
+ // move to the next runnable - this will be our async seam
+ const _next = args[0]
+
+ if (hasTestAlreadyRun(test)) {
+ return _next()
+ }
+
// closure for calculating the actual
// runtime of a runnables fn exection duration
// and also the run of the runnable:after:run:async event
@@ -986,9 +1021,6 @@ const create = function (specWindow, mocha, Cypress, cy) {
test.wallClockStartedAt = wallClockStartedAt
}
- // if this isnt a hook, then the name is 'test'
- const hookName = runnable.type === 'hook' ? getHookName(runnable) : 'test'
-
// if we haven't yet fired this event for this test
// that means that we need to reset the previous state
// of cy - since we now have a new 'test' and all of the
@@ -997,10 +1029,6 @@ const create = function (specWindow, mocha, Cypress, cy) {
fire(TEST_BEFORE_RUN_EVENT, test, Cypress)
}
- // extract out the next(fn) which mocha uses to
- // move to the next runnable - this will be our async seam
- const _next = args[0]
-
const next = function (err) {
// now set the duration of the after runnable run async event
afterFnDurationEnd = (wallClockEnd = new Date())
diff --git a/packages/driver/test/cypress/integration/cypress/runner_spec.js b/packages/driver/test/cypress/integration/cypress/runner_spec.js
index e08df29ae8ad..32e4dd16024d 100644
--- a/packages/driver/test/cypress/integration/cypress/runner_spec.js
+++ b/packages/driver/test/cypress/integration/cypress/runner_spec.js
@@ -1,6 +1,10 @@
+const { _ } = Cypress
+
const pending = []
+const testAfterRunEvents = []
Cypress.on('test:after:run', (test) => {
+ testAfterRunEvents.push(test)
if (test.state === 'pending') {
return pending.push(test)
}
@@ -41,3 +45,18 @@ describe('async timeouts', () => {
cy.then(() => done())
})
})
+
+// NOTE: this test must remain the last test in the spec
+// so we can test the root after hook
+describe('fires test:after:run after root after hook', () => {
+ it('test 1', () => {
+ })
+
+ it('test 2', () => {
+ })
+})
+
+// https://github.com/cypress-io/cypress/issues/2296
+after(() => {
+ expect(_.last(testAfterRunEvents).title, 'test:after:run for test 2 should not have fired yet').eq('test 1')
+})
diff --git a/packages/server/__snapshots__/3_issue_1987.ts.js b/packages/server/__snapshots__/3_issue_1987.ts.js
new file mode 100644
index 000000000000..f8a228bd3797
--- /dev/null
+++ b/packages/server/__snapshots__/3_issue_1987.ts.js
@@ -0,0 +1,132 @@
+exports['e2e issue 1987 / can reload during spec run'] = `
+
+====================================================================================================
+
+ (Run Starting)
+
+ ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
+ │ Cypress: 1.2.3 │
+ │ Browser: FooBrowser 88 │
+ │ Specs: 1 found (beforehook-and-test-navigation.js) │
+ │ Searched: cypress/integration/beforehook-and-test-navigation.js │
+ └────────────────────────────────────────────────────────────────────────────────────────────────┘
+
+
+────────────────────────────────────────────────────────────────────────────────────────────────────
+
+ Running: beforehook-and-test-navigation.js (1 of 1)
+
+
+ suite
+ ✓ test
+ ✓ causes domain navigation
+
+
+ 2 passing
+
+
+ (Results)
+
+ ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
+ │ Tests: 2 │
+ │ Passing: 2 │
+ │ Failing: 0 │
+ │ Pending: 0 │
+ │ Skipped: 0 │
+ │ Screenshots: 0 │
+ │ Video: true │
+ │ Duration: X seconds │
+ │ Spec Ran: beforehook-and-test-navigation.js │
+ └────────────────────────────────────────────────────────────────────────────────────────────────┘
+
+
+ (Video)
+
+ - Started processing: Compressing to 32 CRF
+ - Finished processing: /XXX/XXX/XXX/cypress/videos/beforehook-and-test-navigation. (X second)
+ js.mp4
+
+
+====================================================================================================
+
+ (Run Finished)
+
+
+ Spec Tests Passing Failing Pending Skipped
+ ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
+ │ ✔ beforehook-and-test-navigation.js XX:XX 2 2 - - - │
+ └────────────────────────────────────────────────────────────────────────────────────────────────┘
+ ✔ All specs passed! XX:XX 2 2 - - -
+
+
+`
+
+exports['e2e issue 1987 / can run proper amount of hooks'] = `
+
+====================================================================================================
+
+ (Run Starting)
+
+ ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
+ │ Cypress: 1.2.3 │
+ │ Browser: FooBrowser 88 │
+ │ Specs: 1 found (afterhooks.spec.js) │
+ │ Searched: cypress/integration/afterhooks.spec.js │
+ └────────────────────────────────────────────────────────────────────────────────────────────────┘
+
+
+────────────────────────────────────────────────────────────────────────────────────────────────────
+
+ Running: afterhooks.spec.js (1 of 1)
+
+
+ suite 1
+ ✓ test 1
+ ✓ test 2
+ ✓ test 3
+ ✓ test 4
+
+ suite 2
+ ✓ s2t1
+
+ suite 3
+ ✓ s3t1
+
+
+ 6 passing
+
+
+ (Results)
+
+ ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
+ │ Tests: 6 │
+ │ Passing: 6 │
+ │ Failing: 0 │
+ │ Pending: 0 │
+ │ Skipped: 0 │
+ │ Screenshots: 0 │
+ │ Video: true │
+ │ Duration: X seconds │
+ │ Spec Ran: afterhooks.spec.js │
+ └────────────────────────────────────────────────────────────────────────────────────────────────┘
+
+
+ (Video)
+
+ - Started processing: Compressing to 32 CRF
+ - Finished processing: /XXX/XXX/XXX/cypress/videos/afterhooks.spec.js.mp4 (X second)
+
+
+====================================================================================================
+
+ (Run Finished)
+
+
+ Spec Tests Passing Failing Pending Skipped
+ ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
+ │ ✔ afterhooks.spec.js XX:XX 6 6 - - - │
+ └────────────────────────────────────────────────────────────────────────────────────────────────┘
+ ✔ All specs passed! XX:XX 6 6 - - -
+
+
+`
diff --git a/packages/server/test/e2e/3_issue_1987.ts b/packages/server/test/e2e/3_issue_1987.ts
new file mode 100644
index 000000000000..8917bbd0b8d6
--- /dev/null
+++ b/packages/server/test/e2e/3_issue_1987.ts
@@ -0,0 +1,29 @@
+const e2e = require('../support/helpers/e2e')
+const Fixtures = require('../support/helpers/fixtures')
+
+describe('e2e issue 1987', () => {
+ e2e.setup({
+ servers: [{
+ port: 3434,
+ static: true,
+ },
+ {
+ port: 4545,
+ static: true,
+ }],
+ })
+
+ // https://github.com/cypress-io/cypress/issues/1987
+ // before/after hooks should not be rerun on top navigation
+ e2e.it('can reload during spec run', {
+ project: Fixtures.projectPath('hooks-after-rerun'),
+ spec: 'beforehook-and-test-navigation.js',
+ snapshot: true,
+ })
+
+ e2e.it('can run proper amount of hooks', {
+ project: Fixtures.projectPath('hooks-after-rerun'),
+ spec: 'afterhooks.spec.js',
+ snapshot: true,
+ })
+})
diff --git a/packages/server/test/integration/cypress_spec.js b/packages/server/test/integration/cypress_spec.js
index e6bdd9453dbe..895a85492163 100644
--- a/packages/server/test/integration/cypress_spec.js
+++ b/packages/server/test/integration/cypress_spec.js
@@ -105,7 +105,7 @@ describe('lib/cypress', () => {
require('mocha-banner').register()
beforeEach(function () {
- this.timeout(5000)
+ this.timeout(8000)
cache.__removeSync()
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress.json b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress.json
@@ -0,0 +1 @@
+{}
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/afterhooks.spec.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/afterhooks.spec.js
new file mode 100644
index 000000000000..e59d90359651
--- /dev/null
+++ b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/afterhooks.spec.js
@@ -0,0 +1,85 @@
+const urls = [
+ 'http://localhost:3434',
+ 'http://localhost:4545',
+ // 'http://localhost:3500/fixtures/generic.html',
+ // 'http://localhost:3501/fixtures/generic.html',
+]
+
+describe('suite 1', () => {
+ let local1 = null
+
+ before(() => {
+ local1 = true
+ cy.task('incrState', 'b1')
+ })
+
+ it('test 1', () => {
+ expect(local1, 'should maintain local state in beforehook after navigation ONLY IF first test in suite').eq(true)
+ cy.task('incrState', 't1')
+ cy.visit(urls[0])
+ })
+
+ it('test 2', () => {
+ expect(local1).eq(true)
+ cy.task('incrState', 't2')
+ })
+
+ it('test 3', () => {
+ cy.visit(urls[1])
+ .then(() => {
+ expect(local1, 'null since since localstate not maintained').eq(null)
+ })
+ })
+
+ it('test 4', () => {
+ // switch domain back for next tests
+ cy.visit(urls[0])
+ })
+
+ after(() => {
+ cy.task('incrState', 'a1')
+ })
+})
+
+describe('suite 2', () => {
+ it('s2t1', () => {
+ cy.task('incrState', 's2t1')
+ cy.visit(urls[1])
+ })
+})
+
+describe('suite 3', () => {
+ before(() => {
+ cy.task('incrState', 's3b1')
+ cy.visit(urls[0])
+ })
+
+ before(() => {
+ cy.window().then((win) => {
+ win.__local2 = true
+ })
+ })
+
+ it('s3t1', () => {
+ cy.task('incrState', 's3t1')
+ cy.window().its('__local2').should('eq', true)
+ })
+})
+
+after(() => {
+ cy.task('incrState', 'a2')
+ cy.task('getState').then((state) => {
+ expect(state).deep.eq({
+ // initial domain change causes 2 runs
+ 'b1': 2,
+ 't1': 2,
+ 't2': 1,
+ 'a1': 1,
+ // domain change causes 2 runs
+ 's2t1': 2,
+ 's3b1': 2,
+ 's3t1': 1,
+ 'a2': 1,
+ })
+ })
+})
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/beforehook-and-test-navigation.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/beforehook-and-test-navigation.js
new file mode 100644
index 000000000000..66f1728ba86d
--- /dev/null
+++ b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/beforehook-and-test-navigation.js
@@ -0,0 +1,13 @@
+describe('suite', () => {
+ before(() => {
+ // will cause infinite top navigation
+ cy.visit('http://localhost:3434')
+ })
+
+ it('test', () => {
+ })
+
+ it('causes domain navigation', () => {
+ cy.visit('http://localhost:4545')
+ })
+})
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
new file mode 100644
index 000000000000..3980b57a95f3
--- /dev/null
+++ b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
@@ -0,0 +1,33 @@
+///
+// ***********************************************************
+// This example plugins/index.js can be used to load plugins
+//
+// You can change the location of this file or turn off loading
+// the plugins file with the 'pluginsFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/plugins-guide
+// ***********************************************************
+
+// This function is called when a project is opened or re-opened (e.g. due to
+// the project's config changing)
+
+const state = {}
+
+/**
+ * @type {Cypress.PluginConfig}
+ */
+module.exports = (on, config) => {
+ on('task', {
+ incrState (arg) {
+ state[arg] = state[arg] + 1 || 1
+
+ return null
+ },
+ getState () {
+ return state
+ } })
+
+ // `on` is used to hook into various events Cypress emits
+ // `config` is the resolved Cypress config
+}
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/commands.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/commands.js
new file mode 100644
index 000000000000..ca4d256f3eb1
--- /dev/null
+++ b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/commands.js
@@ -0,0 +1,25 @@
+// ***********************************************
+// This example commands.js shows you how to
+// create various custom commands and overwrite
+// existing commands.
+//
+// For more comprehensive examples of custom
+// commands please read more here:
+// https://on.cypress.io/custom-commands
+// ***********************************************
+//
+//
+// -- This is a parent command --
+// Cypress.Commands.add("login", (email, password) => { ... })
+//
+//
+// -- This is a child command --
+// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
+//
+//
+// -- This is a dual command --
+// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
+//
+//
+// -- This will overwrite an existing command --
+// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js
new file mode 100644
index 000000000000..d68db96df269
--- /dev/null
+++ b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js
@@ -0,0 +1,20 @@
+// ***********************************************************
+// This example support/index.js is processed and
+// loaded automatically before your test files.
+//
+// This is a great place to put global configuration and
+// behavior that modifies Cypress.
+//
+// You can change the location of this file or turn off
+// automatically serving support files with the
+// 'supportFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/configuration
+// ***********************************************************
+
+// Import commands.js using ES2015 syntax:
+import './commands'
+
+// Alternatively you can use CommonJS syntax:
+// require('./commands')
diff --git a/yarn.lock b/yarn.lock
index 0e284953c225..e52c71702884 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9120,7 +9120,7 @@ debug@3.1.0, debug@=3.1.0, debug@~3.1.0:
dependencies:
ms "2.0.0"
-debug@3.2.6, debug@^3.0.0, debug@^3.0.1, debug@^3.1.0, debug@^3.1.1, debug@^3.2.6:
+debug@3.2.6, debug@^3.0.0, debug@^3.0.1, debug@^3.1.0, debug@^3.1.1:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
@@ -9531,7 +9531,7 @@ detect-indent@^6.0.0:
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd"
integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==
-detect-libc@^1.0.2, detect-libc@^1.0.3:
+detect-libc@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
@@ -13506,7 +13506,7 @@ iconv-lite@0.4.23:
dependencies:
safer-buffer ">= 2.1.2 < 3"
-iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@^0.4.5, iconv-lite@~0.4.13:
+iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.5, iconv-lite@~0.4.13:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
@@ -17588,15 +17588,6 @@ nearley@^2.7.10:
randexp "0.4.6"
semver "^5.4.1"
-needle@^2.2.1:
- version "2.4.1"
- resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.1.tgz#14af48732463d7475696f937626b1b993247a56a"
- integrity sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g==
- dependencies:
- debug "^3.2.6"
- iconv-lite "^0.4.4"
- sax "^1.2.4"
-
negotiator@0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
@@ -17838,22 +17829,6 @@ node-notifier@^5.4.2:
shellwords "^0.1.1"
which "^1.3.0"
-node-pre-gyp@*:
- version "0.14.0"
- resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83"
- integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==
- dependencies:
- detect-libc "^1.0.2"
- mkdirp "^0.5.1"
- needle "^2.2.1"
- nopt "^4.0.1"
- npm-packlist "^1.1.6"
- npmlog "^4.0.2"
- rc "^1.2.7"
- rimraf "^2.6.1"
- semver "^5.3.0"
- tar "^4.4.2"
-
node-preload@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/node-preload/-/node-preload-0.2.1.tgz#c03043bb327f417a18fee7ab7ee57b408a144301"
@@ -18043,7 +18018,7 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1:
semver "^5.6.0"
validate-npm-package-name "^3.0.0"
-npm-packlist@^1.1.6, npm-packlist@^1.4.4:
+npm-packlist@^1.4.4:
version "1.4.8"
resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e"
integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==
@@ -18104,7 +18079,7 @@ npm-run-path@^4.0.0:
dependencies:
path-key "^3.0.0"
-"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.1, npmlog@^4.0.2, npmlog@^4.1.2:
+"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.1, npmlog@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
@@ -23156,7 +23131,7 @@ tar@^2.0.0:
fstream "^1.0.12"
inherits "2"
-tar@^4.4.10, tar@^4.4.12, tar@^4.4.2, tar@^4.4.8:
+tar@^4.4.10, tar@^4.4.12, tar@^4.4.8:
version "4.4.13"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==
From 9bd0a553943ced73fb39c7814173481877320a09 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Wed, 29 Apr 2020 11:17:00 -0400
Subject: [PATCH 20/86] temp 04/29/20 [skip ci]
---
packages/driver/src/cypress/runner.js | 41 ++++-----------------------
1 file changed, 6 insertions(+), 35 deletions(-)
diff --git a/packages/driver/src/cypress/runner.js b/packages/driver/src/cypress/runner.js
index d786c642c5d0..f28f621e3dd6 100644
--- a/packages/driver/src/cypress/runner.js
+++ b/packages/driver/src/cypress/runner.js
@@ -243,12 +243,10 @@ const getAllSiblingTests = function (suite, getTestById) {
const getTestFromHook = function (hook, suite, getTestById) {
// if theres already a currentTest use that
- let found; let test
+ const ctxTest = hook.ctx.currentTest
- test = hook != null ? hook.ctx.currentTest : undefined
-
- if (test) {
- return test
+ if (ctxTest) {
+ return ctxTest
}
if (hook.hookName === 'after all') {
@@ -263,36 +261,7 @@ const getTestFromHook = function (hook, suite, getTestById) {
return _.first(siblings)
}
- // if we have a hook id then attempt
- // to find the test by its id
- if (hook != null ? hook.id : undefined) {
- found = onFirstTest(suite, (test) => {
- return hook.id === test.id
- })
-
- if (found) {
- return found
- }
- }
-
- // returns us the very first test
- // which is in our filtered tests array
- // based on walking down the current suite
- // iterating through each test until it matches
- found = onFirstTest(suite, (test) => {
- return getTestById(test.id)
- })
-
- if (found) {
- return found
- }
-
- // have one last final fallback where
- // we just return true on the very first
- // test (used in testing)
- return onFirstTest(suite, (test) => {
- return true
- })
+ throw new Error('failed to get test from hook')
}
// we have to see if this is the last suite amongst
@@ -467,6 +436,7 @@ const normalizeAll = (suite, initialTests = {}, setTestsById, setTests, onRunnab
const normalizedSuite = normalize(suite, tests, initialTests, onRunnable, onLogsById, getTestId)
if (setTestsById) {
+ console.log('settestsbyid', tests)
// use callback here to hand back
// the optimized tests
setTestsById(tests)
@@ -664,6 +634,7 @@ const _runnerListeners = function (_runner, Cypress, _emissions, getTestById, ge
// and we can only associate them by this id
const test = getTestFromHook(hook, hook.parent, getTestById)
+ console.log('got from hook', hook.hookName, test)
hook.id = test.id
hook.ctx.currentTest = test
From e2822895aaf481bf6c8f8bb886a665667767151d Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Wed, 29 Apr 2020 15:44:07 -0400
Subject: [PATCH 21/86] backport changes from test-retries
---
.../test/cypress/integration/runner.spec.js | 15 +++------------
.../cypress/plugins/snapshot/command/index.js | 17 +++++++++++++----
packages/runner/test/cypress/support/helpers.js | 8 ++++++++
3 files changed, 24 insertions(+), 16 deletions(-)
diff --git a/packages/runner/test/cypress/integration/runner.spec.js b/packages/runner/test/cypress/integration/runner.spec.js
index ae564e03ce36..82f4d4559c5e 100644
--- a/packages/runner/test/cypress/integration/runner.spec.js
+++ b/packages/runner/test/cypress/integration/runner.spec.js
@@ -5,11 +5,6 @@ const snapshots = require('../support/eventSnapshots').EventSnapshots
const sinon = require('sinon')
-const backupCy = window.cy
-const backupCypress = window.Cypress
-
-backupCy.__original__ = true
-
const simpleSingleTest = {
suites: { 'suite 1': { tests: [{ name: 'test 1' }] } },
}
@@ -22,11 +17,6 @@ const { visit, snapshotEvents, onInitialized, getAutCypress } = helpers.createCy
describe('src/cypress/runner', () => {
describe('isolated test runner', () => {
- beforeEach(() => {
- window.cy = backupCy
- window.Cypress = backupCypress
- })
-
describe('test events', function () {
it('simple 1 test', () => {
visit(simpleSingleTest)
@@ -123,7 +113,7 @@ describe('src/cypress/runner', () => {
},
} },
},
- }, { config: { numTestRetries: 1 } })
+ }, {})
.then(shouldHaveTestResults(3, 0))
})
@@ -366,9 +356,10 @@ describe('src/cypress/runner', () => {
],
},
},
- }, { config: { numTestRetries: 1 } },
+ }, {},
]
+ // TODO: make this one test with multiple visits
it('serialize state', () => {
visit(...cypressConfig)
.then(shouldHaveTestResults(4, 0))
diff --git a/packages/runner/test/cypress/plugins/snapshot/command/index.js b/packages/runner/test/cypress/plugins/snapshot/command/index.js
index 7cb7efc1dfa6..105d9eddd725 100644
--- a/packages/runner/test/cypress/plugins/snapshot/command/index.js
+++ b/packages/runner/test/cypress/plugins/snapshot/command/index.js
@@ -226,6 +226,10 @@ let options = {
indentChar: ' ',
newLineChar: '\n',
wrap: function wrap (type, text) {
+ if (this.Cypress) {
+ text = `**${text}**`
+ }
+
return typeColors[type](text)
},
}
@@ -290,11 +294,11 @@ function keyChanged (key, text) {
}
function keyRemoved (key, variable) {
- return options.wrap('removed', `**- ${key}: ${printVar(variable)}**`) + options.newLineChar
+ return options.wrap('removed', `- ${key}: ${printVar(variable)}`) + options.newLineChar
}
function keyAdded (key, variable) {
- return options.wrap('added', `**+ ${key}: ${printVar(variable)}**`) + options.newLineChar
+ return options.wrap('added', `+ ${key}: ${printVar(variable)}`) + options.newLineChar
}
function parseMatcher (obj, match) {
@@ -442,7 +446,12 @@ const withMatchers = (matchers, match, expectedOnly = false) => {
let actObj = _.extend({}, act)
let key
- keys.sort()
+ if (_.isArray(exp)) {
+ keys.sort((a, b) => +a - +b)
+ } else {
+ keys.sort()
+ }
+
for (let i = 0; i < keys.length; i++) {
key = keys[i]
const isUndef = exp[key] === undefined
@@ -523,7 +532,7 @@ const withMatchers = (matchers, match, expectedOnly = false) => {
act = printVar(act)
if (exp !== act) {
- text = options.wrap('modified', `**${exp} ⮕ ${act}**`)
+ text = options.wrap('modified', `${exp} ${typeColors['normal']('⮕')} ${act}`)
changed = true
}
}
diff --git a/packages/runner/test/cypress/support/helpers.js b/packages/runner/test/cypress/support/helpers.js
index eb199e645863..feeb2c374079 100644
--- a/packages/runner/test/cypress/support/helpers.js
+++ b/packages/runner/test/cypress/support/helpers.js
@@ -75,6 +75,14 @@ function createCypress () {
snapshotPlugin.registerInCypress()
+ const backupCy = window.cy
+ const backupCypress = window.Cypress
+
+ beforeEach(() => {
+ window.cy = backupCy
+ window.Cypress = backupCypress
+ })
+
let onInitializedListeners = []
const onInitialized = function (fn) {
From 85503d37ca0e00d00a12c13d02c6d9c31f1046dd Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Wed, 29 Apr 2020 21:39:06 -0400
Subject: [PATCH 22/86] change logic to rerun before hooks after top navigation
---
packages/driver/src/cypress/runner.js | 128 +++++++----------
....ts.js => 3_runnable_execution_spec.ts.js} | 90 ++++++++----
...e_1987.ts => 3_runnable_execution_spec.ts} | 16 ++-
.../cypress/integration/afterhooks.spec.js | 85 ------------
.../beforehook-and-test-navigation.js | 22 ++-
.../integration/runnable-run-count.spec.js | 129 ++++++++++++++++++
.../cypress/plugins/index.js | 20 +--
.../cypress/support/commands.js | 25 ----
.../cypress/support/index.js | 20 ---
9 files changed, 275 insertions(+), 260 deletions(-)
rename packages/server/__snapshots__/{3_issue_1987.ts.js => 3_runnable_execution_spec.ts.js} (76%)
rename packages/server/test/e2e/{3_issue_1987.ts => 3_runnable_execution_spec.ts} (56%)
delete mode 100644 packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/afterhooks.spec.js
create mode 100644 packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/runnable-run-count.spec.js
delete mode 100644 packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/commands.js
diff --git a/packages/driver/src/cypress/runner.js b/packages/driver/src/cypress/runner.js
index d786c642c5d0..aea168ed835c 100644
--- a/packages/driver/src/cypress/runner.js
+++ b/packages/driver/src/cypress/runner.js
@@ -241,58 +241,55 @@ const getAllSiblingTests = function (suite, getTestById) {
return tests
}
-const getTestFromHook = function (hook, suite, getTestById) {
+function getOrderFromId (id) {
+ return +id.slice(1)
+}
+
+function isNotAlreadyRunTest (test) {
+ return !(Cypress._RESUMED_AT_TEST && getOrderFromId(test.id) < getOrderFromId(Cypress._RESUMED_AT_TEST))
+}
+
+const getTestFromHook = function (hook) {
// if theres already a currentTest use that
- let found; let test
- test = hook != null ? hook.ctx.currentTest : undefined
+ const test = hook.ctx.currentTest
+
+ if (test) {
+ return test
+ }
+}
+const getTestFromHookOrFindTest = function (hook, getTestById) {
+ const test = getTestFromHook(hook)
if (test) {
return test
}
+ const suite = hook.parent
+
+ // TODO: make performant, use findTestInSuite()
if (hook.hookName === 'after all') {
const siblings = getAllSiblingTests(suite, getTestById)
- return _.last(siblings)
+ return _.findLast(siblings, isNotAlreadyRunTest)
}
if (hook.hookName === 'before all') {
const siblings = getAllSiblingTests(suite, getTestById)
- return _.first(siblings)
- }
-
- // if we have a hook id then attempt
- // to find the test by its id
- if (hook != null ? hook.id : undefined) {
- found = onFirstTest(suite, (test) => {
- return hook.id === test.id
- })
-
- if (found) {
- return found
- }
+ return _.find(siblings, isNotAlreadyRunTest)
}
+}
- // returns us the very first test
- // which is in our filtered tests array
- // based on walking down the current suite
- // iterating through each test until it matches
- found = onFirstTest(suite, (test) => {
- return getTestById(test.id)
- })
+function getTestFromRunnable (runnable) {
+ switch (runnable.type) {
+ case 'hook':
+ return getTestFromHook(runnable)
- if (found) {
- return found
+ case 'test':
+ return runnable
+ default: null
}
-
- // have one last final fallback where
- // we just return true on the very first
- // test (used in testing)
- return onFirstTest(suite, (test) => {
- return true
- })
}
// we have to see if this is the last suite amongst
@@ -404,6 +401,8 @@ const overrideRunnerHook = function (Cypress, _runner, getTestById, getTest, set
// since that will bubble up IF we're the last nested suite
// 3. else if we arent the last nested suite we fire if we're
// the last test that will run
+
+ // TODO: move middle conditional into method
if (
(isRootSuite(this.suite) && isLastTest(t, allTests)) ||
(isRootSuite(this.suite.parent) && !this.suite.parent._afterAll.length && lastTestThatWillRunInSuite(t, siblings)) ||
@@ -585,11 +584,11 @@ const normalize = (runnable, tests, initialTests, onRunnable, onLogsById, getTes
return normalizedRunnable
}
-const hookFailed = function (hook, err, hookName, getTestById, getTest) {
+const hookFailed = function (hook, err, hookName) {
// finds the test by returning the first test from
// the parent or looping through the suites until
// it finds the first test
- const test = getTestFromHook(hook, hook.parent, getTestById)
+ const test = getTestFromHook(hook)
test.err = err
test.state = 'failed'
@@ -650,7 +649,7 @@ const _runnerListeners = function (_runner, Cypress, _emissions, getTestById, ge
hook.hookName = getHookName(hook)
}
- // mocha incorrectly sets currentTest on before all's.
+ // mocha incorrectly sets currentTest on before/after all's.
// if there is a nested suite with a before, then
// currentTest will refer to the previous test run
// and not our current
@@ -662,7 +661,14 @@ const _runnerListeners = function (_runner, Cypress, _emissions, getTestById, ge
// hooks do not have their own id, their
// commands need to grouped with the test
// and we can only associate them by this id
- const test = getTestFromHook(hook, hook.parent, getTestById)
+ const test = getTestFromHookOrFindTest(hook, getTestById)
+
+ if (!test) {
+ // we couldn't find a test to run with this hook
+ // probably because the entire suite has already completed
+ // so return early and tell onRunnableRun to skip the test
+ return
+ }
hook.id = test.id
hook.ctx.currentTest = test
@@ -789,10 +795,6 @@ const _runnerListeners = function (_runner, Cypress, _emissions, getTestById, ge
})
}
-function getOrderFromId (id) {
- return +id.slice(1)
-}
-
const create = function (specWindow, mocha, Cypress, cy) {
let _id = 0
let _hookId = 0
@@ -905,14 +907,6 @@ const create = function (specWindow, mocha, Cypress, cy) {
return _testsById[id]
}
- function hasTestAlreadyRun (test) {
- if (Cypress._RESUMED_AT_TEST) {
- return getOrderFromId(test.id) < getOrderFromId(Cypress._RESUMED_AT_TEST)
- }
-
- return false
- }
-
overrideRunnerHook(Cypress, _runner, getTestById, getTest, setTest, getTests)
return {
@@ -963,43 +957,22 @@ const create = function (specWindow, mocha, Cypress, cy) {
},
onRunnableRun (runnableRun, runnable, args) {
- let lifecycleStart; let test
-
- if (!runnable.id) {
- if (!_stopped) {
- throw new Error('runnable must have an id', runnable.id)
- }
-
- return
- }
-
- switch (runnable.type) {
- case 'hook':
- test = getTestFromHook(runnable, runnable.parent, getTestById)
- break
-
- case 'test':
- test = runnable
- break
-
- default:
- break
- }
-
- // if this isnt a hook, then the name is 'test'
- const hookName = runnable.type === 'hook' ? getHookName(runnable) : 'test'
-
// extract out the next(fn) which mocha uses to
// move to the next runnable - this will be our async seam
const _next = args[0]
- if (hasTestAlreadyRun(test)) {
+ const test = getTestFromRunnable(runnable)
+
+ // if there's no test, this is likely a rouge before/after hook
+ // that should not have run, so skip this runnable
+ if (!test) {
return _next()
}
// closure for calculating the actual
// runtime of a runnables fn exection duration
// and also the run of the runnable:after:run:async event
+ let lifecycleStart
let wallClockStartedAt = null
let wallClockEnd = null
let fnDurationStart = null
@@ -1021,6 +994,9 @@ const create = function (specWindow, mocha, Cypress, cy) {
test.wallClockStartedAt = wallClockStartedAt
}
+ // if this isnt a hook, then the name is 'test'
+ const hookName = runnable.type === 'hook' ? getHookName(runnable) : 'test'
+
// if we haven't yet fired this event for this test
// that means that we need to reset the previous state
// of cy - since we now have a new 'test' and all of the
diff --git a/packages/server/__snapshots__/3_issue_1987.ts.js b/packages/server/__snapshots__/3_runnable_execution_spec.ts.js
similarity index 76%
rename from packages/server/__snapshots__/3_issue_1987.ts.js
rename to packages/server/__snapshots__/3_runnable_execution_spec.ts.js
index f8a228bd3797..40d7768e1abf 100644
--- a/packages/server/__snapshots__/3_issue_1987.ts.js
+++ b/packages/server/__snapshots__/3_runnable_execution_spec.ts.js
@@ -1,4 +1,4 @@
-exports['e2e issue 1987 / can reload during spec run'] = `
+exports['e2e runnable execution / cannot navigate in before hook and test'] = `
====================================================================================================
@@ -17,29 +17,64 @@ exports['e2e issue 1987 / can reload during spec run'] = `
Running: beforehook-and-test-navigation.js (1 of 1)
+ initial domain change
+ ✓ test
+
suite
✓ test
- ✓ causes domain navigation
+ 1) causes domain navigation
2 passing
+ 1 failing
+
+ 1) suite
+ causes domain navigation:
+ CypressError: \`cy.visit()\` failed because you are attempting to visit a URL that is of a different origin.
+
+The new URL is considered a different origin because the following parts of the URL are different:
+
+ > port
+
+You may only \`cy.visit()\` same-origin URLs within a single test.
+
+The previous URL you visited was:
+
+ > 'http://localhost:4545'
+
+You're attempting to visit this URL:
+
+ > 'http://localhost:5656'
+
+You may need to restructure some of your test code to avoid this problem.
+
+https://on.cypress.io/cannot-visit-different-origin-domain
+ [stack trace lines]
+
+
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ Tests: 2 │
+ │ Tests: 3 │
│ Passing: 2 │
- │ Failing: 0 │
+ │ Failing: 1 │
│ Pending: 0 │
│ Skipped: 0 │
- │ Screenshots: 0 │
+ │ Screenshots: 1 │
│ Video: true │
│ Duration: X seconds │
│ Spec Ran: beforehook-and-test-navigation.js │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
+ (Screenshots)
+
+ - /XXX/XXX/XXX/cypress/screenshots/beforehook-and-test-navigation.js/suite -- caus (1280x720)
+ es domain navigation (failed).png
+
+
(Video)
- Started processing: Compressing to 32 CRF
@@ -54,14 +89,14 @@ exports['e2e issue 1987 / can reload during spec run'] = `
Spec Tests Passing Failing Pending Skipped
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ ✔ beforehook-and-test-navigation.js XX:XX 2 2 - - - │
+ │ ✖ beforehook-and-test-navigation.js XX:XX 3 2 1 - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
- ✔ All specs passed! XX:XX 2 2 - - -
+ ✖ 1 of 1 failed (100%) XX:XX 3 2 1 - -
`
-exports['e2e issue 1987 / can run proper amount of hooks'] = `
+exports['e2e runnable execution / runnables run correct number of times with navigation'] = `
====================================================================================================
@@ -70,51 +105,52 @@ exports['e2e issue 1987 / can run proper amount of hooks'] = `
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 1.2.3 │
│ Browser: FooBrowser 88 │
- │ Specs: 1 found (afterhooks.spec.js) │
- │ Searched: cypress/integration/afterhooks.spec.js │
+ │ Specs: 1 found (runnable-run-count.spec.js) │
+ │ Searched: cypress/integration/runnable-run-count.spec.js │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────
- Running: afterhooks.spec.js (1 of 1)
+ Running: runnable-run-count.spec.js (1 of 1)
- suite 1
- ✓ test 1
- ✓ test 2
- ✓ test 3
- ✓ test 4
+ suite 1.0
+ ✓ test 1.0.1
+ ✓ test 1.0.2
+ ✓ test 1.0.3
- suite 2
- ✓ s2t1
+ suite 1.1
+ ✓ test 1.1.1
+ ✓ test 1.1.2
- suite 3
- ✓ s3t1
+ suite 1.2
+ ✓ test 1.2.1
+ ✓ test 1.2.2
- 6 passing
+ 7 passing
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ Tests: 6 │
- │ Passing: 6 │
+ │ Tests: 7 │
+ │ Passing: 7 │
│ Failing: 0 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 0 │
│ Video: true │
│ Duration: X seconds │
- │ Spec Ran: afterhooks.spec.js │
+ │ Spec Ran: runnable-run-count.spec.js │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(Video)
- Started processing: Compressing to 32 CRF
- - Finished processing: /XXX/XXX/XXX/cypress/videos/afterhooks.spec.js.mp4 (X second)
+ - Finished processing: /XXX/XXX/XXX/cypress/videos/runnable-run-count.spec.js.mp4 (X second)
====================================================================================================
@@ -124,9 +160,9 @@ exports['e2e issue 1987 / can run proper amount of hooks'] = `
Spec Tests Passing Failing Pending Skipped
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ ✔ afterhooks.spec.js XX:XX 6 6 - - - │
+ │ ✔ runnable-run-count.spec.js XX:XX 7 7 - - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
- ✔ All specs passed! XX:XX 6 6 - - -
+ ✔ All specs passed! XX:XX 7 7 - - -
`
diff --git a/packages/server/test/e2e/3_issue_1987.ts b/packages/server/test/e2e/3_runnable_execution_spec.ts
similarity index 56%
rename from packages/server/test/e2e/3_issue_1987.ts
rename to packages/server/test/e2e/3_runnable_execution_spec.ts
index 8917bbd0b8d6..0b6aeaf19628 100644
--- a/packages/server/test/e2e/3_issue_1987.ts
+++ b/packages/server/test/e2e/3_runnable_execution_spec.ts
@@ -1,7 +1,7 @@
const e2e = require('../support/helpers/e2e')
const Fixtures = require('../support/helpers/fixtures')
-describe('e2e issue 1987', () => {
+describe('e2e runnable execution', () => {
e2e.setup({
servers: [{
port: 3434,
@@ -10,20 +10,26 @@ describe('e2e issue 1987', () => {
{
port: 4545,
static: true,
+ },
+ {
+ port: 5656,
+ static: true,
}],
})
+ // navigation in before and in test body doesn't cause infinite loop
+ // but throws correct error
// https://github.com/cypress-io/cypress/issues/1987
- // before/after hooks should not be rerun on top navigation
- e2e.it('can reload during spec run', {
+ e2e.it('cannot navigate in before hook and test', {
project: Fixtures.projectPath('hooks-after-rerun'),
spec: 'beforehook-and-test-navigation.js',
snapshot: true,
+ expectedExitCode: 1,
})
- e2e.it('can run proper amount of hooks', {
+ e2e.it('runnables run correct number of times with navigation', {
project: Fixtures.projectPath('hooks-after-rerun'),
- spec: 'afterhooks.spec.js',
+ spec: 'runnable-run-count.spec.js',
snapshot: true,
})
})
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/afterhooks.spec.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/afterhooks.spec.js
deleted file mode 100644
index e59d90359651..000000000000
--- a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/afterhooks.spec.js
+++ /dev/null
@@ -1,85 +0,0 @@
-const urls = [
- 'http://localhost:3434',
- 'http://localhost:4545',
- // 'http://localhost:3500/fixtures/generic.html',
- // 'http://localhost:3501/fixtures/generic.html',
-]
-
-describe('suite 1', () => {
- let local1 = null
-
- before(() => {
- local1 = true
- cy.task('incrState', 'b1')
- })
-
- it('test 1', () => {
- expect(local1, 'should maintain local state in beforehook after navigation ONLY IF first test in suite').eq(true)
- cy.task('incrState', 't1')
- cy.visit(urls[0])
- })
-
- it('test 2', () => {
- expect(local1).eq(true)
- cy.task('incrState', 't2')
- })
-
- it('test 3', () => {
- cy.visit(urls[1])
- .then(() => {
- expect(local1, 'null since since localstate not maintained').eq(null)
- })
- })
-
- it('test 4', () => {
- // switch domain back for next tests
- cy.visit(urls[0])
- })
-
- after(() => {
- cy.task('incrState', 'a1')
- })
-})
-
-describe('suite 2', () => {
- it('s2t1', () => {
- cy.task('incrState', 's2t1')
- cy.visit(urls[1])
- })
-})
-
-describe('suite 3', () => {
- before(() => {
- cy.task('incrState', 's3b1')
- cy.visit(urls[0])
- })
-
- before(() => {
- cy.window().then((win) => {
- win.__local2 = true
- })
- })
-
- it('s3t1', () => {
- cy.task('incrState', 's3t1')
- cy.window().its('__local2').should('eq', true)
- })
-})
-
-after(() => {
- cy.task('incrState', 'a2')
- cy.task('getState').then((state) => {
- expect(state).deep.eq({
- // initial domain change causes 2 runs
- 'b1': 2,
- 't1': 2,
- 't2': 1,
- 'a1': 1,
- // domain change causes 2 runs
- 's2t1': 2,
- 's3b1': 2,
- 's3t1': 1,
- 'a2': 1,
- })
- })
-})
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/beforehook-and-test-navigation.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/beforehook-and-test-navigation.js
index 66f1728ba86d..e1b1b31523a6 100644
--- a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/beforehook-and-test-navigation.js
+++ b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/beforehook-and-test-navigation.js
@@ -1,13 +1,25 @@
+// should fail since before hooks are rerun on domain change
+
+const urls = [
+ 'http://localhost:3434',
+ 'http://localhost:4545',
+ 'http://localhost:5656',
+]
+
+describe('initial domain change', () => {
+ it('test', () => {
+ cy.visit(urls[0])
+ })
+})
+
describe('suite', () => {
before(() => {
- // will cause infinite top navigation
- cy.visit('http://localhost:3434')
+ cy.visit(urls[1])
})
- it('test', () => {
- })
+ it('test', () => {})
it('causes domain navigation', () => {
- cy.visit('http://localhost:4545')
+ cy.visit(urls[2])
})
})
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/runnable-run-count.spec.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/runnable-run-count.spec.js
new file mode 100644
index 000000000000..c2c38c2ac6c3
--- /dev/null
+++ b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/runnable-run-count.spec.js
@@ -0,0 +1,129 @@
+const urls = [
+ 'http://localhost:3434',
+ 'http://localhost:4545',
+ 'http://localhost:5656',
+]
+
+function incrState (key) {
+ // console.log(key)
+ cy.log(key)
+ cy.task('incrState', key)
+}
+
+/**
+ * BeforeEach should be rerun again after domain switch
+ * Before will run again after domain switch on the first test to run of a suite
+ * After hook will always run on last test of a suite
+ */
+
+// visit in sibling tests
+describe('suite 1.0', () => {
+ let local1 = null
+
+ before(() => {
+ local1 = true
+ incrState('b1.0.1')
+ })
+
+ it('test 1.0.1', () => {
+ incrState('t1.0.1')
+ cy.visit(urls[0])
+ .then(() => {
+ expect(local1).eq(true)
+ })
+ })
+
+ it('test 1.0.2', () => {
+ expect(local1).eq(true)
+ incrState('t1.0.2')
+ })
+
+ it('test 1.0.3', () => {
+ incrState('t1.0.3')
+
+ cy.visit(urls[1])
+ .then(() => {
+ expect(local1).eq(true)
+ })
+ })
+
+ after(() => {
+ incrState('a1.0.1')
+ })
+})
+
+// visit in before hook
+describe('suite 1.1', () => {
+ before(() => {
+ incrState('b1.1.1')
+ cy.visit(urls[0])
+ })
+
+ before(() => {
+ incrState('b1.1.2')
+ })
+
+ it('test 1.1.1', () => {
+ incrState('t1.1.1')
+ })
+
+ it('test 1.1.2', () => {
+ incrState('t1.1.2')
+ })
+})
+
+// visit in beforeEach hook
+describe('suite 1.2', () => {
+ before(() => {
+ incrState('b1.2.1')
+ })
+
+ beforeEach(() => {
+ incrState('b1.2.2')
+ cy.visit(urls[1])
+ })
+
+ beforeEach(() => {
+ incrState('b1.2.3')
+ })
+
+ it('test 1.2.1', () => {
+ incrState('t1.2.1')
+ })
+
+ it('test 1.2.2', () => {
+ incrState('t1.2.2')
+ })
+
+ after(() => {
+ incrState('a1.2.1')
+ })
+})
+
+after(() => {
+ cy.task('getState').then((state) => {
+ expect(state).deep.eq({
+ // visit in sibling tests
+ 'b1.0.1': 3,
+ 't1.0.1': 2,
+ 't1.0.2': 1,
+ 't1.0.3': 2,
+ 'a1.0.1': 1,
+
+ // before visit
+ 'b1.1.1': 2,
+ 'b1.1.2': 1,
+ 't1.1.1': 1,
+ 't1.1.2': 1,
+
+ // beforeEach visit
+ 'b1.2.1': 2,
+ 'b1.2.2': 3,
+ 'b1.2.3': 2,
+ 't1.2.1': 1,
+ 't1.2.2': 1,
+ 'a1.2.1': 1,
+
+ })
+ })
+})
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
index 3980b57a95f3..b2bda41e5d3c 100644
--- a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
+++ b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
@@ -1,23 +1,11 @@
///
-// ***********************************************************
-// This example plugins/index.js can be used to load plugins
-//
-// You can change the location of this file or turn off loading
-// the plugins file with the 'pluginsFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/plugins-guide
-// ***********************************************************
-
-// This function is called when a project is opened or re-opened (e.g. due to
-// the project's config changing)
const state = {}
/**
* @type {Cypress.PluginConfig}
*/
-module.exports = (on, config) => {
+module.exports = (on) => {
on('task', {
incrState (arg) {
state[arg] = state[arg] + 1 || 1
@@ -26,8 +14,6 @@ module.exports = (on, config) => {
},
getState () {
return state
- } })
-
- // `on` is used to hook into various events Cypress emits
- // `config` is the resolved Cypress config
+ },
+ })
}
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/commands.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/commands.js
deleted file mode 100644
index ca4d256f3eb1..000000000000
--- a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/commands.js
+++ /dev/null
@@ -1,25 +0,0 @@
-// ***********************************************
-// This example commands.js shows you how to
-// create various custom commands and overwrite
-// existing commands.
-//
-// For more comprehensive examples of custom
-// commands please read more here:
-// https://on.cypress.io/custom-commands
-// ***********************************************
-//
-//
-// -- This is a parent command --
-// Cypress.Commands.add("login", (email, password) => { ... })
-//
-//
-// -- This is a child command --
-// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
-//
-//
-// -- This is a dual command --
-// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
-//
-//
-// -- This will overwrite an existing command --
-// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js
index d68db96df269..e69de29bb2d1 100644
--- a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js
+++ b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js
@@ -1,20 +0,0 @@
-// ***********************************************************
-// This example support/index.js is processed and
-// loaded automatically before your test files.
-//
-// This is a great place to put global configuration and
-// behavior that modifies Cypress.
-//
-// You can change the location of this file or turn off
-// automatically serving support files with the
-// 'supportFile' configuration option.
-//
-// You can read more here:
-// https://on.cypress.io/configuration
-// ***********************************************************
-
-// Import commands.js using ES2015 syntax:
-import './commands'
-
-// Alternatively you can use CommonJS syntax:
-// require('./commands')
From 46c116db16b874e32676f8e3e778057a3dee0d81 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 4 May 2020 14:42:55 -0400
Subject: [PATCH 23/86] fix windowSize for browser e2e test
---
.../hooks-after-rerun/cypress/plugins/index.js | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
index b2bda41e5d3c..a879a0383728 100644
--- a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
+++ b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
@@ -16,4 +16,16 @@ module.exports = (on) => {
return state
},
})
+
+ on('before:browser:launch', (browser, launchOptions) => {
+ // this is needed to ensure correct error screenshot / video recording
+ // resolution of exactly 1280x720 (height must account for url bar)
+ if (browser.family === 'chromium') {
+ launchOptions.args.push('--window-size=1288,805')
+ } else if (browser.family === 'firefox') {
+ launchOptions.args.push('-width', '1280', '-height', '794')
+ }
+
+ return launchOptions
+ })
}
From 590a573407e14cce663bb5a0a501d962525dc03e Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 4 May 2020 15:08:47 -0400
Subject: [PATCH 24/86] fix windowSize for xvfb chrome in e2e test
---
.../projects/hooks-after-rerun/cypress/plugins/index.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
index a879a0383728..c5c41a2145b2 100644
--- a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
+++ b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
@@ -21,7 +21,11 @@ module.exports = (on) => {
// this is needed to ensure correct error screenshot / video recording
// resolution of exactly 1280x720 (height must account for url bar)
if (browser.family === 'chromium') {
- launchOptions.args.push('--window-size=1288,805')
+ if (process.env.DISPLAY === ':99') {
+ launchOptions.args.push('--window-size=1280,792')
+ } else {
+ launchOptions.args.push('--window-size=1288,805')
+ }
} else if (browser.family === 'firefox') {
launchOptions.args.push('-width', '1280', '-height', '794')
}
From 22d2780aada837e9f8d0f74e41f7bdd98b7efee3 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 4 May 2020 16:01:17 -0400
Subject: [PATCH 25/86] ok fine, just disable screenshots
---
.../3_runnable_execution_spec.ts.js | 8 +-------
.../hooks-after-rerun/cypress/plugins/index.js | 16 ----------------
.../hooks-after-rerun/cypress/support/index.js | 3 +++
3 files changed, 4 insertions(+), 23 deletions(-)
diff --git a/packages/server/__snapshots__/3_runnable_execution_spec.ts.js b/packages/server/__snapshots__/3_runnable_execution_spec.ts.js
index 40d7768e1abf..06dc92d31c09 100644
--- a/packages/server/__snapshots__/3_runnable_execution_spec.ts.js
+++ b/packages/server/__snapshots__/3_runnable_execution_spec.ts.js
@@ -62,19 +62,13 @@ https://on.cypress.io/cannot-visit-different-origin-domain
│ Failing: 1 │
│ Pending: 0 │
│ Skipped: 0 │
- │ Screenshots: 1 │
+ │ Screenshots: 0 │
│ Video: true │
│ Duration: X seconds │
│ Spec Ran: beforehook-and-test-navigation.js │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
- (Screenshots)
-
- - /XXX/XXX/XXX/cypress/screenshots/beforehook-and-test-navigation.js/suite -- caus (1280x720)
- es domain navigation (failed).png
-
-
(Video)
- Started processing: Compressing to 32 CRF
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
index c5c41a2145b2..b2bda41e5d3c 100644
--- a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
+++ b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
@@ -16,20 +16,4 @@ module.exports = (on) => {
return state
},
})
-
- on('before:browser:launch', (browser, launchOptions) => {
- // this is needed to ensure correct error screenshot / video recording
- // resolution of exactly 1280x720 (height must account for url bar)
- if (browser.family === 'chromium') {
- if (process.env.DISPLAY === ':99') {
- launchOptions.args.push('--window-size=1280,792')
- } else {
- launchOptions.args.push('--window-size=1288,805')
- }
- } else if (browser.family === 'firefox') {
- launchOptions.args.push('-width', '1280', '-height', '794')
- }
-
- return launchOptions
- })
}
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js
index e69de29bb2d1..69378f3ea16b 100644
--- a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js
+++ b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js
@@ -0,0 +1,3 @@
+Cypress.Screenshot.defaults({
+ screenshotOnRunFailure: false,
+})
From 4c88619b53caf6f0b1096f797865e1d78232f891 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 4 May 2020 18:47:09 -0400
Subject: [PATCH 26/86] fix after merge: decaffed navigation.js
---
packages/driver/src/cy/commands/navigation.js | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/packages/driver/src/cy/commands/navigation.js b/packages/driver/src/cy/commands/navigation.js
index 80b53a4eebd7..f3b96dbd4050 100644
--- a/packages/driver/src/cy/commands/navigation.js
+++ b/packages/driver/src/cy/commands/navigation.js
@@ -107,7 +107,7 @@ const specifyFileByRelativePath = (url, log) => {
})
}
-const aboutBlank = (win) => {
+const aboutBlank = (cy, win) => {
return new Promise((resolve) => {
cy.once('window:load', resolve)
@@ -198,7 +198,7 @@ const formSubmitted = (Cypress, e) => {
})
}
-const pageLoading = (bool, state) => {
+const pageLoading = (bool, Cypress, state) => {
if (state('pageLoading') === bool) {
return
}
@@ -215,7 +215,7 @@ const stabilityChanged = (Cypress, state, config, stable) => {
// if we're currently visiting about blank
// and becoming unstable for the first time
// notifiy that we're page loading
- pageLoading(true, state)
+ pageLoading(true, Cypress, state)
return
}
@@ -226,7 +226,7 @@ const stabilityChanged = (Cypress, state, config, stable) => {
}
// let the world know that the app is page:loading
- pageLoading(!stable, state)
+ pageLoading(!stable, Cypress, state)
// if we aren't becoming unstable
// then just return now
@@ -1007,7 +1007,7 @@ module.exports = (Commands, Cypress, cy, state, config) => {
hasVisitedAboutBlank = true
currentlyVisitingAboutBlank = true
- return aboutBlank(win)
+ return aboutBlank(cy, win)
.then(() => {
currentlyVisitingAboutBlank = false
From edbf156941781d0ffc0f77dd15373ff92278ecd0 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 5 May 2020 11:58:24 -0400
Subject: [PATCH 27/86] update server/unit test snapshots
---
.../__snapshots__/reporter.spec.js.snapshot.js | 14 ++++++++------
packages/server/test/matchDeep.js | 2 ++
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/packages/server/__snapshots__/reporter.spec.js.snapshot.js b/packages/server/__snapshots__/reporter.spec.js.snapshot.js
index 540d099a727d..5ba569d1137d 100644
--- a/packages/server/__snapshots__/reporter.spec.js.snapshot.js
+++ b/packages/server/__snapshots__/reporter.spec.js.snapshot.js
@@ -91,11 +91,11 @@ exports['fail in [afterEach] runner emit'] = [
],
[
"hook",
- "{Object 57}"
+ "{Object 60}"
],
[
"fail",
- "{Object 57}",
+ "{Object 60}",
"{Object 6}"
],
[
@@ -127,7 +127,8 @@ exports['fail in [afterEach] stdout'] = `
1 passing
1 failing
- 1) suite 1 "after each" hook for "test 1":
+ 1) suite 1
+ "after each" hook for "test 1":
@@ -217,11 +218,11 @@ exports['fail in [beforeEach] runner emit'] = [
],
[
"hook",
- "{Object 54}"
+ "{Object 57}"
],
[
"fail",
- "{Object 54}",
+ "{Object 57}",
"{Object 6}"
],
[
@@ -252,7 +253,8 @@ exports['fail in [beforeEach] stdout'] = `
0 passing
1 failing
- 1) suite 1 "before each" hook for "test 1":
+ 1) suite 1
+ "before each" hook for "test 1":
diff --git a/packages/server/test/matchDeep.js b/packages/server/test/matchDeep.js
index 7c30916f9381..ce88cea5f597 100644
--- a/packages/server/test/matchDeep.js
+++ b/packages/server/test/matchDeep.js
@@ -47,6 +47,8 @@ const registerInMocha = () => {
}
}
+ e.message = `Snapshot failed to match\n${e.message}`
+
throw e
}
}
From e7068e1e588e2f9cea3dcb84eb0e15e876569644 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 5 May 2020 12:13:31 -0400
Subject: [PATCH 28/86] fix after merge: decaffed aliases.js
---
packages/driver/src/cy/aliases.js | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/packages/driver/src/cy/aliases.js b/packages/driver/src/cy/aliases.js
index 2500567adffb..8bf2dff1c6ab 100644
--- a/packages/driver/src/cy/aliases.js
+++ b/packages/driver/src/cy/aliases.js
@@ -1,4 +1,3 @@
-/* globals cy */
const _ = require('lodash')
const $errUtils = require('../cypress/error_utils')
@@ -44,14 +43,14 @@ const validateAlias = (alias) => {
}
}
-const create = (state) => {
+const create = (cy) => {
const addAlias = (ctx, aliasObj) => {
const { alias, subject } = aliasObj
- const aliases = state('aliases') || {}
+ const aliases = cy.state('aliases') || {}
aliases[alias] = aliasObj
- state('aliases', aliases)
+ cy.state('aliases', aliases)
const remoteSubject = cy.getRemotejQueryInstance(subject)
@@ -59,7 +58,7 @@ const create = (state) => {
}
const getNextAlias = () => {
- const next = state('current').get('next')
+ const next = cy.state('current').get('next')
if (next && (next.get('name') === 'as')) {
return next.get('args')[0]
@@ -67,7 +66,7 @@ const create = (state) => {
}
const getAlias = (name, cmd, log) => {
- const aliases = state('aliases') || {}
+ const aliases = cy.state('aliases') || {}
// bail if the name doesnt reference an alias
if (!aliasRe.test(name)) {
@@ -85,7 +84,7 @@ const create = (state) => {
}
const getAvailableAliases = () => {
- const aliases = state('aliases')
+ const aliases = cy.state('aliases')
if (!aliases) {
return []
@@ -108,7 +107,7 @@ const create = (state) => {
})
}
- cmd = cmd ?? ((log && log.get('name')) || state('current').get('name'))
+ cmd = cmd ?? ((log && log.get('name')) || cy.state('current').get('name'))
displayName = aliasDisplayName(name)
const errPath = availableAliases.length
From 98408a44eb1bba44efabec0c0b033ea9c62c0761 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 5 May 2020 12:28:39 -0400
Subject: [PATCH 29/86] fix usage of cypress --parallel flag in circle.yml
---
circle.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/circle.yml b/circle.yml
index c6a0e02d2e52..20a3d771f7ab 100644
--- a/circle.yml
+++ b/circle.yml
@@ -575,6 +575,7 @@ jobs:
"runner-integration-tests-chrome":
<<: *defaults
+ parallelism: 2
steps:
- attach_workspace:
at: ~/
@@ -596,6 +597,7 @@ jobs:
"runner-integration-tests-firefox":
<<: *defaults
+ parallelism: 2
steps:
- attach_workspace:
at: ~/
From 8d30b64ea78289055503c1c6db37bab6bef56aeb Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Wed, 6 May 2020 15:15:18 -0400
Subject: [PATCH 30/86] fix circle.yml integration-tests jobs
---
circle.yml | 64 ++++++++++++++++++++----------------------------------
1 file changed, 24 insertions(+), 40 deletions(-)
diff --git a/circle.yml b/circle.yml
index 20a3d771f7ab..5464c62f08bb 100644
--- a/circle.yml
+++ b/circle.yml
@@ -87,26 +87,32 @@ commands:
echo "**** Not updating Chrome. Running tests in '<< parameters.browser >>' ****"
fi
- run-driver-integration-tests:
+ run-integration-tests:
parameters:
browser:
description: browser shortname to target
type: string
+ working_directory:
+ description: directory of package to run integration tests
+ type: string
+ group_name:
+ description: cypress --group name prefix
+ type: string
steps:
- attach_workspace:
at: ~/
- run:
command: yarn start
background: true
- working_directory: packages/driver
+ working_directory: << parameters.working_directory >>
- run:
command: yarn wait-on http://localhost:3500
- run:
command: |
CYPRESS_KONFIG_ENV=production \
CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \
- yarn cypress:run --record --parallel --group 5x-driver-<< parameters.browser >> --browser << parameters.browser >>
- working_directory: packages/driver
+ yarn cypress:run --record --parallel --group << parameters.group_name >>-<< parameters.browser >> --browser << parameters.browser >>
+ working_directory: << parameters.working_directory >>
- verify-mocha-results
- store_test_results:
path: /tmp/cypress
@@ -577,52 +583,28 @@ jobs:
<<: *defaults
parallelism: 2
steps:
- - attach_workspace:
- at: ~/
- - run:
- command: yarn workspace @packages/driver start
- background: true
- - run:
- command: yarn wait-on http://localhost:3500
- - run:
- command: |
- CYPRESS_KONFIG_ENV=production \
- CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \
- yarn workspace @packages/runner cypress:run --record --group runner-integration-chrome --browser chrome
- - store_test_results:
- path: /tmp/cypress
- - store_artifacts:
- path: /tmp/artifacts
- - store-npm-logs
+ - run-integration-tests:
+ browser: chrome
+ working_directory: "packages/runner"
+ group_name: "runner-integration"
"runner-integration-tests-firefox":
<<: *defaults
parallelism: 2
steps:
- - attach_workspace:
- at: ~/
- - run:
- command: yarn workspace @packages/driver start
- background: true
- - run:
- command: yarn wait-on http://localhost:3500
- - run:
- command: |
- CYPRESS_KONFIG_ENV=production \
- CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \
- yarn workspace @packages/runner cypress:run --record --parallel --group runner-integration-chrome --browser firefox
- - store_test_results:
- path: /tmp/cypress
- - store_artifacts:
- path: /tmp/artifacts
- - store-npm-logs
+ - run-integration-tests:
+ browser: firefox
+ working_directory: "packages/runner"
+ group_name: "runner-integration"
"driver-integration-tests-chrome":
<<: *defaults
parallelism: 5
steps:
- - run-driver-integration-tests:
+ - run-integration-tests:
browser: chrome
+ working_directory: "packages/driver"
+ group_name: "driver-integration"
# "driver-integration-tests-electron":
# <<: *defaults
@@ -635,8 +617,10 @@ jobs:
<<: *defaults
parallelism: 5
steps:
- - run-driver-integration-tests:
+ - run-integration-tests:
browser: firefox
+ working_directory: "packages/driver"
+ group_name: "driver-integration"
"desktop-gui-integration-tests-2x":
<<: *defaults
From 63c61a2d7ef756f21462f4b174c678984cbb673b Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Wed, 6 May 2020 15:27:51 -0400
Subject: [PATCH 31/86] fix decaf related typo
---
packages/driver/src/cy/assertions.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/driver/src/cy/assertions.js b/packages/driver/src/cy/assertions.js
index 343114d9011a..4a66bf22a75a 100644
--- a/packages/driver/src/cy/assertions.js
+++ b/packages/driver/src/cy/assertions.js
@@ -110,8 +110,8 @@ const create = function (Cypress, cy) {
// them up with existing ones
cmd.set('assertionIndex', 0)
- if (state('current') != null) {
- state('current').set('currentAssertionCommand', cmd)
+ if (cy.state('current') != null) {
+ cy.state('current').set('currentAssertionCommand', cmd)
}
return cmd.get('fn').originalFn.apply(
From aacc763c4f02dd8e138eec9d2b5c490748bfe2a6 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Wed, 6 May 2020 15:37:18 -0400
Subject: [PATCH 32/86] fix circle.yml separate command for
runner-integration-tests
---
circle.yml | 55 +++++++++++++++++++++++++++++++-----------------------
1 file changed, 32 insertions(+), 23 deletions(-)
diff --git a/circle.yml b/circle.yml
index 5464c62f08bb..3921328c5fbf 100644
--- a/circle.yml
+++ b/circle.yml
@@ -87,32 +87,25 @@ commands:
echo "**** Not updating Chrome. Running tests in '<< parameters.browser >>' ****"
fi
- run-integration-tests:
+ run-driver-integration-tests:
parameters:
browser:
description: browser shortname to target
type: string
- working_directory:
- description: directory of package to run integration tests
- type: string
- group_name:
- description: cypress --group name prefix
- type: string
steps:
- attach_workspace:
at: ~/
- run:
- command: yarn start
+ command: yarn workspace @packages/driver start
background: true
- working_directory: << parameters.working_directory >>
- run:
command: yarn wait-on http://localhost:3500
- run:
command: |
CYPRESS_KONFIG_ENV=production \
CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \
- yarn cypress:run --record --parallel --group << parameters.group_name >>-<< parameters.browser >> --browser << parameters.browser >>
- working_directory: << parameters.working_directory >>
+ yarn cypress:run --record --parallel --group driver-integration-<< parameters.browser >> --browser << parameters.browser >>
+ working_directory: packages/driver
- verify-mocha-results
- store_test_results:
path: /tmp/cypress
@@ -120,6 +113,30 @@ commands:
path: /tmp/artifacts
- store-npm-logs
+ run-runner-integration-tests:
+ parameters:
+ browser:
+ description: browser shortname to target
+ type: string
+ steps:
+ - attach_workspace:
+ at: ~/
+ - run:
+ command: yarn workspace @packages/driver start
+ background: true
+ - run:
+ command: yarn wait-on http://localhost:3500
+ - run:
+ command: |
+ CYPRESS_KONFIG_ENV=production \
+ CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \
+ yarn workspace @packages/runner cypress:run --record --group runner-integration-<< parameters.browser >> --browser <>
+ - store_test_results:
+ path: /tmp/cypress
+ - store_artifacts:
+ path: /tmp/artifacts
+ - store-npm-logs
+
run-e2e-tests:
parameters:
browser:
@@ -583,28 +600,22 @@ jobs:
<<: *defaults
parallelism: 2
steps:
- - run-integration-tests:
+ - run-runner-integration-tests:
browser: chrome
- working_directory: "packages/runner"
- group_name: "runner-integration"
"runner-integration-tests-firefox":
<<: *defaults
parallelism: 2
steps:
- - run-integration-tests:
+ - run-runner-integration-tests:
browser: firefox
- working_directory: "packages/runner"
- group_name: "runner-integration"
"driver-integration-tests-chrome":
<<: *defaults
parallelism: 5
steps:
- - run-integration-tests:
+ - run-driver-integration-tests:
browser: chrome
- working_directory: "packages/driver"
- group_name: "driver-integration"
# "driver-integration-tests-electron":
# <<: *defaults
@@ -617,10 +628,8 @@ jobs:
<<: *defaults
parallelism: 5
steps:
- - run-integration-tests:
+ - run-driver-integration-tests:
browser: firefox
- working_directory: "packages/driver"
- group_name: "driver-integration"
"desktop-gui-integration-tests-2x":
<<: *defaults
From 7777d5a3c66a05c492fb92c8e0bcd914902fe559 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Thu, 7 May 2020 01:16:45 -0400
Subject: [PATCH 33/86] update runner/integration tests + snapshot after error
improvements
---
packages/driver/src/cypress/runner.js | 12 +++----
.../__snapshots__/runner.spec.js.snapshot.js | 32 ++++++++++++-------
.../test/cypress/integration/runner.spec.js | 6 ++--
.../runner/test/cypress/support/helpers.js | 13 ++++++--
4 files changed, 40 insertions(+), 23 deletions(-)
diff --git a/packages/driver/src/cypress/runner.js b/packages/driver/src/cypress/runner.js
index 3c91c93f1eb2..fdaac5de3e97 100644
--- a/packages/driver/src/cypress/runner.js
+++ b/packages/driver/src/cypress/runner.js
@@ -585,11 +585,11 @@ const normalize = (runnable, tests, initialTests, onRunnable, onLogsById, getTes
return normalizedRunnable
}
-const hookFailed = function (hook, err, hookName) {
- // finds the test by returning the first test from
- // the parent or looping through the suites until
- // it finds the first test
- const test = getTestFromHook(hook)
+const hookFailed = function (hook, err, hookName, getTestById) {
+ // NOTE: sometimes mocha will fail a hook without having emitted on('hook')
+ // event, so this hook might not have currentTest set correctly
+ // in which case we need to lookup the test
+ const test = getTestFromHookOrFindTest(hook, getTestById)
test.err = err
test.state = 'failed'
@@ -791,7 +791,7 @@ const _runnerListeners = function (_runner, Cypress, _emissions, getTestById, ge
// if a hook fails (such as a before) then the test will never
// get run and we'll need to make sure we set the test so that
// the TEST_AFTER_RUN_EVENT fires correctly
- return hookFailed(runnable, runnable.err, hookName, getTestById, getTest)
+ return hookFailed(runnable, runnable.err, hookName, getTestById)
}
})
}
diff --git a/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js b/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js
index ebc907535492..b7d849804e3e 100644
--- a/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js
+++ b/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js
@@ -198,7 +198,7 @@ exports['FAIL_IN_AFTER.mocha'] = [
"title": "\"after all\" hook for \"test 2\"",
"hookName": "after all",
"hookId": "h1",
- "err": "{Object 6}",
+ "err": "{Object 9}",
"state": "failed",
"body": "[body]",
"type": "hook",
@@ -209,6 +209,8 @@ exports['FAIL_IN_AFTER.mocha'] = [
"message": "[error message]",
"name": "AssertionError",
"stack": "match.string",
+ "sourceMappedStack": "match.string",
+ "parsedStack": "[Array 16]",
"actual": null,
"showDiff": false
}
@@ -221,7 +223,7 @@ exports['FAIL_IN_AFTER.mocha'] = [
"order": 2,
"title": "test 2",
"hookName": "after all",
- "err": "{Object 6}",
+ "err": "{Object 9}",
"state": "failed",
"failedFromHookId": "h1",
"body": "[body]",
@@ -439,7 +441,7 @@ exports['FAIL_IN_AFTEREACH.mocha'] = [
"title": "\"after each\" hook for \"test 1\"",
"hookName": "after each",
"hookId": "h1",
- "err": "{Object 6}",
+ "err": "{Object 9}",
"state": "failed",
"body": "[body]",
"type": "hook",
@@ -450,6 +452,8 @@ exports['FAIL_IN_AFTEREACH.mocha'] = [
"message": "[error message]",
"name": "AssertionError",
"stack": "match.string",
+ "sourceMappedStack": "match.string",
+ "parsedStack": "[Array 16]",
"actual": null,
"showDiff": false
}
@@ -462,7 +466,7 @@ exports['FAIL_IN_AFTEREACH.mocha'] = [
"order": 1,
"title": "test 1",
"hookName": "after each",
- "err": "{Object 6}",
+ "err": "{Object 9}",
"state": "failed",
"failedFromHookId": "h1",
"body": "[body]",
@@ -603,7 +607,7 @@ exports['FAIL_IN_BEFORE.mocha'] = [
"title": "\"before all\" hook for \"test 1\"",
"hookName": "before all",
"hookId": "h1",
- "err": "{Object 6}",
+ "err": "{Object 9}",
"state": "failed",
"body": "[body]",
"type": "hook",
@@ -614,6 +618,8 @@ exports['FAIL_IN_BEFORE.mocha'] = [
"message": "[error message]",
"name": "AssertionError",
"stack": "match.string",
+ "sourceMappedStack": "match.string",
+ "parsedStack": "[Array 16]",
"actual": null,
"showDiff": false
}
@@ -626,7 +632,7 @@ exports['FAIL_IN_BEFORE.mocha'] = [
"order": 1,
"title": "test 1",
"hookName": "before all",
- "err": "{Object 6}",
+ "err": "{Object 9}",
"state": "failed",
"failedFromHookId": "h1",
"body": "[body]",
@@ -775,7 +781,7 @@ exports['FAIL_IN_BEFOREEACH.mocha'] = [
"title": "\"before each\" hook for \"test 1\"",
"hookName": "before each",
"hookId": "h1",
- "err": "{Object 6}",
+ "err": "{Object 9}",
"state": "failed",
"body": "[body]",
"type": "hook",
@@ -786,6 +792,8 @@ exports['FAIL_IN_BEFOREEACH.mocha'] = [
"message": "[error message]",
"name": "AssertionError",
"stack": "match.string",
+ "sourceMappedStack": "match.string",
+ "parsedStack": "[Array 16]",
"actual": null,
"showDiff": false
}
@@ -798,7 +806,7 @@ exports['FAIL_IN_BEFOREEACH.mocha'] = [
"order": 1,
"title": "test 1",
"hookName": "before each",
- "err": "{Object 6}",
+ "err": "{Object 9}",
"state": "failed",
"failedFromHookId": "h1",
"body": "[body]",
@@ -1021,7 +1029,7 @@ exports['FAIL_WITH_ONLY.mocha'] = [
{
"id": "r5",
"title": "test 2",
- "err": "{Object 6}",
+ "err": "{Object 9}",
"state": "failed",
"body": "[body]",
"type": "test",
@@ -1068,6 +1076,8 @@ exports['FAIL_WITH_ONLY.mocha'] = [
"message": "[error message]",
"name": "AssertionError",
"stack": "match.string",
+ "sourceMappedStack": "match.string",
+ "parsedStack": "[Array 16]",
"actual": null,
"showDiff": false
}
@@ -1078,7 +1088,7 @@ exports['FAIL_WITH_ONLY.mocha'] = [
{
"id": "r5",
"title": "test 2",
- "err": "{Object 6}",
+ "err": "{Object 9}",
"state": "failed",
"body": "[body]",
"type": "test",
@@ -1182,7 +1192,7 @@ exports['FAIL_WITH_ONLY.mocha'] = [
{
"id": "r5",
"title": "test 2",
- "err": "{Object 6}",
+ "err": "{Object 9}",
"state": "failed",
"body": "[body]",
"type": "test",
diff --git a/packages/runner/test/cypress/integration/runner.spec.js b/packages/runner/test/cypress/integration/runner.spec.js
index 82f4d4559c5e..e553c5e45b31 100644
--- a/packages/runner/test/cypress/integration/runner.spec.js
+++ b/packages/runner/test/cypress/integration/runner.spec.js
@@ -415,13 +415,13 @@ describe('src/cypress/runner', () => {
.then(() => {
cy.contains('.test', 'never gets here').should('have.class', 'runnable-failed')
cy.contains('.command', 'beforeEach').should('have.class', 'command-state-failed')
- cy.contains('.runnable-err', 'AssertionError: beforeEach').scrollIntoView().should('be.visible')
+ cy.contains('.runnable-err', 'beforeEach').scrollIntoView().should('be.visible')
cy.contains('.test', 'is pending').should('have.class', 'runnable-pending')
cy.contains('.test', 'fails this').should('have.class', 'runnable-failed')
cy.contains('.command', 'afterEach').should('have.class', 'command-state-failed')
- cy.contains('.runnable-err', 'AssertionError: afterEach').should('be.visible')
+ cy.contains('.runnable-err', 'afterEach').should('be.visible')
cy.contains('.test', 'does not run this').should('have.class', 'runnable-processing')
@@ -429,7 +429,7 @@ describe('src/cypress/runner', () => {
cy.contains('.test', 'fails on this').should('have.class', 'runnable-failed')
cy.contains('.command', 'after').should('have.class', 'command-state-failed')
- cy.contains('.runnable-err', 'AssertionError: after').should('be.visible')
+ cy.contains('.runnable-err', 'after').should('be.visible')
})
})
diff --git a/packages/runner/test/cypress/support/helpers.js b/packages/runner/test/cypress/support/helpers.js
index feeb2c374079..90fc777a8c36 100644
--- a/packages/runner/test/cypress/support/helpers.js
+++ b/packages/runner/test/cypress/support/helpers.js
@@ -24,7 +24,10 @@ const eventCleanseMap = {
afterFnDuration: match.number,
wallClockDuration: match.number,
stack: match.string,
+ sourceMappedStack: match.string,
+ parsedStack: stringifyShort,
message: '[error message]',
+
}
const mochaEventCleanseMap = {
@@ -185,7 +188,6 @@ function createCypress () {
cy.spy(cy.state('window').console, 'log').as('console_log')
cy.spy(cy.state('window').console, 'error').as('console_error')
- console.log(onInitializedListeners)
onInitializedListeners.forEach((fn) => fn(autCypress))
onInitializedListeners = []
@@ -205,10 +207,15 @@ function createCypress () {
cy.stub(autCypress, 'onSpecWindow').snapshot(enableStubSnapshots).callsFake((specWindow) => {
autCypress.onSpecWindow.restore()
- autCypress.onSpecWindow(specWindow)
+ autCypress.onSpecWindow(specWindow, [
+ {
+ absolute: 'cypress/fixtures/empty_spec.js',
+ relative: 'cypress/fixtures/empty_spec.js',
+ relativeUrl: '/__cypress/tests?p=cypress/fixtures/empty_spec.js',
+ },
+ ])
generateMochaTestsForWin(specWindow, mochaTests)
-
specWindow.before = () => {}
specWindow.beforeEach = () => {}
specWindow.afterEach = () => {}
From 8acbd6d697e543cdc68270d9910da3c5bd497e06 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Thu, 7 May 2020 11:14:57 -0400
Subject: [PATCH 34/86] fix runner/integration snapshots for chrome/ff
stacktrace differences
---
.../test/__snapshots__/runner.spec.js.snapshot.js | 10 +++++-----
packages/runner/test/cypress/support/helpers.js | 7 +++----
.../server/__snapshots__/reporter.spec.js.snapshot.js | 4 ++--
packages/server/test/e2e/5_spec_isolation_spec.coffee | 4 ++--
packages/server/test/matchDeep.js | 4 ++++
5 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js b/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js
index b7d849804e3e..6f7292334927 100644
--- a/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js
+++ b/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js
@@ -210,7 +210,7 @@ exports['FAIL_IN_AFTER.mocha'] = [
"name": "AssertionError",
"stack": "match.string",
"sourceMappedStack": "match.string",
- "parsedStack": "[Array 16]",
+ "parsedStack": "match.array",
"actual": null,
"showDiff": false
}
@@ -453,7 +453,7 @@ exports['FAIL_IN_AFTEREACH.mocha'] = [
"name": "AssertionError",
"stack": "match.string",
"sourceMappedStack": "match.string",
- "parsedStack": "[Array 16]",
+ "parsedStack": "match.array",
"actual": null,
"showDiff": false
}
@@ -619,7 +619,7 @@ exports['FAIL_IN_BEFORE.mocha'] = [
"name": "AssertionError",
"stack": "match.string",
"sourceMappedStack": "match.string",
- "parsedStack": "[Array 16]",
+ "parsedStack": "match.array",
"actual": null,
"showDiff": false
}
@@ -793,7 +793,7 @@ exports['FAIL_IN_BEFOREEACH.mocha'] = [
"name": "AssertionError",
"stack": "match.string",
"sourceMappedStack": "match.string",
- "parsedStack": "[Array 16]",
+ "parsedStack": "match.array",
"actual": null,
"showDiff": false
}
@@ -1077,7 +1077,7 @@ exports['FAIL_WITH_ONLY.mocha'] = [
"name": "AssertionError",
"stack": "match.string",
"sourceMappedStack": "match.string",
- "parsedStack": "[Array 16]",
+ "parsedStack": "match.array",
"actual": null,
"showDiff": false
}
diff --git a/packages/runner/test/cypress/support/helpers.js b/packages/runner/test/cypress/support/helpers.js
index 90fc777a8c36..be74c1f02409 100644
--- a/packages/runner/test/cypress/support/helpers.js
+++ b/packages/runner/test/cypress/support/helpers.js
@@ -25,9 +25,8 @@ const eventCleanseMap = {
wallClockDuration: match.number,
stack: match.string,
sourceMappedStack: match.string,
- parsedStack: stringifyShort,
+ parsedStack: match.array,
message: '[error message]',
-
}
const mochaEventCleanseMap = {
@@ -94,9 +93,9 @@ function createCypress () {
}
const visit = (mochaTests, opts = {}) => {
- _.defaults(opts, {
+ _.defaultsDeep(opts, {
state: {},
- config: {},
+ config: { video: false },
})
return cy.visit('/fixtures/isolated-runner.html#/tests/cypress/fixtures/empty_spec.js')
diff --git a/packages/server/__snapshots__/reporter.spec.js.snapshot.js b/packages/server/__snapshots__/reporter.spec.js.snapshot.js
index 5ba569d1137d..1a75a3871a59 100644
--- a/packages/server/__snapshots__/reporter.spec.js.snapshot.js
+++ b/packages/server/__snapshots__/reporter.spec.js.snapshot.js
@@ -96,7 +96,7 @@ exports['fail in [afterEach] runner emit'] = [
[
"fail",
"{Object 60}",
- "{Object 6}"
+ "{Object 9}"
],
[
"test:after:run",
@@ -223,7 +223,7 @@ exports['fail in [beforeEach] runner emit'] = [
[
"fail",
"{Object 57}",
- "{Object 6}"
+ "{Object 9}"
],
[
"test:after:run",
diff --git a/packages/server/test/e2e/5_spec_isolation_spec.coffee b/packages/server/test/e2e/5_spec_isolation_spec.coffee
index dc1f5cd17afe..f13e076a7628 100644
--- a/packages/server/test/e2e/5_spec_isolation_spec.coffee
+++ b/packages/server/test/e2e/5_spec_isolation_spec.coffee
@@ -89,7 +89,7 @@ expectRunsToHaveCorrectStats = (runs = []) ->
run,
"stats.wallClockDuration",
wallClocks,
- wallClocks + 150, ## add 150ms to account for padding
+ wallClocks + 200, ## add 200ms to account for padding
1234
)
@@ -97,7 +97,7 @@ expectRunsToHaveCorrectStats = (runs = []) ->
run,
"reporterStats.duration",
wallClocks,
- wallClocks + 150, ## add 150ms to account for padding
+ wallClocks + 200, ## add 200ms to account for padding
1234
)
diff --git a/packages/server/test/matchDeep.js b/packages/server/test/matchDeep.js
index ce88cea5f597..089e3e856d32 100644
--- a/packages/server/test/matchDeep.js
+++ b/packages/server/test/matchDeep.js
@@ -150,6 +150,10 @@ const getFake = (matcherType) => {
if (matcherType === 'string') {
return 'foobar'
}
+
+ if (matcherType === 'array') {
+ return []
+ }
}
module.exports = {
From f82be9e9d295bcce5051baeb1c02f11fbd25018b Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Thu, 7 May 2020 11:40:47 -0400
Subject: [PATCH 35/86] rerun ci
From d3ed77d3971583cc95cc57a43574b95a8d5eafd5 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Thu, 7 May 2020 12:08:20 -0400
Subject: [PATCH 36/86] fix passing --parallel to runner-integration tests
---
circle.yml | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/circle.yml b/circle.yml
index 3921328c5fbf..fc304085390f 100644
--- a/circle.yml
+++ b/circle.yml
@@ -104,8 +104,7 @@ commands:
command: |
CYPRESS_KONFIG_ENV=production \
CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \
- yarn cypress:run --record --parallel --group driver-integration-<< parameters.browser >> --browser << parameters.browser >>
- working_directory: packages/driver
+ yarn workspace @packages/driver cypress:run --record --parallel --group driver-integration-<< parameters.browser >> --browser << parameters.browser >>
- verify-mocha-results
- store_test_results:
path: /tmp/cypress
@@ -130,7 +129,7 @@ commands:
command: |
CYPRESS_KONFIG_ENV=production \
CYPRESS_RECORD_KEY=$PACKAGES_RECORD_KEY \
- yarn workspace @packages/runner cypress:run --record --group runner-integration-<< parameters.browser >> --browser <>
+ yarn workspace @packages/runner cypress:run --record --parallel --group runner-integration-<< parameters.browser >> --browser <>
- store_test_results:
path: /tmp/cypress
- store_artifacts:
From ec77ac3dce99f68159357f2798bc4b2b035484bc Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Thu, 7 May 2020 18:35:21 -0400
Subject: [PATCH 37/86] perf: faster lookup for hooks without runnables
---
packages/driver/src/cypress/runner.js | 66 +++++++++++++++++----------
1 file changed, 41 insertions(+), 25 deletions(-)
diff --git a/packages/driver/src/cypress/runner.js b/packages/driver/src/cypress/runner.js
index 3c91c93f1eb2..41db9e5dd806 100644
--- a/packages/driver/src/cypress/runner.js
+++ b/packages/driver/src/cypress/runner.js
@@ -208,17 +208,17 @@ const eachHookInSuite = function (suite, fn) {
return null
}
-const onFirstTest = function (suite, fn) {
- let test
-
- for (test of suite.tests) {
+// iterates over a suite's tests (including nested suites)
+// and will return as soon as the callback is true
+const findTestInSuite = function (suite, fn = _.identity) {
+ for (const test of suite.tests) {
if (fn(test)) {
return test
}
}
for (suite of suite.suites) {
- test = onFirstTest(suite, fn)
+ const test = findTestInSuite(suite, fn)
if (test) {
return test
@@ -226,6 +226,25 @@ const onFirstTest = function (suite, fn) {
}
}
+// same as findTestInSuite but iterates backwards
+const findLastTestInSuite = function (suite, fn = _.identity) {
+ for (let i = suite.suites.length - 1; i >= 0; i--) {
+ const test = findLastTestInSuite(suite.suites[i], fn)
+
+ if (test) {
+ return test
+ }
+ }
+
+ for (let i = suite.tests.length - 1; i >= 0; i--) {
+ const test = suite.tests[i]
+
+ if (fn(test)) {
+ return test
+ }
+ }
+}
+
const getAllSiblingTests = function (suite, getTestById) {
const tests = []
@@ -259,7 +278,8 @@ const getTestFromHook = function (hook) {
return test
}
}
-const getTestFromHookOrFindTest = function (hook, getTestById) {
+
+const getTestFromHookOrFindTest = function (hook) {
const test = getTestFromHook(hook)
if (test) {
@@ -268,17 +288,12 @@ const getTestFromHookOrFindTest = function (hook, getTestById) {
const suite = hook.parent
- // TODO: make performant, use findTestInSuite()
if (hook.hookName === 'after all') {
- const siblings = getAllSiblingTests(suite, getTestById)
-
- return _.findLast(siblings, isNotAlreadyRunTest)
+ return findLastTestInSuite(suite, isNotAlreadyRunTest)
}
if (hook.hookName === 'before all') {
- const siblings = getAllSiblingTests(suite, getTestById)
-
- return _.find(siblings, isNotAlreadyRunTest)
+ return findTestInSuite(suite, isNotAlreadyRunTest)
}
}
@@ -395,18 +410,19 @@ const overrideRunnerHook = function (Cypress, _runner, getTestById, getTest, set
if (t) {
const siblings = getAllSiblingTests(t.parent, getTestById)
- // 1. if we're the very last test in the entire allTests
+ // 1. if we have another afterAll hook left to run, obviously this isn't the last hook
+ // 2. if we're the very last test in the entire allTests
// we wait until the root suite fires
- // 2. else we wait until the very last possible moment by waiting
+ // 3. else we wait until the very last possible moment by waiting
// until the root suite is the parent of the current suite
// since that will bubble up IF we're the last nested suite
- // 3. else if we arent the last nested suite we fire if we're
+ // 4. else if we arent the last nested suite we fire if we're
// the last test that will run
- // TODO: move middle conditional into method
if (
+ !this.suite.parent._afterAll.length &&
(isRootSuite(this.suite) && isLastTest(t, allTests)) ||
- (isRootSuite(this.suite.parent) && !this.suite.parent._afterAll.length && lastTestThatWillRunInSuite(t, siblings)) ||
+ (isRootSuite(this.suite.parent) && lastTestThatWillRunInSuite(t, siblings)) ||
(!isLastSuite(this.suite, allTests) && lastTestThatWillRunInSuite(t, siblings))
) {
changeFnToRunAfterHooks()
@@ -450,7 +466,7 @@ const normalizeAll = (suite, initialTests = {}, setTestsById, setTests, onRunnab
let hasTests = false
// only loop until we find the first test
- onFirstTest(suite, (test) => {
+ findTestInSuite(suite, (test) => {
return hasTests = true
})
@@ -586,10 +602,10 @@ const normalize = (runnable, tests, initialTests, onRunnable, onLogsById, getTes
}
const hookFailed = function (hook, err, hookName) {
- // finds the test by returning the first test from
- // the parent or looping through the suites until
- // it finds the first test
- const test = getTestFromHook(hook)
+ // NOTE: sometimes mocha will fail a hook without having emitted on('hook')
+ // event, so this hook might not have currentTest set correctly
+ // in which case we need to lookup the test
+ const test = getTestFromHookOrFindTest(hook)
test.err = err
test.state = 'failed'
@@ -662,7 +678,7 @@ const _runnerListeners = function (_runner, Cypress, _emissions, getTestById, ge
// hooks do not have their own id, their
// commands need to grouped with the test
// and we can only associate them by this id
- const test = getTestFromHookOrFindTest(hook, getTestById)
+ const test = getTestFromHookOrFindTest(hook)
if (!test) {
// we couldn't find a test to run with this hook
@@ -791,7 +807,7 @@ const _runnerListeners = function (_runner, Cypress, _emissions, getTestById, ge
// if a hook fails (such as a before) then the test will never
// get run and we'll need to make sure we set the test so that
// the TEST_AFTER_RUN_EVENT fires correctly
- return hookFailed(runnable, runnable.err, hookName, getTestById, getTest)
+ return hookFailed(runnable, runnable.err, hookName, getTestById)
}
})
}
From 6790888203a9ed6e55f9ede4d744c8e4c7bd5890 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Fri, 8 May 2020 14:58:50 -0400
Subject: [PATCH 38/86] fix afterAll hook switch logic
---
packages/driver/src/cypress/runner.js | 8 ++++----
packages/server/test/support/helpers/e2e.js | 6 +++++-
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/packages/driver/src/cypress/runner.js b/packages/driver/src/cypress/runner.js
index 41db9e5dd806..9848d320aab2 100644
--- a/packages/driver/src/cypress/runner.js
+++ b/packages/driver/src/cypress/runner.js
@@ -344,7 +344,7 @@ const isLastSuite = function (suite, tests) {
// if we failed from a hook and that hook was 'before'
// since then mocha skips the remaining tests in the suite
const lastTestThatWillRunInSuite = (test, tests) => {
- return isLastTest(test, tests) || (test.failedFromHookId && (test.hookName === 'before all'))
+ return !test.parent._afterAll.length || isLastTest(test, tests) || (test.failedFromHookId && (test.hookName === 'before all'))
}
const isLastTest = (test, tests) => {
@@ -420,10 +420,10 @@ const overrideRunnerHook = function (Cypress, _runner, getTestById, getTest, set
// the last test that will run
if (
- !this.suite.parent._afterAll.length &&
+ // !this.suite.parent._afterAll.length &&
(isRootSuite(this.suite) && isLastTest(t, allTests)) ||
- (isRootSuite(this.suite.parent) && lastTestThatWillRunInSuite(t, siblings)) ||
- (!isLastSuite(this.suite, allTests) && lastTestThatWillRunInSuite(t, siblings))
+ (isRootSuite(this.suite.parent) && !this.suite.parent._afterAll.length && lastTestThatWillRunInSuite(t, siblings)) ||
+ (!isLastSuite(this.suite, allTests) && !this.suite.parent._afterAll.length && lastTestThatWillRunInSuite(t, siblings))
) {
changeFnToRunAfterHooks()
}
diff --git a/packages/server/test/support/helpers/e2e.js b/packages/server/test/support/helpers/e2e.js
index 2afc1d1b9159..fa40c02f3c14 100644
--- a/packages/server/test/support/helpers/e2e.js
+++ b/packages/server/test/support/helpers/e2e.js
@@ -442,7 +442,7 @@ const e2e = {
browser: 'electron',
headed: process.env.HEADED || false,
project: e2ePath,
- timeout: options.noExit ? 3000000 : 120000,
+ timeout: 120000,
originalTitle: null,
expectedExitCode: 0,
sanitizeScreenshotDimensions: false,
@@ -450,6 +450,10 @@ const e2e = {
noExit: process.env.NO_EXIT,
})
+ if (options.noExit) {
+ options.timeout = 3000000
+ }
+
if (options.exit != null) {
throw new Error(`
passing { exit: false } to e2e options is no longer supported
From 2f060469bf66186c1b781b20d9ac803b66eee37c Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 19 May 2020 23:23:23 -0400
Subject: [PATCH 39/86] backport to before/after fix
---
packages/driver/src/cypress/runner.js | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/packages/driver/src/cypress/runner.js b/packages/driver/src/cypress/runner.js
index 9848d320aab2..cabde513ff41 100644
--- a/packages/driver/src/cypress/runner.js
+++ b/packages/driver/src/cypress/runner.js
@@ -410,20 +410,18 @@ const overrideRunnerHook = function (Cypress, _runner, getTestById, getTest, set
if (t) {
const siblings = getAllSiblingTests(t.parent, getTestById)
- // 1. if we have another afterAll hook left to run, obviously this isn't the last hook
- // 2. if we're the very last test in the entire allTests
+ // 1. if we're the very last test in the entire allTests
// we wait until the root suite fires
- // 3. else we wait until the very last possible moment by waiting
+ // 2. else we wait until the very last possible moment by waiting
// until the root suite is the parent of the current suite
// since that will bubble up IF we're the last nested suite
- // 4. else if we arent the last nested suite we fire if we're
+ // 3. else if we arent the last nested suite we fire if we're
// the last test that will run
if (
- // !this.suite.parent._afterAll.length &&
(isRootSuite(this.suite) && isLastTest(t, allTests)) ||
(isRootSuite(this.suite.parent) && !this.suite.parent._afterAll.length && lastTestThatWillRunInSuite(t, siblings)) ||
- (!isLastSuite(this.suite, allTests) && !this.suite.parent._afterAll.length && lastTestThatWillRunInSuite(t, siblings))
+ (!isLastSuite(this.suite, allTests) && lastTestThatWillRunInSuite(t, siblings))
) {
changeFnToRunAfterHooks()
}
From d230c17973ea0bbbc6363366a9534550c359e095 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 19 May 2020 23:34:18 -0400
Subject: [PATCH 40/86] backport to before/after fix 2
---
packages/driver/src/cypress/runner.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/driver/src/cypress/runner.js b/packages/driver/src/cypress/runner.js
index cabde513ff41..37afc7cc0593 100644
--- a/packages/driver/src/cypress/runner.js
+++ b/packages/driver/src/cypress/runner.js
@@ -805,7 +805,7 @@ const _runnerListeners = function (_runner, Cypress, _emissions, getTestById, ge
// if a hook fails (such as a before) then the test will never
// get run and we'll need to make sure we set the test so that
// the TEST_AFTER_RUN_EVENT fires correctly
- return hookFailed(runnable, runnable.err, hookName, getTestById)
+ return hookFailed(runnable, runnable.err, hookName)
}
})
}
From 2e3993c3089198df300530134cd2a4c0bc122b8e Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Wed, 20 May 2020 00:07:53 -0400
Subject: [PATCH 41/86] cleanup from decaf, fix ui/package.json
---
packages/driver/src/cy/aliases.coffee | 116 ---
.../driver/src/cy/commands/navigation.coffee | 853 ------------------
packages/driver/test/support/server.coffee | 100 --
packages/driver/test/support/server.js | 2 +
packages/ui-components/package.json | 3 +-
5 files changed, 3 insertions(+), 1071 deletions(-)
delete mode 100644 packages/driver/src/cy/aliases.coffee
delete mode 100644 packages/driver/src/cy/commands/navigation.coffee
delete mode 100644 packages/driver/test/support/server.coffee
diff --git a/packages/driver/src/cy/aliases.coffee b/packages/driver/src/cy/aliases.coffee
deleted file mode 100644
index abece6fdd0ac..000000000000
--- a/packages/driver/src/cy/aliases.coffee
+++ /dev/null
@@ -1,116 +0,0 @@
-_ = require("lodash")
-
-$errUtils = require("../cypress/error_utils")
-
-aliasRe = /^@.+/
-aliasDisplayRe = /^([@]+)/
-requestXhrRe = /\.request$/
-
-blacklist = ["test", "runnable", "timeout", "slow", "skip", "inspect"]
-
-aliasDisplayName = (name) ->
- name.replace(aliasDisplayRe, "")
-
-getXhrTypeByAlias = (alias) ->
- if requestXhrRe.test(alias) then "request" else "response"
-
-validateAlias = (alias) ->
- if not _.isString(alias)
- $errUtils.throwErrByPath "as.invalid_type"
-
- if aliasDisplayRe.test(alias)
- $errUtils.throwErrByPath "as.invalid_first_token", {
- args: {
- alias,
- suggestedName: alias.replace(aliasDisplayRe, '')
- }
- }
-
- if _.isBlank(alias)
- $errUtils.throwErrByPath "as.empty_string"
-
- if alias in blacklist
- $errUtils.throwErrByPath "as.reserved_word", { args: { alias } }
-
-create = (cy) ->
- addAlias = (ctx, aliasObj) ->
- { alias, subject } = aliasObj
-
- aliases = cy.state("aliases") ? {}
- aliases[alias] = aliasObj
- cy.state("aliases", aliases)
-
- remoteSubject = cy.getRemotejQueryInstance(subject)
-
- ## assign the subject to our runnable ctx
- ctx[alias] = remoteSubject ? subject
-
- getNextAlias = ->
- next = cy.state("current").get("next")
-
- if next and next.get("name") is "as"
- next.get("args")[0]
-
- getAlias = (name, cmd, log) ->
- aliases = cy.state("aliases") ? {}
-
- ## bail if the name doesnt reference an alias
- return if not aliasRe.test(name)
-
- ## slice off the '@'
- if not alias = aliases[name.slice(1)]
- aliasNotFoundFor(name, cmd, log)
-
- return alias
-
- getAvailableAliases = ->
- return [] if not aliases = cy.state("aliases")
-
- _.keys(aliases)
-
- aliasNotFoundFor = (name, cmd, log) ->
- availableAliases = getAvailableAliases()
-
- ## throw a very specific error if our alias isnt in the right
- ## format, but its word is found in the availableAliases
- if (not aliasRe.test(name)) and (name in availableAliases)
- displayName = aliasDisplayName(name)
- $errUtils.throwErrByPath "alias.invalid", {
- onFail: log
- args: { name, displayName }
- }
-
- cmd ?= log and log.get("name") or cy.state("current").get("name")
- displayName = aliasDisplayName(name)
-
- errPath = if availableAliases.length
- "alias.not_registered_with_available"
- else
- "alias.not_registered_without_available"
-
- $errUtils.throwErrByPath errPath, {
- onFail: log
- args: { cmd, displayName, availableAliases: availableAliases.join(", ") }
- }
-
- return {
- getAlias
-
- addAlias
-
- ## these are public because its expected other commands
- ## know about them and are expected to call them
- getNextAlias
-
- validateAlias
-
- aliasNotFoundFor
-
- getXhrTypeByAlias
-
- getAvailableAliases
- }
-
-module.exports = {
- create
-}
diff --git a/packages/driver/src/cy/commands/navigation.coffee b/packages/driver/src/cy/commands/navigation.coffee
deleted file mode 100644
index aa83bebe55c6..000000000000
--- a/packages/driver/src/cy/commands/navigation.coffee
+++ /dev/null
@@ -1,853 +0,0 @@
-_ = require("lodash")
-whatIsCircular = require("@cypress/what-is-circular")
-moment = require("moment")
-UrlParse = require("url-parse")
-Promise = require("bluebird")
-
-$utils = require("../../cypress/utils")
-$errUtils = require("../../cypress/error_utils")
-$Log = require("../../cypress/log")
-$Location = require("../../cypress/location")
-
-debug = require('debug')('cypress:driver:navigation')
-
-id = null
-previousDomainVisited = null
-hasVisitedAboutBlank = null
-currentlyVisitingAboutBlank = null
-knownCommandCausedInstability = null
-
-REQUEST_URL_OPTS = "auth failOnStatusCode retryOnNetworkFailure retryOnStatusCodeFailure method body headers"
-.split(" ")
-
-VISIT_OPTS = "url log onBeforeLoad onLoad timeout requestTimeout"
-.split(" ")
-.concat(REQUEST_URL_OPTS)
-
-reset = (test = {}) ->
- knownCommandCausedInstability = false
-
- ## continuously reset this
- ## before each test run!
- previousDomainVisited = false
-
- ## make sure we reset that we haven't
- ## visited about blank again
- hasVisitedAboutBlank = false
-
- currentlyVisitingAboutBlank = false
-
- id = test.id
-
-VALID_VISIT_METHODS = ['GET', 'POST']
-
-isValidVisitMethod = (method) ->
- _.includes(VALID_VISIT_METHODS, method)
-
-timedOutWaitingForPageLoad = (ms, log) ->
- debug('timedOutWaitingForPageLoad')
- $errUtils.throwErrByPath("navigation.timed_out", {
- args: {
- configFile: Cypress.config("configFile")
- ms
- }
- onFail: log
- })
-
-bothUrlsMatchAndRemoteHasHash = (current, remote) ->
- ## the remote has a hash
- ## or the last char of href
- ## is a hash
- (remote.hash or remote.href.slice(-1) is "#") and
-
- ## both must have the same origin
- (current.origin is remote.origin) and
-
- ## both must have the same pathname
- (current.pathname is remote.pathname) and
-
- ## both must have the same query params
- (current.search is remote.search)
-
-cannotVisitDifferentOrigin = (origin, previousUrlVisited, remoteUrl, existingUrl, log) ->
- differences = []
-
- if remoteUrl.protocol isnt existingUrl.protocol
- differences.push('protocol')
- if remoteUrl.port isnt existingUrl.port
- differences.push('port')
- if remoteUrl.superDomain isnt existingUrl.superDomain
- differences.push('superdomain')
-
- errOpts = {
- onFail: log
- args: {
- differences: differences.join(', ')
- previousUrl: previousUrlVisited
- attemptedUrl: origin
- }
- }
-
- $errUtils.throwErrByPath("visit.cannot_visit_different_origin", errOpts)
-
-specifyFileByRelativePath = (url, log) ->
- $errUtils.throwErrByPath("visit.specify_file_by_relative_path", {
- onFail: log
- args: {
- attemptedUrl: url
- }
- })
-
-aboutBlank = (cy, win) ->
- new Promise (resolve) ->
- cy.once("window:load", resolve)
-
- $utils.locHref("about:blank", win)
-
-navigationChanged = (Cypress, cy, state, source, arg) ->
- ## get the current url of our remote application
- url = cy.getRemoteLocation("href")
- debug('navigation changed:', url)
-
- ## dont trigger for empty url's or about:blank
- return if _.isEmpty(url) or url is "about:blank"
-
- ## start storing the history entries
- urls = state("urls") ? []
-
- previousUrl = _.last(urls)
-
- ## ensure our new url doesnt match whatever
- ## the previous was. this prevents logging
- ## additionally when the url didnt actually change
- return if url is previousUrl
-
- ## else notify the world and log this event
- Cypress.action("cy:url:changed", url)
-
- urls.push(url)
-
- state("urls", urls)
-
- state("url", url)
-
- ## don't output a command log for 'load' or 'before:load' events
- # return if source in command
- return if knownCommandCausedInstability
-
- ## ensure our new url doesnt match whatever
- ## the previous was. this prevents logging
- ## additionally when the url didnt actually change
- Cypress.log({
- name: "new url"
- message: url
- event: true
- type: "parent"
- end: true
- snapshot: true
- consoleProps: ->
- obj = {
- "New Url": url
- }
-
- if source
- obj["Url Updated By"] = source
-
- if arg
- obj.Args = arg
-
- return obj
- })
-
-formSubmitted = (Cypress, e) ->
- Cypress.log({
- type: "parent"
- name: "form sub"
- message: "--submitting form--"
- event: true
- end: true
- snapshot: true
- consoleProps: -> {
- "Originated From": e.target
- "Args": e
- }
- })
-
-pageLoading = (bool, Cypress, state) ->
- return if state("pageLoading") is bool
-
- state("pageLoading", bool)
-
- Cypress.action("app:page:loading", bool)
-
-stabilityChanged = (Cypress, state, config, stable, event) ->
- debug('stabilityChanged:', stable)
- if currentlyVisitingAboutBlank
- if stable is false
- ## if we're currently visiting about blank
- ## and becoming unstable for the first time
- ## notifiy that we're page loading
- pageLoading(true, Cypress, state)
- return
- else
- ## else wait until after we finish visiting
- ## about blank
- return
-
- ## let the world know that the app is page:loading
- pageLoading(!stable, Cypress, state)
-
- ## if we aren't becoming unstable
- ## then just return now
- return if stable isnt false
-
- ## if we purposefully just caused the page to load
- ## (and thus instability) don't log this out
- return if knownCommandCausedInstability
-
- ## bail if we dont have a runnable
- ## because beforeunload can happen at any time
- ## we may no longer be testing and thus dont
- ## want to fire a new loading event
- ## TODO
- ## this may change in the future since we want
- ## to add debuggability in the chrome console
- ## which at that point we may keep runnable around
- return if not state("runnable")
-
- options = {}
-
- _.defaults(options, {
- timeout: config("pageLoadTimeout")
- })
-
- options._log = Cypress.log({
- type: "parent"
- name: "page load"
- message: "--waiting for new page to load--"
- event: true
- consoleProps: -> {
- Note: "This event initially fires when your application fires its 'beforeunload' event and completes when your application fires its 'load' event after the next page loads."
- }
- })
-
- cy.clearTimeout("page load")
-
- onPageLoadErr = (err) ->
- state("onPageLoadErr", null)
-
- { originPolicy } = $Location.create(window.location.href)
-
- try
- $errUtils.throwErrByPath("navigation.cross_origin", {
- onFail: options._log
- args: {
- configFile: Cypress.config("configFile")
- message: err.message
- originPolicy: originPolicy
- }
- })
- catch err
- return err
-
- state("onPageLoadErr", onPageLoadErr)
-
- loading = ->
- debug('waiting for window:load')
- new Promise (resolve, reject) ->
- cy.once "window:load", ->
- cy.state("onPageLoadErr", null)
-
- options._log.set("message", "--page loaded--").snapshot().end()
-
- resolve()
-
- reject = (err) ->
- if r = state("reject")
- r(err)
-
- loading()
- .timeout(options.timeout, "page load")
- .catch Promise.TimeoutError, ->
- ## clean this up
- cy.state("onPageLoadErr", null)
-
- try
- timedOutWaitingForPageLoad(options.timeout, options._log)
- catch err
- reject(err)
-
-normalizeTimeoutOptions = (options) ->
- ## there are really two timeout values - pageLoadTimeout
- ## and the underlying responseTimeout. for the purposes
- ## of resolving resolving the url, we only care about
- ## responseTimeout - since pageLoadTimeout is a driver
- ## and browser concern. therefore we normalize the options
- ## object and send 'responseTimeout' as options.timeout
- ## for the backend.
- _
- .chain(options)
- .pick(REQUEST_URL_OPTS)
- .extend({ timeout: options.responseTimeout })
- .value()
-
-module.exports = (Commands, Cypress, cy, state, config) ->
- reset()
-
- Cypress.on "test:before:run:async", ->
- ## reset any state on the backend
- Cypress.backend('reset:server:state')
-
- Cypress.on("test:before:run", reset)
-
- Cypress.on "stability:changed", (bool, event) ->
- ## only send up page loading events when we're
- ## not stable!
- stabilityChanged(Cypress, state, config, bool, event)
-
- Cypress.on "navigation:changed", (source, arg) ->
- navigationChanged(Cypress, cy, state, source, arg)
-
- Cypress.on "form:submitted", (e) ->
- formSubmitted(Cypress, e)
-
- visitFailedByErr = (err, url, fn) ->
- err.url = url
-
- Cypress.action("cy:visit:failed", err)
-
- fn()
-
- requestUrl = (url, options = {}) ->
- Cypress.backend(
- "resolve:url",
- url,
- normalizeTimeoutOptions(options)
- )
- .then (resp = {}) ->
- switch
- ## if we didn't even get an OK response
- ## then immediately die
- when not resp.isOkStatusCode
- err = new Error
- err.gotResponse = true
- _.extend(err, resp)
-
- throw err
-
- when not resp.isHtml
- ## throw invalid contentType error
- err = new Error
- err.invalidContentType = true
- _.extend(err, resp)
-
- throw err
-
- else
- resp
-
- Cypress.on "window:before:load", (contentWindow) ->
- ## TODO: just use a closure here
- current = state("current")
-
- return if not current
-
- runnable = state("runnable")
-
- return if not runnable
-
- options = _.last(current.get("args"))
- options?.onBeforeLoad?.call(runnable.ctx, contentWindow)
-
- Commands.addAll({
- reload: (args...) ->
- throwArgsErr = =>
- $errUtils.throwErrByPath("reload.invalid_arguments")
-
- switch args.length
- when 0
- forceReload = false
- userOptions = {}
-
- when 1
- if _.isObject(args[0])
- userOptions = args[0]
- else
- forceReload = args[0]
-
- when 2
- forceReload = args[0]
- userOptions = args[1]
-
- else
- throwArgsErr()
-
- ## clear the current timeout
- cy.clearTimeout("reload")
-
- cleanup = null
- options = _.defaults({}, userOptions, {
- log: true
- timeout: config("pageLoadTimeout")
- })
-
- reload = ->
- new Promise (resolve, reject) ->
- forceReload ?= false
- userOptions ?= {}
-
- if not _.isObject(userOptions)
- throwArgsErr()
-
- if not _.isBoolean(forceReload)
- throwArgsErr()
-
- if options.log
- options._log = Cypress.log({})
-
- options._log.snapshot("before", {next: "after"})
-
- cleanup = ->
- knownCommandCausedInstability = false
-
- cy.removeListener("window:load", resolve)
-
- knownCommandCausedInstability = true
-
- cy.once("window:load", resolve)
-
- $utils.locReload(forceReload, state("window"))
-
- reload()
- .timeout(options.timeout, "reload")
- .catch Promise.TimeoutError, (err) ->
- timedOutWaitingForPageLoad(options.timeout, options._log)
- .finally ->
- cleanup?()
-
- return null
-
- go: (numberOrString, options = {}) ->
- userOptions = options
- options = _.defaults {}, userOptions, {
- log: true
- timeout: config("pageLoadTimeout")
- }
-
- if options.log
- options._log = Cypress.log({
- })
-
- win = state("window")
-
- goNumber = (num) ->
- if num is 0
- $errUtils.throwErrByPath("go.invalid_number", { onFail: options._log })
-
- cleanup = null
-
- if options._log
- options._log.snapshot("before", {next: "after"})
-
- go = ->
- Promise.try ->
- didUnload = false
-
- beforeUnload = ->
- didUnload = true
-
- ## clear the current timeout
- cy.clearTimeout()
-
- cy.once("window:before:unload", beforeUnload)
-
- didLoad = new Promise (resolve) ->
- cleanup = ->
- cy.removeListener("window:load", resolve)
- cy.removeListener("window:before:unload", beforeUnload)
-
- cy.once("window:load", resolve)
-
- knownCommandCausedInstability = true
-
- win.history.go(num)
-
- retWin = ->
- ## need to set the attributes of 'go'
- ## consoleProps here with win
-
- ## make sure we resolve our go function
- ## with the remove window (just like cy.visit)
- state("window")
-
- Promise
- .delay(100)
- .then ->
- knownCommandCausedInstability = false
-
- ## if we've didUnload then we know we're
- ## doing a full page refresh and we need
- ## to wait until
- if didUnload
- didLoad.then(retWin)
- else
- retWin()
-
- go()
- .timeout(options.timeout, "go")
- .catch Promise.TimeoutError, (err) ->
- timedOutWaitingForPageLoad(options.timeout, options._log)
- .finally ->
- cleanup?()
-
- return null
-
- goString = (str) ->
- switch str
- when "forward" then goNumber(1)
- when "back" then goNumber(-1)
- else
- $errUtils.throwErrByPath("go.invalid_direction", {
- onFail: options._log
- args: { str }
- })
-
- switch
- when _.isFinite(numberOrString) then goNumber(numberOrString)
- when _.isString(numberOrString) then goString(numberOrString)
- else
- $errUtils.throwErrByPath("go.invalid_argument", { onFail: options._log })
-
- visit: (url, options = {}) ->
- if options.url and url
- $errUtils.throwErrByPath("visit.no_duplicate_url", { args: { optionsUrl: options.url, url: url }})
- userOptions = options
-
- if userOptions.url and url
- $utils.throwErrByPath("visit.no_duplicate_url", { args: { optionsUrl: userOptions.url, url: url }})
-
- if _.isObject(url) and _.isEqual(userOptions, {})
- ## options specified as only argument
- userOptions = url
- url = userOptions.url
-
- if not _.isString(url)
- $errUtils.throwErrByPath("visit.invalid_1st_arg")
-
- consoleProps = {}
-
- if not _.isEmpty(userOptions)
- consoleProps["Options"] = _.pick(userOptions, VISIT_OPTS)
-
- options = _.defaults({}, userOptions, {
- auth: null
- failOnStatusCode: true
- retryOnNetworkFailure: true
- retryOnStatusCodeFailure: false
- method: 'GET'
- body: null
- headers: {}
- log: true
- responseTimeout: config('responseTimeout')
- timeout: config("pageLoadTimeout")
- onBeforeLoad: ->
- onLoad: ->
- })
-
- if !_.isUndefined(options.qs) and not _.isObject(options.qs)
- $errUtils.throwErrByPath("visit.invalid_qs", { args: { qs: String(options.qs) }})
-
- if options.retryOnStatusCodeFailure and not options.failOnStatusCode
- $errUtils.throwErrByPath("visit.status_code_flags_invalid")
-
- if not isValidVisitMethod(options.method)
- $errUtils.throwErrByPath("visit.invalid_method", { args: { method: options.method }})
-
- if not _.isObject(options.headers)
- $errUtils.throwErrByPath("visit.invalid_headers")
-
- if _.isObject(options.body) and path = whatIsCircular(options.body)
- $errUtils.throwErrByPath("visit.body_circular", { args: { path }})
-
- if options.log
- message = url
-
- if options.method != 'GET'
- message = "#{options.method} #{message}"
-
- options._log = Cypress.log({
- message: message
- consoleProps: -> consoleProps
- })
-
- url = $Location.normalize(url)
-
- if baseUrl = config("baseUrl")
- url = $Location.qualifyWithBaseUrl(baseUrl, url)
-
- if qs = options.qs
- url = $Location.mergeUrlWithParams(url, qs)
-
- cleanup = null
-
- ## clear the current timeout
- cy.clearTimeout("visit")
-
- win = state("window")
- $autIframe = state("$autIframe")
- runnable = state("runnable")
-
- changeIframeSrc = (url, event) ->
- ## when the remote iframe's load event fires
- ## callback fn
- new Promise (resolve) ->
- ## if we're listening for hashchange
- ## events then change the strategy
- ## to listen to this event emitting
- ## from the window and not cy
- ## see issue 652 for why.
- ## the hashchange events are firing too
- ## fast for us. They even resolve asynchronously
- ## before other application's hashchange events
- ## have even fired.
- if event is "hashchange"
- win.addEventListener("hashchange", resolve)
- else
- cy.once(event, resolve)
-
- cleanup = ->
- if event is "hashchange"
- win.removeEventListener("hashchange", resolve)
- else
- cy.removeListener(event, resolve)
-
- knownCommandCausedInstability = false
-
- knownCommandCausedInstability = true
-
- $utils.iframeSrc($autIframe, url)
-
- onLoad = ({runOnLoadCallback, totalTime}) ->
- ## reset window on load
- win = state("window")
-
- ## the onLoad callback should only be skipped if specified
- if runOnLoadCallback isnt false
- options.onLoad?.call(runnable.ctx, win)
-
- if options._log
- options._log.set({
- url
- totalTime
- })
-
- return Promise.resolve(win)
-
- go = ->
- ## hold onto our existing url
- existing = $utils.locExisting()
-
- ## TODO: $Location.resolve(existing.origin, url)
-
- if $Location.isLocalFileUrl(url)
- return specifyFileByRelativePath(url, options._log)
-
- ## in the case we are visiting a relative url
- ## then prepend the existing origin to it
- ## so we get the right remote url
- if not $Location.isFullyQualifiedUrl(url)
- remoteUrl = $Location.fullyQualifyUrl(url)
-
- remote = $Location.create(remoteUrl ? url)
-
- ## reset auth options if we have them
- if a = remote.authObj
- options.auth = a
-
- ## store the existing hash now since
- ## we'll need to apply it later
- existingHash = remote.hash ? ""
- existingAuth = remote.auth ? ""
-
- if previousDomainVisited and remote.originPolicy isnt existing.originPolicy
- ## if we've already visited a new superDomain
- ## then die else we'd be in a terrible endless loop
- return cannotVisitDifferentOrigin(remote.origin, previousDomainVisited, remote, existing, options._log)
-
- current = $Location.create(win.location.href)
-
- ## if all that is changing is the hash then we know
- ## the browser won't actually make a new http request
- ## for this, and so we need to resolve onLoad immediately
- ## and bypass the actual visit resolution stuff
- if bothUrlsMatchAndRemoteHasHash(current, remote)
- ## https://github.com/cypress-io/cypress/issues/1311
- if current.hash is remote.hash
- consoleProps["Note"] = "Because this visit was to the same hash, the page did not reload and the onBeforeLoad and onLoad callbacks did not fire."
-
- return onLoad({runOnLoadCallback: false})
-
- return changeIframeSrc(remote.href, "hashchange")
- .then(onLoad)
-
- if existingHash
- ## strip out the existing hash if we have one
- ## before telling our backend to resolve this url
- url = url.replace(existingHash, "")
-
- if existingAuth
- ## strip out the existing url if we have one
- url = url.replace(existingAuth + "@", "")
-
- requestUrl(url, options)
- .then (resp = {}) =>
- {url, originalUrl, cookies, redirects, filePath} = resp
-
- ## reapply the existing hash
- url += existingHash
- originalUrl += existingHash
-
- if filePath
- consoleProps["File Served"] = filePath
- else
- if url isnt originalUrl
- consoleProps["Original Url"] = originalUrl
-
- if options.log
- message = options._log.get('message')
-
- if redirects and redirects.length
- message = [message].concat(redirects).join(" -> ")
-
- options._log.set({message: message})
-
- consoleProps["Resolved Url"] = url
- consoleProps["Redirects"] = redirects
- consoleProps["Cookies Set"] = cookies
-
- remote = $Location.create(url)
-
- ## if the origin currently matches
- ## then go ahead and change the iframe's src
- ## and we're good to go
- # if origin is existing.origin
- if remote.originPolicy is existing.originPolicy
- previousDomainVisited = remote.origin
-
- url = $Location.fullyQualifyUrl(url)
-
- changeIframeSrc(url, "window:load")
- .then ->
- onLoad(resp)
- else
- ## if we've already visited a new origin
- ## then die else we'd be in a terrible endless loop
- if previousDomainVisited
- return cannotVisitDifferentOrigin(remote.origin, previousDomainVisited, remote, existing, options._log)
-
- ## tell our backend we're changing domains
- ## TODO: add in other things we want to preserve
- ## state for like scrollTop
- s = {
- currentId: id
- tests: Cypress.getTestsState()
- startTime: Cypress.getStartTime()
- emissions: Cypress.getEmissions()
- }
-
- s.passed = Cypress.countByTestState(s.tests, "passed")
- s.failed = Cypress.countByTestState(s.tests, "failed")
- s.pending = Cypress.countByTestState(s.tests, "pending")
- s.numLogs = $Log.countLogsByTests(s.tests)
-
- Cypress.action("cy:collect:run:state")
- .then (a = []) ->
- ## merge all the states together holla'
- s = _.reduce a, (memo, obj) ->
- _.extend(memo, obj)
- , s
-
- Cypress.backend("preserve:run:state", s)
- .then ->
- ## and now we must change the url to be the new
- ## origin but include the test that we're currently on
- newUri = new UrlParse(remote.origin)
- newUri
- .set("pathname", existing.pathname)
- .set("query", existing.search)
- .set("hash", existing.hash)
-
- ## replace is broken in electron so switching
- ## to href for now
- # $utils.locReplace(window, newUri.toString())
- $utils.locHref(newUri.toString(), window)
-
- ## we are returning a Promise which never resolves
- ## because we're changing top to be a brand new URL
- ## and want to block the rest of our commands
- return Promise.delay(1e9)
- .catch (err) ->
- switch
- when err.gotResponse, err.invalidContentType
- visitFailedByErr err, err.originalUrl, ->
- args = {
- url: err.originalUrl
- path: err.filePath
- status: err.status
- statusText: err.statusText
- redirects: err.redirects
- contentType: err.contentType
- }
-
- msg = switch
- when err.gotResponse
- type = if err.filePath then "file" else "http"
-
- "visit.loading_#{type}_failed"
-
- when err.invalidContentType
- "visit.loading_invalid_content_type"
-
- $errUtils.throwErrByPath(msg, {
- onFail: options._log
- args: args
- })
- else
- visitFailedByErr err, url, ->
- $errUtils.throwErrByPath("visit.loading_network_failed", {
- onFail: options._log
- args: {
- url: url
- error: err
- stack: err.stack
- }
- noStackTrace: true
- })
-
- visit = ->
- ## if we've visiting for the first time during
- ## a test then we want to first visit about:blank
- ## so that we nuke the previous state. subsequent
- ## visits will not navigate to about:blank so that
- ## our history entries are intact
- if not hasVisitedAboutBlank
- hasVisitedAboutBlank = true
- currentlyVisitingAboutBlank = true
-
- aboutBlank(cy, win)
- .then ->
- currentlyVisitingAboutBlank = false
-
- go()
- else
- go()
-
- visit()
- .timeout(options.timeout, "visit")
- .catch Promise.TimeoutError, (err) =>
- timedOutWaitingForPageLoad(options.timeout, options._log)
- .finally ->
- cleanup?()
-
- return null
- })
diff --git a/packages/driver/test/support/server.coffee b/packages/driver/test/support/server.coffee
deleted file mode 100644
index 653569950830..000000000000
--- a/packages/driver/test/support/server.coffee
+++ /dev/null
@@ -1,100 +0,0 @@
-_ = require("lodash")
-fs = require("fs")
-auth = require("basic-auth")
-bodyParser = require("body-parser")
-express = require("express")
-http = require("http")
-path = require("path")
-Promise = require("bluebird")
-coffee = require("@packages/coffee")
-
-args = require("minimist")(process.argv.slice(2))
-
-[3500, 3501].forEach (port) ->
- app = express()
- server = http.Server(app)
-
- app.set("port", port)
-
- app.set("view engine", "html")
-
- app.use(require("morgan")({ format: "dev" }))
-
- app.use(require("cors")())
- app.use(require("compression")())
- app.use(bodyParser.urlencoded({ extended: false }))
- app.use(bodyParser.json())
- app.use(require("method-override")())
-
- app.head "/", (req, res) ->
- res.sendStatus(200)
-
- app.get "/timeout", (req, res) ->
- Promise
- .delay(req.query.ms ? 0)
- .then ->
- res.send "timeout"
-
- app.use "/isolated-runner", express.static(path.join(__dirname, '../../../runner/dist'))
-
- app.get "/node_modules/*", (req, res) ->
- res.sendFile(path.join("node_modules", req.params[0]), {
- root: path.join(__dirname, "../..")
- })
-
- app.get "/xml", (req, res) ->
- res.type("xml").send("bar")
-
- app.get "/buffer", (req, res) ->
- fs.readFile path.join(__dirname, "../cypress/fixtures/sample.pdf"), (err, bytes) ->
- res.type("pdf")
- res.send(bytes)
-
- app.get "/basic_auth", (req, res) ->
- user = auth(req)
-
- if user and (user.name is "cypress" and user.pass is "password123")
- res.send("basic auth worked")
- else
- res
- .set("WWW-Authenticate", "Basic")
- .sendStatus(401)
-
- app.get '/json-content-type', (req, res) ->
- res.send({})
-
- app.get '/invalid-content-type', (req, res) ->
- res.setHeader('Content-Type', 'text/html; charset=utf-8,text/html')
- res.end("TestHello")
-
- app.get '/undefined-content-type', (req, res) ->
- res.end("some stuff that looks likehtml")
-
- app.all '/dump-method', (req, res) ->
- res.send("request method: #{req.method}")
-
- app.all '/dump-qs', (req, res) ->
- res.send("it worked!
request querystring:
#{JSON.stringify(req.query)}")
-
- app.post '/post-only', (req, res) ->
- res.send("it worked!
request body:
#{JSON.stringify(req.body)}")
-
- app.get '/dump-headers', (req, res) ->
- res.send("request headers:
#{JSON.stringify(req.headers)}")
-
- app.get "/status-404", (req, res) ->
- res
- .status(404)
- .send("not found")
-
- app.get "/status-500", (req, res) ->
- res
- .status(500)
- .send("server error")
-
- app.use(express.static(path.join(__dirname, "..", "cypress")))
-
- app.use(require("errorhandler")())
-
- server.listen app.get("port"), ->
- console.log("Express server listening on port", app.get("port"))
diff --git a/packages/driver/test/support/server.js b/packages/driver/test/support/server.js
index 7475f767fd41..4a895d11bf78 100644
--- a/packages/driver/test/support/server.js
+++ b/packages/driver/test/support/server.js
@@ -36,6 +36,8 @@ ports.forEach((port) => {
})
})
+ app.use('/isolated-runner', express.static(path.join(__dirname, '../../../runner/dist')))
+
app.get('/node_modules/*', (req, res) => {
return res.sendFile(path.join('node_modules', req.params[0]), {
root: path.join(__dirname, '../..'),
diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json
index 2f2e5dfe8ca2..db646f256284 100644
--- a/packages/ui-components/package.json
+++ b/packages/ui-components/package.json
@@ -19,7 +19,7 @@
"@babel/plugin-proposal-decorators": "7.8.3",
"@babel/preset-env": "7.9.0",
"@babel/preset-react": "7.9.4",
- "@cypress/eslint-plugin-dev": "5.0.0",
+ "@cypress/webpack-preprocessor": "4.1.5",
"@fortawesome/fontawesome-free": "5.12.1",
"@reach/visually-hidden": "0.6.2",
"babel-loader": "8.1.0",
@@ -31,7 +31,6 @@
"lodash": "4.17.15",
"mobx": "5.15.4",
"mobx-react": "6.1.7",
- "node-sass": "4.13.0",
"prop-types": "15.7.2",
"react": "16.12.0",
"react-dom": "16.12.0",
From 52d71668f7afeba9e8df803bc513cb5a4a499add Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Wed, 20 May 2020 00:12:40 -0400
Subject: [PATCH 42/86] update helpers, simplify runner.spec
---
.../test/cypress/integration/runner.spec.js | 47 +---------------
.../runner/test/cypress/support/helpers.js | 55 ++++++++++++++++++-
packages/server/test/matchDeep.js | 2 +-
packages/server/test/support/helpers/e2e.js | 6 ++
yarn.lock | 35 ++++--------
5 files changed, 76 insertions(+), 69 deletions(-)
diff --git a/packages/runner/test/cypress/integration/runner.spec.js b/packages/runner/test/cypress/integration/runner.spec.js
index e553c5e45b31..0e70466cbee1 100644
--- a/packages/runner/test/cypress/integration/runner.spec.js
+++ b/packages/runner/test/cypress/integration/runner.spec.js
@@ -1,9 +1,10 @@
const { _ } = Cypress
+const sinon = require('sinon')
const helpers = require('../support/helpers')
-
const snapshots = require('../support/eventSnapshots').EventSnapshots
-const sinon = require('sinon')
+const { cleanseRunStateMap, shouldHaveTestResults, getRunState } = helpers
+const { visit, snapshotEvents, onInitialized, getAutCypress } = helpers.createCypress()
const simpleSingleTest = {
suites: { 'suite 1': { tests: [{ name: 'test 1' }] } },
@@ -13,8 +14,6 @@ const threeTestsWithHooks = {
suites: { 'suite 1': { hooks: ['before', 'beforeEach', 'afterEach', 'after'], tests: ['test 1', 'test 2', 'test 3'] } },
}
-const { visit, snapshotEvents, onInitialized, getAutCypress } = helpers.createCypress()
-
describe('src/cypress/runner', () => {
describe('isolated test runner', () => {
describe('test events', function () {
@@ -542,43 +541,3 @@ describe('src/cypress/runner', () => {
})
})
})
-
-const getRunState = (Cypress) => {
- const currentRunnable = Cypress.cy.state('runnable')
- const currentId = currentRunnable && currentRunnable.id
-
- const s = {
- currentId,
- tests: Cypress.getTestsState(),
- startTime: Cypress.getStartTime(),
- emissions: Cypress.getEmissions(),
- }
-
- s.passed = Cypress.countByTestState(s.tests, 'passed')
- s.failed = Cypress.countByTestState(s.tests, 'failed')
- s.pending = Cypress.countByTestState(s.tests, 'pending')
- s.numLogs = Cypress.Log.countLogsByTests(s.tests)
-
- return _.cloneDeep(s)
-}
-
-const cleanseRunStateMap = {
- wallClockStartedAt: new Date(0),
- wallClockDuration: 1,
- fnDuration: 1,
- afterFnDuration: 1,
- lifecycle: 1,
- duration: 1,
- startTime: new Date(0),
- 'err.stack': '[err stack]',
-}
-
-const shouldHaveTestResults = (expPassed, expFailed) => {
- return ({ failed }) => {
- expect(failed, 'resolve with failure count').eq(failed)
- expPassed = expPassed || '--'
- expFailed = expFailed || '--'
- cy.get('header .passed .num').should('have.text', `${expPassed}`)
- cy.get('header .failed .num').should('have.text', `${expFailed}`)
- }
-}
diff --git a/packages/runner/test/cypress/support/helpers.js b/packages/runner/test/cypress/support/helpers.js
index be74c1f02409..92e17ede3456 100644
--- a/packages/runner/test/cypress/support/helpers.js
+++ b/packages/runner/test/cypress/support/helpers.js
@@ -24,9 +24,9 @@ const eventCleanseMap = {
afterFnDuration: match.number,
wallClockDuration: match.number,
stack: match.string,
+ message: '[error message]',
sourceMappedStack: match.string,
parsedStack: match.array,
- message: '[error message]',
}
const mochaEventCleanseMap = {
@@ -271,6 +271,7 @@ function createCypress () {
snapshotEvents,
onInitialized,
getAutCypress,
+
}
}
@@ -407,7 +408,59 @@ const evalFn = (win, fn) => {
}
}
+const cleanseRunStateMap = {
+ wallClockStartedAt: new Date(0),
+ wallClockDuration: 1,
+ fnDuration: 1,
+ afterFnDuration: 1,
+ lifecycle: 1,
+ duration: 1,
+ startTime: new Date(0),
+ 'err.stack': '[err stack]',
+ sourceMappedStack: match.string,
+ parsedStack: match.array,
+}
+
+const shouldHaveTestResults = (expPassed, expFailed) => {
+ return ({ failed }) => {
+ expect(failed, 'resolve with failure count').eq(failed)
+ expPassed = expPassed || '--'
+ expFailed = expFailed || '--'
+ cy.get('header .passed .num').should('have.text', `${expPassed}`)
+ cy.get('header .failed .num').should('have.text', `${expFailed}`)
+ }
+}
+
+const containText = (text) => {
+ return (($el) => {
+ expect($el[0]).property('innerText').contain(text)
+ })
+}
+
+const getRunState = (Cypress) => {
+ const currentRunnable = Cypress.cy.state('runnable')
+ const currentId = currentRunnable && currentRunnable.id
+
+ const s = {
+ currentId,
+ tests: Cypress.getTestsState(),
+ startTime: Cypress.getStartTime(),
+ emissions: Cypress.getEmissions(),
+ }
+
+ s.passed = Cypress.countByTestState(s.tests, 'passed')
+ s.failed = Cypress.countByTestState(s.tests, 'failed')
+ s.pending = Cypress.countByTestState(s.tests, 'pending')
+ s.numLogs = Cypress.Log.countLogsByTests(s.tests)
+
+ return _.cloneDeep(s)
+}
+
module.exports = {
generateMochaTestsForWin,
createCypress,
+ containText,
+ cleanseRunStateMap,
+ shouldHaveTestResults,
+ getRunState,
}
diff --git a/packages/server/test/matchDeep.js b/packages/server/test/matchDeep.js
index 089e3e856d32..b031016b24a8 100644
--- a/packages/server/test/matchDeep.js
+++ b/packages/server/test/matchDeep.js
@@ -35,7 +35,7 @@ const registerInMocha = () => {
try {
matchDeep.call(ctx, m, exp, { message: 'to match snapshot', chai, setGlobalSnapshot: _.noop, sinon })
} catch (e) {
- if (_.has(e, 'act')) {
+ if (_.has(e, 'act') && !e.failedMatcher) {
if (process.env['SNAPSHOT_UPDATE']) {
saveSnapshot({
file,
diff --git a/packages/server/test/support/helpers/e2e.js b/packages/server/test/support/helpers/e2e.js
index e05166024e3d..46627200ba16 100644
--- a/packages/server/test/support/helpers/e2e.js
+++ b/packages/server/test/support/helpers/e2e.js
@@ -611,6 +611,12 @@ const e2e = {
debug('processed options %o', options)
let args = this.args(options)
+ const specifiedBrowser = process.env.BROWSER
+
+ if (specifiedBrowser && (![].concat(options.browser).includes(specifiedBrowser))) {
+ ctx.skip()
+ }
+
args = ['index.js'].concat(args)
let stdout = ''
diff --git a/yarn.lock b/yarn.lock
index 9ab3c962abcb..2f06ccf484ff 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1639,6 +1639,18 @@
"@babel/preset-env" "^7.0.0"
babel-loader "^8.0.2"
+"@cypress/webpack-preprocessor@4.1.5":
+ version "4.1.5"
+ resolved "https://registry.yarnpkg.com/@cypress/webpack-preprocessor/-/webpack-preprocessor-4.1.5.tgz#b47d515d2540af977ee8b69d7c4eed64e3027668"
+ integrity sha512-B4miSaS3VCMVSlfuvbWCjytTywdnquRsF1tQ3quC7TGUzEXnQZ4+o8WUKibjMozrOomALkUdMxqOJ1ib5oFkKw==
+ dependencies:
+ bluebird "3.7.1"
+ debug "4.1.1"
+ optionalDependencies:
+ "@babel/core" "^7.0.1"
+ "@babel/preset-env" "^7.0.0"
+ babel-loader "^8.0.2"
+
"@cypress/webpack-preprocessor@5.4.1":
version "5.4.1"
resolved "https://registry.yarnpkg.com/@cypress/webpack-preprocessor/-/webpack-preprocessor-5.4.1.tgz#eb58f6cd02932a95653c1a674cfd769da2409806"
@@ -18439,29 +18451,6 @@ node-sass@4.12.0:
stdout-stream "^1.4.0"
"true-case-path" "^1.0.2"
-node-sass@4.13.0:
- version "4.13.0"
- resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.13.0.tgz#b647288babdd6a1cb726de4545516b31f90da066"
- integrity sha512-W1XBrvoJ1dy7VsvTAS5q1V45lREbTlZQqFbiHb3R3OTTCma0XBtuG6xZ6Z4506nR4lmHPTqVRwxT6KgtWC97CA==
- dependencies:
- async-foreach "^0.1.3"
- chalk "^1.1.1"
- cross-spawn "^3.0.0"
- gaze "^1.0.0"
- get-stdin "^4.0.1"
- glob "^7.0.3"
- in-publish "^2.0.0"
- lodash "^4.17.15"
- meow "^3.7.0"
- mkdirp "^0.5.1"
- nan "^2.13.2"
- node-gyp "^3.8.0"
- npmlog "^4.0.0"
- request "^2.88.0"
- sass-graph "^2.2.4"
- stdout-stream "^1.4.0"
- "true-case-path" "^1.0.2"
-
node-source-walk@^4.0.0, node-source-walk@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-4.2.0.tgz#c2efe731ea8ba9c03c562aa0a9d984e54f27bc2c"
From 73ee90675650d71f881e4ccf97dac99bcf545d32 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Wed, 20 May 2020 13:31:18 -0400
Subject: [PATCH 43/86] fix lint-types errors, flaky spec
---
packages/runner/test/cypress/plugins/index.js | 4 ----
.../plugins/snapshot/command/index.d.ts | 24 +++++++++----------
.../test/e2e/5_spec_isolation_spec.coffee | 4 ++--
3 files changed, 13 insertions(+), 19 deletions(-)
diff --git a/packages/runner/test/cypress/plugins/index.js b/packages/runner/test/cypress/plugins/index.js
index 3269befd540e..7610b54cda80 100644
--- a/packages/runner/test/cypress/plugins/index.js
+++ b/packages/runner/test/cypress/plugins/index.js
@@ -1,5 +1,3 @@
-///
-
const { getSnapshot, saveSnapshot } = require('./snapshot')
/**
@@ -9,9 +7,7 @@ module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
on('task', {
-
getSnapshot,
-
saveSnapshot,
})
}
diff --git a/packages/runner/test/cypress/plugins/snapshot/command/index.d.ts b/packages/runner/test/cypress/plugins/snapshot/command/index.d.ts
index ddbb4be46ce7..56dfa5abd634 100644
--- a/packages/runner/test/cypress/plugins/snapshot/command/index.d.ts
+++ b/packages/runner/test/cypress/plugins/snapshot/command/index.d.ts
@@ -1,15 +1,13 @@
-///
-
declare namespace Chai {
- interface Assertion {
- matchSnapshot: {
- (name?: string)
- (replacers: object)
- (replacers: object, name?: string)
- }
- matchDeep: {
- (replacers: object, expected: object)
- (expected: object)
- }
- }
+ interface Assertion {
+ matchSnapshot: {
+ (name?: string)
+ (replacers: object)
+ (replacers: object, name?: string)
+ }
+ matchDeep: {
+ (replacers: object, expected: object)
+ (expected: object)
+ }
+ }
}
diff --git a/packages/server/test/e2e/5_spec_isolation_spec.coffee b/packages/server/test/e2e/5_spec_isolation_spec.coffee
index f13e076a7628..b8bdd2ea5dac 100644
--- a/packages/server/test/e2e/5_spec_isolation_spec.coffee
+++ b/packages/server/test/e2e/5_spec_isolation_spec.coffee
@@ -39,7 +39,7 @@ expectDurationWithin = (obj, duration, low, high, reset) ->
return if not _.isNumber(d)
## ensure the duration is within range
- expect(d).to.be.within(low, high)
+ expect(d, duration).to.be.within(low, high)
## once valid, mutate and set static range
_.set(obj, duration, reset)
@@ -126,7 +126,7 @@ expectRunsToHaveCorrectStats = (runs = []) ->
test,
"wallClockDuration",
timings,
- timings + 50, ## add 50ms to account for padding
+ timings + 80, ## add 80ms to account for padding
1234
)
From f2977b7665c4b88bd62123d4e7dc5041cfee380b Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Fri, 22 May 2020 10:54:11 -0400
Subject: [PATCH 44/86] fix noExit passed to e2e test inline options
---
packages/server/test/support/helpers/e2e.js | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/packages/server/test/support/helpers/e2e.js b/packages/server/test/support/helpers/e2e.js
index e05166024e3d..4e2ba243ecea 100644
--- a/packages/server/test/support/helpers/e2e.js
+++ b/packages/server/test/support/helpers/e2e.js
@@ -438,8 +438,6 @@ const e2e = {
},
options (ctx, options = {}) {
- const noExit = process.env.NO_EXIT
-
_.defaults(options, {
browser: 'electron',
headed: process.env.HEADED || false,
@@ -449,13 +447,9 @@ const e2e = {
expectedExitCode: 0,
sanitizeScreenshotDimensions: false,
normalizeStdoutAvailableBrowsers: true,
- noExit,
+ noExit: process.env.NO_EXIT,
})
- if (options.noExit) {
- options.timeout = 3000000
- }
-
if (options.exit != null) {
throw new Error(`
passing { exit: false } to e2e options is no longer supported
@@ -464,7 +458,7 @@ const e2e = {
`)
}
- if (noExit && options.timeout < 3000000) {
+ if (options.noExit && options.timeout < 3000000) {
options.timeout = 3000000
}
From 16be5c2d921989210e7363d0f4f1b410abe7786f Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Fri, 22 May 2020 12:56:36 -0400
Subject: [PATCH 45/86] cleanup snapshot utility - refactor to use util file
---
.../cypress/plugins/snapshot/command/index.js | 288 +++++-------------
.../cypress/plugins/snapshot/command/utils.js | 142 +++++++++
2 files changed, 213 insertions(+), 217 deletions(-)
create mode 100644 packages/runner/test/cypress/plugins/snapshot/command/utils.js
diff --git a/packages/runner/test/cypress/plugins/snapshot/command/index.js b/packages/runner/test/cypress/plugins/snapshot/command/index.js
index 105d9eddd725..6cafbfced62b 100644
--- a/packages/runner/test/cypress/plugins/snapshot/command/index.js
+++ b/packages/runner/test/cypress/plugins/snapshot/command/index.js
@@ -1,16 +1,17 @@
-let _ = require('lodash')
+const _ = require('lodash')
+// if we're in Cypress, we'll need to swap this with Cypress.sinon later
+let sinon = require('sinon')
const Debug = require('debug')
const chalk = require('chalk')
const stripAnsi = require('strip-ansi')
const { stripIndent } = require('common-tags')
-let sinon = require('sinon')
+const { printVar, stringifyShort, isObject, addPluginButton, fmt, typeColors } = require('./utils')
const debug = Debug('plugin:snapshot')
-// window.localStorage.debug = 'spec* plugin:snapshot'
-// Debug.enable('plugin:snapshot')
-
-// prints nice assertion error in command log with modified error message
+/**
+ * prints nice assertion error in command log with modified error message
+ */
function throwErr (e, message, exp, ctx) {
try {
ctx.assert(false, message, 'sdf', exp, e.act, true)
@@ -20,26 +21,61 @@ function throwErr (e, message, exp, ctx) {
}
}
+function getMatchDeepMessage ({ act, exp }) {
+ return `Expected **${chai.util.objDisplay(act)}** to deep match: **${chai.util.objDisplay(exp)}**`
+}
+
+function saveSnapshot (ctx, exactSpecName, file, exp, act) {
+ ctx.assert(true, `snapshot updated: **${exactSpecName}**`, 'dsf', exp, act)
+
+ return cy.task('saveSnapshot', {
+ file,
+ what: act,
+ exactSpecName,
+ }, { log: false })
+}
+
const registerInCypress = () => {
- _ = Cypress._
+ // need to use correct sinon version for matcher.isMatcher to work
sinon = Cypress.sinon
const $ = Cypress.$
+ let snapshotIndex = {}
+
chai = window.chai
+ chai.Assertion.addMethod('matchDeep', matchDeepCypress)
+ chai.Assertion.addMethod('matchSnapshot', matchSnapshotCypress)
- let snapshotIndex = {}
+ after(() => {
+ snapshotIndex = {}
+ })
+
+ before(() => {
+ addPluginButton($, 'toggle-snapshot-update', '', function () {
+ const prev = Cypress.env('SNAPSHOT_UPDATE')
+
+ Cypress.env('SNAPSHOT_UPDATE', !prev)
+ const btnIcon = this.children().first()
- const matchDeepCypress = function (...args) {
+ return btnIcon.text(Cypress.env('SNAPSHOT_UPDATE') ? 'snapshot\nupdate\non' : 'snapshot\nupdate\noff')
+ .css({ 'font-size': '10px', 'line-height': '0.9' })
+ .html(btnIcon.html().replace(/\n/g, '
'))
+ })
+ })
+
+ function matchDeepCypress (...args) {
const exp = args[1] || args[0]
const ctx = this
try {
const res = matchDeep.apply(this, [args[0], args[1], { Cypress, expectedOnly: true }])
- ctx.assert(true, `Expected **${chai.util.objDisplay(res.act)}** to deep match: **${chai.util.objDisplay(exp)}**`)
+ const message = getMatchDeepMessage(res.act, exp)
+
+ ctx.assert(true, message)
Cypress.log({
name: 'assert',
- message: `Expected **${chai.util.objDisplay(res.act)}** to deep match: **${chai.util.objDisplay(exp)}**`,
+ message,
state: 'passed',
consoleProps: () => {
return {
@@ -50,17 +86,17 @@ const registerInCypress = () => {
} catch (e) {
throwErr(
e,
- `Expected **${chai.util.objDisplay(e.act)}** to deep match: **${chai.util.objDisplay(args[1] || args[0])}**`,
+ getMatchDeepMessage(e.act, args[1] || args[0]),
exp,
ctx,
)
}
}
- const matchSnapshotCypress = function (m, snapshotName) {
+ function matchSnapshotCypress (m, snapshotName) {
const ctx = this
- const testName = Cypress.mocha.getRunner().test.fullTitle()
const file = Cypress.spec.name
+ const testName = Cypress.mocha.getRunner().test.fullTitle()
return cy.then(() => {
snapshotIndex[testName] = (snapshotIndex[testName] || 1)
@@ -77,14 +113,12 @@ const registerInCypress = () => {
ctx.assert(true, `snapshot matched: **${exactSpecName}**`, res.act)
} catch (e) {
- if (Cypress.env('SNAPSHOT_UPDATE') && !e.failedMatcher && e.act) {
- ctx.assert(true, `snapshot updated: **${exactSpecName}**`, 'dsf', exp, e.act)
+ if (!e.known) {
+ throw e
+ }
- return cy.task('saveSnapshot', {
- file,
- what: e.act,
- exactSpecName,
- }, { log: false })
+ if (Cypress.env('SNAPSHOT_UPDATE') && !e.failedMatcher && e.act) {
+ return saveSnapshot(ctx, exactSpecName, file, exp, e.act)
}
throwErr(e, `**snapshot failed to match**: ${exactSpecName}`, exp, ctx)
@@ -92,57 +126,8 @@ const registerInCypress = () => {
})
})
}
-
- chai.Assertion.addMethod('matchDeep', matchDeepCypress)
- chai.Assertion.addMethod('matchSnapshot', matchSnapshotCypress)
-
- after(() => {
- snapshotIndex = {}
- })
-
- before(() => {
- const btn = addButton('toggle-snapshot-update', '', () => {
- const prev = Cypress.env('SNAPSHOT_UPDATE')
-
- Cypress.env('SNAPSHOT_UPDATE', !prev)
- updateText()
- })
- const btnIcon = btn.children().first()
- const updateText = () => {
- return btnIcon.text(Cypress.env('SNAPSHOT_UPDATE') ? 'snapshot\nupdate\non' : 'snapshot\nupdate\noff')
- .css({ 'font-size': '10px', 'line-height': '0.9' })
- .html(btnIcon.html().replace(/\n/g, '
'))
- }
-
- updateText()
- })
-
- const addButton = (name, faClass, fn) => {
- $(`#${name}`, window.top.document).remove()
-
- const btn = $(``, window.top.document)
- const container = $(
- '.toggle-auto-scrolling.auto-scrolling-enabled',
- window.top.document,
- ).closest('.controls')
-
- container.prepend(btn)
-
- btn.on('click', fn)
-
- return btn
- }
}
-// // unfortunate, but sinon uses isPrototype of, which will not work for
-// // two different sinon versions
-// match.isMatcher = (obj) => {
-// return _.isFunction(_.get(obj, 'test')) &&
-// _.isString(_.get(obj, 'message')) &&
-// _.isFunction(_.get(obj, 'and')) &&
-// _.isFunction(_.get(obj, 'or'))
-// }
-
const matcherStringToObj = (mes) => {
const res = mes.replace(/typeOf\("(\w+)"\)/, '$1')
@@ -167,8 +152,6 @@ const matchDeep = function (matchers, exp, optsArg) {
m = {}
}
- // debug(optsArg)
-
const opts = _.defaults(optsArg, {
message: 'to match',
Cypress: false,
@@ -186,8 +169,6 @@ const matchDeep = function (matchers, exp, optsArg) {
const act = this._obj
- debug('matchDeep:actual:', act)
-
m = _.map(m, (val, key) => {
return [key.split('.'), val]
})
@@ -195,7 +176,7 @@ const matchDeep = function (matchers, exp, optsArg) {
const diffStr = withMatchers(m, match, opts.expectedOnly)(exp, act)
if (diffStr.changed) {
- let e = _.extend(new Error(), { act: diffStr.act, failedMatcher: diffStr.opts.failedMatcher })
+ let e = _.extend(new Error(), { known: true, act: diffStr.act, failedMatcher: diffStr.opts.failedMatcher })
e.message = isAnsi ? `\n${diffStr.text}` : stripAnsi(diffStr.text)
@@ -213,94 +194,6 @@ const matchDeep = function (matchers, exp, optsArg) {
return diffStr
}
-let typeColors = {
- modified: chalk.yellow,
- added: chalk.green,
- removed: chalk.red,
- normal: chalk.gray,
- failed: chalk.redBright,
-}
-
-let options = {
- indent: 2,
- indentChar: ' ',
- newLineChar: '\n',
- wrap: function wrap (type, text) {
- if (this.Cypress) {
- text = `**${text}**`
- }
-
- return typeColors[type](text)
- },
-}
-
-let indent = ''
-
-for (let i = 0; i < options.indent; i++) {
- indent += options.indentChar
-}
-
-function isObject (obj) {
- return typeof obj === 'object' && obj && getType(obj) !== 'RegExp'
- // return typeof obj === 'object' && obj && !Array.isArray(obj)
-}
-
-function printVar (variable) {
- switch (getType(variable)) {
- case 'Null':
- return variable
- case 'Undefined':
- return variable
- case 'Boolean':
- return variable
- case 'Number':
- return variable
- case 'Function':
- return `[Function${variable.name ? ` ${variable.name}` : ''}]`
-
- // return variable.toString().replace(/\{.+\}/, '{}')
-
- case 'Array':
- case 'Object':
-
- if (variable.toJSON) {
- return variable.toJSON()
- }
-
- return stringifyShort(variable)
-
- case 'String':
- return `${variable}`
-
- default: return `${variable}`
- }
-}
-
-function indentSubItem (text) {
- return text.split(options.newLineChar).map(function onMap (line, index) {
- if (index === 0) {
- return line
- }
-
- return indent + line
- }).join(options.newLineChar)
-}
-
-function getType (obj) {
- return Object.prototype.toString.call(obj).split('[object ').join('').slice(0, -1)
-}
-function keyChanged (key, text) {
- return `${indent + key}: ${indentSubItem(text)}${options.newLineChar}`
-}
-
-function keyRemoved (key, variable) {
- return options.wrap('removed', `- ${key}: ${printVar(variable)}`) + options.newLineChar
-}
-
-function keyAdded (key, variable) {
- return options.wrap('added', `+ ${key}: ${printVar(variable)}`) + options.newLineChar
-}
-
function parseMatcher (obj, match) {
if (match.isMatcher(obj)) {
return obj
@@ -312,8 +205,6 @@ function parseMatcher (obj, match) {
const parsed = /match\.(.*)/.exec(parseObj)
if (parsed) {
- // debug('parsed matcher from string:', parsed[1])
-
return match[parsed[1]]
}
@@ -348,35 +239,28 @@ const withMatchers = (matchers, match, expectedOnly = false) => {
const matched = _path.join('.').endsWith(rep[0].join('.'))
- // (_.last(_path) === _.last(val[0]))
- // && _.isEqual(_.intersection(_path, val[0]), val[0])
-
if (matched) {
return rep[1]
}
}
- return no_replacement
+ return NO_REPLACEMENT
}
const testValue = (matcher, value) => {
- // if (match.isMatcher(value)) {
- // if (value.toString() === matcher.toString()) {
- // return true
- // }
- // }
-
if (matcher.test(value)) {
return true
}
- // addErr(new Error(`replace matcher failed: ${genError(newPath, matcher.toString(), value)}`))
-
return false
}
- const no_replacement = {}
+ const NO_REPLACEMENT = {}
+ /**
+ * diffing function that produces human-readable diff output.
+ * unfortunately it is also unreadable code in itself.
+ */
const diff = (exp, act, path = ['^'], optsArg) => {
const opts = _.defaults({}, optsArg, {
expectedOnly,
@@ -386,7 +270,6 @@ const withMatchers = (matchers, match, expectedOnly = false) => {
throw new Error(`exceeded max depth on ${path.slice(0, 4)} ... ${path.slice(-4)}`)
}
- // console.log(act)
let text = ''
let changed = false
let itemDiff
@@ -395,9 +278,7 @@ const withMatchers = (matchers, match, expectedOnly = false) => {
let replacement = getReplacementFor(path, matchers)
- // console.log(path)
-
- if (replacement !== no_replacement) {
+ if (replacement !== NO_REPLACEMENT) {
if (match.isMatcher(replacement)) {
if (testValue(replacement, act)) {
act = matcherStringToObj(replacement.message).toJSON()
@@ -430,7 +311,7 @@ const withMatchers = (matchers, match, expectedOnly = false) => {
}
return {
- text: options.wrap('failed', `${chalk.green(printVar(act))} ⛔ ${matcherStringToObj(exp.message).toJSON()}`),
+ text: fmt.wrap('failed', `${chalk.green(printVar(act))} ⛔ ${matcherStringToObj(exp.message).toJSON()}`),
changed: true,
act,
}
@@ -461,11 +342,11 @@ const withMatchers = (matchers, match, expectedOnly = false) => {
_.defaults(opts, itemDiff.opts)
act[key] = itemDiff.act
if (itemDiff.changed) {
- subOutput += keyChanged(key, itemDiff.text)
+ subOutput += fmt.keyChanged(key, itemDiff.text)
changed = true
}
} else {
- subOutput += keyRemoved(key, exp[key])
+ subOutput += fmt.keyRemoved(key, exp[key])
changed = true
}
@@ -489,7 +370,7 @@ const withMatchers = (matchers, match, expectedOnly = false) => {
if (opts.failedMatcher) {
subOutput += addDiff.text
} else {
- subOutput += keyAdded(key, act[key])
+ subOutput += fmt.keyAdded(key, act[key])
}
changed = true
@@ -497,21 +378,12 @@ const withMatchers = (matchers, match, expectedOnly = false) => {
}
if (changed) {
- let renderBracket = false
-
- if (_.isArray(act) && _.isArray(exp)) {
- renderBracket = true
- }
-
- const _O = renderBracket ? '[' : '{'
- const _C = renderBracket ? ']' : '}'
-
- text = options.wrap('normal', `${_O}${options.newLineChar}${subOutput}${_C}`)
+ text = fmt.wrapObjectLike(exp, act, subOutput)
}
} else if (match.isMatcher(exp)) {
debug('is matcher')
if (!testValue(exp, act)) {
- text = options.wrap('failed', `${chalk.green(printVar(act))} ⛔ ${matcherStringToObj(exp.message).toJSON()}`)
+ text = fmt.wrap('failed', `${chalk.green(printVar(act))} ⛔ ${matcherStringToObj(exp.message).toJSON()}`)
changed = true
}
} else if (isObject(act)) {
@@ -524,7 +396,7 @@ const withMatchers = (matchers, match, expectedOnly = false) => {
return _.extend({},
addDiff, {
changed: true,
- text: options.wrap('removed', `${printVar(exp)}\n${options.wrap('added', addDiff.text)}`),
+ text: fmt.wrap('removed', `${printVar(exp)}\n${fmt.wrap('added', addDiff.text)}`),
})
} else {
debug('neither is obj')
@@ -532,7 +404,7 @@ const withMatchers = (matchers, match, expectedOnly = false) => {
act = printVar(act)
if (exp !== act) {
- text = options.wrap('modified', `${exp} ${typeColors['normal']('⮕')} ${act}`)
+ text = fmt.wrap('modified', `${exp} ${typeColors['normal']('⮕')} ${act}`)
changed = true
}
}
@@ -548,24 +420,6 @@ const withMatchers = (matchers, match, expectedOnly = false) => {
return diff
}
-const stringifyShort = (obj) => {
- const constructorName = _.get(obj, 'constructor.name')
-
- if (constructorName && !_.includes(['Object', 'Array'], constructorName)) {
- return `{${constructorName}}`
- }
-
- if (_.isArray(obj)) {
- return `[Array ${obj.length}]`
- }
-
- if (_.isObject(obj)) {
- return `{Object ${Object.keys(obj).length}}`
- }
-
- return obj
-}
-
module.exports = {
registerInCypress,
matchDeep,
diff --git a/packages/runner/test/cypress/plugins/snapshot/command/utils.js b/packages/runner/test/cypress/plugins/snapshot/command/utils.js
new file mode 100644
index 000000000000..856236d26c13
--- /dev/null
+++ b/packages/runner/test/cypress/plugins/snapshot/command/utils.js
@@ -0,0 +1,142 @@
+const _ = require('lodash')
+const chalk = require('chalk')
+
+function printVar (variable) {
+ switch (getType(variable)) {
+ case 'Null':
+ return variable
+ case 'Undefined':
+ return variable
+ case 'Boolean':
+ return variable
+ case 'Number':
+ return variable
+ case 'Function':
+ return `[Function${variable.name ? ` ${variable.name}` : ''}]`
+
+ case 'Array':
+ case 'Object':
+
+ if (variable.toJSON) {
+ return variable.toJSON()
+ }
+
+ return stringifyShort(variable)
+
+ case 'String':
+ return `${variable}`
+
+ default: return `${variable}`
+ }
+}
+
+function getType (obj) {
+ return Object.prototype.toString.call(obj).split('[object ').join('').slice(0, -1)
+}
+
+const stringifyShort = (obj) => {
+ const constructorName = _.get(obj, 'constructor.name')
+
+ if (constructorName && !_.includes(['Object', 'Array'], constructorName)) {
+ return `{${constructorName}}`
+ }
+
+ if (_.isArray(obj)) {
+ return `[Array ${obj.length}]`
+ }
+
+ if (_.isObject(obj)) {
+ return `{Object ${Object.keys(obj).length}}`
+ }
+
+ return obj
+}
+
+function isObject (obj) {
+ return typeof obj === 'object' && obj && getType(obj) !== 'RegExp'
+}
+
+function addPluginButton ($, name, faClass, fn) {
+ $(`#${name}`, window.top.document).remove()
+
+ const btn = $(``, window.top.document)
+ const container = $(
+ '.toggle-auto-scrolling.auto-scrolling-enabled',
+ window.top.document,
+ ).closest('.controls')
+
+ container.prepend(btn)
+
+ btn.on('click', fn)
+ fn.apply(btn)
+
+ return btn
+}
+
+const typeColors = {
+ modified: chalk.yellow,
+ added: chalk.green,
+ removed: chalk.red,
+ normal: chalk.gray,
+ failed: chalk.redBright,
+}
+
+const fmtOpts = {
+ indent: ' ',
+ newLineChar: '\n',
+}
+
+const fmt = {
+ wrap: function wrap (type, text) {
+ if (this.Cypress) {
+ text = `**${text}**`
+ }
+
+ return typeColors[type](text)
+ },
+
+ wrapObjectLike (exp, act, subOutput) {
+ let renderBracket = false
+
+ if (_.isArray(act) && _.isArray(exp)) {
+ renderBracket = true
+ }
+
+ const _O = renderBracket ? '[' : '{'
+ const _C = renderBracket ? ']' : '}'
+
+ return fmt.wrap('normal', `${_O}${fmtOpts.newLineChar}${subOutput}${_C}`)
+ },
+
+ indentSubItem (text) {
+ return text.split(fmtOpts.newLineChar).map(function (line, index) {
+ if (index === 0) {
+ return line
+ }
+
+ return fmtOpts.indent + line
+ }).join(fmtOpts.newLineChar)
+ },
+
+ keyChanged (key, text) {
+ return `${fmtOpts.indent + key}: ${fmt.indentSubItem(text)}${fmtOpts.newLineChar}`
+ },
+
+ keyRemoved (key, variable) {
+ return fmtOpts.wrap('removed', `- ${key}: ${printVar(variable)}`) + fmtOpts.newLineChar
+ },
+
+ keyAdded (key, variable) {
+ return fmtOpts.wrap('added', `+ ${key}: ${printVar(variable)}`) + fmtOpts.newLineChar
+ },
+}
+
+module.exports = {
+ printVar,
+ stringifyShort,
+ isObject,
+ addPluginButton,
+ fmt,
+ fmtOpts,
+ typeColors,
+}
From bee840866245252ce447c6e70eead32c944e9c20 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 26 May 2020 16:16:31 -0400
Subject: [PATCH 46/86] remove before/after changes
---
packages/driver/src/cypress/runner.js | 152 ++++++++++++--------------
1 file changed, 67 insertions(+), 85 deletions(-)
diff --git a/packages/driver/src/cypress/runner.js b/packages/driver/src/cypress/runner.js
index 37afc7cc0593..e3b86e714002 100644
--- a/packages/driver/src/cypress/runner.js
+++ b/packages/driver/src/cypress/runner.js
@@ -208,17 +208,17 @@ const eachHookInSuite = function (suite, fn) {
return null
}
-// iterates over a suite's tests (including nested suites)
-// and will return as soon as the callback is true
-const findTestInSuite = function (suite, fn = _.identity) {
- for (const test of suite.tests) {
+const onFirstTest = function (suite, fn) {
+ let test
+
+ for (test of suite.tests) {
if (fn(test)) {
return test
}
}
for (suite of suite.suites) {
- const test = findTestInSuite(suite, fn)
+ test = onFirstTest(suite, fn)
if (test) {
return test
@@ -226,25 +226,6 @@ const findTestInSuite = function (suite, fn = _.identity) {
}
}
-// same as findTestInSuite but iterates backwards
-const findLastTestInSuite = function (suite, fn = _.identity) {
- for (let i = suite.suites.length - 1; i >= 0; i--) {
- const test = findLastTestInSuite(suite.suites[i], fn)
-
- if (test) {
- return test
- }
- }
-
- for (let i = suite.tests.length - 1; i >= 0; i--) {
- const test = suite.tests[i]
-
- if (fn(test)) {
- return test
- }
- }
-}
-
const getAllSiblingTests = function (suite, getTestById) {
const tests = []
@@ -261,51 +242,46 @@ const getAllSiblingTests = function (suite, getTestById) {
return tests
}
-function getOrderFromId (id) {
- return +id.slice(1)
-}
-
-function isNotAlreadyRunTest (test) {
- return !(Cypress._RESUMED_AT_TEST && getOrderFromId(test.id) < getOrderFromId(Cypress._RESUMED_AT_TEST))
-}
-
-const getTestFromHook = function (hook) {
+const getTestFromHook = function (hook, suite, getTestById) {
// if theres already a currentTest use that
+ let found; let test
- const test = hook.ctx.currentTest
+ test = hook != null ? hook.ctx.currentTest : undefined
if (test) {
return test
}
-}
-const getTestFromHookOrFindTest = function (hook) {
- const test = getTestFromHook(hook)
+ // if we have a hook id then attempt
+ // to find the test by its id
+ if (hook != null ? hook.id : undefined) {
+ found = onFirstTest(suite, (test) => {
+ return hook.id === test.id
+ })
- if (test) {
- return test
+ if (found) {
+ return found
+ }
}
- const suite = hook.parent
+ // returns us the very first test
+ // which is in our filtered tests array
+ // based on walking down the current suite
+ // iterating through each test until it matches
+ found = onFirstTest(suite, (test) => {
+ return getTestById(test.id)
+ })
- if (hook.hookName === 'after all') {
- return findLastTestInSuite(suite, isNotAlreadyRunTest)
+ if (found) {
+ return found
}
- if (hook.hookName === 'before all') {
- return findTestInSuite(suite, isNotAlreadyRunTest)
- }
-}
-
-function getTestFromRunnable (runnable) {
- switch (runnable.type) {
- case 'hook':
- return getTestFromHook(runnable)
-
- case 'test':
- return runnable
- default: null
- }
+ // have one last final fallback where
+ // we just return true on the very first
+ // test (used in testing)
+ return onFirstTest(suite, (test) => {
+ return true
+ })
}
// we have to see if this is the last suite amongst
@@ -344,7 +320,7 @@ const isLastSuite = function (suite, tests) {
// if we failed from a hook and that hook was 'before'
// since then mocha skips the remaining tests in the suite
const lastTestThatWillRunInSuite = (test, tests) => {
- return !test.parent._afterAll.length || isLastTest(test, tests) || (test.failedFromHookId && (test.hookName === 'before all'))
+ return isLastTest(test, tests) || (test.failedFromHookId && (test.hookName === 'before all'))
}
const isLastTest = (test, tests) => {
@@ -417,10 +393,9 @@ const overrideRunnerHook = function (Cypress, _runner, getTestById, getTest, set
// since that will bubble up IF we're the last nested suite
// 3. else if we arent the last nested suite we fire if we're
// the last test that will run
-
if (
(isRootSuite(this.suite) && isLastTest(t, allTests)) ||
- (isRootSuite(this.suite.parent) && !this.suite.parent._afterAll.length && lastTestThatWillRunInSuite(t, siblings)) ||
+ (isRootSuite(this.suite.parent) && lastTestThatWillRunInSuite(t, siblings)) ||
(!isLastSuite(this.suite, allTests) && lastTestThatWillRunInSuite(t, siblings))
) {
changeFnToRunAfterHooks()
@@ -464,7 +439,7 @@ const normalizeAll = (suite, initialTests = {}, setTestsById, setTests, onRunnab
let hasTests = false
// only loop until we find the first test
- findTestInSuite(suite, (test) => {
+ onFirstTest(suite, (test) => {
return hasTests = true
})
@@ -599,11 +574,11 @@ const normalize = (runnable, tests, initialTests, onRunnable, onLogsById, getTes
return normalizedRunnable
}
-const hookFailed = function (hook, err, hookName) {
- // NOTE: sometimes mocha will fail a hook without having emitted on('hook')
- // event, so this hook might not have currentTest set correctly
- // in which case we need to lookup the test
- const test = getTestFromHookOrFindTest(hook)
+const hookFailed = function (hook, err, hookName, getTestById, getTest) {
+ // finds the test by returning the first test from
+ // the parent or looping through the suites until
+ // it finds the first test
+ const test = getTest() || getTestFromHook(hook, hook.parent, getTestById)
test.err = err
test.state = 'failed'
@@ -664,11 +639,11 @@ const _runnerListeners = function (_runner, Cypress, _emissions, getTestById, ge
hook.hookName = getHookName(hook)
}
- // mocha incorrectly sets currentTest on before/after all's.
+ // mocha incorrectly sets currentTest on before all's.
// if there is a nested suite with a before, then
// currentTest will refer to the previous test run
// and not our current
- if ((hook.hookName === 'before all' || hook.hookName === 'after all') && hook.ctx.currentTest) {
+ if ((hook.hookName === 'before all') && hook.ctx.currentTest) {
delete hook.ctx.currentTest
}
@@ -676,14 +651,7 @@ const _runnerListeners = function (_runner, Cypress, _emissions, getTestById, ge
// hooks do not have their own id, their
// commands need to grouped with the test
// and we can only associate them by this id
- const test = getTestFromHookOrFindTest(hook)
-
- if (!test) {
- // we couldn't find a test to run with this hook
- // probably because the entire suite has already completed
- // so return early and tell onRunnableRun to skip the test
- return
- }
+ const test = getTest() || getTestFromHook(hook, hook.parent, getTestById)
hook.id = test.id
hook.ctx.currentTest = test
@@ -805,7 +773,7 @@ const _runnerListeners = function (_runner, Cypress, _emissions, getTestById, ge
// if a hook fails (such as a before) then the test will never
// get run and we'll need to make sure we set the test so that
// the TEST_AFTER_RUN_EVENT fires correctly
- return hookFailed(runnable, runnable.err, hookName)
+ return hookFailed(runnable, runnable.err, hookName, getTestById, getTest)
}
})
}
@@ -975,22 +943,32 @@ const create = function (specWindow, mocha, Cypress, cy) {
},
onRunnableRun (runnableRun, runnable, args) {
- // extract out the next(fn) which mocha uses to
- // move to the next runnable - this will be our async seam
- const _next = args[0]
+ let lifecycleStart; let test
- const test = getTestFromRunnable(runnable)
+ if (!runnable.id) {
+ if (!_stopped) {
+ throw new Error('runnable must have an id', runnable.id)
+ }
- // if there's no test, this is likely a rouge before/after hook
- // that should not have run, so skip this runnable
- if (!test) {
- return _next()
+ return
+ }
+
+ switch (runnable.type) {
+ case 'hook':
+ test = getTest() || getTestFromHook(runnable, runnable.parent, getTestById)
+ break
+
+ case 'test':
+ test = runnable
+ break
+
+ default:
+ break
}
// closure for calculating the actual
// runtime of a runnables fn exection duration
// and also the run of the runnable:after:run:async event
- let lifecycleStart
let wallClockStartedAt = null
let wallClockEnd = null
let fnDurationStart = null
@@ -1023,6 +1001,10 @@ const create = function (specWindow, mocha, Cypress, cy) {
fire(TEST_BEFORE_RUN_EVENT, test, Cypress)
}
+ // extract out the next(fn) which mocha uses to
+ // move to the next runnable - this will be our async seam
+ const _next = args[0]
+
const next = function (err) {
// now set the duration of the after runnable run async event
afterFnDurationEnd = (wallClockEnd = new Date())
From d82f8b49271136cad16596f9a536e7b7985aa819 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 26 May 2020 16:46:13 -0400
Subject: [PATCH 47/86] make cy obj a class instance
---
packages/driver/src/cypress/cy.js | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/packages/driver/src/cypress/cy.js b/packages/driver/src/cypress/cy.js
index cb78af0b0d27..272045e10d5b 100644
--- a/packages/driver/src/cypress/cy.js
+++ b/packages/driver/src/cypress/cy.js
@@ -5,7 +5,6 @@ const Promise = require('bluebird')
const $dom = require('../dom')
const $utils = require('./utils')
-const $selection = require('../dom/selection')
const $errUtils = require('./error_utils')
const $stackUtils = require('./stack_utils')
const $Chai = require('../cy/chai')
@@ -26,6 +25,7 @@ const $Timers = require('../cy/timers')
const $Timeouts = require('../cy/timeouts')
const $Retries = require('../cy/retries')
const $Stability = require('../cy/stability')
+const $selection = require('../dom/selection')
const $Snapshots = require('../cy/snapshots')
const $CommandQueue = require('./command_queue')
const $VideoRecorder = require('../cy/video-recorder')
@@ -103,8 +103,12 @@ const setTopOnError = function (cy) {
})
}
+// NOTE: this makes the cy object an instance
+// TODO: refactor the 'create' method below into this class
+class $Cy {}
+
const create = function (specWindow, Cypress, Cookies, state, config, log) {
- let cy = {}
+ let cy = new $Cy()
let stopped = false
const commandFns = {}
From 90ba8ba9ed821574d427e38844969a46b6096563 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 26 May 2020 16:46:31 -0400
Subject: [PATCH 48/86] cleanup/unmerge before/after fixes PR...
---
.../integration/cypress/runner_spec.js | 19 --
packages/runner/src/lib/logger.js | 2 +-
.../3_runnable_execution_spec.ts.js | 162 ------------------
.../test/e2e/3_runnable_execution_spec.ts | 35 ----
.../projects/hooks-after-rerun/cypress.json | 1 -
.../beforehook-and-test-navigation.js | 25 ---
.../integration/runnable-run-count.spec.js | 129 --------------
.../cypress/plugins/index.js | 19 --
.../cypress/support/index.js | 3 -
9 files changed, 1 insertion(+), 394 deletions(-)
delete mode 100644 packages/server/__snapshots__/3_runnable_execution_spec.ts.js
delete mode 100644 packages/server/test/e2e/3_runnable_execution_spec.ts
delete mode 100644 packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress.json
delete mode 100644 packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/beforehook-and-test-navigation.js
delete mode 100644 packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/runnable-run-count.spec.js
delete mode 100644 packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
delete mode 100644 packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js
diff --git a/packages/driver/test/cypress/integration/cypress/runner_spec.js b/packages/driver/test/cypress/integration/cypress/runner_spec.js
index 32e4dd16024d..e08df29ae8ad 100644
--- a/packages/driver/test/cypress/integration/cypress/runner_spec.js
+++ b/packages/driver/test/cypress/integration/cypress/runner_spec.js
@@ -1,10 +1,6 @@
-const { _ } = Cypress
-
const pending = []
-const testAfterRunEvents = []
Cypress.on('test:after:run', (test) => {
- testAfterRunEvents.push(test)
if (test.state === 'pending') {
return pending.push(test)
}
@@ -45,18 +41,3 @@ describe('async timeouts', () => {
cy.then(() => done())
})
})
-
-// NOTE: this test must remain the last test in the spec
-// so we can test the root after hook
-describe('fires test:after:run after root after hook', () => {
- it('test 1', () => {
- })
-
- it('test 2', () => {
- })
-})
-
-// https://github.com/cypress-io/cypress/issues/2296
-after(() => {
- expect(_.last(testAfterRunEvents).title, 'test:after:run for test 2 should not have fired yet').eq('test 1')
-})
diff --git a/packages/runner/src/lib/logger.js b/packages/runner/src/lib/logger.js
index 51a02cc82fc9..a52ee1cacc13 100644
--- a/packages/runner/src/lib/logger.js
+++ b/packages/runner/src/lib/logger.js
@@ -12,7 +12,7 @@ export default {
},
clearLog () {
- // if (console.clear) console.clear()
+ if (console.clear) console.clear()
},
logFormatted (consoleProps) {
diff --git a/packages/server/__snapshots__/3_runnable_execution_spec.ts.js b/packages/server/__snapshots__/3_runnable_execution_spec.ts.js
deleted file mode 100644
index 06dc92d31c09..000000000000
--- a/packages/server/__snapshots__/3_runnable_execution_spec.ts.js
+++ /dev/null
@@ -1,162 +0,0 @@
-exports['e2e runnable execution / cannot navigate in before hook and test'] = `
-
-====================================================================================================
-
- (Run Starting)
-
- ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ Cypress: 1.2.3 │
- │ Browser: FooBrowser 88 │
- │ Specs: 1 found (beforehook-and-test-navigation.js) │
- │ Searched: cypress/integration/beforehook-and-test-navigation.js │
- └────────────────────────────────────────────────────────────────────────────────────────────────┘
-
-
-────────────────────────────────────────────────────────────────────────────────────────────────────
-
- Running: beforehook-and-test-navigation.js (1 of 1)
-
-
- initial domain change
- ✓ test
-
- suite
- ✓ test
- 1) causes domain navigation
-
-
- 2 passing
- 1 failing
-
- 1) suite
- causes domain navigation:
- CypressError: \`cy.visit()\` failed because you are attempting to visit a URL that is of a different origin.
-
-The new URL is considered a different origin because the following parts of the URL are different:
-
- > port
-
-You may only \`cy.visit()\` same-origin URLs within a single test.
-
-The previous URL you visited was:
-
- > 'http://localhost:4545'
-
-You're attempting to visit this URL:
-
- > 'http://localhost:5656'
-
-You may need to restructure some of your test code to avoid this problem.
-
-https://on.cypress.io/cannot-visit-different-origin-domain
- [stack trace lines]
-
-
-
-
- (Results)
-
- ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ Tests: 3 │
- │ Passing: 2 │
- │ Failing: 1 │
- │ Pending: 0 │
- │ Skipped: 0 │
- │ Screenshots: 0 │
- │ Video: true │
- │ Duration: X seconds │
- │ Spec Ran: beforehook-and-test-navigation.js │
- └────────────────────────────────────────────────────────────────────────────────────────────────┘
-
-
- (Video)
-
- - Started processing: Compressing to 32 CRF
- - Finished processing: /XXX/XXX/XXX/cypress/videos/beforehook-and-test-navigation. (X second)
- js.mp4
-
-
-====================================================================================================
-
- (Run Finished)
-
-
- Spec Tests Passing Failing Pending Skipped
- ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ ✖ beforehook-and-test-navigation.js XX:XX 3 2 1 - - │
- └────────────────────────────────────────────────────────────────────────────────────────────────┘
- ✖ 1 of 1 failed (100%) XX:XX 3 2 1 - -
-
-
-`
-
-exports['e2e runnable execution / runnables run correct number of times with navigation'] = `
-
-====================================================================================================
-
- (Run Starting)
-
- ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ Cypress: 1.2.3 │
- │ Browser: FooBrowser 88 │
- │ Specs: 1 found (runnable-run-count.spec.js) │
- │ Searched: cypress/integration/runnable-run-count.spec.js │
- └────────────────────────────────────────────────────────────────────────────────────────────────┘
-
-
-────────────────────────────────────────────────────────────────────────────────────────────────────
-
- Running: runnable-run-count.spec.js (1 of 1)
-
-
- suite 1.0
- ✓ test 1.0.1
- ✓ test 1.0.2
- ✓ test 1.0.3
-
- suite 1.1
- ✓ test 1.1.1
- ✓ test 1.1.2
-
- suite 1.2
- ✓ test 1.2.1
- ✓ test 1.2.2
-
-
- 7 passing
-
-
- (Results)
-
- ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ Tests: 7 │
- │ Passing: 7 │
- │ Failing: 0 │
- │ Pending: 0 │
- │ Skipped: 0 │
- │ Screenshots: 0 │
- │ Video: true │
- │ Duration: X seconds │
- │ Spec Ran: runnable-run-count.spec.js │
- └────────────────────────────────────────────────────────────────────────────────────────────────┘
-
-
- (Video)
-
- - Started processing: Compressing to 32 CRF
- - Finished processing: /XXX/XXX/XXX/cypress/videos/runnable-run-count.spec.js.mp4 (X second)
-
-
-====================================================================================================
-
- (Run Finished)
-
-
- Spec Tests Passing Failing Pending Skipped
- ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
- │ ✔ runnable-run-count.spec.js XX:XX 7 7 - - - │
- └────────────────────────────────────────────────────────────────────────────────────────────────┘
- ✔ All specs passed! XX:XX 7 7 - - -
-
-
-`
diff --git a/packages/server/test/e2e/3_runnable_execution_spec.ts b/packages/server/test/e2e/3_runnable_execution_spec.ts
deleted file mode 100644
index 0b6aeaf19628..000000000000
--- a/packages/server/test/e2e/3_runnable_execution_spec.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-const e2e = require('../support/helpers/e2e')
-const Fixtures = require('../support/helpers/fixtures')
-
-describe('e2e runnable execution', () => {
- e2e.setup({
- servers: [{
- port: 3434,
- static: true,
- },
- {
- port: 4545,
- static: true,
- },
- {
- port: 5656,
- static: true,
- }],
- })
-
- // navigation in before and in test body doesn't cause infinite loop
- // but throws correct error
- // https://github.com/cypress-io/cypress/issues/1987
- e2e.it('cannot navigate in before hook and test', {
- project: Fixtures.projectPath('hooks-after-rerun'),
- spec: 'beforehook-and-test-navigation.js',
- snapshot: true,
- expectedExitCode: 1,
- })
-
- e2e.it('runnables run correct number of times with navigation', {
- project: Fixtures.projectPath('hooks-after-rerun'),
- spec: 'runnable-run-count.spec.js',
- snapshot: true,
- })
-})
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress.json b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress.json
deleted file mode 100644
index 0967ef424bce..000000000000
--- a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/beforehook-and-test-navigation.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/beforehook-and-test-navigation.js
deleted file mode 100644
index e1b1b31523a6..000000000000
--- a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/beforehook-and-test-navigation.js
+++ /dev/null
@@ -1,25 +0,0 @@
-// should fail since before hooks are rerun on domain change
-
-const urls = [
- 'http://localhost:3434',
- 'http://localhost:4545',
- 'http://localhost:5656',
-]
-
-describe('initial domain change', () => {
- it('test', () => {
- cy.visit(urls[0])
- })
-})
-
-describe('suite', () => {
- before(() => {
- cy.visit(urls[1])
- })
-
- it('test', () => {})
-
- it('causes domain navigation', () => {
- cy.visit(urls[2])
- })
-})
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/runnable-run-count.spec.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/runnable-run-count.spec.js
deleted file mode 100644
index c2c38c2ac6c3..000000000000
--- a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/integration/runnable-run-count.spec.js
+++ /dev/null
@@ -1,129 +0,0 @@
-const urls = [
- 'http://localhost:3434',
- 'http://localhost:4545',
- 'http://localhost:5656',
-]
-
-function incrState (key) {
- // console.log(key)
- cy.log(key)
- cy.task('incrState', key)
-}
-
-/**
- * BeforeEach should be rerun again after domain switch
- * Before will run again after domain switch on the first test to run of a suite
- * After hook will always run on last test of a suite
- */
-
-// visit in sibling tests
-describe('suite 1.0', () => {
- let local1 = null
-
- before(() => {
- local1 = true
- incrState('b1.0.1')
- })
-
- it('test 1.0.1', () => {
- incrState('t1.0.1')
- cy.visit(urls[0])
- .then(() => {
- expect(local1).eq(true)
- })
- })
-
- it('test 1.0.2', () => {
- expect(local1).eq(true)
- incrState('t1.0.2')
- })
-
- it('test 1.0.3', () => {
- incrState('t1.0.3')
-
- cy.visit(urls[1])
- .then(() => {
- expect(local1).eq(true)
- })
- })
-
- after(() => {
- incrState('a1.0.1')
- })
-})
-
-// visit in before hook
-describe('suite 1.1', () => {
- before(() => {
- incrState('b1.1.1')
- cy.visit(urls[0])
- })
-
- before(() => {
- incrState('b1.1.2')
- })
-
- it('test 1.1.1', () => {
- incrState('t1.1.1')
- })
-
- it('test 1.1.2', () => {
- incrState('t1.1.2')
- })
-})
-
-// visit in beforeEach hook
-describe('suite 1.2', () => {
- before(() => {
- incrState('b1.2.1')
- })
-
- beforeEach(() => {
- incrState('b1.2.2')
- cy.visit(urls[1])
- })
-
- beforeEach(() => {
- incrState('b1.2.3')
- })
-
- it('test 1.2.1', () => {
- incrState('t1.2.1')
- })
-
- it('test 1.2.2', () => {
- incrState('t1.2.2')
- })
-
- after(() => {
- incrState('a1.2.1')
- })
-})
-
-after(() => {
- cy.task('getState').then((state) => {
- expect(state).deep.eq({
- // visit in sibling tests
- 'b1.0.1': 3,
- 't1.0.1': 2,
- 't1.0.2': 1,
- 't1.0.3': 2,
- 'a1.0.1': 1,
-
- // before visit
- 'b1.1.1': 2,
- 'b1.1.2': 1,
- 't1.1.1': 1,
- 't1.1.2': 1,
-
- // beforeEach visit
- 'b1.2.1': 2,
- 'b1.2.2': 3,
- 'b1.2.3': 2,
- 't1.2.1': 1,
- 't1.2.2': 1,
- 'a1.2.1': 1,
-
- })
- })
-})
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
deleted file mode 100644
index b2bda41e5d3c..000000000000
--- a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/plugins/index.js
+++ /dev/null
@@ -1,19 +0,0 @@
-///
-
-const state = {}
-
-/**
- * @type {Cypress.PluginConfig}
- */
-module.exports = (on) => {
- on('task', {
- incrState (arg) {
- state[arg] = state[arg] + 1 || 1
-
- return null
- },
- getState () {
- return state
- },
- })
-}
diff --git a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js b/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js
deleted file mode 100644
index 69378f3ea16b..000000000000
--- a/packages/server/test/support/fixtures/projects/hooks-after-rerun/cypress/support/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-Cypress.Screenshot.defaults({
- screenshotOnRunFailure: false,
-})
From 6500edc3f4e61656d32ab128e7ff7fafe65b187f Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 26 May 2020 17:03:18 -0400
Subject: [PATCH 49/86] more cleanup
---
packages/server/test/unit/reporter.spec.js | 1 +
packages/ui-components/package.json | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/server/test/unit/reporter.spec.js b/packages/server/test/unit/reporter.spec.js
index 6eff5baac8b2..3437e5be8197 100644
--- a/packages/server/test/unit/reporter.spec.js
+++ b/packages/server/test/unit/reporter.spec.js
@@ -25,6 +25,7 @@ const getSnapshot = (snapshotName) => {
let stdoutStub
+// TODO: refactor into utility, remove module.exports
function createReporter ({ setRunnables, mocha }) {
stdoutStub = stdout.capture()
diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json
index cbbafe360c19..e3d51cdd27cb 100644
--- a/packages/ui-components/package.json
+++ b/packages/ui-components/package.json
@@ -19,7 +19,6 @@
"@babel/plugin-proposal-decorators": "7.8.3",
"@babel/preset-env": "7.9.0",
"@babel/preset-react": "7.9.4",
- "@cypress/webpack-preprocessor": "4.1.5",
"@fortawesome/fontawesome-free": "5.12.1",
"@reach/visually-hidden": "0.6.2",
"babel-loader": "8.1.0",
From 156d51bc825c08058633b53e7f17dff1ddc5c314 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Thu, 28 May 2020 16:44:06 -0400
Subject: [PATCH 50/86] add comment
---
packages/driver/test/support/server.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/driver/test/support/server.js b/packages/driver/test/support/server.js
index 4a895d11bf78..657f88aa7ffb 100644
--- a/packages/driver/test/support/server.js
+++ b/packages/driver/test/support/server.js
@@ -36,6 +36,7 @@ ports.forEach((port) => {
})
})
+ // allows us to serve the testrunner into an iframe for testing
app.use('/isolated-runner', express.static(path.join(__dirname, '../../../runner/dist')))
app.get('/node_modules/*', (req, res) => {
From e57b5769a6c8b183f66e9e91d6b499966e2dba99 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 1 Jun 2020 14:10:41 -0400
Subject: [PATCH 51/86] fix runner.spec
---
.../runner/test/cypress/plugins/snapshot/command/utils.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/runner/test/cypress/plugins/snapshot/command/utils.js b/packages/runner/test/cypress/plugins/snapshot/command/utils.js
index 856236d26c13..5b8e8efbbaf7 100644
--- a/packages/runner/test/cypress/plugins/snapshot/command/utils.js
+++ b/packages/runner/test/cypress/plugins/snapshot/command/utils.js
@@ -123,11 +123,11 @@ const fmt = {
},
keyRemoved (key, variable) {
- return fmtOpts.wrap('removed', `- ${key}: ${printVar(variable)}`) + fmtOpts.newLineChar
+ return fmt.wrap('removed', `- ${key}: ${printVar(variable)}`) + fmtOpts.newLineChar
},
keyAdded (key, variable) {
- return fmtOpts.wrap('added', `+ ${key}: ${printVar(variable)}`) + fmtOpts.newLineChar
+ return fmt.wrap('added', `+ ${key}: ${printVar(variable)}`) + fmtOpts.newLineChar
},
}
From ece048180834d847e944dfe35d10a3a237cb6d13 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Thu, 4 Jun 2020 12:41:02 -0400
Subject: [PATCH 52/86] cleanup snapshot utility more, cleanup reporter.spec
---
packages/runner/README.md | 2 +-
.../__snapshots__/runner.spec.js.snapshot.js | 144 +++++++++---------
packages/runner/test/cypress/.eslintrc.json | 3 -
packages/runner/test/cypress/plugins/index.js | 2 +-
.../plugins/snapshot/{command => }/index.d.ts | 0
.../{command/index.js => snapshotCommand.js} | 29 ++--
.../snapshot/{index.js => snapshotPlugin.js} | 0
.../{command/utils.js => snapshotUtils.js} | 0
.../runner/test/cypress/support/helpers.js | 14 +-
.../reporter.spec.js.snapshot.js | 24 +--
packages/server/test/matchDeep.js | 70 ++-------
packages/server/test/support/helpers/utils.js | 7 +-
packages/server/test/unit/reporter.spec.js | 64 ++++----
yarn.lock | 12 --
14 files changed, 162 insertions(+), 209 deletions(-)
rename packages/runner/test/cypress/plugins/snapshot/{command => }/index.d.ts (100%)
rename packages/runner/test/cypress/plugins/snapshot/{command/index.js => snapshotCommand.js} (95%)
rename packages/runner/test/cypress/plugins/snapshot/{index.js => snapshotPlugin.js} (100%)
rename packages/runner/test/cypress/plugins/snapshot/{command/utils.js => snapshotUtils.js} (100%)
diff --git a/packages/runner/README.md b/packages/runner/README.md
index 348bbae0fe5f..c8ba1fda64a3 100644
--- a/packages/runner/README.md
+++ b/packages/runner/README.md
@@ -45,7 +45,7 @@ yarn lerna run build-prod --scope @packages/runner --stream
yarn lerna run test --scope @packages/runner --stream
```
-### Cypress
+### Cypress Tests
You'll need to start the server from the [`driver`](../driver) package in order to get Cypress running.
diff --git a/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js b/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js
index 6f7292334927..4e2b91c4a6a1 100644
--- a/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js
+++ b/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js
@@ -203,7 +203,8 @@ exports['FAIL_IN_AFTER.mocha'] = [
"body": "[body]",
"type": "hook",
"duration": "match.number",
- "file": null
+ "file": null,
+ "originalTitle": "\"after all\" hook"
},
{
"message": "[error message]",
@@ -215,6 +216,17 @@ exports['FAIL_IN_AFTER.mocha'] = [
"showDiff": false
}
],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
[
"mocha",
"test:after:run",
@@ -248,17 +260,6 @@ exports['FAIL_IN_AFTER.mocha'] = [
"file": null
}
],
- [
- "mocha",
- "suite end",
- {
- "id": "r2",
- "title": "suite 1",
- "root": false,
- "type": "suite",
- "file": null
- }
- ],
[
"mocha",
"suite end",
@@ -446,7 +447,8 @@ exports['FAIL_IN_AFTEREACH.mocha'] = [
"body": "[body]",
"type": "hook",
"duration": "match.number",
- "file": null
+ "file": null,
+ "originalTitle": "\"after each\" hook"
},
{
"message": "[error message]",
@@ -458,6 +460,17 @@ exports['FAIL_IN_AFTEREACH.mocha'] = [
"showDiff": false
}
],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
[
"mocha",
"test:after:run",
@@ -491,17 +504,6 @@ exports['FAIL_IN_AFTEREACH.mocha'] = [
"file": null
}
],
- [
- "mocha",
- "suite end",
- {
- "id": "r2",
- "title": "suite 1",
- "root": false,
- "type": "suite",
- "file": null
- }
- ],
[
"mocha",
"suite end",
@@ -612,7 +614,8 @@ exports['FAIL_IN_BEFORE.mocha'] = [
"body": "[body]",
"type": "hook",
"duration": "match.number",
- "file": null
+ "file": null,
+ "originalTitle": "\"before all\" hook"
},
{
"message": "[error message]",
@@ -624,6 +627,17 @@ exports['FAIL_IN_BEFORE.mocha'] = [
"showDiff": false
}
],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
[
"mocha",
"test:after:run",
@@ -653,17 +667,6 @@ exports['FAIL_IN_BEFORE.mocha'] = [
"file": null
}
],
- [
- "mocha",
- "suite end",
- {
- "id": "r2",
- "title": "suite 1",
- "root": false,
- "type": "suite",
- "file": null
- }
- ],
[
"mocha",
"suite end",
@@ -786,7 +789,8 @@ exports['FAIL_IN_BEFOREEACH.mocha'] = [
"body": "[body]",
"type": "hook",
"duration": "match.number",
- "file": null
+ "file": null,
+ "originalTitle": "\"before each\" hook"
},
{
"message": "[error message]",
@@ -798,6 +802,17 @@ exports['FAIL_IN_BEFOREEACH.mocha'] = [
"showDiff": false
}
],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
[
"mocha",
"test:after:run",
@@ -827,17 +842,6 @@ exports['FAIL_IN_BEFOREEACH.mocha'] = [
"file": null
}
],
- [
- "mocha",
- "suite end",
- {
- "id": "r2",
- "title": "suite 1",
- "root": false,
- "type": "suite",
- "file": null
- }
- ],
[
"mocha",
"suite end",
@@ -1788,6 +1792,17 @@ exports['SIMPLE_SINGLE_TEST.mocha'] = [
"file": null
}
],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
[
"mocha",
"test:after:run",
@@ -1811,17 +1826,6 @@ exports['SIMPLE_SINGLE_TEST.mocha'] = [
"file": null
}
],
- [
- "mocha",
- "suite end",
- {
- "id": "r2",
- "title": "suite 1",
- "root": false,
- "type": "suite",
- "file": null
- }
- ],
[
"mocha",
"suite end",
@@ -2516,6 +2520,17 @@ exports['THREE_TESTS_WITH_HOOKS.mocha'] = [
"file": null
}
],
+ [
+ "mocha",
+ "suite end",
+ {
+ "id": "r2",
+ "title": "suite 1",
+ "root": false,
+ "type": "suite",
+ "file": null
+ }
+ ],
[
"mocha",
"test:after:run",
@@ -2560,17 +2575,6 @@ exports['THREE_TESTS_WITH_HOOKS.mocha'] = [
"file": null
}
],
- [
- "mocha",
- "suite end",
- {
- "id": "r2",
- "title": "suite 1",
- "root": false,
- "type": "suite",
- "file": null
- }
- ],
[
"mocha",
"suite end",
diff --git a/packages/runner/test/cypress/.eslintrc.json b/packages/runner/test/cypress/.eslintrc.json
index 1d43eafa7f5e..16184dd4a3e4 100644
--- a/packages/runner/test/cypress/.eslintrc.json
+++ b/packages/runner/test/cypress/.eslintrc.json
@@ -7,8 +7,5 @@
],
"env": {
"cypress/globals": true
- },
- "rules": {
- "mocha/no-global-tests": "off"
}
}
diff --git a/packages/runner/test/cypress/plugins/index.js b/packages/runner/test/cypress/plugins/index.js
index 7610b54cda80..ce30426fe65d 100644
--- a/packages/runner/test/cypress/plugins/index.js
+++ b/packages/runner/test/cypress/plugins/index.js
@@ -1,4 +1,4 @@
-const { getSnapshot, saveSnapshot } = require('./snapshot')
+const { getSnapshot, saveSnapshot } = require('./snapshot/snapshotPlugin')
/**
* @type {Cypress.PluginConfig}
diff --git a/packages/runner/test/cypress/plugins/snapshot/command/index.d.ts b/packages/runner/test/cypress/plugins/snapshot/index.d.ts
similarity index 100%
rename from packages/runner/test/cypress/plugins/snapshot/command/index.d.ts
rename to packages/runner/test/cypress/plugins/snapshot/index.d.ts
diff --git a/packages/runner/test/cypress/plugins/snapshot/command/index.js b/packages/runner/test/cypress/plugins/snapshot/snapshotCommand.js
similarity index 95%
rename from packages/runner/test/cypress/plugins/snapshot/command/index.js
rename to packages/runner/test/cypress/plugins/snapshot/snapshotCommand.js
index 6cafbfced62b..fde1b1b5c52d 100644
--- a/packages/runner/test/cypress/plugins/snapshot/command/index.js
+++ b/packages/runner/test/cypress/plugins/snapshot/snapshotCommand.js
@@ -5,7 +5,7 @@ const Debug = require('debug')
const chalk = require('chalk')
const stripAnsi = require('strip-ansi')
const { stripIndent } = require('common-tags')
-const { printVar, stringifyShort, isObject, addPluginButton, fmt, typeColors } = require('./utils')
+const { printVar, stringifyShort, isObject, addPluginButton, fmt, typeColors } = require('./snapshotUtils')
const debug = Debug('plugin:snapshot')
@@ -194,21 +194,31 @@ const matchDeep = function (matchers, exp, optsArg) {
return diffStr
}
-function parseMatcher (obj, match) {
+const parseMatcherFromString = (matcher) => {
+ const regex = /match\.(.*)/
+
+ if (_.isString(matcher)) {
+ const parsed = regex.exec(matcher)
+
+ if (parsed) {
+ return parsed[1]
+ }
+ }
+}
+
+function parseMatcherFromObj (obj, match) {
if (match.isMatcher(obj)) {
return obj
}
- let parseObj = (_.isString(obj) && obj) || (obj && obj.toJSON && obj.toJSON())
+ const objStr = (_.isString(obj) && obj) || (obj && obj.toJSON && obj.toJSON())
- if (parseObj) {
- const parsed = /match\.(.*)/.exec(parseObj)
+ if (objStr) {
+ const parsed = parseMatcherFromString(objStr)
if (parsed) {
- return match[parsed[1]]
+ return match[parsed]
}
-
- return obj
}
return obj
@@ -298,7 +308,7 @@ const withMatchers = (matchers, match, expectedOnly = false) => {
act = _.clone(act)
}
- exp = parseMatcher(exp, match)
+ exp = parseMatcherFromObj(exp, match)
if (match.isMatcher(exp)) {
if (testValue(exp, act)) {
act = matcherStringToObj(exp.message).toJSON()
@@ -424,4 +434,5 @@ module.exports = {
registerInCypress,
matchDeep,
stringifyShort,
+ parseMatcherFromString,
}
diff --git a/packages/runner/test/cypress/plugins/snapshot/index.js b/packages/runner/test/cypress/plugins/snapshot/snapshotPlugin.js
similarity index 100%
rename from packages/runner/test/cypress/plugins/snapshot/index.js
rename to packages/runner/test/cypress/plugins/snapshot/snapshotPlugin.js
diff --git a/packages/runner/test/cypress/plugins/snapshot/command/utils.js b/packages/runner/test/cypress/plugins/snapshot/snapshotUtils.js
similarity index 100%
rename from packages/runner/test/cypress/plugins/snapshot/command/utils.js
rename to packages/runner/test/cypress/plugins/snapshot/snapshotUtils.js
diff --git a/packages/runner/test/cypress/support/helpers.js b/packages/runner/test/cypress/support/helpers.js
index 92e17ede3456..8cc8dcaa65cc 100644
--- a/packages/runner/test/cypress/support/helpers.js
+++ b/packages/runner/test/cypress/support/helpers.js
@@ -2,7 +2,7 @@
const { _ } = Cypress
const debug = require('debug')('spec')
-const snapshotPlugin = require('../plugins/snapshot/command')
+const snapshotPlugin = require('../plugins/snapshot/snapshotCommand')
/**
* @type {sinon.SinonMatch}
@@ -443,14 +443,14 @@ const getRunState = (Cypress) => {
const s = {
currentId,
- tests: Cypress.getTestsState(),
- startTime: Cypress.getStartTime(),
- emissions: Cypress.getEmissions(),
+ tests: Cypress.runner.getTestsState(),
+ startTime: Cypress.runner.getStartTime(),
+ emissions: Cypress.runner.getEmissions(),
}
- s.passed = Cypress.countByTestState(s.tests, 'passed')
- s.failed = Cypress.countByTestState(s.tests, 'failed')
- s.pending = Cypress.countByTestState(s.tests, 'pending')
+ s.passed = Cypress.runner.countByTestState(s.tests, 'passed')
+ s.failed = Cypress.runner.countByTestState(s.tests, 'failed')
+ s.pending = Cypress.runner.countByTestState(s.tests, 'pending')
s.numLogs = Cypress.Log.countLogsByTests(s.tests)
return _.cloneDeep(s)
diff --git a/packages/server/__snapshots__/reporter.spec.js.snapshot.js b/packages/server/__snapshots__/reporter.spec.js.snapshot.js
index 1a75a3871a59..c9b184bbb449 100644
--- a/packages/server/__snapshots__/reporter.spec.js.snapshot.js
+++ b/packages/server/__snapshots__/reporter.spec.js.snapshot.js
@@ -98,14 +98,14 @@ exports['fail in [afterEach] runner emit'] = [
"{Object 60}",
"{Object 9}"
],
- [
- "test:after:run",
- "{Test}"
- ],
[
"suite end",
"{Suite}"
],
+ [
+ "test:after:run",
+ "{Test}"
+ ],
[
"suite end",
"{Suite}"
@@ -225,14 +225,14 @@ exports['fail in [beforeEach] runner emit'] = [
"{Object 57}",
"{Object 9}"
],
- [
- "test:after:run",
- "{Test}"
- ],
[
"suite end",
"{Suite}"
],
+ [
+ "test:after:run",
+ "{Test}"
+ ],
[
"suite end",
"{Suite}"
@@ -338,14 +338,14 @@ exports['simple_single_test runner emit'] = [
"test end",
"{Test}"
],
- [
- "test:after:run",
- "{Test}"
- ],
[
"suite end",
"{Suite}"
],
+ [
+ "test:after:run",
+ "{Test}"
+ ],
[
"suite end",
"{Suite}"
diff --git a/packages/server/test/matchDeep.js b/packages/server/test/matchDeep.js
index b031016b24a8..73ef3cf8a695 100644
--- a/packages/server/test/matchDeep.js
+++ b/packages/server/test/matchDeep.js
@@ -1,10 +1,8 @@
-const { matchDeep } = require('../../runner/test/cypress/plugins/snapshot/command')
-const { getSnapshot, saveSnapshot } = require('../../runner/test/cypress/plugins/snapshot')
-const chai = require('chai')
const _ = require('lodash')
+const chai = require('chai')
const sinon = require('sinon')
-// const Debug = require('debug')
-// const debug = Debug('plugin:snapshot')
+const { matchDeep, stringifyShort, parseMatcherFromString } = require('../../runner/test/cypress/plugins/snapshot/snapshotCommand')
+const { getSnapshot, saveSnapshot } = require('../../runner/test/cypress/plugins/snapshot/snapshotPlugin')
/** @type {Mocha.ITest} */
let currentTest
@@ -19,6 +17,7 @@ const registerInMocha = () => {
}
})
+ // chai assertion 'matchSnapshot
const matchSnapshot = function (m, snapshotName) {
const ctx = this
const testName = currentTest.fullTitle()
@@ -53,6 +52,7 @@ const registerInMocha = () => {
}
}
+ // chai assertion 'matchDeep'
const matchDeepMocha = function (...args) {
let ret
let act
@@ -79,65 +79,26 @@ const registerInMocha = () => {
chai.Assertion.addMethod('matchSnapshot', matchSnapshot)
chai.Assertion.addMethod('matchDeep', matchDeepMocha)
+ // print debug messages if `expect(...).debug.to.matchDeep(..)`
chai.Assertion.addProperty('debug', function () {
this.__flags.debug = true
- // debug(this)
})
}
-const stringifyShort = (obj) => {
- const constructorName = _.get(obj, 'constructor.name')
-
- if (constructorName && !_.includes(['Object'], constructorName)) {
- return `{${constructorName}}`
- }
-
- if (_.isArray(obj)) {
- return `[Array ${obj.length}]`
- }
-
- if (_.isObject(obj)) {
- return `{Object ${Object.keys(obj).length}}`
- }
-
- return obj
-}
-const parseMatcher = (matcher) => {
- const regex = /match\.(.*)/
+const parseSnapshot = (s) => {
+ return _.cloneDeepWith(s, (value) => {
+ const matcherType = parseMatcherFromString(value)
- if (_.isString(matcher)) {
- const parsed = regex.exec(matcher)
+ if (matcherType) {
+ const replacement = getFake(matcherType)
- if (parsed) {
- return parsed[1]
+ return replacement
}
- }
+ })
}
-const parseSnapshot = (s) => {
- s = _.cloneDeep(s)
- const recurse = (_obj) => {
- _.each(_obj, (value, key) => {
- const matcherType = parseMatcher(value)
-
- if (matcherType) {
- const replacement = getFake(matcherType)
-
- _obj[key] = replacement
-
- return
- }
-
- if (_.isObjectLike(value)) {
- return recurse(value)
- }
- })
- }
-
- recurse(s)
-
- return s
-}
+// returns deterministic fake data based on stored snapshot matcher type
+// TODO: maybe make this data more interesting
const getFake = (matcherType) => {
if (matcherType === 'number') {
return 1
@@ -160,5 +121,4 @@ module.exports = {
registerInMocha,
stringifyShort,
parseSnapshot,
-
}
diff --git a/packages/server/test/support/helpers/utils.js b/packages/server/test/support/helpers/utils.js
index 88550dddcac7..2297acf3dcc6 100644
--- a/packages/server/test/support/helpers/utils.js
+++ b/packages/server/test/support/helpers/utils.js
@@ -1,9 +1,10 @@
/* eslint-disable prefer-rest-params */
-const _write = process.stdout.write
const _ = require('lodash')
-const stripAnsi = require('strip-ansi')
-const debug = require('debug')('utils')
const chalk = require('chalk')
+const debug = require('debug')('utils')
+const stripAnsi = require('strip-ansi')
+
+const _write = process.stdout.write
const spyStdout = (obj, props) => {
return spyOn(obj, props, () => stdout.capture(), (ret) => {
diff --git a/packages/server/test/unit/reporter.spec.js b/packages/server/test/unit/reporter.spec.js
index 3437e5be8197..d1d7f6cd2705 100644
--- a/packages/server/test/unit/reporter.spec.js
+++ b/packages/server/test/unit/reporter.spec.js
@@ -1,48 +1,49 @@
// process.env.SNAPSHOT_UPDATE = 1
-require('../spec_helper.coffee')
-const Reporter = require('../../lib/reporter')
const _ = require('lodash')
const sinon = require('sinon')
const Debug = require('debug')
-const debug = Debug('spec:retries')
+
+const Reporter = require('../../lib/reporter')
const { spyOn, stdout } = require('../support/helpers/utils')
const { registerInMocha, parseSnapshot, stringifyShort } = require('../matchDeep')
+const events = require('../../../runner/test/__snapshots__/runner.spec.js.snapshot')
+const { EventSnapshots } = require('../../../runner/test/cypress/support/eventSnapshots')
+
+require('../spec_helper.coffee')
registerInMocha()
+const debug = Debug('spec:retries')
const { match } = sinon
-const events = require('../../../runner/test/__snapshots__/runner.spec.js.snapshot')
-const { EventSnapshots } = require('../../../runner/test/cypress/support/eventSnapshots')
-
-let currentReporter
-let currentStubs
+const runnerEmitCleanseMap = {
+ '^.*.1': stringifyShort,
+ parent: stringifyShort,
+}
+// TODO: maybe refactor into utility, remove module.exports
/** @param {typeof EventSnapshots.FAIL_IN_AFTER} snapshotName */
-const getSnapshot = (snapshotName) => {
- return _.mapValues(snapshotName, (v) => parseSnapshot(events[v]))
-}
+function createReporter (snapshotName) {
+ const getSnapshot = (snapshotName) => {
+ return _.mapValues(snapshotName, (v) => parseSnapshot(events[v]))
+ }
-let stdoutStub
+ const { setRunnables, mocha } = getSnapshot(snapshotName)
-// TODO: refactor into utility, remove module.exports
-function createReporter ({ setRunnables, mocha }) {
- stdoutStub = stdout.capture()
+ const stdoutStub = stdout.capture()
const reporter = Reporter()
- currentReporter = reporter
+ const currentReporter = reporter
const runnables = parseSnapshot(setRunnables)[0][1]
const mochaEvents = parseSnapshot(mocha)
- // const runnables = setRunnables[0][1]
-
reporter.setRunnables(runnables)
const stubs = {}
- currentStubs = stubs
+ const currentStubs = stubs
stubs.reporterEmit = spyOn(reporter, 'emit', debug.extend('reporter:emit'))
stubs.runnerEmit = spyOn(reporter.runner, 'emit', debug.extend('runner:emit'))
@@ -52,15 +53,6 @@ function createReporter ({ setRunnables, mocha }) {
})
stdout.restore()
-
- return { stubs, reporter }
-}
-
-module.exports = {
- createReporter,
-}
-
-describe('reporter retries', () => {
const snapshot = (name) => {
if (!name) throw new Error('snapshot name cannot be empty')
@@ -77,29 +69,29 @@ describe('reporter retries', () => {
.matchSnapshot({ '^': (v) => v.replace(/\(\d+ms\)/g, '') }, `${name} stdout`)
}
+ return { stubs, reporter, snapshot }
+}
+
+describe('reporter retries', () => {
afterEach(() => {
stdout.restore()
})
it('simple single test', () => {
- createReporter(getSnapshot(EventSnapshots.SIMPLE_SINGLE_TEST))
+ const { snapshot } = createReporter(EventSnapshots.SIMPLE_SINGLE_TEST)
+
snapshot('simple_single_test')
})
it('fail [afterEach]', () => {
- createReporter(getSnapshot(EventSnapshots.FAIL_IN_AFTEREACH))
+ const { snapshot } = createReporter(EventSnapshots.FAIL_IN_AFTEREACH)
snapshot('fail in [afterEach]')
})
it('fail [beforeEach]', () => {
- createReporter(getSnapshot(EventSnapshots.FAIL_IN_BEFOREEACH))
+ const { snapshot } = createReporter(EventSnapshots.FAIL_IN_BEFOREEACH)
snapshot('fail in [beforeEach]')
})
})
-
-const runnerEmitCleanseMap = {
- '^.*.1': stringifyShort,
- parent: stringifyShort,
-}
diff --git a/yarn.lock b/yarn.lock
index a5d43ee34748..38679362cb55 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1639,18 +1639,6 @@
"@babel/preset-env" "^7.0.0"
babel-loader "^8.0.2"
-"@cypress/webpack-preprocessor@4.1.5":
- version "4.1.5"
- resolved "https://registry.yarnpkg.com/@cypress/webpack-preprocessor/-/webpack-preprocessor-4.1.5.tgz#b47d515d2540af977ee8b69d7c4eed64e3027668"
- integrity sha512-B4miSaS3VCMVSlfuvbWCjytTywdnquRsF1tQ3quC7TGUzEXnQZ4+o8WUKibjMozrOomALkUdMxqOJ1ib5oFkKw==
- dependencies:
- bluebird "3.7.1"
- debug "4.1.1"
- optionalDependencies:
- "@babel/core" "^7.0.1"
- "@babel/preset-env" "^7.0.0"
- babel-loader "^8.0.2"
-
"@cypress/webpack-preprocessor@5.4.1":
version "5.4.1"
resolved "https://registry.yarnpkg.com/@cypress/webpack-preprocessor/-/webpack-preprocessor-5.4.1.tgz#eb58f6cd02932a95653c1a674cfd769da2409806"
From 230ffef5e5edb25e5ba4fba0e2cd1391ee85e1d8 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 8 Jun 2020 13:11:42 -0400
Subject: [PATCH 53/86] minor rename variable
---
packages/runner/test/cypress/support/helpers.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/packages/runner/test/cypress/support/helpers.js b/packages/runner/test/cypress/support/helpers.js
index 8cc8dcaa65cc..595083e5ed35 100644
--- a/packages/runner/test/cypress/support/helpers.js
+++ b/packages/runner/test/cypress/support/helpers.js
@@ -2,14 +2,14 @@
const { _ } = Cypress
const debug = require('debug')('spec')
-const snapshotPlugin = require('../plugins/snapshot/snapshotCommand')
+const snapshotCommand = require('../plugins/snapshot/snapshotCommand')
/**
* @type {sinon.SinonMatch}
*/
const match = Cypress.sinon.match
-const { stringifyShort } = snapshotPlugin
+const { stringifyShort } = snapshotCommand
const eventCleanseMap = {
snapshots: stringifyShort,
parent: stringifyShort,
@@ -75,7 +75,7 @@ function createCypress () {
expect(mochaStubs.args).to.matchSnapshot(mochaEventCleanseMap, name.mocha)
}
- snapshotPlugin.registerInCypress()
+ snapshotCommand.registerInCypress()
const backupCy = window.cy
const backupCypress = window.Cypress
From edd75b08ee341a180cd4a8c61947062a666b38a6 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 8 Jun 2020 16:43:57 -0400
Subject: [PATCH 54/86] fix specName in reporterHeader, spec_helper require
---
.../cypress/fixtures/isolated-runner.html | 31 ++++++++++---------
packages/reporter/src/main.tsx | 6 ++--
.../src/runnables/runnable-header.tsx | 25 +++++++++------
packages/reporter/src/runnables/runnables.tsx | 12 +++----
packages/runner/src/app/app.jsx | 9 ++++--
.../plugins/snapshot/snapshotCommand.js | 2 +-
.../cypress/plugins/snapshot/snapshotUtils.js | 2 +-
packages/server/test/unit/reporter.spec.js | 2 +-
8 files changed, 52 insertions(+), 37 deletions(-)
diff --git a/packages/driver/test/cypress/fixtures/isolated-runner.html b/packages/driver/test/cypress/fixtures/isolated-runner.html
index 2e4667eed586..20b9e67021ec 100644
--- a/packages/driver/test/cypress/fixtures/isolated-runner.html
+++ b/packages/driver/test/cypress/fixtures/isolated-runner.html
@@ -1,20 +1,23 @@
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/packages/reporter/src/main.tsx b/packages/reporter/src/main.tsx
index 7233fbc70c5d..3e655a04efad 100644
--- a/packages/reporter/src/main.tsx
+++ b/packages/reporter/src/main.tsx
@@ -28,7 +28,7 @@ export interface ReporterProps {
statsStore: StatsStore
events: Events
error?: Error
- specPath: string
+ spec: Cypress.Cypress['spec']
}
@observer
@@ -67,7 +67,7 @@ class Reporter extends Component {
error={this.props.error}
runnablesStore={this.props.runnablesStore}
scroller={this.props.scroller}
- specPath={this.props.specPath}
+ spec={this.props.spec}
/>
{
+ // @ts-ignore
render(} />, document.getElementById('app'))
}
}
diff --git a/packages/reporter/src/runnables/runnable-header.tsx b/packages/reporter/src/runnables/runnable-header.tsx
index 973d4edca477..a9d649436fb9 100644
--- a/packages/reporter/src/runnables/runnable-header.tsx
+++ b/packages/reporter/src/runnables/runnable-header.tsx
@@ -1,27 +1,34 @@
-import React, { Component } from 'react'
+import React, { Component, ReactElement } from 'react'
import FileOpener from '../opener/file-opener'
+const renderRunnableHeader = (children:ReactElement) => {children}
+
interface RunnableHeaderProps {
- specPath: string
+ spec: Cypress.Cypress['spec']
}
class RunnableHeader extends Component {
render () {
- const { specPath } = this.props
- const relativeSpecPath = window.Cypress?.spec.relative
+ const { spec } = this.props
+ const relativeSpecPath = spec.relative
+
+ if (relativeSpecPath === null) {
+ return renderRunnableHeader(
+ All Specs,
+ )
+ }
+
const fileDetails = {
- absoluteFile: specPath,
+ absoluteFile: spec.absolute!,
column: 0,
line: 0,
originalFile: relativeSpecPath,
relativeFile: relativeSpecPath,
}
- return (
-
- { relativeSpecPath === '__all' ? All Specs : }
-
+ return renderRunnableHeader(
+ ,
)
}
}
diff --git a/packages/reporter/src/runnables/runnables.tsx b/packages/reporter/src/runnables/runnables.tsx
index e9cfb157fb88..9d730ec62aef 100644
--- a/packages/reporter/src/runnables/runnables.tsx
+++ b/packages/reporter/src/runnables/runnables.tsx
@@ -10,7 +10,7 @@ import { RunnablesStore, RunnableArray } from './runnables-store'
import { Scroller } from '../lib/scroller'
import { AppState } from '../lib/app-state'
-const noTestsError = (specPath: string) => ({
+const noTestsError = (specPath: string | null) => ({
title: 'No tests found in your file:',
link: 'https://on.cypress.io/no-tests-found-in-your-file',
callout: specPath,
@@ -29,7 +29,7 @@ const RunnablesList = observer(({ runnables }: RunnablesListProps) => (
))
-function content ({ isReady, runnables }: RunnablesStore, specPath: string, error?: Error) {
+function content ({ isReady, runnables }: RunnablesStore, specPath: string | null, error?: Error) {
if (!isReady) return null
// show error if there are no tests, but only if there
@@ -44,7 +44,7 @@ function content ({ isReady, runnables }: RunnablesStore, specPath: string, erro
interface RunnablesProps {
error?: Error
runnablesStore: RunnablesStore
- specPath: string
+ spec: Cypress.Cypress['spec']
scroller: Scroller
appState?: AppState
}
@@ -52,12 +52,12 @@ interface RunnablesProps {
@observer
class Runnables extends Component {
render () {
- const { error, runnablesStore, specPath } = this.props
+ const { error, runnablesStore, spec } = this.props
return (
-
- {content(runnablesStore, specPath, error)}
+
+ {content(runnablesStore, spec.relative, error)}
)
}
diff --git a/packages/runner/src/app/app.jsx b/packages/runner/src/app/app.jsx
index d2630b18d0d1..5f5b6113293c 100644
--- a/packages/runner/src/app/app.jsx
+++ b/packages/runner/src/app/app.jsx
@@ -21,7 +21,10 @@ class App extends Component {
@observable isReporterResizing = false
render () {
- const specPath = this.props.util.absoluteSpecPath(this.props.config)
+ /**
+ * @type {Cypress.Cypress['spec']}
+ */
+ const spec = this.props.config.spec
return (
diff --git a/packages/runner/test/cypress/plugins/snapshot/snapshotCommand.js b/packages/runner/test/cypress/plugins/snapshot/snapshotCommand.js
index fde1b1b5c52d..de68f69b5228 100644
--- a/packages/runner/test/cypress/plugins/snapshot/snapshotCommand.js
+++ b/packages/runner/test/cypress/plugins/snapshot/snapshotCommand.js
@@ -55,7 +55,7 @@ const registerInCypress = () => {
const prev = Cypress.env('SNAPSHOT_UPDATE')
Cypress.env('SNAPSHOT_UPDATE', !prev)
- const btnIcon = this.children().first()
+ const btnIcon = $(this).children().first()
return btnIcon.text(Cypress.env('SNAPSHOT_UPDATE') ? 'snapshot\nupdate\non' : 'snapshot\nupdate\noff')
.css({ 'font-size': '10px', 'line-height': '0.9' })
diff --git a/packages/runner/test/cypress/plugins/snapshot/snapshotUtils.js b/packages/runner/test/cypress/plugins/snapshot/snapshotUtils.js
index 5b8e8efbbaf7..f0f875b9800c 100644
--- a/packages/runner/test/cypress/plugins/snapshot/snapshotUtils.js
+++ b/packages/runner/test/cypress/plugins/snapshot/snapshotUtils.js
@@ -68,7 +68,7 @@ function addPluginButton ($, name, faClass, fn) {
container.prepend(btn)
btn.on('click', fn)
- fn.apply(btn)
+ fn.apply(btn[0])
return btn
}
diff --git a/packages/server/test/unit/reporter.spec.js b/packages/server/test/unit/reporter.spec.js
index d1d7f6cd2705..0b3839c1a099 100644
--- a/packages/server/test/unit/reporter.spec.js
+++ b/packages/server/test/unit/reporter.spec.js
@@ -1,4 +1,5 @@
// process.env.SNAPSHOT_UPDATE = 1
+require('../spec_helper')
const _ = require('lodash')
const sinon = require('sinon')
const Debug = require('debug')
@@ -10,7 +11,6 @@ const { registerInMocha, parseSnapshot, stringifyShort } = require('../matchDeep
const events = require('../../../runner/test/__snapshots__/runner.spec.js.snapshot')
const { EventSnapshots } = require('../../../runner/test/cypress/support/eventSnapshots')
-require('../spec_helper.coffee')
registerInMocha()
const debug = Debug('spec:retries')
From 9143eef52be43aee233614dd871c271127418948 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 8 Jun 2020 17:40:00 -0400
Subject: [PATCH 55/86] replace reporter specPath usages with spec object from
config
---
cli/types/cypress.d.ts | 4 +--
.../cypress/integration/aliases_spec.js | 12 +++++++--
.../cypress/integration/controls_spec.ts | 6 ++++-
.../cypress/integration/shortcuts_spec.ts | 6 ++++-
.../cypress/integration/suites_spec.ts | 6 ++++-
.../cypress/integration/test_errors_spec.js | 6 ++++-
.../cypress/integration/tests_spec.ts | 6 ++++-
packages/reporter/src/main.spec.tsx | 10 +++++---
packages/reporter/src/main.tsx | 10 +++++---
.../src/runnables/runnable-header.tsx | 25 ++++++++++++-------
.../reporter/src/runnables/runnables.spec.tsx | 20 +++++++++------
packages/reporter/src/runnables/runnables.tsx | 8 +++---
packages/runner/src/app/app.jsx | 9 ++++---
13 files changed, 90 insertions(+), 38 deletions(-)
diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts
index f65fa76b1c37..d81d3ffedcaa 100644
--- a/cli/types/cypress.d.ts
+++ b/cli/types/cypress.d.ts
@@ -216,8 +216,8 @@ declare namespace Cypress {
*/
spec: {
name: string // "config_passing_spec.coffee"
- relative: string | null // "cypress/integration/config_passing_spec.coffee"
- absolute: string | null
+ relative: string // "cypress/integration/config_passing_spec.coffee" or "__all" if clicked all specs button
+ absolute: string
}
/**
diff --git a/packages/reporter/cypress/integration/aliases_spec.js b/packages/reporter/cypress/integration/aliases_spec.js
index 70bd052a9a29..01b2eef7b7ac 100644
--- a/packages/reporter/cypress/integration/aliases_spec.js
+++ b/packages/reporter/cypress/integration/aliases_spec.js
@@ -27,7 +27,11 @@ describe('aliases', () => {
cy.visit('dist').then((win) => {
return win.render({
runner: this.runner,
- specPath: '/foo/bar',
+ spec: {
+ name: 'foo',
+ absolute: '/foo/bar',
+ relative: 'foo/bar',
+ },
})
})
@@ -286,7 +290,11 @@ describe('aliases', () => {
cy.visit('cypress/support/index.html').then((win) => {
return win.render({
runner: this.runner,
- specPath: '/foo/bar',
+ spec: {
+ name: 'foo',
+ absolute: '/foo/bar',
+ relative: 'foo/bar',
+ },
})
})
diff --git a/packages/reporter/cypress/integration/controls_spec.ts b/packages/reporter/cypress/integration/controls_spec.ts
index 68d35ab41483..1135c9d54337 100644
--- a/packages/reporter/cypress/integration/controls_spec.ts
+++ b/packages/reporter/cypress/integration/controls_spec.ts
@@ -9,7 +9,11 @@ describe('controls', function () {
cy.visit('/dist').then((win) => {
win.render({
runner: this.runner,
- specPath: '/foo/bar',
+ spec: {
+ name: 'foo.js',
+ relative: 'relative/path/to/foo.js',
+ absolute: '/absolute/path/to/foo.js',
+ },
})
})
diff --git a/packages/reporter/cypress/integration/shortcuts_spec.ts b/packages/reporter/cypress/integration/shortcuts_spec.ts
index eb5114ce8c4b..b1de68dd581e 100644
--- a/packages/reporter/cypress/integration/shortcuts_spec.ts
+++ b/packages/reporter/cypress/integration/shortcuts_spec.ts
@@ -26,7 +26,11 @@ describe('controls', function () {
cy.visit('/dist').then((win) => {
win.render({
runner,
- specPath: '/foo/bar',
+ spec: {
+ name: 'foo.js',
+ relative: 'relative/path/to/foo.js',
+ absolute: '/absolute/path/to/foo.js',
+ },
})
})
diff --git a/packages/reporter/cypress/integration/suites_spec.ts b/packages/reporter/cypress/integration/suites_spec.ts
index 2039eed502df..b5702c10ee5b 100644
--- a/packages/reporter/cypress/integration/suites_spec.ts
+++ b/packages/reporter/cypress/integration/suites_spec.ts
@@ -9,7 +9,11 @@ describe('controls', function () {
cy.visit('/dist').then((win) => {
win.render({
runner: this.runner,
- specPath: '/foo/bar',
+ spec: {
+ name: 'foo.js',
+ relative: 'relative/path/to/foo.js',
+ absolute: '/absolute/path/to/foo.js',
+ },
})
})
diff --git a/packages/reporter/cypress/integration/test_errors_spec.js b/packages/reporter/cypress/integration/test_errors_spec.js
index aa24876b0e5f..a7ede47b0368 100644
--- a/packages/reporter/cypress/integration/test_errors_spec.js
+++ b/packages/reporter/cypress/integration/test_errors_spec.js
@@ -113,7 +113,11 @@ describe('test errors', function () {
cy.visit('cypress/support/index.html').then((win) => {
win.render({
runner: this.runner,
- specPath: '/foo/bar',
+ spec: {
+ name: 'foo.js',
+ relative: 'relative/path/to/foo.js',
+ absolute: '/absolute/path/to/foo.js',
+ },
config: {
projectRoot: '/root',
},
diff --git a/packages/reporter/cypress/integration/tests_spec.ts b/packages/reporter/cypress/integration/tests_spec.ts
index 026daf9f9051..5a93471dfcbc 100644
--- a/packages/reporter/cypress/integration/tests_spec.ts
+++ b/packages/reporter/cypress/integration/tests_spec.ts
@@ -10,7 +10,11 @@ describe('controls', function () {
cy.visit('/dist').then((win) => {
win.render({
runner: this.runner,
- specPath: '/foo/bar',
+ spec: {
+ name: 'foo.js',
+ relative: 'relative/path/to/foo.js',
+ absolute: '/absolute/path/to/foo.js',
+ },
})
})
diff --git a/packages/reporter/src/main.spec.tsx b/packages/reporter/src/main.spec.tsx
index 1fbdf41a2aa8..ab2b573d185f 100644
--- a/packages/reporter/src/main.spec.tsx
+++ b/packages/reporter/src/main.spec.tsx
@@ -28,7 +28,11 @@ const getProps = (props?: Partial) => {
autoScrollingEnabled: true,
error,
runner: { emit: () => {}, on: () => {} },
- specPath: 'the spec path',
+ spec: {
+ name: 'foo.js',
+ relative: 'relative/path/to/foo.js',
+ absolute: '/absolute/path/to/foo.js',
+ },
appState: {
setAutoScrolling: sinon.spy(),
},
@@ -104,12 +108,12 @@ describe('', () => {
expect(component.find(Header)).to.have.prop('statsStore', statsStore)
})
- it('renders the runnables with the error, runnables store, and spec path', () => {
+ it('renders the runnables with the error, runnables store, and spec obj', () => {
const component = shallow()
expect(component.find(Runnables)).to.have.prop('error', error)
expect(component.find(Runnables)).to.have.prop('runnablesStore', runnablesStore)
- expect(component.find(Runnables)).to.have.prop('specPath', 'the spec path')
+ expect(component.find(Runnables)).to.have.prop('spec').deep.eq(getProps().spec)
})
it('renders the forced gc warning with the appState and events', () => {
diff --git a/packages/reporter/src/main.tsx b/packages/reporter/src/main.tsx
index 7233fbc70c5d..319858cc6a88 100644
--- a/packages/reporter/src/main.tsx
+++ b/packages/reporter/src/main.tsx
@@ -28,7 +28,7 @@ export interface ReporterProps {
statsStore: StatsStore
events: Events
error?: Error
- specPath: string
+ spec: Cypress.Cypress['spec']
}
@observer
@@ -45,7 +45,11 @@ class Reporter extends Component {
emit: PropTypes.func.isRequired,
on: PropTypes.func.isRequired,
}).isRequired,
- specPath: PropTypes.string.isRequired,
+ spec: PropTypes.shape({
+ name: PropTypes.string.isRequired,
+ relative: PropTypes.string.isRequired,
+ absolute: PropTypes.string.isRequired,
+ }),
}
static defaultProps = {
@@ -67,7 +71,7 @@ class Reporter extends Component {
error={this.props.error}
runnablesStore={this.props.runnablesStore}
scroller={this.props.scroller}
- specPath={this.props.specPath}
+ spec={this.props.spec}
/>
{children}
+
interface RunnableHeaderProps {
- specPath: string
+ spec: Cypress.Cypress['spec']
}
class RunnableHeader extends Component {
render () {
- const { specPath } = this.props
- const relativeSpecPath = window.Cypress?.spec.relative
+ const { spec } = this.props
+ const relativeSpecPath = spec.relative
+
+ if (spec.relative === '__all') {
+ return renderRunnableHeader(
+ All Specs,
+ )
+ }
+
const fileDetails = {
- absoluteFile: specPath,
+ absoluteFile: spec.absolute,
column: 0,
line: 0,
originalFile: relativeSpecPath,
relativeFile: relativeSpecPath,
}
- return (
-
- { relativeSpecPath === '__all' ? All Specs : }
-
+ return renderRunnableHeader(
+ ,
)
}
}
diff --git a/packages/reporter/src/runnables/runnables.spec.tsx b/packages/reporter/src/runnables/runnables.spec.tsx
index c117c1a72c78..e545727d0fbc 100644
--- a/packages/reporter/src/runnables/runnables.spec.tsx
+++ b/packages/reporter/src/runnables/runnables.spec.tsx
@@ -10,6 +10,12 @@ import { RunnablesStore } from './runnables-store'
import { Scroller } from '../lib/scroller'
import TestModel from '../test/test-model'
+const specStub = {
+ name: 'foo.js',
+ relative: 'relative/path/to/foo.js',
+ absolute: '/absolute/path/to/foo.js',
+}
+
type AppStateStub = AppState & {
temporarilySetAutoScrolling: SinonSpy
}
@@ -42,7 +48,7 @@ describe('', () => {
,
)
@@ -54,7 +60,7 @@ describe('', () => {
,
)
@@ -62,7 +68,7 @@ describe('', () => {
expect(component.find(AnError).prop('error')).to.eql({
title: 'No tests found in your file:',
link: 'https://on.cypress.io/no-tests-found-in-your-file',
- callout: '/path/to/foo_spec.js',
+ callout: specStub.relative,
message: 'We could not detect any tests in the above file. Write some tests and re-run.',
})
})
@@ -72,7 +78,7 @@ describe('', () => {
,
)
@@ -85,7 +91,7 @@ describe('', () => {
,
)
@@ -101,7 +107,7 @@ describe('', () => {
appState={appState}
runnablesStore={runnablesStoreStub()}
scroller={scroller}
- specPath=''
+ spec={specStub}
/>,
)
@@ -118,7 +124,7 @@ describe('', () => {
appState={appState}
runnablesStore={runnablesStoreStub()}
scroller={scroller}
- specPath=''
+ spec={specStub}
/>,
)
diff --git a/packages/reporter/src/runnables/runnables.tsx b/packages/reporter/src/runnables/runnables.tsx
index e9cfb157fb88..f27d103174e3 100644
--- a/packages/reporter/src/runnables/runnables.tsx
+++ b/packages/reporter/src/runnables/runnables.tsx
@@ -44,7 +44,7 @@ function content ({ isReady, runnables }: RunnablesStore, specPath: string, erro
interface RunnablesProps {
error?: Error
runnablesStore: RunnablesStore
- specPath: string
+ spec: Cypress.Cypress['spec']
scroller: Scroller
appState?: AppState
}
@@ -52,12 +52,12 @@ interface RunnablesProps {
@observer
class Runnables extends Component {
render () {
- const { error, runnablesStore, specPath } = this.props
+ const { error, runnablesStore, spec } = this.props
return (
-
- {content(runnablesStore, specPath, error)}
+
+ {content(runnablesStore, spec.relative, error)}
)
}
diff --git a/packages/runner/src/app/app.jsx b/packages/runner/src/app/app.jsx
index d2630b18d0d1..5f5b6113293c 100644
--- a/packages/runner/src/app/app.jsx
+++ b/packages/runner/src/app/app.jsx
@@ -21,7 +21,10 @@ class App extends Component {
@observable isReporterResizing = false
render () {
- const specPath = this.props.util.absoluteSpecPath(this.props.config)
+ /**
+ * @type {Cypress.Cypress['spec']}
+ */
+ const spec = this.props.config.spec
return (
From 1348b35e103d77b5c78c7322988245ddde510d62 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Mon, 8 Jun 2020 18:14:43 -0400
Subject: [PATCH 56/86] cleanup, fix specs, fix types tests
---
cli/types/tests/kitchen-sink.ts | 4 ++--
.../reporter/cypress/integration/tests_spec.ts | 4 ++--
packages/reporter/cypress/support/utils.js | 4 ++--
packages/runner/src/app/app.spec.jsx | 10 ++++++----
packages/runner/src/lib/util.js | 17 -----------------
5 files changed, 12 insertions(+), 27 deletions(-)
diff --git a/cli/types/tests/kitchen-sink.ts b/cli/types/tests/kitchen-sink.ts
index 0ed7423c2665..ad0d299b102e 100644
--- a/cli/types/tests/kitchen-sink.ts
+++ b/cli/types/tests/kitchen-sink.ts
@@ -41,8 +41,8 @@ const serverOptions: Partial = {
cy.server(serverOptions)
Cypress.spec.name // $ExpectType string
-Cypress.spec.relative // $ExpectType string | null
-Cypress.spec.absolute // $ExpectType string | null
+Cypress.spec.relative // $ExpectType string
+Cypress.spec.absolute // $ExpectType string
Cypress.browser // $ExpectType Browser
diff --git a/packages/reporter/cypress/integration/tests_spec.ts b/packages/reporter/cypress/integration/tests_spec.ts
index 5a93471dfcbc..44a45617e428 100644
--- a/packages/reporter/cypress/integration/tests_spec.ts
+++ b/packages/reporter/cypress/integration/tests_spec.ts
@@ -111,11 +111,11 @@ describe('controls', function () {
describe('header', function () {
it('displays', function () {
- cy.get('.runnable-header').find('a').should('have.text', 'cypress/integration/tests_spec.ts')
+ cy.get('.runnable-header').find('a').should('have.text', 'relative/path/to/foo.js')
})
itHandlesFileOpening('.runnable-header', {
- file: '/foo/bar',
+ file: '/absolute/path/to/foo.js',
line: 0,
column: 0,
})
diff --git a/packages/reporter/cypress/support/utils.js b/packages/reporter/cypress/support/utils.js
index 38fece319b29..1ada17780ed1 100644
--- a/packages/reporter/cypress/support/utils.js
+++ b/packages/reporter/cypress/support/utils.js
@@ -63,8 +63,8 @@ export const itHandlesFileOpening = (containerSelector, file, stackTrace = false
// the changes to the path don't bubble up correctly. this only happens
// in the Cypress test and not when running the actual app
it.skip('updates "Other" path when typed into', function () {
- cy.contains('Other').find('input[type="text"]').type('/path/to/editor')
- .should('have.value', '/path/to/editor')
+ cy.contains('Other').find('input[type="text"]').type('/absolute/path/to/foo.js')
+ .should('have.value', '/absolute/path/to/foo.js')
})
describe('when editor is not selected', function () {
diff --git a/packages/runner/src/app/app.spec.jsx b/packages/runner/src/app/app.spec.jsx
index c9e35200601a..9b15d15edfdc 100644
--- a/packages/runner/src/app/app.spec.jsx
+++ b/packages/runner/src/app/app.spec.jsx
@@ -20,6 +20,11 @@ const createProps = () => ({
viewportHeight: 800,
viewportWidth: 500,
state: {},
+ spec: {
+ name: 'foo.js',
+ relative: 'relative/path/to/foo.js',
+ absolute: '/absolute/path/to/foo.js',
+ },
},
eventManager: {
notifyRunningSpec: sinon.spy(),
@@ -30,9 +35,6 @@ const createProps = () => ({
},
},
state: new State(),
- util: {
- absoluteSpecPath: sinon.stub().returns('/path/to/int/some-spec.js'),
- },
})
const shallowRender = (component) => {
@@ -70,7 +72,7 @@ describe('', () => {
props.config.integrationFolder = 'path/to/int'
const component = shallowRender()
- expect(component.find(Reporter)).to.have.prop('specPath', '/path/to/int/some-spec.js')
+ expect(component.find(Reporter)).to.have.prop('spec').deep.eq(props.config.spec)
})
it('renders the with the autoScrollingEnabled flag', () => {
diff --git a/packages/runner/src/lib/util.js b/packages/runner/src/lib/util.js
index a64e5a20579b..baa2518c3b3c 100644
--- a/packages/runner/src/lib/util.js
+++ b/packages/runner/src/lib/util.js
@@ -1,5 +1,3 @@
-import path from 'path'
-
export default {
hasSpecFile () {
return !!location.hash
@@ -14,19 +12,4 @@ export default {
return ''
},
-
- // TODO: handle both integration and components specs
- absoluteSpecPath (config) {
- if (config.spec.specType === 'component') {
- // hmm, the "spec" object seems to have all paths
- // name: relative path wrt to its parent folder (integration or component)
- // absolute: absolute file path on the host machine
- // relative: relative path wrt to "cypress" folder
- return config.spec.absolute
- }
-
- const relativeSpecPath = path.relative('integration', this.specPath())
-
- return path.join(config.integrationFolder, relativeSpecPath)
- },
}
From 1ab5d71b84b0a0eb3da38788d06ac1493686dcdb Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 9 Jun 2020 12:16:05 -0400
Subject: [PATCH 57/86] fix config spec paths in isolated runner, fix snapshot
plugin button
---
.../__snapshots__/runner.spec.js.snapshot.js | 48 +++++++++----------
.../plugins/snapshot/snapshotCommand.js | 22 +++++----
.../cypress/plugins/snapshot/snapshotUtils.js | 10 ++--
.../runner/test/cypress/support/helpers.js | 8 +++-
4 files changed, 52 insertions(+), 36 deletions(-)
diff --git a/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js b/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js
index 4e2b91c4a6a1..2d257b67add3 100644
--- a/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js
+++ b/packages/runner/test/__snapshots__/runner.spec.js.snapshot.js
@@ -14,7 +14,7 @@ exports['FAIL_IN_AFTER.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -268,7 +268,7 @@ exports['FAIL_IN_AFTER.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -288,7 +288,7 @@ exports['FAIL_IN_AFTER.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js",
+ "file": "relative/path/to/spec.js",
"tests": [],
"suites": [
{
@@ -337,7 +337,7 @@ exports['FAIL_IN_AFTEREACH.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -512,7 +512,7 @@ exports['FAIL_IN_AFTEREACH.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -532,7 +532,7 @@ exports['FAIL_IN_AFTEREACH.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js",
+ "file": "relative/path/to/spec.js",
"tests": [],
"suites": [
{
@@ -574,7 +574,7 @@ exports['FAIL_IN_BEFORE.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -675,7 +675,7 @@ exports['FAIL_IN_BEFORE.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -695,7 +695,7 @@ exports['FAIL_IN_BEFORE.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js",
+ "file": "relative/path/to/spec.js",
"tests": [],
"suites": [
{
@@ -737,7 +737,7 @@ exports['FAIL_IN_BEFOREEACH.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -850,7 +850,7 @@ exports['FAIL_IN_BEFOREEACH.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -870,7 +870,7 @@ exports['FAIL_IN_BEFOREEACH.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js",
+ "file": "relative/path/to/spec.js",
"tests": [],
"suites": [
{
@@ -912,7 +912,7 @@ exports['FAIL_WITH_ONLY.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -1260,7 +1260,7 @@ exports['FAIL_WITH_ONLY.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -1280,7 +1280,7 @@ exports['FAIL_WITH_ONLY.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js",
+ "file": "relative/path/to/spec.js",
"tests": [],
"suites": [
{
@@ -1323,7 +1323,7 @@ exports['PASS_WITH_ONLY.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -1659,7 +1659,7 @@ exports['PASS_WITH_ONLY.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -1679,7 +1679,7 @@ exports['PASS_WITH_ONLY.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js",
+ "file": "relative/path/to/spec.js",
"tests": [],
"suites": [
{
@@ -1722,7 +1722,7 @@ exports['SIMPLE_SINGLE_TEST.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -1834,7 +1834,7 @@ exports['SIMPLE_SINGLE_TEST.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -1854,7 +1854,7 @@ exports['SIMPLE_SINGLE_TEST.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js",
+ "file": "relative/path/to/spec.js",
"tests": [],
"suites": [
{
@@ -1896,7 +1896,7 @@ exports['THREE_TESTS_WITH_HOOKS.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -2583,7 +2583,7 @@ exports['THREE_TESTS_WITH_HOOKS.mocha'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js"
+ "file": "relative/path/to/spec.js"
}
],
[
@@ -2603,7 +2603,7 @@ exports['THREE_TESTS_WITH_HOOKS.setRunnables'] = [
"title": "",
"root": true,
"type": "suite",
- "file": "cypress/integration/runner.spec.js",
+ "file": "relative/path/to/spec.js",
"tests": [],
"suites": [
{
diff --git a/packages/runner/test/cypress/plugins/snapshot/snapshotCommand.js b/packages/runner/test/cypress/plugins/snapshot/snapshotCommand.js
index de68f69b5228..5fc8b44ef8fc 100644
--- a/packages/runner/test/cypress/plugins/snapshot/snapshotCommand.js
+++ b/packages/runner/test/cypress/plugins/snapshot/snapshotCommand.js
@@ -26,7 +26,7 @@ function getMatchDeepMessage ({ act, exp }) {
}
function saveSnapshot (ctx, exactSpecName, file, exp, act) {
- ctx.assert(true, `snapshot updated: **${exactSpecName}**`, 'dsf', exp, act)
+ ctx.assert(true, `snapshot updated: **${exactSpecName}**`, '', exp, act)
return cy.task('saveSnapshot', {
file,
@@ -51,15 +51,21 @@ const registerInCypress = () => {
})
before(() => {
- addPluginButton($, 'toggle-snapshot-update', '', function () {
- const prev = Cypress.env('SNAPSHOT_UPDATE')
+ addPluginButton($, 'toggle-snapshot-update', '', {
+ render () {
+ const btnIcon = $(this).children().first()
- Cypress.env('SNAPSHOT_UPDATE', !prev)
- const btnIcon = $(this).children().first()
+ return btnIcon.text(Cypress.env('SNAPSHOT_UPDATE') ? 'snapshot\nupdate\non' : 'snapshot\nupdate\noff')
+ .css({ 'font-size': '10px', 'line-height': '0.9' })
+ .html(btnIcon.html().replace(/\n/g, '
'))
+ },
+ click () {
+ const prev = Cypress.env('SNAPSHOT_UPDATE')
- return btnIcon.text(Cypress.env('SNAPSHOT_UPDATE') ? 'snapshot\nupdate\non' : 'snapshot\nupdate\noff')
- .css({ 'font-size': '10px', 'line-height': '0.9' })
- .html(btnIcon.html().replace(/\n/g, '
'))
+ Cypress.env('SNAPSHOT_UPDATE', !prev)
+ },
+
+ }, function () {
})
})
diff --git a/packages/runner/test/cypress/plugins/snapshot/snapshotUtils.js b/packages/runner/test/cypress/plugins/snapshot/snapshotUtils.js
index f0f875b9800c..17bfc8de5657 100644
--- a/packages/runner/test/cypress/plugins/snapshot/snapshotUtils.js
+++ b/packages/runner/test/cypress/plugins/snapshot/snapshotUtils.js
@@ -56,7 +56,7 @@ function isObject (obj) {
return typeof obj === 'object' && obj && getType(obj) !== 'RegExp'
}
-function addPluginButton ($, name, faClass, fn) {
+function addPluginButton ($, name, faClass, { render, click }) {
$(`#${name}`, window.top.document).remove()
const btn = $(``, window.top.document)
@@ -67,8 +67,12 @@ function addPluginButton ($, name, faClass, fn) {
container.prepend(btn)
- btn.on('click', fn)
- fn.apply(btn[0])
+ btn.on('click', () => {
+ click.apply(btn[0])
+ render.apply(btn[0])
+ })
+
+ render.apply(btn[0])
return btn
}
diff --git a/packages/runner/test/cypress/support/helpers.js b/packages/runner/test/cypress/support/helpers.js
index 595083e5ed35..2edaf411e70b 100644
--- a/packages/runner/test/cypress/support/helpers.js
+++ b/packages/runner/test/cypress/support/helpers.js
@@ -254,7 +254,13 @@ function createCypress () {
.withArgs('automation:request')
.yieldsAsync({ response: {} })
- const c = _.extend({}, Cypress.config(), { isTextTerminal: true }, opts.config)
+ const c = _.extend({}, Cypress.config(), {
+ isTextTerminal: true,
+ spec: {
+ relative: 'relative/path/to/spec.js',
+ absolute: '/absolute/path/to/spec.js',
+ },
+ }, opts.config)
c.state = {}
// c.state = opts.state
From 9211e5370a56a01dde6c05d0661a71c9a77c7fd6 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 9 Jun 2020 13:03:10 -0400
Subject: [PATCH 58/86] combine runner.spec.js and runner_spec.js
---
.../server/__snapshots__/reporter_spec.js | 2 +-
...apshot.js => reporter_spec.js.snapshot.js} | 0
packages/server/test/unit/reporter.spec.js | 97 -----
packages/server/test/unit/reporter_spec.js | 333 ++++++++++++------
4 files changed, 218 insertions(+), 214 deletions(-)
rename packages/server/__snapshots__/{reporter.spec.js.snapshot.js => reporter_spec.js.snapshot.js} (100%)
delete mode 100644 packages/server/test/unit/reporter.spec.js
diff --git a/packages/server/__snapshots__/reporter_spec.js b/packages/server/__snapshots__/reporter_spec.js
index 5d5fdae8205a..2d67f3fb5c5f 100644
--- a/packages/server/__snapshots__/reporter_spec.js
+++ b/packages/server/__snapshots__/reporter_spec.js
@@ -1,4 +1,4 @@
-exports['lib/reporter #stats has reporterName stats, reporterStats, etc 1'] = {
+exports['lib/reporter unit #stats has reporterName stats, reporterStats, etc 1'] = {
"stats": {
"suites": 2,
"tests": 2,
diff --git a/packages/server/__snapshots__/reporter.spec.js.snapshot.js b/packages/server/__snapshots__/reporter_spec.js.snapshot.js
similarity index 100%
rename from packages/server/__snapshots__/reporter.spec.js.snapshot.js
rename to packages/server/__snapshots__/reporter_spec.js.snapshot.js
diff --git a/packages/server/test/unit/reporter.spec.js b/packages/server/test/unit/reporter.spec.js
deleted file mode 100644
index 0b3839c1a099..000000000000
--- a/packages/server/test/unit/reporter.spec.js
+++ /dev/null
@@ -1,97 +0,0 @@
-// process.env.SNAPSHOT_UPDATE = 1
-require('../spec_helper')
-const _ = require('lodash')
-const sinon = require('sinon')
-const Debug = require('debug')
-
-const Reporter = require('../../lib/reporter')
-const { spyOn, stdout } = require('../support/helpers/utils')
-const { registerInMocha, parseSnapshot, stringifyShort } = require('../matchDeep')
-
-const events = require('../../../runner/test/__snapshots__/runner.spec.js.snapshot')
-const { EventSnapshots } = require('../../../runner/test/cypress/support/eventSnapshots')
-
-registerInMocha()
-
-const debug = Debug('spec:retries')
-const { match } = sinon
-
-const runnerEmitCleanseMap = {
- '^.*.1': stringifyShort,
- parent: stringifyShort,
-}
-
-// TODO: maybe refactor into utility, remove module.exports
-/** @param {typeof EventSnapshots.FAIL_IN_AFTER} snapshotName */
-function createReporter (snapshotName) {
- const getSnapshot = (snapshotName) => {
- return _.mapValues(snapshotName, (v) => parseSnapshot(events[v]))
- }
-
- const { setRunnables, mocha } = getSnapshot(snapshotName)
-
- const stdoutStub = stdout.capture()
-
- const reporter = Reporter()
-
- const currentReporter = reporter
-
- const runnables = parseSnapshot(setRunnables)[0][1]
- const mochaEvents = parseSnapshot(mocha)
-
- reporter.setRunnables(runnables)
-
- const stubs = {}
-
- const currentStubs = stubs
-
- stubs.reporterEmit = spyOn(reporter, 'emit', debug.extend('reporter:emit'))
- stubs.runnerEmit = spyOn(reporter.runner, 'emit', debug.extend('runner:emit'))
-
- _.each(mochaEvents, (event) => {
- reporter.emit(...event.slice(1, 3))
- })
-
- stdout.restore()
- const snapshot = (name) => {
- if (!name) throw new Error('snapshot name cannot be empty')
-
- expect(currentStubs.runnerEmit.args).to.matchSnapshot(runnerEmitCleanseMap, `${name} runner emit`)
- expect(currentReporter.results()).to.matchSnapshot({
- 'reporterStats.end': match.date,
- 'reporterStats.start': match.date,
- 'reporterStats.duration': match.number,
- }, `${name} reporter results`)
-
- expect(stdoutStub.toString())
-
- expect(stdoutStub.toString())
- .matchSnapshot({ '^': (v) => v.replace(/\(\d+ms\)/g, '') }, `${name} stdout`)
- }
-
- return { stubs, reporter, snapshot }
-}
-
-describe('reporter retries', () => {
- afterEach(() => {
- stdout.restore()
- })
-
- it('simple single test', () => {
- const { snapshot } = createReporter(EventSnapshots.SIMPLE_SINGLE_TEST)
-
- snapshot('simple_single_test')
- })
-
- it('fail [afterEach]', () => {
- const { snapshot } = createReporter(EventSnapshots.FAIL_IN_AFTEREACH)
-
- snapshot('fail in [afterEach]')
- })
-
- it('fail [beforeEach]', () => {
- const { snapshot } = createReporter(EventSnapshots.FAIL_IN_BEFOREEACH)
-
- snapshot('fail in [beforeEach]')
- })
-})
diff --git a/packages/server/test/unit/reporter_spec.js b/packages/server/test/unit/reporter_spec.js
index 6888b89131df..a3c93231b99a 100644
--- a/packages/server/test/unit/reporter_spec.js
+++ b/packages/server/test/unit/reporter_spec.js
@@ -1,162 +1,263 @@
require('../spec_helper')
-const Reporter = require(`${root}lib/reporter`)
+const _ = require('lodash')
+const sinon = require('sinon')
+const Debug = require('debug')
const snapshot = require('snap-shot-it')
-describe('lib/reporter', () => {
- beforeEach(function () {
- this.reporter = new Reporter()
-
- this.root = {
- id: 'r1',
- root: true,
- title: '',
- tests: [],
- suites: [
- {
- id: 'r2',
- title: 'TodoMVC - React',
- tests: [],
- suites: [
- {
- id: 'r3',
- title: 'When page is initially opened',
- tests: [
- {
- id: 'r4',
- title: 'should focus on the todo input field',
- duration: 4,
- state: 'failed',
- timedOut: false,
- async: 0,
- sync: true,
- err: {
- message: 'foo',
- stack: [1, 2, 3],
- },
- },
- {
- id: 'r5',
- title: 'does something good',
- duration: 4,
- state: 'pending',
- timedOut: false,
- async: 0,
- sync: true,
- },
- ],
- suites: [],
- },
- ],
- },
- ],
- }
+const Reporter = require(`../../lib/reporter`)
+const { spyOn, stdout } = require('../support/helpers/utils')
+const { registerInMocha, parseSnapshot, stringifyShort } = require('../matchDeep')
+const events = require('../../../runner/test/__snapshots__/runner.spec.js.snapshot')
+const { EventSnapshots } = require('../../../runner/test/cypress/support/eventSnapshots')
- this.testObj = this.root.suites[0].suites[0].tests[0]
+registerInMocha()
- return this.reporter.setRunnables(this.root)
- })
+const debug = Debug('spec:retries')
+const { match } = sinon
- context('.create', () => {
- it('can create mocha-teamcity-reporter', function () {
- const teamCityFn = sinon.stub()
+const runnerEmitCleanseMap = {
+ '^.*.1': stringifyShort,
+ parent: stringifyShort,
+}
- mockery.registerMock('mocha-teamcity-reporter', teamCityFn)
+/** @param {typeof EventSnapshots.FAIL_IN_AFTER} snapshotName */
+function createReporterFromSnapshot (snapshotName) {
+ const getSnapshot = (snapshotName) => {
+ return _.mapValues(snapshotName, (v) => parseSnapshot(events[v]))
+ }
- const reporter = Reporter.create('teamcity')
+ const { setRunnables, mocha } = getSnapshot(snapshotName)
- reporter.setRunnables(this.root)
+ const stdoutStub = stdout.capture()
- expect(reporter.reporterName).to.eq('teamcity')
+ const reporter = new Reporter()
- expect(teamCityFn).to.be.calledWith(reporter.runner)
- })
+ const currentReporter = reporter
- it('can create mocha-junit-reporter', function () {
- const junitFn = sinon.stub()
+ const runnables = parseSnapshot(setRunnables)[0][1]
+ const mochaEvents = parseSnapshot(mocha)
- mockery.registerMock('mocha-junit-reporter', junitFn)
+ reporter.setRunnables(runnables)
- const reporter = Reporter.create('junit')
+ const stubs = {}
- reporter.setRunnables(this.root)
+ const currentStubs = stubs
- expect(reporter.reporterName).to.eq('junit')
+ stubs.reporterEmit = spyOn(reporter, 'emit', debug.extend('reporter:emit'))
+ stubs.runnerEmit = spyOn(reporter.runner, 'emit', debug.extend('runner:emit'))
- expect(junitFn).to.be.calledWith(reporter.runner)
- })
+ _.each(mochaEvents, (event) => {
+ reporter.emit(...event.slice(1, 3))
})
- context('createSuite', () => {
- beforeEach(function () {
- this.errorObj = {
- message: 'expected true to be false',
- name: 'AssertionError',
- stack: 'AssertionError: expected true to be false',
- actual: true,
- expected: false,
- showDiff: false,
- }
- })
-
- it('recursively creates suites for fullTitle', function () {
- const args = this.reporter.parseArgs('fail', [this.testObj])
-
- console.log(args)
- expect(args[0]).to.eq('fail')
+ stdout.restore()
+ const snapshot = (name) => {
+ if (!name) throw new Error('snapshot name cannot be empty')
+
+ expect(currentStubs.runnerEmit.args).to.matchSnapshot(runnerEmitCleanseMap, `${name} runner emit`)
+ expect(currentReporter.results()).to.matchSnapshot({
+ 'reporterStats.end': match.date,
+ 'reporterStats.start': match.date,
+ 'reporterStats.duration': match.number,
+ }, `${name} reporter results`)
+
+ expect(stdoutStub.toString())
+
+ expect(stdoutStub.toString())
+ .matchSnapshot({ '^': (v) => v.replace(/\(\d+ms\)/g, '') }, `${name} stdout`)
+ }
+
+ return { stubs, reporter, snapshot }
+}
+
+function createReporter () {
+ const reporter = new Reporter()
+
+ const root = {
+ id: 'r1',
+ root: true,
+ title: '',
+ tests: [],
+ suites: [
+ {
+ id: 'r2',
+ title: 'TodoMVC - React',
+ tests: [],
+ suites: [
+ {
+ id: 'r3',
+ title: 'When page is initially opened',
+ tests: [
+ {
+ id: 'r4',
+ title: 'should focus on the todo input field',
+ duration: 4,
+ state: 'failed',
+ timedOut: false,
+ async: 0,
+ sync: true,
+ err: {
+ message: 'foo',
+ stack: [1, 2, 3],
+ },
+ },
+ {
+ id: 'r5',
+ title: 'does something good',
+ duration: 4,
+ state: 'pending',
+ timedOut: false,
+ async: 0,
+ sync: true,
+ },
+ ],
+ suites: [],
+ },
+ ],
+ },
+ ],
+ }
+
+ const testObj = root.suites[0].suites[0].tests[0]
+
+ reporter.setRunnables(root)
+
+ return { reporter, testObj, root }
+}
- const title = 'TodoMVC - React When page is initially opened should focus on the todo input field'
+describe('lib/reporter', () => {
+ describe('integration from snapshots', () => {
+ it('simple single test', () => {
+ const { snapshot } = createReporterFromSnapshot(EventSnapshots.SIMPLE_SINGLE_TEST)
- expect(args[1].fullTitle()).to.eq(title)
+ snapshot('simple_single_test')
})
- })
- context('#stats', () => {
- it('has reporterName stats, reporterStats, etc', function () {
- sinon.stub(Date, 'now').returns(1234)
+ it('fail [afterEach]', () => {
+ const { snapshot } = createReporterFromSnapshot(EventSnapshots.FAIL_IN_AFTEREACH)
- this.reporter.emit('test', this.testObj)
- this.reporter.emit('fail', this.testObj)
- this.reporter.emit('test end', this.testObj)
+ snapshot('fail in [afterEach]')
+ })
- this.reporter.reporterName = 'foo'
+ it('fail [beforeEach]', () => {
+ const { snapshot } = createReporterFromSnapshot(EventSnapshots.FAIL_IN_BEFOREEACH)
- return snapshot(this.reporter.results())
+ snapshot('fail in [beforeEach]')
})
})
- context('#emit', () => {
+ describe('unit', () => {
+ let reporter
+ let testObj
+ let root
+
beforeEach(function () {
- this.emit = sinon.spy(this.reporter.runner, 'emit')
+ ({ reporter, testObj, root } = createReporter())
})
- it('emits start', function () {
- this.reporter.emit('start')
- expect(this.emit).to.be.calledWith('start')
+ context('.create', () => {
+ it('can create mocha-teamcity-reporter', function () {
+ const teamCityFn = sinon.stub()
+
+ mockery.registerMock('mocha-teamcity-reporter', teamCityFn)
+
+ const reporter = Reporter.create('teamcity')
+
+ reporter.setRunnables(root)
+
+ expect(reporter.reporterName).to.eq('teamcity')
- expect(this.emit).to.be.calledOn(this.reporter.runner)
+ expect(teamCityFn).to.be.calledWith(reporter.runner)
+ })
+
+ it('can create mocha-junit-reporter', function () {
+ const junitFn = sinon.stub()
+
+ mockery.registerMock('mocha-junit-reporter', junitFn)
+
+ const reporter = Reporter.create('junit')
+
+ reporter.setRunnables(root)
+
+ expect(reporter.reporterName).to.eq('junit')
+
+ expect(junitFn).to.be.calledWith(reporter.runner)
+ })
})
- it('emits test with updated properties', function () {
- this.reporter.emit('test', { id: 'r5', state: 'passed' })
- expect(this.emit).to.be.calledWith('test')
- expect(this.emit.getCall(0).args[1].title).to.eq('does something good')
+ context('createSuite', () => {
+ beforeEach(function () {
+ this.errorObj = {
+ message: 'expected true to be false',
+ name: 'AssertionError',
+ stack: 'AssertionError: expected true to be false',
+ actual: true,
+ expected: false,
+ showDiff: false,
+ }
+ })
+
+ it('recursively creates suites for fullTitle', function () {
+ const args = reporter.parseArgs('fail', [testObj])
+
+ expect(args[0]).to.eq('fail')
- expect(this.emit.getCall(0).args[1].state).to.eq('passed')
+ const title = 'TodoMVC - React When page is initially opened should focus on the todo input field'
+
+ expect(args[1].fullTitle()).to.eq(title)
+ })
})
- it('ignores events not in the events table', function () {
- this.reporter.emit('foo')
+ context('#stats', () => {
+ it('has reporterName stats, reporterStats, etc', function () {
+ sinon.stub(Date, 'now').returns(1234)
+
+ reporter.emit('test', testObj)
+ reporter.emit('fail', testObj)
+ reporter.emit('test end', testObj)
- expect(this.emit).not.to.be.called
+ reporter.reporterName = 'foo'
+
+ return snapshot(reporter.results())
+ })
})
- it('sends suites with updated properties and nested subtree', function () {
- this.reporter.emit('suite', { id: 'r3', state: 'passed' })
- expect(this.emit).to.be.calledWith('suite')
- expect(this.emit.getCall(0).args[1].state).to.eq('passed')
+ context('#emit', () => {
+ let emit
+
+ beforeEach(function () {
+ emit = sinon.spy(reporter.runner, 'emit')
+ })
+
+ it('emits start', function () {
+ reporter.emit('start')
+ expect(emit).to.be.calledWith('start')
+
+ expect(emit).to.be.calledOn(reporter.runner)
+ })
+
+ it('emits test with updated properties', function () {
+ reporter.emit('test', { id: 'r5', state: 'passed' })
+ expect(emit).to.be.calledWith('test')
+ expect(emit.getCall(0).args[1].title).to.eq('does something good')
+
+ expect(emit.getCall(0).args[1].state).to.eq('passed')
+ })
+
+ it('ignores events not in the events table', function () {
+ reporter.emit('foo')
+
+ expect(emit).not.to.be.called
+ })
+
+ it('sends suites with updated properties and nested subtree', function () {
+ reporter.emit('suite', { id: 'r3', state: 'passed' })
+ expect(emit).to.be.calledWith('suite')
+ expect(emit.getCall(0).args[1].state).to.eq('passed')
- expect(this.emit.getCall(0).args[1].tests.length).to.equal(2)
+ expect(emit.getCall(0).args[1].tests.length).to.equal(2)
+ })
})
})
})
From 5a9a7a83bf00eb775b22f062988e9b32a4552c83 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 9 Jun 2020 13:11:00 -0400
Subject: [PATCH 59/86] fix incorrect merge
---
packages/reporter/src/runnables/runnables.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/reporter/src/runnables/runnables.tsx b/packages/reporter/src/runnables/runnables.tsx
index 9d730ec62aef..f27d103174e3 100644
--- a/packages/reporter/src/runnables/runnables.tsx
+++ b/packages/reporter/src/runnables/runnables.tsx
@@ -10,7 +10,7 @@ import { RunnablesStore, RunnableArray } from './runnables-store'
import { Scroller } from '../lib/scroller'
import { AppState } from '../lib/app-state'
-const noTestsError = (specPath: string | null) => ({
+const noTestsError = (specPath: string) => ({
title: 'No tests found in your file:',
link: 'https://on.cypress.io/no-tests-found-in-your-file',
callout: specPath,
@@ -29,7 +29,7 @@ const RunnablesList = observer(({ runnables }: RunnablesListProps) => (
))
-function content ({ isReady, runnables }: RunnablesStore, specPath: string | null, error?: Error) {
+function content ({ isReady, runnables }: RunnablesStore, specPath: string, error?: Error) {
if (!isReady) return null
// show error if there are no tests, but only if there
From fec95dedc92f75c51096363527a3dbe1207f22db Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 9 Jun 2020 13:43:23 -0400
Subject: [PATCH 60/86] minor minor cleanup
---
packages/runner/test/cypress/support/helpers.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/runner/test/cypress/support/helpers.js b/packages/runner/test/cypress/support/helpers.js
index 2edaf411e70b..539f2675806c 100644
--- a/packages/runner/test/cypress/support/helpers.js
+++ b/packages/runner/test/cypress/support/helpers.js
@@ -10,6 +10,7 @@ const snapshotCommand = require('../plugins/snapshot/snapshotCommand')
const match = Cypress.sinon.match
const { stringifyShort } = snapshotCommand
+
const eventCleanseMap = {
snapshots: stringifyShort,
parent: stringifyShort,
@@ -88,7 +89,6 @@ function createCypress () {
let onInitializedListeners = []
const onInitialized = function (fn) {
- console.log(fn)
onInitializedListeners.push(fn)
}
From 08724137bec640fad1a2291d5b8ffb7d9b44fc07 Mon Sep 17 00:00:00 2001
From: Ben Kucera <14625260+Bkucera@users.noreply.github.com>
Date: Tue, 9 Jun 2020 16:00:03 -0400
Subject: [PATCH 61/86] rename driver/test/cypress to driver/test
---
.gitignore | 4 +-
circle.yml | 49 +++++++++---------
packages/driver/.gitignore | 4 +-
packages/driver/{test => }/cypress.json | 4 +-
.../driver/{test => cypress}/.eslintrc.json | 6 +++
.../cypress/fixtures/active-elements.html | 0
.../{test => }/cypress/fixtures/angular.html | 0
.../driver/{test => }/cypress/fixtures/app.js | 0
.../{test => }/cypress/fixtures/app.json | 0
.../fixtures/blocking_beforeunload_event.html | 0
.../fixtures/blocking_onbeforeunload.html | 0
.../cypress/fixtures/comma-separated.csv | 0
.../cypress/fixtures/command-log.html | 0
.../{test => }/cypress/fixtures/commands.html | 0
.../cypress/fixtures/cross_origin.html | 0
.../cypress/fixtures/cross_origin_name.html | 0
.../cypress/fixtures/dangling-element.html | 0
.../cypress/fixtures/dimensions.html | 0
.../{test => }/cypress/fixtures/dom.html | 0
.../{test => }/cypress/fixtures/dropdown.html | 0
.../{test => }/cypress/fixtures/example.json | 0
.../cypress/fixtures/fixed-nav.html | 0
.../cypress/fixtures/foo.bar.baz.json | 0
.../{test => }/cypress/fixtures/form.html | 0
.../{test => }/cypress/fixtures/generic.html | 0
.../cypress/fixtures/generic_styles.css | 0
.../cypress/fixtures/generic_styles_2.css | 0
.../cypress/fixtures/generic_styles_3.css | 0
.../cypress/fixtures/generic_styles_print.css | 0
.../cypress/fixtures/global-error.html | 0
.../cypress/fixtures/hook_uncaught.html | 0
.../cypress/fixtures/iframe-inner.html | 0
.../cypress/fixtures/iframe-outer.html | 0
.../cypress/fixtures/invalid_html.html | 0
.../cypress/fixtures/issue-1436.html | 0
.../cypress/fixtures/issue-2956.html | 0
.../cypress/fixtures/issue-4373.html | 0
.../cypress/fixtures/issue-565.html | 0
.../cypress/fixtures/issue-5682.html | 0
.../cypress/fixtures/issue-5707.html | 0
.../cypress/fixtures/issue-599.html | 0
.../cypress/fixtures/issue-652.html | 0
.../cypress/fixtures/jquery-3.2.1.js | 0
.../{test => }/cypress/fixtures/jquery.html | 0
.../cypress/fixtures/js-redirect-timeout.html | 0
.../cypress/fixtures/js-redirect.html | 0
.../cypress/fixtures/media/small.mp4 | Bin
.../fixtures/meta-redirect-timeout.html | 0
.../cypress/fixtures/meta-redirect.html | 0
.../{test => }/cypress/fixtures/mui.html | 0
.../cypress/fixtures/nested/with_paths.css | 0
.../{test => }/cypress/fixtures/no_html.html | 0
.../{test => }/cypress/fixtures/null.json | 0
.../fixtures/problematic-source-map-urls.txt | 0
.../{test => }/cypress/fixtures/react-15.html | 0
.../{test => }/cypress/fixtures/react-16.html | 0
.../cypress/fixtures/screenshots.html | 0
.../cypress/fixtures/scrolling.html | 0
.../{test => }/cypress/fixtures/security.html | 0
.../{test => }/cypress/fixtures/security.js | 0
.../cypress/fixtures/shadow-dom.html | 0
.../{test => }/cypress/fixtures/sinon.html | 0
.../cypress/fixtures/sync_error.html | 0
.../cypress/fixtures/text-mask.html | 0
.../{test => }/cypress/fixtures/type.html | 0
.../{test => }/cypress/fixtures/valid.json | 0
.../{test => }/cypress/fixtures/video.html | 0
.../cypress/fixtures/visibility.html | 0
.../cypress/fixtures/visit_error.html | 0
.../{test => }/cypress/fixtures/webcam.html | 0
.../{test => }/cypress/fixtures/wtf.html | 0
.../cypress/fixtures/xhr-triggered.html | 0
.../{test => }/cypress/fixtures/xhr.html | 0
.../{test => }/cypress/fixtures/yaml.yaml | 0
.../{test => }/cypress/fixtures/zonejs.html | 0
.../commands/actions/check_spec.js | 0
.../commands/actions/clear_spec.js | 0
.../commands/actions/click_spec.js | 0
.../commands/actions/focus_spec.js | 0
.../commands/actions/hover_spec.js | 0
.../commands/actions/scroll_spec.js | 0
.../commands/actions/select_spec.js | 0
.../commands/actions/submit_spec.js | 0
.../commands/actions/trigger_spec.js | 0
.../commands/actions/type_errors_spec.js | 0
.../integration/commands/actions/type_spec.js | 0
.../actions/type_special_chars_spec.js | 0
.../integration/commands/agents_spec.js | 0
.../integration/commands/aliasing_spec.js | 0
.../integration/commands/angular_spec.js | 0
.../integration/commands/assertions_spec.js | 0
.../integration/commands/clock_spec.js | 0
.../commands/command_option_immutability.js | 0
.../integration/commands/commands_spec.js | 0
.../integration/commands/connectors_spec.js | 0
.../integration/commands/cookies_spec.js | 0
.../integration/commands/debugging_spec.js | 0
.../cypress/integration/commands/exec_spec.js | 0
.../integration/commands/files_spec.js | 0
.../integration/commands/fixtures_spec.js | 0
.../commands/local_storage_spec.js | 0
.../integration/commands/location_spec.js | 0
.../cypress/integration/commands/misc_spec.js | 0
.../integration/commands/navigation_spec.js | 0
.../integration/commands/popups_spec.js | 0
.../integration/commands/querying_spec.js | 0
.../integration/commands/request_spec.js | 0
.../integration/commands/screenshot_spec.js | 0
.../cypress/integration/commands/task_spec.js | 0
.../integration/commands/traversals_spec.js | 0
.../integration/commands/waiting_spec.js | 0
.../integration/commands/window_spec.js | 0
.../cypress/integration/commands/xhr_spec.js | 6 ++-
.../integration/cy/snapshot_css_spec.js | 0
.../cypress/integration/cy/snapshot_spec.js | 0
.../cypress/integration/cy/timeouts_spec.js | 0
.../cypress/integration/cy/timers_spec.js | 0
.../integration/cypress/browser_spec.js | 0
.../cypress/integration/cypress/cy_spec.js | 0
.../integration/cypress/cypress_spec.js | 0
.../integration/cypress/error_utils_spec.js | 0
.../integration/cypress/location_spec.js | 0
.../cypress/integration/cypress/log_spec.js | 0
.../integration/cypress/network_utils_spec.js | 0
.../integration/cypress/resolvers_spec.ts | 0
.../integration/cypress/runner_spec.js | 0
.../integration/cypress/screenshot_spec.js | 0
.../integration/cypress/script_utils_spec.js | 0
.../cypress/selector_playground_spec.js | 0
.../cypress/source_map_utils_spec.js | 0
.../integration/cypress/stack_utils_spec.js | 0
.../cypress/integration/cypress/utils_spec.js | 0
.../cypress/xml_http_request_spec.js | 0
.../integration/dom/coordinates_spec.ts | 0
.../cypress/integration/dom/elements_spec.ts | 0
.../cypress/integration/dom/jquery_spec.ts | 0
.../integration/dom/visibility_spec.ts | 0
.../abort_beforeunload_event_child_spec.ts | 0
.../e2e/abort_beforeunload_event_spec.ts | 0
.../e2e/abort_onbeforeunload_child_spec.ts | 0
.../e2e/abort_onbeforeunload_spec.ts | 0
.../integration/e2e/cancelation_spec.js | 0
.../integration/e2e/dom_hitbox.spec.js | 0
.../integration/e2e/ended_early_spec.js | 0
.../integration/e2e/focus_blur_spec.js | 0
.../cypress/integration/e2e/keyboard_spec.js | 0
.../cypress/integration/e2e/promises_spec.js | 0
.../cypress/integration/e2e/react-15_spec.js | 0
.../cypress/integration/e2e/react-16_spec.js | 0
.../cypress/integration/e2e/redirects_spec.js | 0
.../cypress/integration/e2e/rerun_spec.js | 0
.../integration/e2e/return_value_spec.js | 0
.../cypress/integration/e2e/security_spec.js | 0
.../e2e/selector_playground.spec.js | 0
.../e2e/testConfigOverrides.spec.js | 0
.../cypress/integration/e2e/text_mask_spec.js | 0
.../integration/e2e/uncaught_errors_spec.js | 0
.../cypress/integration/e2e/video_spec.js | 0
.../integration/e2e/visibility_spec.js | 0
.../cypress/integration/e2e/webcam_spec.js | 0
.../cypress/integration/e2e/zonejs_spec.js | 0
.../cypress/integration/issues/1436_spec.js | 0
.../cypress/integration/issues/1854_spec.js | 0
.../cypress/integration/issues/1909_spec.js | 0
.../integration/issues/1939_1940_2190_spec.js | 0
.../cypress/integration/issues/2582_spec.js | 0
.../cypress/integration/issues/2784_spec.js | 0
.../cypress/integration/issues/2850_spec.js | 0
.../cypress/integration/issues/3253_spec.js | 0
.../cypress/integration/issues/3847_spec.js | 0
.../cypress/integration/issues/4373_spec.js | 0
.../cypress/integration/issues/510_spec.js | 0
.../cypress/integration/issues/565_spec.js | 0
.../cypress/integration/issues/5682_spec.js | 0
.../cypress/integration/issues/5707_spec.js | 0
.../cypress/integration/issues/573_spec.js | 0
.../cypress/integration/issues/599_spec.js | 0
.../cypress/integration/issues/652_spec.js | 0
.../integration/issues/761_2968_3973_spec.js | 0
.../util/firefox_forced_gc_spec.ts | 0
.../integration/util/limited_map_spec.js | 0
.../{test => }/cypress/plugins/index.js | 2 +-
.../support => cypress/plugins}/server.js | 13 +++--
.../{test => }/cypress/support/defaults.js | 0
.../{test => }/cypress/support/helpers.js | 0
.../{test => }/cypress/support/index.js | 0
.../{test => }/cypress/support/utils.js | 0
.../videos/commands/navigation_spec.js.mp4 | Bin 0 -> 1646621 bytes
packages/driver/package.json | 2 +-
packages/driver/src/cy/chai.js | 2 +-
packages/driver/test/cypress/.eslintrc.json | 8 ---
.../driver/test/support/unit_spec_helper.js | 25 ---------
packages/driver/test/support/watch | 3 --
193 files changed, 51 insertions(+), 77 deletions(-)
rename packages/driver/{test => }/cypress.json (58%)
rename packages/driver/{test => cypress}/.eslintrc.json (58%)
rename packages/driver/{test => }/cypress/fixtures/active-elements.html (100%)
rename packages/driver/{test => }/cypress/fixtures/angular.html (100%)
rename packages/driver/{test => }/cypress/fixtures/app.js (100%)
rename packages/driver/{test => }/cypress/fixtures/app.json (100%)
rename packages/driver/{test => }/cypress/fixtures/blocking_beforeunload_event.html (100%)
rename packages/driver/{test => }/cypress/fixtures/blocking_onbeforeunload.html (100%)
rename packages/driver/{test => }/cypress/fixtures/comma-separated.csv (100%)
rename packages/driver/{test => }/cypress/fixtures/command-log.html (100%)
rename packages/driver/{test => }/cypress/fixtures/commands.html (100%)
rename packages/driver/{test => }/cypress/fixtures/cross_origin.html (100%)
rename packages/driver/{test => }/cypress/fixtures/cross_origin_name.html (100%)
rename packages/driver/{test => }/cypress/fixtures/dangling-element.html (100%)
rename packages/driver/{test => }/cypress/fixtures/dimensions.html (100%)
rename packages/driver/{test => }/cypress/fixtures/dom.html (100%)
rename packages/driver/{test => }/cypress/fixtures/dropdown.html (100%)
rename packages/driver/{test => }/cypress/fixtures/example.json (100%)
rename packages/driver/{test => }/cypress/fixtures/fixed-nav.html (100%)
rename packages/driver/{test => }/cypress/fixtures/foo.bar.baz.json (100%)
rename packages/driver/{test => }/cypress/fixtures/form.html (100%)
rename packages/driver/{test => }/cypress/fixtures/generic.html (100%)
rename packages/driver/{test => }/cypress/fixtures/generic_styles.css (100%)
rename packages/driver/{test => }/cypress/fixtures/generic_styles_2.css (100%)
rename packages/driver/{test => }/cypress/fixtures/generic_styles_3.css (100%)
rename packages/driver/{test => }/cypress/fixtures/generic_styles_print.css (100%)
rename packages/driver/{test => }/cypress/fixtures/global-error.html (100%)
rename packages/driver/{test => }/cypress/fixtures/hook_uncaught.html (100%)
rename packages/driver/{test => }/cypress/fixtures/iframe-inner.html (100%)
rename packages/driver/{test => }/cypress/fixtures/iframe-outer.html (100%)
rename packages/driver/{test => }/cypress/fixtures/invalid_html.html (100%)
rename packages/driver/{test => }/cypress/fixtures/issue-1436.html (100%)
rename packages/driver/{test => }/cypress/fixtures/issue-2956.html (100%)
rename packages/driver/{test => }/cypress/fixtures/issue-4373.html (100%)
rename packages/driver/{test => }/cypress/fixtures/issue-565.html (100%)
rename packages/driver/{test => }/cypress/fixtures/issue-5682.html (100%)
rename packages/driver/{test => }/cypress/fixtures/issue-5707.html (100%)
rename packages/driver/{test => }/cypress/fixtures/issue-599.html (100%)
rename packages/driver/{test => }/cypress/fixtures/issue-652.html (100%)
rename packages/driver/{test => }/cypress/fixtures/jquery-3.2.1.js (100%)
rename packages/driver/{test => }/cypress/fixtures/jquery.html (100%)
rename packages/driver/{test => }/cypress/fixtures/js-redirect-timeout.html (100%)
rename packages/driver/{test => }/cypress/fixtures/js-redirect.html (100%)
rename packages/driver/{test => }/cypress/fixtures/media/small.mp4 (100%)
rename packages/driver/{test => }/cypress/fixtures/meta-redirect-timeout.html (100%)
rename packages/driver/{test => }/cypress/fixtures/meta-redirect.html (100%)
rename packages/driver/{test => }/cypress/fixtures/mui.html (100%)
rename packages/driver/{test => }/cypress/fixtures/nested/with_paths.css (100%)
rename packages/driver/{test => }/cypress/fixtures/no_html.html (100%)
rename packages/driver/{test => }/cypress/fixtures/null.json (100%)
rename packages/driver/{test => }/cypress/fixtures/problematic-source-map-urls.txt (100%)
rename packages/driver/{test => }/cypress/fixtures/react-15.html (100%)
rename packages/driver/{test => }/cypress/fixtures/react-16.html (100%)
rename packages/driver/{test => }/cypress/fixtures/screenshots.html (100%)
rename packages/driver/{test => }/cypress/fixtures/scrolling.html (100%)
rename packages/driver/{test => }/cypress/fixtures/security.html (100%)
rename packages/driver/{test => }/cypress/fixtures/security.js (100%)
rename packages/driver/{test => }/cypress/fixtures/shadow-dom.html (100%)
rename packages/driver/{test => }/cypress/fixtures/sinon.html (100%)
rename packages/driver/{test => }/cypress/fixtures/sync_error.html (100%)
rename packages/driver/{test => }/cypress/fixtures/text-mask.html (100%)
rename packages/driver/{test => }/cypress/fixtures/type.html (100%)
rename packages/driver/{test => }/cypress/fixtures/valid.json (100%)
rename packages/driver/{test => }/cypress/fixtures/video.html (100%)
rename packages/driver/{test => }/cypress/fixtures/visibility.html (100%)
rename packages/driver/{test => }/cypress/fixtures/visit_error.html (100%)
rename packages/driver/{test => }/cypress/fixtures/webcam.html (100%)
rename packages/driver/{test => }/cypress/fixtures/wtf.html (100%)
rename packages/driver/{test => }/cypress/fixtures/xhr-triggered.html (100%)
rename packages/driver/{test => }/cypress/fixtures/xhr.html (100%)
rename packages/driver/{test => }/cypress/fixtures/yaml.yaml (100%)
rename packages/driver/{test => }/cypress/fixtures/zonejs.html (100%)
rename packages/driver/{test => }/cypress/integration/commands/actions/check_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/actions/clear_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/actions/click_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/actions/focus_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/actions/hover_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/actions/scroll_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/actions/select_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/actions/submit_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/actions/trigger_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/actions/type_errors_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/actions/type_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/actions/type_special_chars_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/agents_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/aliasing_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/angular_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/assertions_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/clock_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/command_option_immutability.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/commands_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/connectors_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/cookies_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/debugging_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/exec_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/files_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/fixtures_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/local_storage_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/location_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/misc_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/navigation_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/popups_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/querying_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/request_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/screenshot_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/task_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/traversals_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/waiting_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/window_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/commands/xhr_spec.js (99%)
rename packages/driver/{test => }/cypress/integration/cy/snapshot_css_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cy/snapshot_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cy/timeouts_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cy/timers_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cypress/browser_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cypress/cy_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cypress/cypress_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cypress/error_utils_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cypress/location_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cypress/log_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cypress/network_utils_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cypress/resolvers_spec.ts (100%)
rename packages/driver/{test => }/cypress/integration/cypress/runner_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cypress/screenshot_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cypress/script_utils_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cypress/selector_playground_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cypress/source_map_utils_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cypress/stack_utils_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cypress/utils_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/cypress/xml_http_request_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/dom/coordinates_spec.ts (100%)
rename packages/driver/{test => }/cypress/integration/dom/elements_spec.ts (100%)
rename packages/driver/{test => }/cypress/integration/dom/jquery_spec.ts (100%)
rename packages/driver/{test => }/cypress/integration/dom/visibility_spec.ts (100%)
rename packages/driver/{test => }/cypress/integration/e2e/abort_beforeunload_event_child_spec.ts (100%)
rename packages/driver/{test => }/cypress/integration/e2e/abort_beforeunload_event_spec.ts (100%)
rename packages/driver/{test => }/cypress/integration/e2e/abort_onbeforeunload_child_spec.ts (100%)
rename packages/driver/{test => }/cypress/integration/e2e/abort_onbeforeunload_spec.ts (100%)
rename packages/driver/{test => }/cypress/integration/e2e/cancelation_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/dom_hitbox.spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/ended_early_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/focus_blur_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/keyboard_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/promises_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/react-15_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/react-16_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/redirects_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/rerun_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/return_value_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/security_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/selector_playground.spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/testConfigOverrides.spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/text_mask_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/uncaught_errors_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/video_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/visibility_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/webcam_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/e2e/zonejs_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/1436_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/1854_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/1909_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/1939_1940_2190_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/2582_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/2784_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/2850_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/3253_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/3847_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/4373_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/510_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/565_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/5682_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/5707_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/573_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/599_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/652_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/issues/761_2968_3973_spec.js (100%)
rename packages/driver/{test => }/cypress/integration/util/firefox_forced_gc_spec.ts (100%)
rename packages/driver/{test => }/cypress/integration/util/limited_map_spec.js (100%)
rename packages/driver/{test => }/cypress/plugins/index.js (98%)
rename packages/driver/{test/support => cypress/plugins}/server.js (89%)
rename packages/driver/{test => }/cypress/support/defaults.js (100%)
rename packages/driver/{test => }/cypress/support/helpers.js (100%)
rename packages/driver/{test => }/cypress/support/index.js (100%)
rename packages/driver/{test => }/cypress/support/utils.js (100%)
create mode 100644 packages/driver/cypress/videos/commands/navigation_spec.js.mp4
delete mode 100644 packages/driver/test/cypress/.eslintrc.json
delete mode 100644 packages/driver/test/support/unit_spec_helper.js
delete mode 100755 packages/driver/test/support/watch
diff --git a/.gitignore b/.gitignore
index f6c53b9cacbb..2fec763ebab1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,8 +17,8 @@ packages/desktop-gui/cypress/videos
packages/desktop-gui/src/jsconfig.json
# from driver
-packages/driver/test/cypress/videos
-packages/driver/test/cypress/screenshots
+packages/driver/cypress/videos
+packages/driver/cypress/screenshots
# from example
packages/example/app
diff --git a/circle.yml b/circle.yml
index 061571d34424..1e6d86299ad1 100644
--- a/circle.yml
+++ b/circle.yml
@@ -114,7 +114,7 @@ commands:
yarn cypress:run --record --parallel --group 5x-driver-<< parameters.browser >> --browser << parameters.browser >>
else
# external PR
- TESTFILES=$(circleci tests glob "test/cypress/integration/**/*_spec.*" | circleci tests split --total=$CIRCLE_NODE_TOTAL)
+ TESTFILES=$(circleci tests glob "cypress/integration/**/*_spec.*" | circleci tests split --total=$CIRCLE_NODE_TOTAL)
echo "Test files for this machine are $TESTFILES"
if [[ -z "$TESTFILES" ]]; then
@@ -290,29 +290,28 @@ commands:
- unless:
condition: << parameters.folder >>
steps:
- - when:
- condition: << parameters.browser >>
- steps:
- - run:
- name: Run tests using browser "<< parameters.browser >>"
- working_directory: /tmp/<>
- command: <> -- --browser <>
- - unless:
- condition: << parameters.browser >>
- steps:
- - run:
- name: Run tests using command
- working_directory: /tmp/<>
- command: <>
- - store_artifacts:
- name: screenshots
- path: /tmp/<>/cypress/screenshots
- - store_artifacts:
- name: videos
- path: /tmp/<>/cypress/videos
+ - when:
+ condition: << parameters.browser >>
+ steps:
+ - run:
+ name: Run tests using browser "<< parameters.browser >>"
+ working_directory: /tmp/<>
+ command: <> -- --browser <>
+ - unless:
+ condition: << parameters.browser >>
+ steps:
+ - run:
+ name: Run tests using command
+ working_directory: /tmp/<>
+ command: <>
+ - store_artifacts:
+ name: screenshots
+ path: /tmp/<>/cypress/screenshots
+ - store_artifacts:
+ name: videos
+ path: /tmp/<>/cypress/videos
- store-npm-logs
-
jobs:
## code checkout and yarn installs
build:
@@ -482,8 +481,8 @@ jobs:
- store-npm-logs
"server-performance-tests":
- <<: *defaults
- steps:
+ <<: *defaults
+ steps:
- attach_workspace:
at: ~/
- run:
@@ -1188,7 +1187,7 @@ jobs:
name: Scaffold and test examples 🏗
working_directory: << parameters.wd >>
environment:
- CYPRESS_INTERNAL_FORCE_SCAFFOLD: '1'
+ CYPRESS_INTERNAL_FORCE_SCAFFOLD: "1"
command: |
echo '{}' > cypress.json
npx cypress run
diff --git a/packages/driver/.gitignore b/packages/driver/.gitignore
index 1404cb71a490..944d461994e6 100644
--- a/packages/driver/.gitignore
+++ b/packages/driver/.gitignore
@@ -1,2 +1,2 @@
-test/cypress/videos
-test/cypress/screenshots
+cypress/videos
+cypress/screenshots
diff --git a/packages/driver/test/cypress.json b/packages/driver/cypress.json
similarity index 58%
rename from packages/driver/test/cypress.json
rename to packages/driver/cypress.json
index ed128fb0a96f..7eac07ef42f9 100644
--- a/packages/driver/test/cypress.json
+++ b/packages/driver/cypress.json
@@ -4,9 +4,9 @@
"hosts": {
"*.foobar.com": "127.0.0.1"
},
- "reporter": "../../../node_modules/cypress-multi-reporters/index.js",
+ "reporter": "cypress-multi-reporters",
"reporterOptions": {
- "configFile": "../../../mocha-reporter-config.json"
+ "configFile": "../../mocha-reporter-config.json"
},
"experimentalShadowDomSupport": true
}
diff --git a/packages/driver/test/.eslintrc.json b/packages/driver/cypress/.eslintrc.json
similarity index 58%
rename from packages/driver/test/.eslintrc.json
rename to packages/driver/cypress/.eslintrc.json
index f7cfa5c3a5fe..9b012d59420f 100644
--- a/packages/driver/test/.eslintrc.json
+++ b/packages/driver/cypress/.eslintrc.json
@@ -1,7 +1,13 @@
{
+ "plugins": [
+ "cypress"
+ ],
"extends": [
"plugin:@cypress/dev/tests"
],
+ "env": {
+ "cypress/globals": true
+ },
"rules": {
"mocha/no-global-tests": "off"
}
diff --git a/packages/driver/test/cypress/fixtures/active-elements.html b/packages/driver/cypress/fixtures/active-elements.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/active-elements.html
rename to packages/driver/cypress/fixtures/active-elements.html
diff --git a/packages/driver/test/cypress/fixtures/angular.html b/packages/driver/cypress/fixtures/angular.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/angular.html
rename to packages/driver/cypress/fixtures/angular.html
diff --git a/packages/driver/test/cypress/fixtures/app.js b/packages/driver/cypress/fixtures/app.js
similarity index 100%
rename from packages/driver/test/cypress/fixtures/app.js
rename to packages/driver/cypress/fixtures/app.js
diff --git a/packages/driver/test/cypress/fixtures/app.json b/packages/driver/cypress/fixtures/app.json
similarity index 100%
rename from packages/driver/test/cypress/fixtures/app.json
rename to packages/driver/cypress/fixtures/app.json
diff --git a/packages/driver/test/cypress/fixtures/blocking_beforeunload_event.html b/packages/driver/cypress/fixtures/blocking_beforeunload_event.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/blocking_beforeunload_event.html
rename to packages/driver/cypress/fixtures/blocking_beforeunload_event.html
diff --git a/packages/driver/test/cypress/fixtures/blocking_onbeforeunload.html b/packages/driver/cypress/fixtures/blocking_onbeforeunload.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/blocking_onbeforeunload.html
rename to packages/driver/cypress/fixtures/blocking_onbeforeunload.html
diff --git a/packages/driver/test/cypress/fixtures/comma-separated.csv b/packages/driver/cypress/fixtures/comma-separated.csv
similarity index 100%
rename from packages/driver/test/cypress/fixtures/comma-separated.csv
rename to packages/driver/cypress/fixtures/comma-separated.csv
diff --git a/packages/driver/test/cypress/fixtures/command-log.html b/packages/driver/cypress/fixtures/command-log.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/command-log.html
rename to packages/driver/cypress/fixtures/command-log.html
diff --git a/packages/driver/test/cypress/fixtures/commands.html b/packages/driver/cypress/fixtures/commands.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/commands.html
rename to packages/driver/cypress/fixtures/commands.html
diff --git a/packages/driver/test/cypress/fixtures/cross_origin.html b/packages/driver/cypress/fixtures/cross_origin.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/cross_origin.html
rename to packages/driver/cypress/fixtures/cross_origin.html
diff --git a/packages/driver/test/cypress/fixtures/cross_origin_name.html b/packages/driver/cypress/fixtures/cross_origin_name.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/cross_origin_name.html
rename to packages/driver/cypress/fixtures/cross_origin_name.html
diff --git a/packages/driver/test/cypress/fixtures/dangling-element.html b/packages/driver/cypress/fixtures/dangling-element.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/dangling-element.html
rename to packages/driver/cypress/fixtures/dangling-element.html
diff --git a/packages/driver/test/cypress/fixtures/dimensions.html b/packages/driver/cypress/fixtures/dimensions.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/dimensions.html
rename to packages/driver/cypress/fixtures/dimensions.html
diff --git a/packages/driver/test/cypress/fixtures/dom.html b/packages/driver/cypress/fixtures/dom.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/dom.html
rename to packages/driver/cypress/fixtures/dom.html
diff --git a/packages/driver/test/cypress/fixtures/dropdown.html b/packages/driver/cypress/fixtures/dropdown.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/dropdown.html
rename to packages/driver/cypress/fixtures/dropdown.html
diff --git a/packages/driver/test/cypress/fixtures/example.json b/packages/driver/cypress/fixtures/example.json
similarity index 100%
rename from packages/driver/test/cypress/fixtures/example.json
rename to packages/driver/cypress/fixtures/example.json
diff --git a/packages/driver/test/cypress/fixtures/fixed-nav.html b/packages/driver/cypress/fixtures/fixed-nav.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/fixed-nav.html
rename to packages/driver/cypress/fixtures/fixed-nav.html
diff --git a/packages/driver/test/cypress/fixtures/foo.bar.baz.json b/packages/driver/cypress/fixtures/foo.bar.baz.json
similarity index 100%
rename from packages/driver/test/cypress/fixtures/foo.bar.baz.json
rename to packages/driver/cypress/fixtures/foo.bar.baz.json
diff --git a/packages/driver/test/cypress/fixtures/form.html b/packages/driver/cypress/fixtures/form.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/form.html
rename to packages/driver/cypress/fixtures/form.html
diff --git a/packages/driver/test/cypress/fixtures/generic.html b/packages/driver/cypress/fixtures/generic.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/generic.html
rename to packages/driver/cypress/fixtures/generic.html
diff --git a/packages/driver/test/cypress/fixtures/generic_styles.css b/packages/driver/cypress/fixtures/generic_styles.css
similarity index 100%
rename from packages/driver/test/cypress/fixtures/generic_styles.css
rename to packages/driver/cypress/fixtures/generic_styles.css
diff --git a/packages/driver/test/cypress/fixtures/generic_styles_2.css b/packages/driver/cypress/fixtures/generic_styles_2.css
similarity index 100%
rename from packages/driver/test/cypress/fixtures/generic_styles_2.css
rename to packages/driver/cypress/fixtures/generic_styles_2.css
diff --git a/packages/driver/test/cypress/fixtures/generic_styles_3.css b/packages/driver/cypress/fixtures/generic_styles_3.css
similarity index 100%
rename from packages/driver/test/cypress/fixtures/generic_styles_3.css
rename to packages/driver/cypress/fixtures/generic_styles_3.css
diff --git a/packages/driver/test/cypress/fixtures/generic_styles_print.css b/packages/driver/cypress/fixtures/generic_styles_print.css
similarity index 100%
rename from packages/driver/test/cypress/fixtures/generic_styles_print.css
rename to packages/driver/cypress/fixtures/generic_styles_print.css
diff --git a/packages/driver/test/cypress/fixtures/global-error.html b/packages/driver/cypress/fixtures/global-error.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/global-error.html
rename to packages/driver/cypress/fixtures/global-error.html
diff --git a/packages/driver/test/cypress/fixtures/hook_uncaught.html b/packages/driver/cypress/fixtures/hook_uncaught.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/hook_uncaught.html
rename to packages/driver/cypress/fixtures/hook_uncaught.html
diff --git a/packages/driver/test/cypress/fixtures/iframe-inner.html b/packages/driver/cypress/fixtures/iframe-inner.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/iframe-inner.html
rename to packages/driver/cypress/fixtures/iframe-inner.html
diff --git a/packages/driver/test/cypress/fixtures/iframe-outer.html b/packages/driver/cypress/fixtures/iframe-outer.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/iframe-outer.html
rename to packages/driver/cypress/fixtures/iframe-outer.html
diff --git a/packages/driver/test/cypress/fixtures/invalid_html.html b/packages/driver/cypress/fixtures/invalid_html.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/invalid_html.html
rename to packages/driver/cypress/fixtures/invalid_html.html
diff --git a/packages/driver/test/cypress/fixtures/issue-1436.html b/packages/driver/cypress/fixtures/issue-1436.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/issue-1436.html
rename to packages/driver/cypress/fixtures/issue-1436.html
diff --git a/packages/driver/test/cypress/fixtures/issue-2956.html b/packages/driver/cypress/fixtures/issue-2956.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/issue-2956.html
rename to packages/driver/cypress/fixtures/issue-2956.html
diff --git a/packages/driver/test/cypress/fixtures/issue-4373.html b/packages/driver/cypress/fixtures/issue-4373.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/issue-4373.html
rename to packages/driver/cypress/fixtures/issue-4373.html
diff --git a/packages/driver/test/cypress/fixtures/issue-565.html b/packages/driver/cypress/fixtures/issue-565.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/issue-565.html
rename to packages/driver/cypress/fixtures/issue-565.html
diff --git a/packages/driver/test/cypress/fixtures/issue-5682.html b/packages/driver/cypress/fixtures/issue-5682.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/issue-5682.html
rename to packages/driver/cypress/fixtures/issue-5682.html
diff --git a/packages/driver/test/cypress/fixtures/issue-5707.html b/packages/driver/cypress/fixtures/issue-5707.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/issue-5707.html
rename to packages/driver/cypress/fixtures/issue-5707.html
diff --git a/packages/driver/test/cypress/fixtures/issue-599.html b/packages/driver/cypress/fixtures/issue-599.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/issue-599.html
rename to packages/driver/cypress/fixtures/issue-599.html
diff --git a/packages/driver/test/cypress/fixtures/issue-652.html b/packages/driver/cypress/fixtures/issue-652.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/issue-652.html
rename to packages/driver/cypress/fixtures/issue-652.html
diff --git a/packages/driver/test/cypress/fixtures/jquery-3.2.1.js b/packages/driver/cypress/fixtures/jquery-3.2.1.js
similarity index 100%
rename from packages/driver/test/cypress/fixtures/jquery-3.2.1.js
rename to packages/driver/cypress/fixtures/jquery-3.2.1.js
diff --git a/packages/driver/test/cypress/fixtures/jquery.html b/packages/driver/cypress/fixtures/jquery.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/jquery.html
rename to packages/driver/cypress/fixtures/jquery.html
diff --git a/packages/driver/test/cypress/fixtures/js-redirect-timeout.html b/packages/driver/cypress/fixtures/js-redirect-timeout.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/js-redirect-timeout.html
rename to packages/driver/cypress/fixtures/js-redirect-timeout.html
diff --git a/packages/driver/test/cypress/fixtures/js-redirect.html b/packages/driver/cypress/fixtures/js-redirect.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/js-redirect.html
rename to packages/driver/cypress/fixtures/js-redirect.html
diff --git a/packages/driver/test/cypress/fixtures/media/small.mp4 b/packages/driver/cypress/fixtures/media/small.mp4
similarity index 100%
rename from packages/driver/test/cypress/fixtures/media/small.mp4
rename to packages/driver/cypress/fixtures/media/small.mp4
diff --git a/packages/driver/test/cypress/fixtures/meta-redirect-timeout.html b/packages/driver/cypress/fixtures/meta-redirect-timeout.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/meta-redirect-timeout.html
rename to packages/driver/cypress/fixtures/meta-redirect-timeout.html
diff --git a/packages/driver/test/cypress/fixtures/meta-redirect.html b/packages/driver/cypress/fixtures/meta-redirect.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/meta-redirect.html
rename to packages/driver/cypress/fixtures/meta-redirect.html
diff --git a/packages/driver/test/cypress/fixtures/mui.html b/packages/driver/cypress/fixtures/mui.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/mui.html
rename to packages/driver/cypress/fixtures/mui.html
diff --git a/packages/driver/test/cypress/fixtures/nested/with_paths.css b/packages/driver/cypress/fixtures/nested/with_paths.css
similarity index 100%
rename from packages/driver/test/cypress/fixtures/nested/with_paths.css
rename to packages/driver/cypress/fixtures/nested/with_paths.css
diff --git a/packages/driver/test/cypress/fixtures/no_html.html b/packages/driver/cypress/fixtures/no_html.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/no_html.html
rename to packages/driver/cypress/fixtures/no_html.html
diff --git a/packages/driver/test/cypress/fixtures/null.json b/packages/driver/cypress/fixtures/null.json
similarity index 100%
rename from packages/driver/test/cypress/fixtures/null.json
rename to packages/driver/cypress/fixtures/null.json
diff --git a/packages/driver/test/cypress/fixtures/problematic-source-map-urls.txt b/packages/driver/cypress/fixtures/problematic-source-map-urls.txt
similarity index 100%
rename from packages/driver/test/cypress/fixtures/problematic-source-map-urls.txt
rename to packages/driver/cypress/fixtures/problematic-source-map-urls.txt
diff --git a/packages/driver/test/cypress/fixtures/react-15.html b/packages/driver/cypress/fixtures/react-15.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/react-15.html
rename to packages/driver/cypress/fixtures/react-15.html
diff --git a/packages/driver/test/cypress/fixtures/react-16.html b/packages/driver/cypress/fixtures/react-16.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/react-16.html
rename to packages/driver/cypress/fixtures/react-16.html
diff --git a/packages/driver/test/cypress/fixtures/screenshots.html b/packages/driver/cypress/fixtures/screenshots.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/screenshots.html
rename to packages/driver/cypress/fixtures/screenshots.html
diff --git a/packages/driver/test/cypress/fixtures/scrolling.html b/packages/driver/cypress/fixtures/scrolling.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/scrolling.html
rename to packages/driver/cypress/fixtures/scrolling.html
diff --git a/packages/driver/test/cypress/fixtures/security.html b/packages/driver/cypress/fixtures/security.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/security.html
rename to packages/driver/cypress/fixtures/security.html
diff --git a/packages/driver/test/cypress/fixtures/security.js b/packages/driver/cypress/fixtures/security.js
similarity index 100%
rename from packages/driver/test/cypress/fixtures/security.js
rename to packages/driver/cypress/fixtures/security.js
diff --git a/packages/driver/test/cypress/fixtures/shadow-dom.html b/packages/driver/cypress/fixtures/shadow-dom.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/shadow-dom.html
rename to packages/driver/cypress/fixtures/shadow-dom.html
diff --git a/packages/driver/test/cypress/fixtures/sinon.html b/packages/driver/cypress/fixtures/sinon.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/sinon.html
rename to packages/driver/cypress/fixtures/sinon.html
diff --git a/packages/driver/test/cypress/fixtures/sync_error.html b/packages/driver/cypress/fixtures/sync_error.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/sync_error.html
rename to packages/driver/cypress/fixtures/sync_error.html
diff --git a/packages/driver/test/cypress/fixtures/text-mask.html b/packages/driver/cypress/fixtures/text-mask.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/text-mask.html
rename to packages/driver/cypress/fixtures/text-mask.html
diff --git a/packages/driver/test/cypress/fixtures/type.html b/packages/driver/cypress/fixtures/type.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/type.html
rename to packages/driver/cypress/fixtures/type.html
diff --git a/packages/driver/test/cypress/fixtures/valid.json b/packages/driver/cypress/fixtures/valid.json
similarity index 100%
rename from packages/driver/test/cypress/fixtures/valid.json
rename to packages/driver/cypress/fixtures/valid.json
diff --git a/packages/driver/test/cypress/fixtures/video.html b/packages/driver/cypress/fixtures/video.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/video.html
rename to packages/driver/cypress/fixtures/video.html
diff --git a/packages/driver/test/cypress/fixtures/visibility.html b/packages/driver/cypress/fixtures/visibility.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/visibility.html
rename to packages/driver/cypress/fixtures/visibility.html
diff --git a/packages/driver/test/cypress/fixtures/visit_error.html b/packages/driver/cypress/fixtures/visit_error.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/visit_error.html
rename to packages/driver/cypress/fixtures/visit_error.html
diff --git a/packages/driver/test/cypress/fixtures/webcam.html b/packages/driver/cypress/fixtures/webcam.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/webcam.html
rename to packages/driver/cypress/fixtures/webcam.html
diff --git a/packages/driver/test/cypress/fixtures/wtf.html b/packages/driver/cypress/fixtures/wtf.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/wtf.html
rename to packages/driver/cypress/fixtures/wtf.html
diff --git a/packages/driver/test/cypress/fixtures/xhr-triggered.html b/packages/driver/cypress/fixtures/xhr-triggered.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/xhr-triggered.html
rename to packages/driver/cypress/fixtures/xhr-triggered.html
diff --git a/packages/driver/test/cypress/fixtures/xhr.html b/packages/driver/cypress/fixtures/xhr.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/xhr.html
rename to packages/driver/cypress/fixtures/xhr.html
diff --git a/packages/driver/test/cypress/fixtures/yaml.yaml b/packages/driver/cypress/fixtures/yaml.yaml
similarity index 100%
rename from packages/driver/test/cypress/fixtures/yaml.yaml
rename to packages/driver/cypress/fixtures/yaml.yaml
diff --git a/packages/driver/test/cypress/fixtures/zonejs.html b/packages/driver/cypress/fixtures/zonejs.html
similarity index 100%
rename from packages/driver/test/cypress/fixtures/zonejs.html
rename to packages/driver/cypress/fixtures/zonejs.html
diff --git a/packages/driver/test/cypress/integration/commands/actions/check_spec.js b/packages/driver/cypress/integration/commands/actions/check_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/actions/check_spec.js
rename to packages/driver/cypress/integration/commands/actions/check_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/actions/clear_spec.js b/packages/driver/cypress/integration/commands/actions/clear_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/actions/clear_spec.js
rename to packages/driver/cypress/integration/commands/actions/clear_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/actions/click_spec.js b/packages/driver/cypress/integration/commands/actions/click_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/actions/click_spec.js
rename to packages/driver/cypress/integration/commands/actions/click_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/actions/focus_spec.js b/packages/driver/cypress/integration/commands/actions/focus_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/actions/focus_spec.js
rename to packages/driver/cypress/integration/commands/actions/focus_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/actions/hover_spec.js b/packages/driver/cypress/integration/commands/actions/hover_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/actions/hover_spec.js
rename to packages/driver/cypress/integration/commands/actions/hover_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/actions/scroll_spec.js b/packages/driver/cypress/integration/commands/actions/scroll_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/actions/scroll_spec.js
rename to packages/driver/cypress/integration/commands/actions/scroll_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/actions/select_spec.js b/packages/driver/cypress/integration/commands/actions/select_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/actions/select_spec.js
rename to packages/driver/cypress/integration/commands/actions/select_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/actions/submit_spec.js b/packages/driver/cypress/integration/commands/actions/submit_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/actions/submit_spec.js
rename to packages/driver/cypress/integration/commands/actions/submit_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/actions/trigger_spec.js b/packages/driver/cypress/integration/commands/actions/trigger_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/actions/trigger_spec.js
rename to packages/driver/cypress/integration/commands/actions/trigger_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/actions/type_errors_spec.js b/packages/driver/cypress/integration/commands/actions/type_errors_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/actions/type_errors_spec.js
rename to packages/driver/cypress/integration/commands/actions/type_errors_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/actions/type_spec.js b/packages/driver/cypress/integration/commands/actions/type_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/actions/type_spec.js
rename to packages/driver/cypress/integration/commands/actions/type_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/actions/type_special_chars_spec.js b/packages/driver/cypress/integration/commands/actions/type_special_chars_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/actions/type_special_chars_spec.js
rename to packages/driver/cypress/integration/commands/actions/type_special_chars_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/agents_spec.js b/packages/driver/cypress/integration/commands/agents_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/agents_spec.js
rename to packages/driver/cypress/integration/commands/agents_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/aliasing_spec.js b/packages/driver/cypress/integration/commands/aliasing_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/aliasing_spec.js
rename to packages/driver/cypress/integration/commands/aliasing_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/angular_spec.js b/packages/driver/cypress/integration/commands/angular_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/angular_spec.js
rename to packages/driver/cypress/integration/commands/angular_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/assertions_spec.js b/packages/driver/cypress/integration/commands/assertions_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/assertions_spec.js
rename to packages/driver/cypress/integration/commands/assertions_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/clock_spec.js b/packages/driver/cypress/integration/commands/clock_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/clock_spec.js
rename to packages/driver/cypress/integration/commands/clock_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/command_option_immutability.js b/packages/driver/cypress/integration/commands/command_option_immutability.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/command_option_immutability.js
rename to packages/driver/cypress/integration/commands/command_option_immutability.js
diff --git a/packages/driver/test/cypress/integration/commands/commands_spec.js b/packages/driver/cypress/integration/commands/commands_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/commands_spec.js
rename to packages/driver/cypress/integration/commands/commands_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/connectors_spec.js b/packages/driver/cypress/integration/commands/connectors_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/connectors_spec.js
rename to packages/driver/cypress/integration/commands/connectors_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/cookies_spec.js b/packages/driver/cypress/integration/commands/cookies_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/cookies_spec.js
rename to packages/driver/cypress/integration/commands/cookies_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/debugging_spec.js b/packages/driver/cypress/integration/commands/debugging_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/debugging_spec.js
rename to packages/driver/cypress/integration/commands/debugging_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/exec_spec.js b/packages/driver/cypress/integration/commands/exec_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/exec_spec.js
rename to packages/driver/cypress/integration/commands/exec_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/files_spec.js b/packages/driver/cypress/integration/commands/files_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/files_spec.js
rename to packages/driver/cypress/integration/commands/files_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/fixtures_spec.js b/packages/driver/cypress/integration/commands/fixtures_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/fixtures_spec.js
rename to packages/driver/cypress/integration/commands/fixtures_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/local_storage_spec.js b/packages/driver/cypress/integration/commands/local_storage_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/local_storage_spec.js
rename to packages/driver/cypress/integration/commands/local_storage_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/location_spec.js b/packages/driver/cypress/integration/commands/location_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/location_spec.js
rename to packages/driver/cypress/integration/commands/location_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/misc_spec.js b/packages/driver/cypress/integration/commands/misc_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/misc_spec.js
rename to packages/driver/cypress/integration/commands/misc_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/navigation_spec.js b/packages/driver/cypress/integration/commands/navigation_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/navigation_spec.js
rename to packages/driver/cypress/integration/commands/navigation_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/popups_spec.js b/packages/driver/cypress/integration/commands/popups_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/popups_spec.js
rename to packages/driver/cypress/integration/commands/popups_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/querying_spec.js b/packages/driver/cypress/integration/commands/querying_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/querying_spec.js
rename to packages/driver/cypress/integration/commands/querying_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/request_spec.js b/packages/driver/cypress/integration/commands/request_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/request_spec.js
rename to packages/driver/cypress/integration/commands/request_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/screenshot_spec.js b/packages/driver/cypress/integration/commands/screenshot_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/screenshot_spec.js
rename to packages/driver/cypress/integration/commands/screenshot_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/task_spec.js b/packages/driver/cypress/integration/commands/task_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/task_spec.js
rename to packages/driver/cypress/integration/commands/task_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/traversals_spec.js b/packages/driver/cypress/integration/commands/traversals_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/traversals_spec.js
rename to packages/driver/cypress/integration/commands/traversals_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/waiting_spec.js b/packages/driver/cypress/integration/commands/waiting_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/waiting_spec.js
rename to packages/driver/cypress/integration/commands/waiting_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/window_spec.js b/packages/driver/cypress/integration/commands/window_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/commands/window_spec.js
rename to packages/driver/cypress/integration/commands/window_spec.js
diff --git a/packages/driver/test/cypress/integration/commands/xhr_spec.js b/packages/driver/cypress/integration/commands/xhr_spec.js
similarity index 99%
rename from packages/driver/test/cypress/integration/commands/xhr_spec.js
rename to packages/driver/cypress/integration/commands/xhr_spec.js
index 47593ed4ee57..b720bebad347 100644
--- a/packages/driver/test/cypress/integration/commands/xhr_spec.js
+++ b/packages/driver/cypress/integration/commands/xhr_spec.js
@@ -739,17 +739,19 @@ describe('src/cy/commands/xhr', () => {
it('handles arraybuffer', () => {
cy
.server()
- .route('GET', /buffer/).as('getBuffer')
+ .route('GET', /arraybuffer/).as('getBuffer')
.window().then((win) => {
const xhr = new win.XMLHttpRequest
xhr.responseType = 'arraybuffer'
- xhr.open('GET', '/buffer')
+ xhr.open('GET', '/arraybuffer')
xhr.send()
return null
})
.wait('@getBuffer').then((xhr) => {
+ expect(xhr.status).eq(200)
+ expect(xhr.responseBody.byteLength).gt(0)
expect(xhr.responseBody.toString()).to.eq('[object ArrayBuffer]')
})
})
diff --git a/packages/driver/test/cypress/integration/cy/snapshot_css_spec.js b/packages/driver/cypress/integration/cy/snapshot_css_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cy/snapshot_css_spec.js
rename to packages/driver/cypress/integration/cy/snapshot_css_spec.js
diff --git a/packages/driver/test/cypress/integration/cy/snapshot_spec.js b/packages/driver/cypress/integration/cy/snapshot_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cy/snapshot_spec.js
rename to packages/driver/cypress/integration/cy/snapshot_spec.js
diff --git a/packages/driver/test/cypress/integration/cy/timeouts_spec.js b/packages/driver/cypress/integration/cy/timeouts_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cy/timeouts_spec.js
rename to packages/driver/cypress/integration/cy/timeouts_spec.js
diff --git a/packages/driver/test/cypress/integration/cy/timers_spec.js b/packages/driver/cypress/integration/cy/timers_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cy/timers_spec.js
rename to packages/driver/cypress/integration/cy/timers_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/browser_spec.js b/packages/driver/cypress/integration/cypress/browser_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/browser_spec.js
rename to packages/driver/cypress/integration/cypress/browser_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/cy_spec.js b/packages/driver/cypress/integration/cypress/cy_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/cy_spec.js
rename to packages/driver/cypress/integration/cypress/cy_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/cypress_spec.js b/packages/driver/cypress/integration/cypress/cypress_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/cypress_spec.js
rename to packages/driver/cypress/integration/cypress/cypress_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/error_utils_spec.js b/packages/driver/cypress/integration/cypress/error_utils_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/error_utils_spec.js
rename to packages/driver/cypress/integration/cypress/error_utils_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/location_spec.js b/packages/driver/cypress/integration/cypress/location_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/location_spec.js
rename to packages/driver/cypress/integration/cypress/location_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/log_spec.js b/packages/driver/cypress/integration/cypress/log_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/log_spec.js
rename to packages/driver/cypress/integration/cypress/log_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/network_utils_spec.js b/packages/driver/cypress/integration/cypress/network_utils_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/network_utils_spec.js
rename to packages/driver/cypress/integration/cypress/network_utils_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/resolvers_spec.ts b/packages/driver/cypress/integration/cypress/resolvers_spec.ts
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/resolvers_spec.ts
rename to packages/driver/cypress/integration/cypress/resolvers_spec.ts
diff --git a/packages/driver/test/cypress/integration/cypress/runner_spec.js b/packages/driver/cypress/integration/cypress/runner_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/runner_spec.js
rename to packages/driver/cypress/integration/cypress/runner_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/screenshot_spec.js b/packages/driver/cypress/integration/cypress/screenshot_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/screenshot_spec.js
rename to packages/driver/cypress/integration/cypress/screenshot_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/script_utils_spec.js b/packages/driver/cypress/integration/cypress/script_utils_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/script_utils_spec.js
rename to packages/driver/cypress/integration/cypress/script_utils_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/selector_playground_spec.js b/packages/driver/cypress/integration/cypress/selector_playground_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/selector_playground_spec.js
rename to packages/driver/cypress/integration/cypress/selector_playground_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/source_map_utils_spec.js b/packages/driver/cypress/integration/cypress/source_map_utils_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/source_map_utils_spec.js
rename to packages/driver/cypress/integration/cypress/source_map_utils_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/stack_utils_spec.js b/packages/driver/cypress/integration/cypress/stack_utils_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/stack_utils_spec.js
rename to packages/driver/cypress/integration/cypress/stack_utils_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/utils_spec.js b/packages/driver/cypress/integration/cypress/utils_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/utils_spec.js
rename to packages/driver/cypress/integration/cypress/utils_spec.js
diff --git a/packages/driver/test/cypress/integration/cypress/xml_http_request_spec.js b/packages/driver/cypress/integration/cypress/xml_http_request_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/cypress/xml_http_request_spec.js
rename to packages/driver/cypress/integration/cypress/xml_http_request_spec.js
diff --git a/packages/driver/test/cypress/integration/dom/coordinates_spec.ts b/packages/driver/cypress/integration/dom/coordinates_spec.ts
similarity index 100%
rename from packages/driver/test/cypress/integration/dom/coordinates_spec.ts
rename to packages/driver/cypress/integration/dom/coordinates_spec.ts
diff --git a/packages/driver/test/cypress/integration/dom/elements_spec.ts b/packages/driver/cypress/integration/dom/elements_spec.ts
similarity index 100%
rename from packages/driver/test/cypress/integration/dom/elements_spec.ts
rename to packages/driver/cypress/integration/dom/elements_spec.ts
diff --git a/packages/driver/test/cypress/integration/dom/jquery_spec.ts b/packages/driver/cypress/integration/dom/jquery_spec.ts
similarity index 100%
rename from packages/driver/test/cypress/integration/dom/jquery_spec.ts
rename to packages/driver/cypress/integration/dom/jquery_spec.ts
diff --git a/packages/driver/test/cypress/integration/dom/visibility_spec.ts b/packages/driver/cypress/integration/dom/visibility_spec.ts
similarity index 100%
rename from packages/driver/test/cypress/integration/dom/visibility_spec.ts
rename to packages/driver/cypress/integration/dom/visibility_spec.ts
diff --git a/packages/driver/test/cypress/integration/e2e/abort_beforeunload_event_child_spec.ts b/packages/driver/cypress/integration/e2e/abort_beforeunload_event_child_spec.ts
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/abort_beforeunload_event_child_spec.ts
rename to packages/driver/cypress/integration/e2e/abort_beforeunload_event_child_spec.ts
diff --git a/packages/driver/test/cypress/integration/e2e/abort_beforeunload_event_spec.ts b/packages/driver/cypress/integration/e2e/abort_beforeunload_event_spec.ts
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/abort_beforeunload_event_spec.ts
rename to packages/driver/cypress/integration/e2e/abort_beforeunload_event_spec.ts
diff --git a/packages/driver/test/cypress/integration/e2e/abort_onbeforeunload_child_spec.ts b/packages/driver/cypress/integration/e2e/abort_onbeforeunload_child_spec.ts
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/abort_onbeforeunload_child_spec.ts
rename to packages/driver/cypress/integration/e2e/abort_onbeforeunload_child_spec.ts
diff --git a/packages/driver/test/cypress/integration/e2e/abort_onbeforeunload_spec.ts b/packages/driver/cypress/integration/e2e/abort_onbeforeunload_spec.ts
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/abort_onbeforeunload_spec.ts
rename to packages/driver/cypress/integration/e2e/abort_onbeforeunload_spec.ts
diff --git a/packages/driver/test/cypress/integration/e2e/cancelation_spec.js b/packages/driver/cypress/integration/e2e/cancelation_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/cancelation_spec.js
rename to packages/driver/cypress/integration/e2e/cancelation_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/dom_hitbox.spec.js b/packages/driver/cypress/integration/e2e/dom_hitbox.spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/dom_hitbox.spec.js
rename to packages/driver/cypress/integration/e2e/dom_hitbox.spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/ended_early_spec.js b/packages/driver/cypress/integration/e2e/ended_early_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/ended_early_spec.js
rename to packages/driver/cypress/integration/e2e/ended_early_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/focus_blur_spec.js b/packages/driver/cypress/integration/e2e/focus_blur_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/focus_blur_spec.js
rename to packages/driver/cypress/integration/e2e/focus_blur_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/keyboard_spec.js b/packages/driver/cypress/integration/e2e/keyboard_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/keyboard_spec.js
rename to packages/driver/cypress/integration/e2e/keyboard_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/promises_spec.js b/packages/driver/cypress/integration/e2e/promises_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/promises_spec.js
rename to packages/driver/cypress/integration/e2e/promises_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/react-15_spec.js b/packages/driver/cypress/integration/e2e/react-15_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/react-15_spec.js
rename to packages/driver/cypress/integration/e2e/react-15_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/react-16_spec.js b/packages/driver/cypress/integration/e2e/react-16_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/react-16_spec.js
rename to packages/driver/cypress/integration/e2e/react-16_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/redirects_spec.js b/packages/driver/cypress/integration/e2e/redirects_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/redirects_spec.js
rename to packages/driver/cypress/integration/e2e/redirects_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/rerun_spec.js b/packages/driver/cypress/integration/e2e/rerun_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/rerun_spec.js
rename to packages/driver/cypress/integration/e2e/rerun_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/return_value_spec.js b/packages/driver/cypress/integration/e2e/return_value_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/return_value_spec.js
rename to packages/driver/cypress/integration/e2e/return_value_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/security_spec.js b/packages/driver/cypress/integration/e2e/security_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/security_spec.js
rename to packages/driver/cypress/integration/e2e/security_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/selector_playground.spec.js b/packages/driver/cypress/integration/e2e/selector_playground.spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/selector_playground.spec.js
rename to packages/driver/cypress/integration/e2e/selector_playground.spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/testConfigOverrides.spec.js b/packages/driver/cypress/integration/e2e/testConfigOverrides.spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/testConfigOverrides.spec.js
rename to packages/driver/cypress/integration/e2e/testConfigOverrides.spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/text_mask_spec.js b/packages/driver/cypress/integration/e2e/text_mask_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/text_mask_spec.js
rename to packages/driver/cypress/integration/e2e/text_mask_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/uncaught_errors_spec.js b/packages/driver/cypress/integration/e2e/uncaught_errors_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/uncaught_errors_spec.js
rename to packages/driver/cypress/integration/e2e/uncaught_errors_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/video_spec.js b/packages/driver/cypress/integration/e2e/video_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/video_spec.js
rename to packages/driver/cypress/integration/e2e/video_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/visibility_spec.js b/packages/driver/cypress/integration/e2e/visibility_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/visibility_spec.js
rename to packages/driver/cypress/integration/e2e/visibility_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/webcam_spec.js b/packages/driver/cypress/integration/e2e/webcam_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/webcam_spec.js
rename to packages/driver/cypress/integration/e2e/webcam_spec.js
diff --git a/packages/driver/test/cypress/integration/e2e/zonejs_spec.js b/packages/driver/cypress/integration/e2e/zonejs_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/e2e/zonejs_spec.js
rename to packages/driver/cypress/integration/e2e/zonejs_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/1436_spec.js b/packages/driver/cypress/integration/issues/1436_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/1436_spec.js
rename to packages/driver/cypress/integration/issues/1436_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/1854_spec.js b/packages/driver/cypress/integration/issues/1854_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/1854_spec.js
rename to packages/driver/cypress/integration/issues/1854_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/1909_spec.js b/packages/driver/cypress/integration/issues/1909_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/1909_spec.js
rename to packages/driver/cypress/integration/issues/1909_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/1939_1940_2190_spec.js b/packages/driver/cypress/integration/issues/1939_1940_2190_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/1939_1940_2190_spec.js
rename to packages/driver/cypress/integration/issues/1939_1940_2190_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/2582_spec.js b/packages/driver/cypress/integration/issues/2582_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/2582_spec.js
rename to packages/driver/cypress/integration/issues/2582_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/2784_spec.js b/packages/driver/cypress/integration/issues/2784_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/2784_spec.js
rename to packages/driver/cypress/integration/issues/2784_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/2850_spec.js b/packages/driver/cypress/integration/issues/2850_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/2850_spec.js
rename to packages/driver/cypress/integration/issues/2850_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/3253_spec.js b/packages/driver/cypress/integration/issues/3253_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/3253_spec.js
rename to packages/driver/cypress/integration/issues/3253_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/3847_spec.js b/packages/driver/cypress/integration/issues/3847_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/3847_spec.js
rename to packages/driver/cypress/integration/issues/3847_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/4373_spec.js b/packages/driver/cypress/integration/issues/4373_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/4373_spec.js
rename to packages/driver/cypress/integration/issues/4373_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/510_spec.js b/packages/driver/cypress/integration/issues/510_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/510_spec.js
rename to packages/driver/cypress/integration/issues/510_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/565_spec.js b/packages/driver/cypress/integration/issues/565_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/565_spec.js
rename to packages/driver/cypress/integration/issues/565_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/5682_spec.js b/packages/driver/cypress/integration/issues/5682_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/5682_spec.js
rename to packages/driver/cypress/integration/issues/5682_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/5707_spec.js b/packages/driver/cypress/integration/issues/5707_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/5707_spec.js
rename to packages/driver/cypress/integration/issues/5707_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/573_spec.js b/packages/driver/cypress/integration/issues/573_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/573_spec.js
rename to packages/driver/cypress/integration/issues/573_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/599_spec.js b/packages/driver/cypress/integration/issues/599_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/599_spec.js
rename to packages/driver/cypress/integration/issues/599_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/652_spec.js b/packages/driver/cypress/integration/issues/652_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/652_spec.js
rename to packages/driver/cypress/integration/issues/652_spec.js
diff --git a/packages/driver/test/cypress/integration/issues/761_2968_3973_spec.js b/packages/driver/cypress/integration/issues/761_2968_3973_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/issues/761_2968_3973_spec.js
rename to packages/driver/cypress/integration/issues/761_2968_3973_spec.js
diff --git a/packages/driver/test/cypress/integration/util/firefox_forced_gc_spec.ts b/packages/driver/cypress/integration/util/firefox_forced_gc_spec.ts
similarity index 100%
rename from packages/driver/test/cypress/integration/util/firefox_forced_gc_spec.ts
rename to packages/driver/cypress/integration/util/firefox_forced_gc_spec.ts
diff --git a/packages/driver/test/cypress/integration/util/limited_map_spec.js b/packages/driver/cypress/integration/util/limited_map_spec.js
similarity index 100%
rename from packages/driver/test/cypress/integration/util/limited_map_spec.js
rename to packages/driver/cypress/integration/util/limited_map_spec.js
diff --git a/packages/driver/test/cypress/plugins/index.js b/packages/driver/cypress/plugins/index.js
similarity index 98%
rename from packages/driver/test/cypress/plugins/index.js
rename to packages/driver/cypress/plugins/index.js
index 046ea2613c35..90a80591f241 100644
--- a/packages/driver/test/cypress/plugins/index.js
+++ b/packages/driver/cypress/plugins/index.js
@@ -1,6 +1,6 @@
// only required to read in webpack config, since it is .ts
require('@packages/ts/register')
-
+require('./server')
const _ = require('lodash')
const path = require('path')
const fs = require('fs-extra')
diff --git a/packages/driver/test/support/server.js b/packages/driver/cypress/plugins/server.js
similarity index 89%
rename from packages/driver/test/support/server.js
rename to packages/driver/cypress/plugins/server.js
index 7475f767fd41..7c0bbfddbaad 100644
--- a/packages/driver/test/support/server.js
+++ b/packages/driver/cypress/plugins/server.js
@@ -6,6 +6,7 @@ const http = require('http')
const path = require('path')
const Promise = require('bluebird')
+const PATH_TO_SERVER_PKG = path.dirname(require.resolve('@packages/server'))
const ports = [3500, 3501]
ports.forEach((port) => {
@@ -16,8 +17,6 @@ ports.forEach((port) => {
app.set('view engine', 'html')
- app.use(require('morgan')({ format: 'dev' }))
-
app.use(require('cors')())
app.use(require('compression')())
app.use(bodyParser.urlencoded({ extended: false }))
@@ -46,8 +45,12 @@ ports.forEach((port) => {
return res.type('xml').send('bar')
})
- app.get('/buffer', (req, res) => {
- return fs.readFile(path.join(__dirname, '../cypress/fixtures/sample.pdf'), (err, bytes) => {
+ app.get('/arraybuffer', (req, res) => {
+ return fs.readFile(path.join(PATH_TO_SERVER_PKG, 'test/support/fixtures/sample.pdf'), (err, bytes) => {
+ if (err) {
+ return res.status(500).send(err.stack)
+ }
+
res.type('pdf')
return res.send(bytes)
@@ -108,7 +111,7 @@ ports.forEach((port) => {
.send('server error')
})
- app.use(express.static(path.join(__dirname, '..', 'cypress')))
+ app.use(express.static(path.join(__dirname, '..')))
app.use(require('errorhandler')())
diff --git a/packages/driver/test/cypress/support/defaults.js b/packages/driver/cypress/support/defaults.js
similarity index 100%
rename from packages/driver/test/cypress/support/defaults.js
rename to packages/driver/cypress/support/defaults.js
diff --git a/packages/driver/test/cypress/support/helpers.js b/packages/driver/cypress/support/helpers.js
similarity index 100%
rename from packages/driver/test/cypress/support/helpers.js
rename to packages/driver/cypress/support/helpers.js
diff --git a/packages/driver/test/cypress/support/index.js b/packages/driver/cypress/support/index.js
similarity index 100%
rename from packages/driver/test/cypress/support/index.js
rename to packages/driver/cypress/support/index.js
diff --git a/packages/driver/test/cypress/support/utils.js b/packages/driver/cypress/support/utils.js
similarity index 100%
rename from packages/driver/test/cypress/support/utils.js
rename to packages/driver/cypress/support/utils.js
diff --git a/packages/driver/cypress/videos/commands/navigation_spec.js.mp4 b/packages/driver/cypress/videos/commands/navigation_spec.js.mp4
new file mode 100644
index 0000000000000000000000000000000000000000..0d6541513ce59734f00ad6b73c8248e3cfe1ca29
GIT binary patch
literal 1646621
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