fix: absolute positioning element blackouts in cy.screenshot (#22756)

* add regression tests

* attempt to fix screenshot blacks basing blackouted element around container instead of body dimensions

* move blackout test to screenshot viewport suite and update snapshot

* add github issue to test
This commit is contained in:
Bill Glesias
2022-07-13 15:16:50 -04:00
committed by GitHub
parent 782dfb7a90
commit e16820b23e
5 changed files with 130 additions and 16 deletions
+12 -8
View File
@@ -337,7 +337,7 @@ const takeScreenshot = (Cypress, state, screenshotConfig, options: TakeScreensho
}
}
const before = ($el) => {
const before = ($body, $container) => {
return Promise.try(() => {
if (disableTimersAndAnimations) {
return cy.pauseTimers(true)
@@ -349,11 +349,11 @@ const takeScreenshot = (Cypress, state, screenshotConfig, options: TakeScreensho
// could fail if iframe is cross-origin, so fail gracefully
try {
if (disableTimersAndAnimations) {
$dom.addCssAnimationDisabler($el)
$dom.addCssAnimationDisabler($body)
}
_.each(getBlackout(screenshotConfig), (selector) => {
$dom.addBlackouts($el, selector)
$dom.addBlackouts($body, $container, selector)
})
} catch (err) {
/* eslint-disable no-console */
@@ -366,14 +366,14 @@ const takeScreenshot = (Cypress, state, screenshotConfig, options: TakeScreensho
})
}
const after = ($el) => {
const after = ($body) => {
// could fail if iframe is cross-origin, so fail gracefully
try {
if (disableTimersAndAnimations) {
$dom.removeCssAnimationDisabler($el)
$dom.removeCssAnimationDisabler($body)
}
$dom.removeBlackouts($el)
$dom.removeBlackouts($body)
} catch (err) {
/* eslint-disable no-console */
console.error('Failed to modify app dom:')
@@ -417,7 +417,11 @@ const takeScreenshot = (Cypress, state, screenshotConfig, options: TakeScreensho
? subject
: $dom.wrap(state('document').documentElement)
return before($el)
// get the current body of the AUT to accurately calculate screenshot blackouts
// as well as properly enable/disable CSS animations while screenshotting is happening
const $body = Cypress.$('body')
return before($body, $el)
.then(() => {
if (onBeforeScreenshot) {
onBeforeScreenshot.call(state('ctx'), $el)
@@ -444,7 +448,7 @@ const takeScreenshot = (Cypress, state, screenshotConfig, options: TakeScreensho
return props
})
.finally(() => after($el))
.finally(() => after($body))
}
interface InternalScreenshotOptions extends Partial<Cypress.Loggable & Cypress.Timeoutable & Cypress.ScreenshotOptions> {
+3 -2
View File
@@ -32,11 +32,12 @@ function addBlackoutForElement ($body, $el) {
$(`<div class="__cypress-blackout" style="${style}">`).appendTo($body)
}
function addBlackouts ($body, selector) {
function addBlackouts ($body, $container, selector) {
let $el
try {
$el = $body.find(selector)
// only scope blacked out elements to to screenshotted element, not necessarily the whole body
$el = $container.find(selector)
if (!$el.length) return
} catch (err) {
// if it's an invalid selector, just ignore it
@@ -18,19 +18,20 @@ exports['e2e screenshot viewport capture / passes'] = `
✓ takes consistent viewport captures
✓ properly blacks out absolute elements within a relative container
1 passing
2 passing
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 1
│ Passing: 1
│ Tests: 2
│ Passing: 2
│ Failing: 0 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 26
│ Screenshots: 27
│ Video: true │
│ Duration: X seconds │
│ Spec Ran: screenshot_viewport_capture.cy.js │
@@ -91,6 +92,8 @@ exports['e2e screenshot viewport capture / passes'] = `
are (23).png
- /XXX/XXX/XXX/cypress/screenshots/screenshot_viewport_capture.cy.js/viewport-comp (1000x660)
are (24).png
- /XXX/XXX/XXX/cypress/screenshots/screenshot_viewport_capture.cy.js/properly blac (400x400)
ks out absolute elements within a relative container.png
(Video)
@@ -107,9 +110,9 @@ exports['e2e screenshot viewport capture / passes'] = `
Spec Tests Passing Failing Pending Skipped
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ✔ screenshot_viewport_capture.cy.js XX:XX 1 1 - - - │
│ ✔ screenshot_viewport_capture.cy.js XX:XX 2 2 - - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
✔ All specs passed! XX:XX 1 1 - - -
✔ All specs passed! XX:XX 2 2 - - -
`
@@ -26,3 +26,62 @@ it('takes consistent viewport captures', () => {
Cypress._.times(25, fn)
})
})
// @see https://github.com/cypress-io/cypress/issues/22173
it('properly blacks out absolute elements within a relative container', () => {
cy.visit('cypress/fixtures/screenshot-blackout.html')
.get('.centered-container')
.screenshot({
blackout: ['.blue'],
onBeforeScreenshot () {
const blackedOutElementCoordinates = Cypress.$(
'#__cypress-animation-disabler+div.__cypress-blackout',
)[0].getBoundingClientRect()
const actualElementCoordinates = Cypress.$(
'.centered-container .blue',
)[0].getBoundingClientRect()
// make sure blackout element is within 1 pixel of it's element it is supposed to black out
expect(blackedOutElementCoordinates.bottom).to.be.closeTo(
actualElementCoordinates.bottom,
1,
)
expect(blackedOutElementCoordinates.height).to.be.closeTo(
actualElementCoordinates.height,
1,
)
expect(blackedOutElementCoordinates.left).to.be.closeTo(
actualElementCoordinates.left,
1,
)
expect(blackedOutElementCoordinates.right).to.be.closeTo(
actualElementCoordinates.right,
1,
)
expect(blackedOutElementCoordinates.top).to.be.closeTo(
actualElementCoordinates.top,
1,
)
expect(blackedOutElementCoordinates.width).to.be.closeTo(
actualElementCoordinates.width,
1,
)
expect(blackedOutElementCoordinates.x).to.be.closeTo(
actualElementCoordinates.x,
1,
)
expect(blackedOutElementCoordinates.y).to.be.closeTo(
actualElementCoordinates.y,
1,
)
},
})
})
@@ -0,0 +1,47 @@
<html>
<head>
<style>
.centered-container {
position: relative;
height: 400px;
width: 400px;
max-width: 100%;
margin: 0 auto;
text-align: center;
vertical-align: middle;
}
.third-container {
color: white;
}
.grey {
background-color: grey;
}
.blue {
background-color: blue;
position: absolute;
top: 0;
right: 0%;
}
.red {
background-color: red;
position: absolute;
top: 0;
right: 33.33%;
}
.purple {
background-color: purple;
position: absolute;
top: 0;
right: 66.66%;
}
</style>
</head>
<body>
<h1>Screenshot Blackout Test Absolute Positioning</h1>
<div class="centered-container grey">
<div class="third-container blue"> Blue Container</div>
<div class="third-container red"> Red Container</div>
<div class="third-container purple"> Purple Container</div>
</div>
</body>
</html>