UNPKG

@revoloo/cypress6

Version:

Cypress.io end to end testing tool

156 lines (128 loc) 4.82 kB
const _ = require('lodash') const Promise = require('bluebird') const $dom = require('../../../dom') const $errUtils = require('../../../cypress/error_utils') const $actionability = require('../../actionability') const dispatch = (target, appWindow, eventName, options) => { const eventConstructor = options.eventConstructor ?? 'Event' const ctor = appWindow[eventConstructor] if (typeof ctor !== 'function') { $errUtils.throwErrByPath('trigger.invalid_event_type', { args: { eventConstructor }, }) } // eventConstructor property should not be added to event instance. delete options.eventConstructor // https://github.com/cypress-io/cypress/issues/3686 // UIEvent and its derived events like MouseEvent, KeyboardEvent // has a property, view, which is the window object where the event happened. // Logic below checks the ctor function is UIEvent itself or its children // and adds view to the instance init object. if (ctor === appWindow['UIEvent'] || ctor.prototype instanceof appWindow['UIEvent']) { options.view = appWindow } const event = new ctor(eventName, options) // some options, like clientX & clientY, must be set on the // instance instead of passing them into the constructor _.extend(event, options) return target.dispatchEvent(event) } module.exports = (Commands, Cypress, cy, state, config) => { return Commands.addAll({ prevSubject: ['element', 'window', 'document'] }, { trigger (subject, eventName, positionOrX, y, userOptions = {}) { let position let x ({ options: userOptions, position, x, y } = $actionability.getPositionFromArguments(positionOrX, y, userOptions)) const options = _.defaults({}, userOptions, { log: true, $el: subject, bubbles: true, cancelable: true, position, x, y, waitForAnimations: config('waitForAnimations'), animationDistanceThreshold: config('animationDistanceThreshold'), scrollBehavior: config('scrollBehavior'), }) if ($dom.isWindow(options.$el)) { // get this into a jquery object options.$el = $dom.wrap(options.$el) } // omit entries we know aren't part of an event, but pass anything // else through so user can specify what the event object needs let eventOptions = _.omit(options, 'log', '$el', 'position', 'x', 'y', 'waitForAnimations', 'animationDistanceThreshold') if (options.log) { options._log = Cypress.log({ $el: subject, timeout: options.timeout, consoleProps () { return { 'Yielded': subject, 'Event options': eventOptions, } }, }) options._log.snapshot('before', { next: 'after' }) } if (!_.isString(eventName)) { $errUtils.throwErrByPath('trigger.invalid_argument', { onFail: options._log, args: { eventName }, }) } if (options.$el.length > 1) { $errUtils.throwErrByPath('trigger.multiple_elements', { onFail: options._log, args: { num: options.$el.length }, }) } let dispatchEarly = false // if we're window or document then dispatch early // and avoid waiting for actionability if ($dom.isWindow(subject) || $dom.isDocument(subject)) { dispatchEarly = true } else { subject = options.$el.first() } const trigger = () => { if (dispatchEarly) { return dispatch(subject, state('window'), eventName, eventOptions) } return $actionability.verify(cy, subject, options, { onScroll ($el, type) { Cypress.action('cy:scrolled', $el, type) }, onReady ($elToClick, coords) { const { fromElWindow, fromElViewport, fromAutWindow } = coords if (options._log) { // display the red dot at these coords options._log.set({ coords: fromAutWindow, }) } eventOptions = _.extend({ clientX: fromElViewport.x, clientY: fromElViewport.y, screenX: fromElViewport.x, screenY: fromElViewport.y, pageX: fromElWindow.x, pageY: fromElWindow.y, }, eventOptions) return dispatch($elToClick.get(0), state('window'), eventName, eventOptions) }, }) } return Promise .try(trigger) .then(() => { const verifyAssertions = () => { return cy.verifyUpcomingAssertions(options.$el, options, { onRetry: verifyAssertions, }) } return verifyAssertions() }) }, }) }