Merge pull request #476 from cypress-io/issue-655

Issue 655
This commit is contained in:
Brian Mann
2017-09-16 15:16:59 -04:00
committed by GitHub
25 changed files with 379 additions and 180 deletions

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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()

View File

@@ -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, {

View File

@@ -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)

View File

@@ -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

View File

@@ -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")

View File

@@ -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
"""

View File

@@ -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
})

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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) ->

View File

@@ -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>", {

View File

@@ -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")

View File

@@ -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 = []

View File

@@ -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")

View File

@@ -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

View File

@@ -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 ->

View File

@@ -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"

View File

@@ -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)

View File

@@ -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."

View File

@@ -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