mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-14 19:20:42 -06:00
@@ -2,11 +2,13 @@ _ = require("lodash")
|
||||
$ = require("jquery")
|
||||
Promise = require("bluebird")
|
||||
|
||||
$dom = require("../../../dom")
|
||||
$utils = require("../../../cypress/utils")
|
||||
$dom = require("../dom")
|
||||
$utils = require("../cypress/utils")
|
||||
|
||||
delay = 50
|
||||
|
||||
getFixedOrStickyEl = $dom.getFirstFixedOrStickyPositionParent
|
||||
|
||||
dispatchPrimedChangeEvents = (state) ->
|
||||
## if we have a changeEvent, dispatch it
|
||||
if changeEvent = state("changeEvent")
|
||||
@@ -33,17 +35,19 @@ getPositionFromArguments = (positionOrX, y, options) ->
|
||||
|
||||
return {options, position, x, y}
|
||||
|
||||
getFixedOrStickyEl = ($el) ->
|
||||
$dom.getFirstFixedOrStickyPositionParent($el)
|
||||
|
||||
ensureElIsNotCovered = (cy, win, $el, fromViewport, options, log, onScroll) ->
|
||||
$elAtCoords = null
|
||||
|
||||
getElementAtPointFromViewport = (fromViewport) ->
|
||||
## get the element at point from the viewport based
|
||||
## on the desired x/y normalized coordinations
|
||||
if elAtCoords = $dom.getElementAtPointFromViewport(win.document, fromViewport.x, fromViewport.y)
|
||||
$elAtCoords = $dom.wrap(elAtCoords)
|
||||
|
||||
ensureDescendents = (fromViewport) ->
|
||||
## figure out the deepest element we are about to interact
|
||||
## with at these coordinates
|
||||
if elAtCoords = $dom.getElementAtPointFromViewport(win.document, fromViewport.left, fromViewport.top)
|
||||
$elAtCoords = $dom.wrap(elAtCoords)
|
||||
$elAtCoords = getElementAtPointFromViewport(fromViewport)
|
||||
|
||||
cy.ensureDescendents($el, $elAtCoords, log)
|
||||
|
||||
@@ -149,9 +153,7 @@ ensureElIsNotCovered = (cy, win, $el, fromViewport, options, log, onScroll) ->
|
||||
## we failed here, but before scrolling the next container
|
||||
## we need to first verify that the element covering up
|
||||
## is the same one as before our scroll
|
||||
if elAtCoords = $dom.getElementAtPointFromViewport(win.document, fromViewport.left, fromViewport.top)
|
||||
$elAtCoords = $dom.wrap(elAtCoords)
|
||||
|
||||
if $elAtCoords = getElementAtPointFromViewport(fromViewport)
|
||||
## get the fixed element again
|
||||
$fixed = getFixedOrStickyEl($elAtCoords)
|
||||
|
||||
@@ -201,7 +203,7 @@ ensureNotAnimating = (cy, $el, coordsHistory, animationDistanceThreshold) ->
|
||||
## 5 pixels of x/y
|
||||
cy.ensureElementIsNotAnimating($el, coordsHistory, animationDistanceThreshold)
|
||||
|
||||
waitForActionability = (cy, $el, options, callbacks) ->
|
||||
verify = (cy, $el, options, callbacks) ->
|
||||
win = $dom.getWindowByElement($el.get(0))
|
||||
|
||||
{ _log, force, position } = options
|
||||
@@ -209,7 +211,7 @@ waitForActionability = (cy, $el, options, callbacks) ->
|
||||
{ onReady, onScroll } = callbacks
|
||||
|
||||
if not onReady
|
||||
throw new Error("waitForActionability must be passed an onReady callback")
|
||||
throw new Error("actionability.verify must be passed an onReady callback")
|
||||
|
||||
## if we have a position we must validate
|
||||
## this ahead of time else bail early
|
||||
@@ -272,7 +274,7 @@ waitForActionability = (cy, $el, options, callbacks) ->
|
||||
|
||||
module.exports = {
|
||||
delay
|
||||
verify
|
||||
dispatchPrimedChangeEvents
|
||||
getPositionFromArguments
|
||||
waitForActionability
|
||||
}
|
||||
@@ -4,15 +4,9 @@ Promise = require("bluebird")
|
||||
|
||||
$Mouse = require("../../../cypress/mouse")
|
||||
|
||||
{
|
||||
delay,
|
||||
dispatchPrimedChangeEvents,
|
||||
waitForActionability,
|
||||
getPositionFromArguments
|
||||
} = require("./utils")
|
||||
|
||||
$dom = require("../../../dom")
|
||||
$utils = require("../../../cypress/utils")
|
||||
$actionability = require("../../actionability")
|
||||
|
||||
module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
Commands.addAll({ prevSubject: "element" }, {
|
||||
@@ -20,7 +14,7 @@ module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
## TODO handle pointer-events: none
|
||||
## http://caniuse.com/#feat=pointer-events
|
||||
|
||||
{options, position, x, y} = getPositionFromArguments(positionOrX, y, options)
|
||||
{options, position, x, y} = $actionability.getPositionFromArguments(positionOrX, y, options)
|
||||
|
||||
_.defaults(options, {
|
||||
$el: subject
|
||||
@@ -101,7 +95,7 @@ module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
consoleObj = _.defaults consoleObj ? {}, {
|
||||
"Applied To": $dom.getElements($el)
|
||||
"Elements": $el.length
|
||||
"Coords": fromWindow ## always absolute
|
||||
"Coords": _.pick(fromWindow, "x", "y") ## always absolute
|
||||
"Options": deltaOptions
|
||||
}
|
||||
|
||||
@@ -132,7 +126,7 @@ module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
consoleObj
|
||||
|
||||
return Promise
|
||||
.delay(delay, "click")
|
||||
.delay($actionability.delay, "click")
|
||||
.then ->
|
||||
## display the red dot at these coords
|
||||
if options._log
|
||||
@@ -176,13 +170,13 @@ module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
## we want to add this delay delta to our
|
||||
## runnables timeout so we prevent it from
|
||||
## timing out from multiple clicks
|
||||
cy.timeout(delay, true, "click")
|
||||
cy.timeout($actionability.delay, true, "click")
|
||||
|
||||
## must use callbacks here instead of .then()
|
||||
## because we're issuing the clicks synchonrously
|
||||
## once we establish the coordinates and the element
|
||||
## passes all of the internal checks
|
||||
waitForActionability(cy, $el, options, {
|
||||
$actionability.verify(cy, $el, options, {
|
||||
onScroll: ($el, type) ->
|
||||
Cypress.action("cy:scrolled", $el, type)
|
||||
|
||||
@@ -218,7 +212,7 @@ module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
## method might not get triggered if
|
||||
## our window is in focus since the
|
||||
## browser may fire blur events naturally
|
||||
dispatchPrimedChangeEvents(state)
|
||||
$actionability.dispatchPrimedChangeEvents(state)
|
||||
|
||||
## send in a focus event!
|
||||
cy.now("focus", $elToFocus, {$el: $elToFocus, error: false, verify: false, log: false})
|
||||
@@ -261,7 +255,7 @@ module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
## we want to add this delay delta to our
|
||||
## runnables timeout so we prevent it from
|
||||
## timing out from multiple clicks
|
||||
cy.timeout(delay, true, "dblclick")
|
||||
cy.timeout($actionability.delay, true, "dblclick")
|
||||
|
||||
if options.log
|
||||
log = Cypress.log
|
||||
@@ -288,7 +282,7 @@ module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
## chaining thenable promises
|
||||
return null
|
||||
|
||||
.delay(delay, "dblclick")
|
||||
.delay($actionability.delay, "dblclick")
|
||||
|
||||
dblclicks.push(p)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
_ = require("lodash")
|
||||
Promise = require("bluebird")
|
||||
|
||||
{ delay, dispatchPrimedChangeEvents } = require("./utils")
|
||||
$dom = require("../../../dom")
|
||||
$utils = require("../../../cypress/utils")
|
||||
$actionability = require("../../actionability")
|
||||
|
||||
module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
Commands.addAll({ prevSubject: ["element", "window"] }, {
|
||||
@@ -72,13 +72,13 @@ module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
|
||||
cleanup()
|
||||
|
||||
cy.timeout(delay, true)
|
||||
cy.timeout($actionability.delay, true)
|
||||
|
||||
## TODO: this is really weird how we're
|
||||
## resolving the promise here but letting
|
||||
## the lower promise also still run
|
||||
Promise
|
||||
.delay(delay)
|
||||
.delay($actionability.delay)
|
||||
.then(resolve)
|
||||
|
||||
options.$el.on("focus", focused)
|
||||
@@ -231,17 +231,17 @@ module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
|
||||
cleanup()
|
||||
|
||||
cy.timeout(delay, true)
|
||||
cy.timeout($actionability.delay, true)
|
||||
|
||||
Promise
|
||||
.delay(delay)
|
||||
.delay($actionability.delay)
|
||||
.then(resolve)
|
||||
|
||||
options.$el.on("blur", blurred)
|
||||
|
||||
## for simplicity we allow change events
|
||||
## to be triggered by a manual blur
|
||||
dispatchPrimedChangeEvents(state)
|
||||
$actionability.dispatchPrimedChangeEvents(state)
|
||||
|
||||
options.$el.get(0).blur()
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
_ = require("lodash")
|
||||
Promise = require("bluebird")
|
||||
|
||||
{ delay } = require("./utils")
|
||||
$dom = require("../../../dom")
|
||||
$utils = require("../../../cypress/utils")
|
||||
$actionability = require("../../actionability")
|
||||
|
||||
module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
Commands.addAll({ prevSubject: "element" }, {
|
||||
@@ -53,10 +53,10 @@ module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
## dont submit the form if our dispatched event was cancelled (false)
|
||||
form.submit() if dispatched
|
||||
|
||||
cy.timeout(delay, true)
|
||||
cy.timeout($actionability.delay, true)
|
||||
|
||||
Promise
|
||||
.delay(delay, "submit")
|
||||
.delay($actionability.delay, "submit")
|
||||
.then ->
|
||||
do verifyAssertions = =>
|
||||
cy.verifyUpcomingAssertions(options.$el, options, {
|
||||
@@ -1,9 +1,9 @@
|
||||
_ = require("lodash")
|
||||
Promise = require("bluebird")
|
||||
|
||||
{ waitForActionability, getPositionFromArguments } = require("./utils")
|
||||
$dom = require("../../../dom")
|
||||
$utils = require("../../../cypress/utils")
|
||||
$actionability = require("../../actionability")
|
||||
|
||||
dispatch = (target, eventName, options) ->
|
||||
event = new Event(eventName, options)
|
||||
@@ -17,7 +17,7 @@ dispatch = (target, eventName, options) ->
|
||||
module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
Commands.addAll({ prevSubject: ["element", "window", "document"] }, {
|
||||
trigger: (subject, eventName, positionOrX, y, options = {}) ->
|
||||
{options, position, x, y} = getPositionFromArguments(positionOrX, y, options)
|
||||
{options, position, x, y} = $actionability.getPositionFromArguments(positionOrX, y, options)
|
||||
|
||||
_.defaults(options, {
|
||||
log: true
|
||||
@@ -72,7 +72,7 @@ module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
if dispatchEarly
|
||||
return dispatch(subject, eventName, eventOptions)
|
||||
|
||||
waitForActionability(cy, subject, options, {
|
||||
$actionability.verify(cy, subject, options, {
|
||||
onScroll: ($el, type) ->
|
||||
Cypress.action("cy:scrolled", $el, type)
|
||||
|
||||
@@ -81,13 +81,15 @@ module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
|
||||
if options._log
|
||||
## display the red dot at these coords
|
||||
options._log.set({coords: fromWindow})
|
||||
options._log.set({
|
||||
coords: fromWindow
|
||||
})
|
||||
|
||||
eventOptions = _.extend({
|
||||
clientX: fromViewport.left
|
||||
clientY: fromViewport.top
|
||||
pageX: fromWindow.left
|
||||
pageY: fromWindow.top
|
||||
clientX: fromViewport.x
|
||||
clientY: fromViewport.y
|
||||
pageX: fromWindow.x
|
||||
pageY: fromWindow.y
|
||||
}, eventOptions)
|
||||
|
||||
dispatch($elToClick.get(0), eventName, eventOptions)
|
||||
|
||||
@@ -3,10 +3,10 @@ $ = require("jquery")
|
||||
Promise = require("bluebird")
|
||||
moment = require("moment")
|
||||
|
||||
{ delay, waitForActionability } = require("./utils")
|
||||
$dom = require("../../../dom")
|
||||
$Keyboard = require("../../../cypress/keyboard")
|
||||
$utils = require("../../../cypress/utils")
|
||||
$actionability = require("../../actionability")
|
||||
|
||||
inputEvents = "textInput input".split(" ")
|
||||
|
||||
@@ -317,7 +317,7 @@ module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
|
||||
cy.now("focused", {log: false, verify: false})
|
||||
.then ($focused) ->
|
||||
waitForActionability(cy, options.$el, options, {
|
||||
$actionability.verify(cy, options.$el, options, {
|
||||
onScroll: ($el, type) ->
|
||||
Cypress.action("cy:scrolled", $el, type)
|
||||
|
||||
@@ -345,10 +345,10 @@ module.exports = (Commands, Cypress, cy, state, config) ->
|
||||
|
||||
handleFocused()
|
||||
.then ->
|
||||
cy.timeout(delay, true, "type")
|
||||
cy.timeout($actionability.delay, true, "type")
|
||||
|
||||
Promise
|
||||
.delay(delay, "type")
|
||||
.delay($actionability.delay, "type")
|
||||
.then ->
|
||||
## command which consume cy.type may
|
||||
## want to handle verification themselves
|
||||
@@ -2,15 +2,15 @@ _ = require("lodash")
|
||||
$utils = require("./utils")
|
||||
|
||||
builtInCommands = [
|
||||
require("../cy/commands/actions/checkbox")
|
||||
require("../cy/commands/actions/clicking")
|
||||
require("../cy/commands/actions/check")
|
||||
require("../cy/commands/actions/click")
|
||||
require("../cy/commands/actions/focus")
|
||||
require("../cy/commands/actions/form")
|
||||
require("../cy/commands/actions/hover")
|
||||
require("../cy/commands/actions/scrolling")
|
||||
require("../cy/commands/actions/scroll")
|
||||
require("../cy/commands/actions/select")
|
||||
require("../cy/commands/actions/text")
|
||||
require("../cy/commands/actions/submit")
|
||||
require("../cy/commands/actions/trigger")
|
||||
require("../cy/commands/actions/type")
|
||||
require("../cy/commands/agents")
|
||||
require("../cy/commands/aliasing")
|
||||
require("../cy/commands/angular")
|
||||
|
||||
@@ -139,19 +139,25 @@ module.exports = {
|
||||
|
||||
dom:
|
||||
animating: """
|
||||
#{cmd('{{cmd}}')} could not be issued because this element is currently animating:\n
|
||||
{{node}}\n
|
||||
#{cmd('{{cmd}}')} could not be issued because this element is currently animating:
|
||||
|
||||
{{node}}
|
||||
|
||||
You can fix this problem by:
|
||||
- Passing {force: true} which disables all error checking
|
||||
- Passing {waitForAnimations: false} which disables waiting on animations
|
||||
- Passing {animationDistanceThreshold: 20} which decreases the sensitivity\n
|
||||
- Passing {animationDistanceThreshold: 20} which decreases the sensitivity
|
||||
|
||||
https://on.cypress.io/element-is-animating
|
||||
"""
|
||||
animation_check_failed: "Not enough coord points provided to calculate distance."
|
||||
center_hidden: """
|
||||
#{cmd('{{cmd}}')} failed because the center of this element is hidden from view:\n
|
||||
{{node}}\n
|
||||
Fix this problem, or use {force: true} to disable error checking.\n
|
||||
#{cmd('{{cmd}}')} failed because the center of this element is hidden from view:
|
||||
|
||||
{{node}}
|
||||
|
||||
Fix this problem, or use {force: true} to disable error checking.
|
||||
|
||||
https://on.cypress.io/element-cannot-be-interacted-with
|
||||
"""
|
||||
covered: (obj) ->
|
||||
@@ -169,9 +175,12 @@ module.exports = {
|
||||
https://on.cypress.io/element-cannot-be-interacted-with
|
||||
"""
|
||||
disabled: """
|
||||
#{cmd('{{cmd}}')} failed because this element is disabled:\n
|
||||
{{node}}\n
|
||||
Fix this problem, or use {force: true} to disable error checking.\n
|
||||
#{cmd('{{cmd}}')} failed because this element is disabled:
|
||||
|
||||
{{node}}
|
||||
|
||||
Fix this problem, or use {force: true} to disable error checking.
|
||||
|
||||
https://on.cypress.io/element-cannot-be-interacted-with
|
||||
"""
|
||||
invalid_position_argument: "Invalid position argument: '{{position}}'. Position may only be {{validPositions}}."
|
||||
@@ -180,10 +189,14 @@ module.exports = {
|
||||
{{node}}\n
|
||||
"""
|
||||
not_visible: """
|
||||
#{cmd('{{cmd}}')} failed because this element is not visible:\n
|
||||
{{node}}\n
|
||||
{{reason}}\n
|
||||
Fix this problem, or use {force: true} to disable error checking.\n
|
||||
#{cmd('{{cmd}}')} failed because this element is not visible:
|
||||
|
||||
{{node}}
|
||||
|
||||
{{reason}}
|
||||
|
||||
Fix this problem, or use {force: true} to disable error checking.
|
||||
|
||||
https://on.cypress.io/element-cannot-be-interacted-with
|
||||
"""
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ module.exports = {
|
||||
bubbles: true
|
||||
cancelable: true
|
||||
view: win
|
||||
clientX: fromViewport.left
|
||||
clientY: fromViewport.top
|
||||
clientX: fromViewport.x
|
||||
clientY: fromViewport.y
|
||||
buttons: 1
|
||||
detail: 1
|
||||
})
|
||||
@@ -51,8 +51,8 @@ module.exports = {
|
||||
bubbles: true
|
||||
cancelable: true
|
||||
view: win
|
||||
clientX: fromViewport.left
|
||||
clientY: fromViewport.top
|
||||
clientX: fromViewport.x
|
||||
clientY: fromViewport.y
|
||||
buttons: 0
|
||||
detail: 1
|
||||
})
|
||||
@@ -86,8 +86,8 @@ module.exports = {
|
||||
bubbles: true
|
||||
cancelable: true
|
||||
view: win
|
||||
clientX: fromViewport.left
|
||||
clientY: fromViewport.top
|
||||
clientX: fromViewport.x
|
||||
clientY: fromViewport.y
|
||||
buttons: 0
|
||||
detail: 1
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
$window = require("./window")
|
||||
|
||||
getElementAtPointFromViewport = (doc, left, top) ->
|
||||
doc.elementFromPoint(left, top)
|
||||
getElementAtPointFromViewport = (doc, x, y) ->
|
||||
doc.elementFromPoint(x, y)
|
||||
|
||||
getElementPositioning = ($el) ->
|
||||
el = $el[0]
|
||||
@@ -12,6 +12,13 @@ getElementPositioning = ($el) ->
|
||||
## are relative to the top left of the viewport
|
||||
rect = el.getBoundingClientRect()
|
||||
|
||||
center = getCenterCoordinates(rect)
|
||||
|
||||
## add the center coordinates
|
||||
## because its useful to any caller
|
||||
topCenter = center.y
|
||||
leftCenter = center.x
|
||||
|
||||
return {
|
||||
scrollTop: el.scrollTop
|
||||
scrollLeft: el.scrollLeft
|
||||
@@ -22,14 +29,18 @@ getElementPositioning = ($el) ->
|
||||
left: rect.left
|
||||
right: rect.right
|
||||
bottom: rect.bottom
|
||||
topCenter
|
||||
leftCenter
|
||||
}
|
||||
fromWindow: {
|
||||
top: rect.top + win.pageYOffset
|
||||
left: rect.left + win.pageXOffset
|
||||
topCenter: topCenter + win.pageYOffset
|
||||
leftCenter: leftCenter + win.pageXOffset
|
||||
}
|
||||
}
|
||||
|
||||
normalizeCoords = (left, top, xPosition = "center", yPosition = "center") ->
|
||||
getCoordsByPosition = (left, top, xPosition = "center", yPosition = "center") ->
|
||||
left = switch xPosition
|
||||
when "left" then Math.ceil(left)
|
||||
when "center" then Math.floor(left)
|
||||
@@ -40,52 +51,59 @@ normalizeCoords = (left, top, xPosition = "center", yPosition = "center") ->
|
||||
when "center" then Math.floor(top)
|
||||
when "bottom" then Math.floor(top) - 1
|
||||
|
||||
return { top, left }
|
||||
## returning x/y here because this is
|
||||
## about the target position we want
|
||||
## to fire the event at based on what
|
||||
## the desired xPosition and yPosition is
|
||||
return {
|
||||
x: left
|
||||
y: top
|
||||
}
|
||||
|
||||
getTopLeftCoordinates = (rect) ->
|
||||
x = rect.left
|
||||
y = rect.top
|
||||
normalizeCoords(x, y, "left", "top")
|
||||
getCoordsByPosition(x, y, "left", "top")
|
||||
|
||||
getTopCoordinates = (rect) ->
|
||||
x = rect.left + rect.width / 2
|
||||
y = rect.top
|
||||
normalizeCoords(x, y, "center", "top")
|
||||
getCoordsByPosition(x, y, "center", "top")
|
||||
|
||||
getTopRightCoordinates = (rect) ->
|
||||
x = rect.left + rect.width
|
||||
y = rect.top
|
||||
normalizeCoords(x, y, "right", "top")
|
||||
getCoordsByPosition(x, y, "right", "top")
|
||||
|
||||
getLeftCoordinates = (rect) ->
|
||||
x = rect.left
|
||||
y = rect.top + rect.height / 2
|
||||
normalizeCoords(x, y, "left", "center")
|
||||
getCoordsByPosition(x, y, "left", "center")
|
||||
|
||||
getCenterCoordinates = (rect) ->
|
||||
x = rect.left + rect.width / 2
|
||||
y = rect.top + rect.height / 2
|
||||
normalizeCoords(x, y, "center", "center")
|
||||
getCoordsByPosition(x, y, "center", "center")
|
||||
|
||||
getRightCoordinates = (rect) ->
|
||||
x = rect.left + rect.width
|
||||
y = rect.top + rect.height / 2
|
||||
normalizeCoords(x, y, "right", "center")
|
||||
getCoordsByPosition(x, y, "right", "center")
|
||||
|
||||
getBottomLeftCoordinates = (rect) ->
|
||||
x = rect.left
|
||||
y = rect.top + rect.height
|
||||
normalizeCoords(x, y, "left", "bottom")
|
||||
getCoordsByPosition(x, y, "left", "bottom")
|
||||
|
||||
getBottomCoordinates = (rect) ->
|
||||
x = rect.left + rect.width / 2
|
||||
y = rect.top + rect.height
|
||||
normalizeCoords(x, y, "center", "bottom")
|
||||
getCoordsByPosition(x, y, "center", "bottom")
|
||||
|
||||
getBottomRightCoordinates = (rect) ->
|
||||
x = rect.left + rect.width
|
||||
y = rect.top + rect.height
|
||||
normalizeCoords(x, y, "right", "bottom")
|
||||
getCoordsByPosition(x, y, "right", "bottom")
|
||||
|
||||
getElementCoordinatesByPositionRelativeToXY = ($el, x, y) ->
|
||||
positionProps = getElementPositioning($el)
|
||||
@@ -98,14 +116,14 @@ getElementCoordinatesByPositionRelativeToXY = ($el, x, y) ->
|
||||
fromWindow.left += x
|
||||
fromWindow.top += y
|
||||
|
||||
normalizeFromViewport = normalizeCoords(fromViewport.left, fromViewport.top)
|
||||
normalizeFromWindow = normalizeCoords(fromWindow.left, fromWindow.top)
|
||||
viewportTargetCoords = getTopLeftCoordinates(fromViewport)
|
||||
windowTargetCoords = getTopLeftCoordinates(fromWindow)
|
||||
|
||||
fromViewport.left = normalizeFromViewport.left
|
||||
fromViewport.top = normalizeFromViewport.top
|
||||
fromViewport.x = viewportTargetCoords.x
|
||||
fromViewport.y = viewportTargetCoords.y
|
||||
|
||||
fromWindow.left = normalizeFromWindow.left
|
||||
fromWindow.top = normalizeFromWindow.top
|
||||
fromWindow.x = windowTargetCoords.x
|
||||
fromWindow.y = windowTargetCoords.y
|
||||
|
||||
return positionProps
|
||||
|
||||
@@ -127,22 +145,38 @@ getElementCoordinatesByPosition = ($el, position = "center") ->
|
||||
|
||||
fn = calculations[fnName]
|
||||
|
||||
## get the desired x/y coords based on
|
||||
## what position we're trying to target
|
||||
viewportTargetCoords = fn({
|
||||
width
|
||||
height
|
||||
top: fromViewport.top
|
||||
left: fromViewport.left
|
||||
})
|
||||
|
||||
## get the desired x/y coords based on
|
||||
## what position we're trying to target
|
||||
windowTargetCoords = fn({
|
||||
width
|
||||
height
|
||||
top: fromWindow.top
|
||||
left: fromWindow.left
|
||||
})
|
||||
|
||||
fromViewport.x = viewportTargetCoords.x
|
||||
fromViewport.y = viewportTargetCoords.y
|
||||
|
||||
fromWindow.x = windowTargetCoords.x
|
||||
fromWindow.y = windowTargetCoords.y
|
||||
|
||||
## return an object with both sets
|
||||
## of normalized coordinates for both
|
||||
## the window and the viewport
|
||||
return {
|
||||
fromViewport: fn({
|
||||
width
|
||||
height
|
||||
top: fromViewport.top
|
||||
left: fromViewport.left
|
||||
}),
|
||||
fromWindow: fn({
|
||||
width
|
||||
height
|
||||
top: fromWindow.top
|
||||
left: fromWindow.left
|
||||
})
|
||||
width
|
||||
height
|
||||
fromViewport
|
||||
fromWindow
|
||||
}
|
||||
|
||||
calculations = {
|
||||
@@ -158,7 +192,7 @@ calculations = {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
normalizeCoords
|
||||
getCoordsByPosition
|
||||
|
||||
getElementPositioning
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ $coordinates = require("./coordinates")
|
||||
{ wrap, unwrap, isJquery } = $jquery
|
||||
{ isVisible, isHidden, getReasonIsHidden } = $visibility
|
||||
{ isType, isFocusable, isElement, isScrollable, stringify, getElements, isDetached, isAttached, isTextLike, isSelector, isDescendent, getFirstFixedOrStickyPositionParent, getFirstScrollableParent } = $elements
|
||||
{ normalizeCoords, getElementPositioning, getElementCoordinatesByPosition, getElementAtPointFromViewport, getElementCoordinatesByPositionRelativeToXY } = $coordinates
|
||||
{ getCoordsByPosition, getElementPositioning, getElementCoordinatesByPosition, getElementAtPointFromViewport, getElementCoordinatesByPositionRelativeToXY } = $coordinates
|
||||
|
||||
isDom = (obj) ->
|
||||
isElement(obj) or isWindow(obj) or isDocument(obj)
|
||||
@@ -67,7 +67,7 @@ module.exports = {
|
||||
|
||||
getFirstFixedOrStickyPositionParent
|
||||
|
||||
normalizeCoords
|
||||
getCoordsByPosition
|
||||
|
||||
getElementPositioning
|
||||
|
||||
|
||||
@@ -43,7 +43,15 @@ isHidden = (el, name) ->
|
||||
## to see if its hidden by a parent
|
||||
elIsHiddenByAncestors($el) or
|
||||
|
||||
elIsOutOfBoundsOfAncestorsOverflow($el)
|
||||
## if this is a fixed element check if its covered
|
||||
(
|
||||
if elIsFixed($el)
|
||||
elIsNotElementFromPoint($el)
|
||||
else
|
||||
## else check if el is outside the bounds
|
||||
## of its ancestors overflow
|
||||
elIsOutOfBoundsOfAncestorsOverflow($el)
|
||||
)
|
||||
|
||||
elHasNoEffectiveWidthOrHeight = ($el) ->
|
||||
elOffsetWidth($el) <= 0 or elOffsetHeight($el) <= 0 or $el[0].getClientRects().length <= 0
|
||||
@@ -89,6 +97,20 @@ canClipContent = ($el, $ancestor) ->
|
||||
|
||||
return true
|
||||
|
||||
elIsFixed = ($el) ->
|
||||
if $stickyOrFixedEl = $elements.getFirstFixedOrStickyPositionParent($el)
|
||||
$stickyOrFixedEl.css("position") is "fixed"
|
||||
|
||||
elAtCenterPoint = ($el) ->
|
||||
elProps = $coordinates.getElementPositioning($el)
|
||||
|
||||
{ topCenter, leftCenter } = elProps.fromViewport
|
||||
|
||||
doc = $document.getDocumentFromElement($el.get(0))
|
||||
|
||||
if el = $coordinates.getElementAtPointFromViewport(doc, leftCenter, topCenter)
|
||||
$jquery.wrap(el)
|
||||
|
||||
elDescendentsHavePositionFixedOrAbsolute = ($parent, $child) ->
|
||||
## create an array of all elements between $parent and $child
|
||||
## including child but excluding parent
|
||||
@@ -99,14 +121,11 @@ elDescendentsHavePositionFixedOrAbsolute = ($parent, $child) ->
|
||||
fixedOrAbsoluteRe.test $jquery.wrap(el).css("position")
|
||||
|
||||
elIsNotElementFromPoint = ($el) ->
|
||||
elProps = $coordinates.getElementPositioning($el)
|
||||
|
||||
{top, left} = elProps.fromViewport
|
||||
|
||||
doc = $document.getDocumentFromElement($el.get(0))
|
||||
|
||||
if el = $coordinates.getElementAtPointFromViewport(doc, left, top)
|
||||
$elAtPoint = $jquery.wrap(el)
|
||||
## if we have a fixed position element that means
|
||||
## it is fixed 'relative' to the viewport which means
|
||||
## it MUST be available with elementFromPoint because
|
||||
## that is also relative to the viewport
|
||||
$elAtPoint = elAtCenterPoint($el)
|
||||
|
||||
## if the element at point is not a descendent
|
||||
## of our $el then we know it's being covered or its
|
||||
@@ -114,14 +133,6 @@ elIsNotElementFromPoint = ($el) ->
|
||||
return not $elements.isDescendent($el, $elAtPoint)
|
||||
|
||||
elIsOutOfBoundsOfAncestorsOverflow = ($el, $ancestor) ->
|
||||
if $stickyOrFixedEl = $elements.getFirstFixedOrStickyPositionParent($el)
|
||||
if $stickyOrFixedEl.css("position") is "fixed"
|
||||
## if we have a fixed position element that means
|
||||
## it is fixed 'relative' to the viewport which means
|
||||
## it MUST be available with elementFromPoint because
|
||||
## that is also relative to the viewport
|
||||
return elIsNotElementFromPoint($stickyOrFixedEl)
|
||||
|
||||
$ancestor ?= $el.parent()
|
||||
|
||||
## no ancestor, not out of bounds!
|
||||
@@ -227,10 +238,12 @@ getReasonIsHidden = ($el) ->
|
||||
|
||||
when $parent = parentHasDisplayNone($el.parent())
|
||||
parentNode = $elements.stringify($parent, "short")
|
||||
|
||||
"This element '#{node}' is not visible because its parent '#{parentNode}' has CSS property: 'display: none'"
|
||||
|
||||
when $parent = parentHasVisibilityNone($el.parent())
|
||||
parentNode = $elements.stringify($parent, "short")
|
||||
|
||||
"This element '#{node}' is not visible because its parent '#{parentNode}' has CSS property: 'visibility: hidden'"
|
||||
|
||||
when elHasVisibilityHidden($el)
|
||||
@@ -239,19 +252,33 @@ getReasonIsHidden = ($el) ->
|
||||
when elHasNoOffsetWidthOrHeight($el)
|
||||
width = elOffsetWidth($el)
|
||||
height = elOffsetHeight($el)
|
||||
|
||||
"This element '#{node}' is not visible because it has an effective width and height of: '#{width} x #{height}' pixels."
|
||||
|
||||
when $parent = parentHasNoOffsetWidthOrHeightAndOverflowHidden($el.parent())
|
||||
parentNode = $elements.stringify($parent, "short")
|
||||
width = elOffsetWidth($parent)
|
||||
height = elOffsetHeight($parent)
|
||||
|
||||
"This element '#{node}' is not visible because its parent '#{parentNode}' has CSS property: 'overflow: hidden' and an effective width and height of: '#{width} x #{height}' pixels."
|
||||
|
||||
when elIsOutOfBoundsOfAncestorsOverflow($el)
|
||||
"This element '#{node}' is not visible because its content is being clipped by one of its parent elements, which has a CSS property of overflow: 'hidden', 'scroll' or 'auto'"
|
||||
|
||||
else
|
||||
"Cypress could not determine why this element '#{node}' is not visible."
|
||||
## nested else --___________--
|
||||
if elIsFixed($el)
|
||||
if elIsNotElementFromPoint($el)
|
||||
## show the long element here
|
||||
covered = $elements.stringify(elAtCenterPoint($el))
|
||||
|
||||
return """
|
||||
This element '#{node}' is not visible because it has CSS property: 'position: fixed' and its being covered by another element:
|
||||
|
||||
#{covered}
|
||||
"""
|
||||
else
|
||||
if elIsOutOfBoundsOfAncestorsOverflow($el)
|
||||
return "This element '#{node}' is not visible because its content is being clipped by one of its parent elements, which has a CSS property of overflow: 'hidden', 'scroll' or 'auto'"
|
||||
|
||||
return "Cypress could not determine why this element '#{node}' is not visible."
|
||||
|
||||
module.exports = {
|
||||
isVisible
|
||||
|
||||
@@ -2,7 +2,7 @@ $ = Cypress.$.bind(Cypress)
|
||||
_ = Cypress._
|
||||
Promise = Cypress.Promise
|
||||
|
||||
describe "src/cy/commands/actions/checkbox", ->
|
||||
describe "src/cy/commands/actions/check", ->
|
||||
before ->
|
||||
cy
|
||||
.visit("/fixtures/dom.html")
|
||||
@@ -435,7 +435,9 @@ describe "src/cy/commands/actions/checkbox", ->
|
||||
expect(console.Command).to.eq "check"
|
||||
expect(console["Applied To"]).to.eq lastLog.get("$el").get(0)
|
||||
expect(console.Elements).to.eq 1
|
||||
expect(console.Coords).to.deep.eq(fromWindow)
|
||||
expect(console.Coords).to.deep.eq(
|
||||
_.pick(fromWindow, "x", "y")
|
||||
)
|
||||
|
||||
it "#consoleProps when checkbox is already checked", ->
|
||||
cy.get("[name=colors][value=blue]").invoke("prop", "checked", true).check().then ($input) ->
|
||||
@@ -824,7 +826,9 @@ describe "src/cy/commands/actions/checkbox", ->
|
||||
expect(console.Command).to.eq "uncheck"
|
||||
expect(console["Applied To"]).to.eq lastLog.get("$el").get(0)
|
||||
expect(console.Elements).to.eq(1)
|
||||
expect(console.Coords).to.deep.eq(fromWindow)
|
||||
expect(console.Coords).to.deep.eq(
|
||||
_.pick(fromWindow, "x", "y")
|
||||
)
|
||||
|
||||
it "#consoleProps when checkbox is already unchecked", ->
|
||||
cy.get("[name=colors][value=blue]").invoke("prop", "checked", false).uncheck().then ($input) ->
|
||||
@@ -5,7 +5,7 @@ Promise = Cypress.Promise
|
||||
fail = (str) ->
|
||||
throw new Error(str)
|
||||
|
||||
describe "src/cy/commands/actions/clicking", ->
|
||||
describe "src/cy/commands/actions/click", ->
|
||||
before ->
|
||||
cy
|
||||
.visit("/fixtures/dom.html")
|
||||
@@ -41,8 +41,8 @@ describe "src/cy/commands/actions/clicking", ->
|
||||
type: "click"
|
||||
}
|
||||
|
||||
expect(e.clientX).to.be.closeTo(fromViewport.left, 1)
|
||||
expect(e.clientY).to.be.closeTo(fromViewport.top, 1)
|
||||
expect(e.clientX).to.be.closeTo(fromViewport.x, 1)
|
||||
expect(e.clientY).to.be.closeTo(fromViewport.y, 1)
|
||||
done()
|
||||
|
||||
cy.get("#button").click()
|
||||
@@ -82,8 +82,8 @@ describe "src/cy/commands/actions/clicking", ->
|
||||
type: "mousedown"
|
||||
}
|
||||
|
||||
expect(e.clientX).to.be.closeTo(fromViewport.left, 1)
|
||||
expect(e.clientY).to.be.closeTo(fromViewport.top, 1)
|
||||
expect(e.clientX).to.be.closeTo(fromViewport.x, 1)
|
||||
expect(e.clientY).to.be.closeTo(fromViewport.y, 1)
|
||||
done()
|
||||
|
||||
cy.get("#button").click()
|
||||
@@ -113,8 +113,8 @@ describe "src/cy/commands/actions/clicking", ->
|
||||
type: "mouseup"
|
||||
}
|
||||
|
||||
expect(e.clientX).to.be.closeTo(fromViewport.left, 1)
|
||||
expect(e.clientY).to.be.closeTo(fromViewport.top, 1)
|
||||
expect(e.clientX).to.be.closeTo(fromViewport.x, 1)
|
||||
expect(e.clientY).to.be.closeTo(fromViewport.y, 1)
|
||||
done()
|
||||
|
||||
cy.get("#button").click()
|
||||
@@ -140,7 +140,7 @@ describe "src/cy/commands/actions/clicking", ->
|
||||
{ fromViewport } = Cypress.dom.getElementCoordinatesByPosition($btn)
|
||||
|
||||
expect(win.pageXOffset).to.be.gt(0)
|
||||
expect(e.clientX).to.be.closeTo(fromViewport.left, 1)
|
||||
expect(e.clientX).to.be.closeTo(fromViewport.x, 1)
|
||||
done()
|
||||
|
||||
cy.get("#scrolledBtn").click()
|
||||
@@ -154,7 +154,7 @@ describe "src/cy/commands/actions/clicking", ->
|
||||
{ fromViewport } = Cypress.dom.getElementCoordinatesByPosition($btn)
|
||||
|
||||
expect(win.pageYOffset).to.be.gt(0)
|
||||
expect(e.clientY).to.be.closeTo(fromViewport.top, 1)
|
||||
expect(e.clientY).to.be.closeTo(fromViewport.y, 1)
|
||||
done()
|
||||
|
||||
cy.get("#scrolledBtn").click()
|
||||
@@ -661,11 +661,12 @@ describe "src/cy/commands/actions/clicking", ->
|
||||
|
||||
it "can click topLeft", (done) ->
|
||||
$btn = $("<button>button covered</button>").attr("id", "button-covered-in-span").css({height: 100, width: 100}).prependTo(cy.$$("body"))
|
||||
span = $("<span>span</span>").css(position: "absolute", left: $btn.offset().left, top: $btn.offset().top, padding: 5, display: "inline-block", backgroundColor: "yellow").appendTo($btn)
|
||||
|
||||
$span = $("<span>span</span>").css(position: "absolute", left: $btn.offset().left, top: $btn.offset().top, padding: 5, display: "inline-block", backgroundColor: "yellow").appendTo($btn)
|
||||
|
||||
clicked = _.after 2, -> done()
|
||||
|
||||
span.on "click", clicked
|
||||
$span.on "click", clicked
|
||||
$btn.on "click", clicked
|
||||
|
||||
cy.get("#button-covered-in-span").click("topLeft")
|
||||
@@ -1027,6 +1028,42 @@ describe "src/cy/commands/actions/clicking", ->
|
||||
|
||||
cy.get("#button-covered-in-span").click()
|
||||
|
||||
it "throws when element is fixed position and being covered", (done) ->
|
||||
$btn = $("<button>button covered</button>")
|
||||
.attr("id", "button-covered-in-span")
|
||||
.css({position: "fixed", left: 0, top: 0})
|
||||
.prependTo(cy.$$("body"))
|
||||
|
||||
$span = $("<span>span on button</span>")
|
||||
.css({position: "fixed", left: 0, top: 0, padding: 20, display: "inline-block", backgroundColor: "yellow", zIndex: 10})
|
||||
.prependTo(cy.$$("body"))
|
||||
|
||||
cy.on "fail", (err) =>
|
||||
lastLog = @lastLog
|
||||
|
||||
## get + click logs
|
||||
expect(@logs.length).eq(2)
|
||||
expect(lastLog.get("error")).to.eq(err)
|
||||
|
||||
## there should still be 2 snapshots on error (before + after)
|
||||
expect(lastLog.get("snapshots").length).to.eq(2)
|
||||
expect(lastLog.get("snapshots")[0]).to.be.an("object")
|
||||
expect(lastLog.get("snapshots")[0].name).to.eq("before")
|
||||
expect(lastLog.get("snapshots")[1]).to.be.an("object")
|
||||
expect(lastLog.get("snapshots")[1].name).to.eq("after")
|
||||
expect(err.message).to.include "cy.click() failed because this element is not visible:"
|
||||
expect(err.message).to.include ">button ...</button>"
|
||||
expect(err.message).to.include "'<button#button-covered-in-span>' is not visible because it has CSS property: 'position: fixed' and its being covered"
|
||||
expect(err.message).to.include ">span on...</span>"
|
||||
|
||||
console = lastLog.invoke("consoleProps")
|
||||
expect(console["Tried to Click"]).to.be.undefined
|
||||
expect(console["But its Covered By"]).to.be.undefined
|
||||
|
||||
done()
|
||||
|
||||
cy.get("#button-covered-in-span").click()
|
||||
|
||||
it "throws when element is hidden and theres no element specifically covering it", (done) ->
|
||||
## i cant come up with a way to easily make getElementAtCoordinates
|
||||
## return null so we are just forcing it to return null to simulate
|
||||
@@ -1194,13 +1231,13 @@ describe "src/cy/commands/actions/clicking", ->
|
||||
console = lastLog.invoke("consoleProps")
|
||||
{ fromWindow } = Cypress.dom.getElementCoordinatesByPosition($button)
|
||||
logCoords = lastLog.get("coords")
|
||||
expect(logCoords.left).to.be.closeTo(fromWindow.left, 1) ## ensure we are within 1
|
||||
expect(logCoords.top).to.be.closeTo(fromWindow.top, 1) ## ensure we are within 1
|
||||
expect(logCoords.x).to.be.closeTo(fromWindow.x, 1) ## ensure we are within 1
|
||||
expect(logCoords.y).to.be.closeTo(fromWindow.y, 1) ## ensure we are within 1
|
||||
expect(console.Command).to.eq "click"
|
||||
expect(console["Applied To"]).to.eq lastLog.get("$el").get(0)
|
||||
expect(console.Elements).to.eq 1
|
||||
expect(console.Coords.left).to.be.closeTo(fromWindow.left, 1) ## ensure we are within 1
|
||||
expect(console.Coords.top).to.be.closeTo(fromWindow.top, 1) ## ensure we are within 1
|
||||
expect(console.Coords.x).to.be.closeTo(fromWindow.x, 1) ## ensure we are within 1
|
||||
expect(console.Coords.y).to.be.closeTo(fromWindow.y, 1) ## ensure we are within 1
|
||||
|
||||
it "#consoleProps actual element clicked", ->
|
||||
$btn = $("<button>", {
|
||||
@@ -1,7 +1,7 @@
|
||||
$ = Cypress.$Cypress.$
|
||||
_ = Cypress._
|
||||
|
||||
describe "src/cy/commands/actions/scrolling", ->
|
||||
describe "src/cy/commands/actions/scroll", ->
|
||||
before ->
|
||||
cy
|
||||
.visit("/fixtures/scrolling.html")
|
||||
@@ -375,8 +375,8 @@ describe "src/cy/commands/actions/select", ->
|
||||
expect(console.Command).to.eq("select")
|
||||
expect(console.Selected).to.deep.eq ["de_dust2"]
|
||||
expect(console["Applied To"]).to.eq $select.get(0)
|
||||
expect(console.Coords.left).to.be.closeTo(fromWindow.left, 10)
|
||||
expect(console.Coords.top).to.be.closeTo(fromWindow.top, 10)
|
||||
expect(console.Coords.x).to.be.closeTo(fromWindow.x, 10)
|
||||
expect(console.Coords.y).to.be.closeTo(fromWindow.y, 10)
|
||||
|
||||
it "logs only one select event", ->
|
||||
types = []
|
||||
|
||||
@@ -2,7 +2,7 @@ $ = Cypress.$.bind(Cypress)
|
||||
_ = Cypress._
|
||||
Promise = Cypress.Promise
|
||||
|
||||
describe "src/cy/commands/actions/form", ->
|
||||
describe "src/cy/commands/actions/submit", ->
|
||||
before ->
|
||||
cy
|
||||
.visit("/fixtures/dom.html")
|
||||
@@ -28,8 +28,8 @@ describe "src/cy/commands/actions/trigger", ->
|
||||
type: "mouseover"
|
||||
}
|
||||
|
||||
expect(e.clientX).to.be.closeTo(fromViewport.left, 1)
|
||||
expect(e.clientY).to.be.closeTo(fromViewport.top, 1)
|
||||
expect(e.clientX).to.be.closeTo(fromViewport.x, 1)
|
||||
expect(e.clientY).to.be.closeTo(fromViewport.y, 1)
|
||||
done()
|
||||
|
||||
cy.get("#button").trigger("mouseover")
|
||||
@@ -84,7 +84,7 @@ describe "src/cy/commands/actions/trigger", ->
|
||||
{ fromViewport } = Cypress.dom.getElementCoordinatesByPosition($btn)
|
||||
|
||||
expect(win.pageXOffset).to.be.gt(0)
|
||||
expect(e.clientX).to.be.closeTo(fromViewport.left, 1)
|
||||
expect(e.clientX).to.be.closeTo(fromViewport.x, 1)
|
||||
done()
|
||||
|
||||
cy.get("#scrolledBtn").trigger("mouseover")
|
||||
@@ -98,7 +98,7 @@ describe "src/cy/commands/actions/trigger", ->
|
||||
{ fromViewport } = Cypress.dom.getElementCoordinatesByPosition($btn)
|
||||
|
||||
expect(win.pageXOffset).to.be.gt(0)
|
||||
expect(e.clientY).to.be.closeTo(fromViewport.top, 1)
|
||||
expect(e.clientY).to.be.closeTo(fromViewport.y, 1)
|
||||
done()
|
||||
|
||||
cy.get("#scrolledBtn").trigger("mouseover")
|
||||
@@ -758,7 +758,7 @@ describe "src/cy/commands/actions/trigger", ->
|
||||
lastLog = @lastLog
|
||||
|
||||
{ fromWindow } = Cypress.dom.getElementCoordinatesByPosition($btn)
|
||||
expect(lastLog.get("coords")).to.deep.eq(fromWindow)
|
||||
expect(lastLog.get("coords")).to.deep.eq(fromWindow, "x", "y")
|
||||
|
||||
it "#consoleProps", ->
|
||||
cy.get("button:first").trigger("mouseover").then ($button) =>
|
||||
@@ -766,8 +766,8 @@ describe "src/cy/commands/actions/trigger", ->
|
||||
{ fromWindow } = Cypress.dom.getElementCoordinatesByPosition($button)
|
||||
logCoords = @lastLog.get("coords")
|
||||
eventOptions = consoleProps["Event options"]
|
||||
expect(logCoords.left).to.be.closeTo(fromWindow.left, 1) ## ensure we are within 1
|
||||
expect(logCoords.top).to.be.closeTo(fromWindow.top, 1) ## ensure we are within 1
|
||||
expect(logCoords.x).to.be.closeTo(fromWindow.x, 1) ## ensure we are within 1
|
||||
expect(logCoords.y).to.be.closeTo(fromWindow.y, 1) ## ensure we are within 1
|
||||
expect(consoleProps.Command).to.eq "trigger"
|
||||
expect(eventOptions.bubbles).to.be.true
|
||||
expect(eventOptions.cancelable).to.be.true
|
||||
|
||||
@@ -4,7 +4,7 @@ Keyboard = Cypress.Keyboard
|
||||
bililiteRange = Cypress.bililiteRange
|
||||
Promise = Cypress.Promise
|
||||
|
||||
describe "src/cy/commands/actions/text", ->
|
||||
describe "src/cy/commands/actions/type", ->
|
||||
before ->
|
||||
cy
|
||||
.visit("/fixtures/dom.html")
|
||||
@@ -2011,8 +2011,8 @@ describe "src/cy/commands/actions/text", ->
|
||||
expect(console.Command).to.eq("type")
|
||||
expect(console.Typed).to.eq("foobar")
|
||||
expect(console["Applied To"]).to.eq $input.get(0)
|
||||
expect(console.Coords.left).to.be.closeTo(fromWindow.left, 1)
|
||||
expect(console.Coords.top).to.be.closeTo(fromWindow.top, 1)
|
||||
expect(console.Coords.x).to.be.closeTo(fromWindow.x, 1)
|
||||
expect(console.Coords.y).to.be.closeTo(fromWindow.y, 1)
|
||||
|
||||
it "has a table of keys", ->
|
||||
cy.get(":text:first").type("{cmd}{option}foo{enter}b{leftarrow}{del}{enter}").then ->
|
||||
@@ -53,6 +53,7 @@ describe "src/cy/commands/waiting", ->
|
||||
.route("GET", /.*/, response).as("fetch")
|
||||
.window().then (win) ->
|
||||
win.$.get("/foo")
|
||||
null
|
||||
.wait("@fetch").then (xhr) ->
|
||||
expect(xhr.responseBody).to.deep.eq response
|
||||
|
||||
@@ -191,6 +192,7 @@ describe "src/cy/commands/waiting", ->
|
||||
.route(/foo/, {}).as("foo")
|
||||
.window().then (win) ->
|
||||
win.$.get("/foo")
|
||||
null
|
||||
.wait(["@foo", "@bar"])
|
||||
|
||||
it "throws when 2nd alias is missing '@' but matches an available alias", (done) ->
|
||||
@@ -204,6 +206,7 @@ describe "src/cy/commands/waiting", ->
|
||||
.route(/bar/, {}).as("bar")
|
||||
.window().then (win) ->
|
||||
win.$.get("/foo")
|
||||
null
|
||||
.wait(["@foo", "bar"])
|
||||
|
||||
it "throws when 2nd alias isnt a route alias", (done) ->
|
||||
@@ -217,6 +220,7 @@ describe "src/cy/commands/waiting", ->
|
||||
.get("body").as("bar")
|
||||
.window().then (win) ->
|
||||
win.$.get("/foo")
|
||||
null
|
||||
.wait(["@foo", "@bar"])
|
||||
|
||||
it "throws whenever an alias times out", (done) ->
|
||||
@@ -527,6 +531,7 @@ describe "src/cy/commands/waiting", ->
|
||||
.window().then (win) ->
|
||||
win.$.get("/users")
|
||||
win.$.get("/posts")
|
||||
null
|
||||
.wait(["@getUsers", "@getPosts"]).spread (xhr1, xhr2) ->
|
||||
expect(xhr1.responseBody).to.deep.eq resp1
|
||||
expect(xhr2.responseBody).to.deep.eq resp2
|
||||
@@ -767,6 +772,7 @@ describe "src/cy/commands/waiting", ->
|
||||
.route(/bar/, {}).as("getBar")
|
||||
.window().then (win) ->
|
||||
xhrGet(win, "/foo")
|
||||
null
|
||||
.wait(["@getFoo", "@getBar"])
|
||||
|
||||
describe "function argument errors", ->
|
||||
@@ -781,6 +787,7 @@ describe "src/cy/commands/waiting", ->
|
||||
.route(/foo/, {}).as("getFoo")
|
||||
.window().then (win) ->
|
||||
xhrGet(win, "/foo")
|
||||
null
|
||||
.wait("@getFoo").then ->
|
||||
lastLog = @lastLog
|
||||
|
||||
@@ -794,6 +801,7 @@ describe "src/cy/commands/waiting", ->
|
||||
.window().then (win) ->
|
||||
xhrGet(win, "/foo")
|
||||
xhrGet(win, "/bar")
|
||||
null
|
||||
.wait(["@getFoo", "@getBar"]).then (xhrs) ->
|
||||
lastLog = @lastLog
|
||||
|
||||
@@ -805,6 +813,7 @@ describe "src/cy/commands/waiting", ->
|
||||
.route(/foo/, {}).as("getFoo")
|
||||
.window().then (win) ->
|
||||
xhrGet(win, "/foo")
|
||||
null
|
||||
.wait("@getFoo").then (xhr) ->
|
||||
expect(@lastLog.invoke("consoleProps")).to.deep.eq {
|
||||
Command: "wait"
|
||||
@@ -820,6 +829,7 @@ describe "src/cy/commands/waiting", ->
|
||||
.window().then (win) ->
|
||||
xhrGet(win, "/foo")
|
||||
xhrGet(win, "/bar")
|
||||
null
|
||||
.wait(["@getFoo", "@getBar"]).then (xhrs) ->
|
||||
expect(@lastLog.invoke("consoleProps")).to.deep.eq {
|
||||
Command: "wait"
|
||||
|
||||
@@ -15,9 +15,44 @@ describe "src/dom/coordinates", ->
|
||||
@$button = $("<button style='position: absolute; top: 25px; left: 50px; width: 100px; line-height: 50px; padding: 10px; margin: 10px; border: 10px solid black'>foo</button>")
|
||||
.appendTo(cy.$$("body"))
|
||||
|
||||
context ".normalizeCoords", ->
|
||||
context ".getElementPositioning", ->
|
||||
## this is necessary so that document.elementFromPoint
|
||||
## does not miss elements
|
||||
it "returns the leftCenter and topCenter normalized", ->
|
||||
win = Cypress.dom.getWindowByElement(@$button.get(0))
|
||||
|
||||
pageYOffset = Object.getOwnPropertyDescriptor(win, "pageYOffset")
|
||||
pageXOffset = Object.getOwnPropertyDescriptor(win, "pageXOffset")
|
||||
|
||||
Object.defineProperty(win, "pageYOffset", {
|
||||
value: 10
|
||||
})
|
||||
|
||||
Object.defineProperty(win, "pageXOffset", {
|
||||
value: 20
|
||||
})
|
||||
|
||||
cy.stub(@$button.get(0), "getBoundingClientRect").returns({
|
||||
top: 100.9
|
||||
left: 60.9
|
||||
width: 50
|
||||
height: 40
|
||||
})
|
||||
|
||||
{ fromViewport, fromWindow } = Cypress.dom.getElementPositioning(@$button)
|
||||
|
||||
expect(fromViewport.topCenter).to.eq(120)
|
||||
expect(fromViewport.leftCenter).to.eq(85)
|
||||
|
||||
expect(fromWindow.topCenter).to.eq(130)
|
||||
expect(fromWindow.leftCenter).to.eq(105)
|
||||
|
||||
Object.defineProperty(win, "pageYOffset", pageYOffset)
|
||||
Object.defineProperty(win, "pageXOffset", pageXOffset)
|
||||
|
||||
context ".getCoordsByPosition", ->
|
||||
it "rounds down x and y values to object", ->
|
||||
expect(Cypress.dom.normalizeCoords(5.96, 7.68)).to.deep.eq({left: 5, top: 7})
|
||||
expect(Cypress.dom.getCoordsByPosition(5.96, 7.68)).to.deep.eq({x: 5, y: 7})
|
||||
|
||||
context ".getElementAtPointFromViewport", ->
|
||||
it "returns same element based on x/y coords", ->
|
||||
@@ -31,54 +66,91 @@ describe "src/dom/coordinates", ->
|
||||
expect(Cypress.dom.getElementAtPointFromViewport(@doc, 1e9, 1e9)).to.be.null
|
||||
|
||||
context ".getElementCoordinatesByPosition", ->
|
||||
beforeEach ->
|
||||
@fromWindowPos = (pos) =>
|
||||
Cypress.dom.getElementCoordinatesByPosition(@$button, pos)
|
||||
.fromWindow
|
||||
|
||||
describe "topLeft", ->
|
||||
it "returns top left x/y including padding + border", ->
|
||||
obj = @fromWindowPos("topLeft")
|
||||
|
||||
## padding is added to the line-height but width includes the padding
|
||||
expect(Cypress.dom.getElementCoordinatesByPosition(@$button, "topLeft").fromWindow).to.deep.eq {left: 60, top: 35}
|
||||
expect(obj.x).to.eq(60)
|
||||
expect(obj.y).to.eq(35)
|
||||
|
||||
describe "top", ->
|
||||
it "returns top center x/y including padding + border", ->
|
||||
obj = @fromWindowPos("top")
|
||||
|
||||
## padding is added to the line-height but width includes the padding
|
||||
expect(Cypress.dom.getElementCoordinatesByPosition(@$button, "top").fromWindow).to.deep.eq {left: 110, top: 35}
|
||||
expect(obj.x).to.eq(110)
|
||||
expect(obj.y).to.eq(35)
|
||||
|
||||
describe "topRight", ->
|
||||
it "returns top right x/y including padding + border", ->
|
||||
obj = @fromWindowPos("topRight")
|
||||
|
||||
## padding is added to the line-height but width includes the padding
|
||||
expect(Cypress.dom.getElementCoordinatesByPosition(@$button, "topRight").fromWindow).to.deep.eq {left: 159, top: 35}
|
||||
expect(obj.x).to.eq(159)
|
||||
expect(obj.y).to.eq(35)
|
||||
|
||||
describe "left", ->
|
||||
it "returns center left x/y including padding + border", ->
|
||||
obj = @fromWindowPos("left")
|
||||
|
||||
## padding is added to the line-height but width includes the padding
|
||||
expect(Cypress.dom.getElementCoordinatesByPosition(@$button, "left").fromWindow).to.deep.eq {left: 60, top: 80}
|
||||
expect(obj.x).to.eq(60)
|
||||
expect(obj.y).to.eq(80)
|
||||
|
||||
describe "center", ->
|
||||
it "returns center x/y including padding + border", ->
|
||||
obj = @fromWindowPos()
|
||||
|
||||
## padding is added to the line-height but width includes the padding
|
||||
expect(Cypress.dom.getElementCoordinatesByPosition(@$button).fromWindow).to.deep.eq {left: 110, top: 80}
|
||||
expect(obj.x).to.eq(110)
|
||||
expect(obj.y).to.eq(80)
|
||||
|
||||
it "returns width / height factoring in rotation transforms", ->
|
||||
## normally our outerWidth is 100 and outerHeight is 70
|
||||
## after we've been rotated these are reversed and our previous
|
||||
## calculation would be wrong. using getBoundingClientRect passes this test
|
||||
@$button.css({transform: "rotate(90deg)"})
|
||||
expect(Cypress.dom.getElementCoordinatesByPosition(@$button).fromWindow).to.deep.eq {left: 110, top: 80}
|
||||
|
||||
obj = @fromWindowPos()
|
||||
|
||||
## padding is added to the line-height but width includes the padding
|
||||
expect(obj.x).to.eq(110)
|
||||
expect(obj.y).to.eq(80)
|
||||
|
||||
describe "right", ->
|
||||
it "returns center right x/y including padding + border", ->
|
||||
obj = @fromWindowPos("right")
|
||||
|
||||
## padding is added to the line-height but width includes the padding
|
||||
expect(Cypress.dom.getElementCoordinatesByPosition(@$button, "right").fromWindow).to.deep.eq {left: 159, top: 80}
|
||||
expect(obj.x).to.eq(159)
|
||||
expect(obj.y).to.eq(80)
|
||||
|
||||
describe "bottomLeft", ->
|
||||
it "returns bottom left x/y including padding + border", ->
|
||||
obj = @fromWindowPos("bottomLeft")
|
||||
|
||||
## padding is added to the line-height but width includes the padding
|
||||
expect(Cypress.dom.getElementCoordinatesByPosition(@$button, "bottomLeft").fromWindow).to.deep.eq {left: 60, top: 124}
|
||||
expect(obj.x).to.eq(60)
|
||||
expect(obj.y).to.eq(124)
|
||||
|
||||
context "bottom", ->
|
||||
it "returns bottom center x/y including padding + border", ->
|
||||
obj = @fromWindowPos("bottom")
|
||||
|
||||
## padding is added to the line-height but width includes the padding
|
||||
expect(Cypress.dom.getElementCoordinatesByPosition(@$button, "bottom").fromWindow).to.deep.eq {left: 110, top: 124}
|
||||
expect(obj.x).to.eq(110)
|
||||
expect(obj.y).to.eq(124)
|
||||
|
||||
context "bottomRight", ->
|
||||
it "returns bottom right x/y including padding + border", ->
|
||||
obj = @fromWindowPos("bottomRight")
|
||||
|
||||
## padding is added to the line-height but width includes the padding
|
||||
expect(Cypress.dom.getElementCoordinatesByPosition(@$button, "bottomRight").fromWindow).to.deep.eq {left: 159, top: 124}
|
||||
expect(obj.x).to.eq(159)
|
||||
expect(obj.y).to.eq(124)
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
$dom = Cypress.dom
|
||||
$ = Cypress.$.bind(Cypress)
|
||||
|
||||
describe "src/cypress/dom", ->
|
||||
describe "src/cypress/dom/visibility", ->
|
||||
beforeEach ->
|
||||
cy.visit("/fixtures/generic.html")
|
||||
|
||||
it "attaches to Cypress namespace", ->
|
||||
expect($dom).to.be.an("object")
|
||||
|
||||
context "isHidden", ->
|
||||
it "exposes isHidden", ->
|
||||
expect($dom.isHidden).to.be.a("function")
|
||||
@@ -418,8 +415,8 @@ describe "src/cypress/dom", ->
|
||||
expect(@$descendantInPosFixed.find("#descendantInPosFixed")).not.to.be.hidden
|
||||
|
||||
it "is hidden if position: fixed and covered up", ->
|
||||
expect(@$coveredUpPosFixed).to.be.hidden
|
||||
expect(@$coveredUpPosFixed).not.to.be.visible
|
||||
expect(@$coveredUpPosFixed.find("#coveredUpPosFixed")).to.be.hidden
|
||||
expect(@$coveredUpPosFixed.find("#coveredUpPosFixed")).not.to.be.visible
|
||||
|
||||
it "is hidden if position: fixed and off screent", ->
|
||||
expect(@$offScreenPosFixed).to.be.hidden
|
||||
@@ -517,5 +514,12 @@ describe "src/cypress/dom", ->
|
||||
it "element sits outside boundaries of parent with overflow clipping", ->
|
||||
@reasonIs @$elOutOfParentBoundsToRight.find("span"), "This element '<span>' is not visible because its content is being clipped by one of its parent elements, which has a CSS property of overflow: \'hidden\', \'scroll\' or \'auto\'"
|
||||
|
||||
it "element is fixed and being covered", ->
|
||||
@reasonIs @$coveredUpPosFixed.find("#coveredUpPosFixed"), """
|
||||
This element '<div#coveredUpPosFixed>' is not visible because it has CSS property: 'position: fixed' and its being covered by another element:
|
||||
|
||||
<div style="position: fixed; bottom: 0; left: 0">on top</div>
|
||||
"""
|
||||
|
||||
it "cannot determine why element is not visible", ->
|
||||
@reasonIs @$btnOpacity, "Cypress could not determine why this element '<button>' is not visible."
|
||||
@@ -18,8 +18,8 @@ function addHitBoxLayer (coords, body) {
|
||||
const dotHeight = 4
|
||||
const dotWidth = 4
|
||||
|
||||
const top = coords.top - height / 2
|
||||
const left = coords.left - width / 2
|
||||
const top = coords.y - height / 2
|
||||
const left = coords.x - width / 2
|
||||
|
||||
const dotTop = height / 2 - dotHeight / 2
|
||||
const dotLeft = width / 2 - dotWidth / 2
|
||||
|
||||
Reference in New Issue
Block a user