UNPKG

@revoloo/cypress6

Version:

Cypress.io end to end testing tool

286 lines (219 loc) 6.28 kB
const _ = require('lodash') const sinon = require('sinon') const Promise = require('bluebird') const $utils = require('../../cypress/utils') const $errUtils = require('../../cypress/error_utils') let counts = null sinon.setFormatter($utils.stringifyArg.bind($utils)) const createSandbox = () => { return sinon.createSandbox().usingPromise(Promise) } const display = (name) => { if (name === 'spy') { return 'Spied Obj' } if (name === 'stub') { return 'Stubbed Obj' } } const formatArgs = (args) => { return _.map(args, (arg) => { return $utils.stringifyArg(arg) }) } const getMessage = (method, args) => { method = method ?? 'function' args = args.length > 3 ? formatArgs(args.slice(0, 3)).concat('...') : formatArgs(args) return `${method}(${args.join(', ')})` } const onInvoke = function (Cypress, obj, args) { const { agent } = obj const agentName = agent._cyName // bail if we've turned off logging this agent if (agent._log === false) { return } // fakes are children of the agent created with `withArgs` const fakes = agent.matchingFakes(args) agent._cyLog.set('callCount', agent.callCount) for (let fake of fakes) { fake._cyLog.set('callCount', fake.callCount) } const logProps = { name: agentName, message: obj.message, error: obj.error, type: 'parent', end: true, snapshot: !agent._noSnapshot, event: true, consoleProps () { const consoleObj = {} consoleObj.Command = null consoleObj.Error = null consoleObj.Event = `${agentName} called` consoleObj[agent._cyType] = agent consoleObj['Call #'] = agent.callCount consoleObj.Alias = agent._cyAlias consoleObj[display(obj.name)] = obj.obj consoleObj.Arguments = obj.call.args consoleObj.Context = obj.call.thisValue consoleObj.Returned = obj.call.returnValue if (obj.error) { consoleObj.Error = obj.error.stack } for (let fake of fakes) { const count = fake._cyCount consoleObj[`Child ${fake._cyType} (${count})`] = '---' consoleObj[` ${count} ${fake._cyType}`] = fake consoleObj[` ${count} call #`] = fake.callCount consoleObj[` ${count} alias`] = fake._cyAlias consoleObj[` ${count} matching arguments`] = fake.matchingArguments } return consoleObj }, } const aliases = _.compact([agent._cyAlias].concat(_.map(fakes, '_cyAlias'))) if (aliases.length) { logProps.alias = aliases logProps.aliasType = 'agent' } return Cypress.log(logProps) } const onError = (err) => { return $errUtils.throwErr(err) } // create a global sandbox // to be used through all the tests const sandbox = createSandbox() const reset = () => { counts = { spy: 0, stub: 0, children: {}, } sandbox.restore() return null } module.exports = function (Commands, Cypress, cy, state) { // reset initially on a new run because we could be // re-running from the UI or from a spec file change reset() const resetAndSetSandbox = () => { reset() // attach the sandbox to state return state('sandbox', sandbox) } // before each of our tests we always want // to reset the counts + the sandbox Cypress.on('test:before:run', resetAndSetSandbox) const wrap = function (ctx, type, agent, obj, method, count) { if (!count) { count = (counts[type] += 1) } const name = `${type}-${count}` if (!agent.parent) { counts.children[name] = 0 } const log = Cypress.log({ instrument: 'agent', name, type: name, functionName: method, count, callCount: 0, }) agent._cyCount = count agent._cyLog = log agent._cyName = name agent._cyType = type const { invoke } = agent agent.invoke = function (func, thisValue, args) { let error = null let returned = null // because our spy could potentially fail here // we need to wrap this in a try / catch // so we still emit the command that failed // and the user can easily find the error try { returned = invoke.call(this, func, thisValue, args) } catch (e) { error = e } const props = { count, name: type, message: getMessage(method, args), obj, agent, call: agent.lastCall, callCount: agent.callCount, error, log, } onInvoke(Cypress, props, args) // if an error did exist then we need // to bubble it up if (error) { onError(error) } // make sure we return the invoked return value // of the spy return returned } // enable not logging this agent agent.log = (bool = true) => { agent._log = bool 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, { subject: agent, command: log, alias, }) agent._cyAlias = alias log.set({ alias, aliasType: 'agent', }) agent.named(alias) return agent } const { withArgs } = agent agent.withArgs = function (...args) { const childCount = (counts.children[name] += 1) return wrap(ctx, type, withArgs.apply(this, args), obj, method, `${count}.${childCount}`) } return agent } const spy = function (obj, method) { const theSpy = sandbox.spy(obj, method) return wrap(this, 'spy', theSpy, obj, method) } const stub = function (obj, method, replacerFnOrValue) { let theStub = sandbox.stub.call(sandbox, obj, method) // sinon 2 changed the stub signature // this maintains the 3-argument signature so it's not breaking if (arguments.length === 3) { if (_.isFunction(replacerFnOrValue)) { theStub = theStub.callsFake(replacerFnOrValue) } else { theStub = theStub.value(replacerFnOrValue) } } return wrap(this, 'stub', theStub, obj, method) } return Commands.addAllSync({ spy, stub, }) }