Skip to content

Commit

Permalink
fix: absolute positioning element blackouts in cy.screenshot (#22756)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
AtofStryker committed Jul 13, 2022
1 parent 782dfb7 commit e16820b
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 16 deletions.
20 changes: 12 additions & 8 deletions packages/driver/src/cy/commands/screenshot.ts
Expand Up @@ -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)
Expand All @@ -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 */
Expand All @@ -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:')
Expand Down Expand Up @@ -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)
Expand All @@ -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> {
Expand Down
5 changes: 3 additions & 2 deletions packages/driver/src/dom/blackout.ts
Expand Up @@ -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
Expand Down
15 changes: 9 additions & 6 deletions system-tests/__snapshots__/screenshot_viewport_capture_spec.js
Expand Up @@ -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 │
Expand Down Expand Up @@ -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)
Expand All @@ -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 - - -
`
Expand Up @@ -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>

5 comments on commit e16820b

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on e16820b Jul 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.3.1/linux-x64/develop-e16820b23eed5abfdab55c6616909ca3b494cdda/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on e16820b Jul 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux arm64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.3.1/linux-arm64/develop-e16820b23eed5abfdab55c6616909ca3b494cdda/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on e16820b Jul 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin arm64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.3.1/darwin-arm64/develop-e16820b23eed5abfdab55c6616909ca3b494cdda/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on e16820b Jul 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.3.1/darwin-x64/develop-e16820b23eed5abfdab55c6616909ca3b494cdda/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on e16820b Jul 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the win32 x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.3.1/win32-x64/develop-e16820b23eed5abfdab55c6616909ca3b494cdda/cypress.tgz

Please sign in to comment.