diff --git a/packages/driver/src/cy/commands/screenshot.ts b/packages/driver/src/cy/commands/screenshot.ts index 38deb62b069e..c0c392ab23ec 100644 --- a/packages/driver/src/cy/commands/screenshot.ts +++ b/packages/driver/src/cy/commands/screenshot.ts @@ -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 { diff --git a/packages/driver/src/dom/blackout.ts b/packages/driver/src/dom/blackout.ts index bf8e991b475a..864f7a6913e0 100644 --- a/packages/driver/src/dom/blackout.ts +++ b/packages/driver/src/dom/blackout.ts @@ -32,11 +32,12 @@ function addBlackoutForElement ($body, $el) { $(`
`).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 diff --git a/system-tests/__snapshots__/screenshot_viewport_capture_spec.js b/system-tests/__snapshots__/screenshot_viewport_capture_spec.js index dc09adfa3e10..2f318697830a 100644 --- a/system-tests/__snapshots__/screenshot_viewport_capture_spec.js +++ b/system-tests/__snapshots__/screenshot_viewport_capture_spec.js @@ -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 - - - ` diff --git a/system-tests/projects/e2e/cypress/e2e/screenshot_viewport_capture.cy.js b/system-tests/projects/e2e/cypress/e2e/screenshot_viewport_capture.cy.js index 2a809542a889..4b1e859fa4b0 100644 --- a/system-tests/projects/e2e/cypress/e2e/screenshot_viewport_capture.cy.js +++ b/system-tests/projects/e2e/cypress/e2e/screenshot_viewport_capture.cy.js @@ -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, + ) + }, + }) +}) diff --git a/system-tests/projects/e2e/cypress/fixtures/screenshot-blackout.html b/system-tests/projects/e2e/cypress/fixtures/screenshot-blackout.html new file mode 100644 index 000000000000..c956465a063b --- /dev/null +++ b/system-tests/projects/e2e/cypress/fixtures/screenshot-blackout.html @@ -0,0 +1,47 @@ + + + + + +

Screenshot Blackout Test Absolute Positioning

+
+
Blue Container
+
Red Container
+
Purple Container
+
+ + \ No newline at end of file