diff --git a/cli/types/index.d.ts b/cli/types/index.d.ts index 3a3b45a77c7..020b76e007c 100644 --- a/cli/types/index.d.ts +++ b/cli/types/index.d.ts @@ -2189,11 +2189,19 @@ declare namespace Cypress { height: number } + type Padding = + | number + | [number] + | [number, number] + | [number, number, number] + | [number, number, number, number] + interface ScreenshotOptions { blackout: string[] capture: 'runner' | 'viewport' | 'fullPage' clip: Dimensions disableTimersAndAnimations: boolean + padding: Padding scale: boolean beforeScreenshot(doc: Document): void afterScreenshot(doc: Document): void diff --git a/packages/driver/src/cy/commands/screenshot.coffee b/packages/driver/src/cy/commands/screenshot.coffee index 0a7eb796ab6..24e4cb77b15 100644 --- a/packages/driver/src/cy/commands/screenshot.coffee +++ b/packages/driver/src/cy/commands/screenshot.coffee @@ -81,6 +81,12 @@ scrollOverrides = (win, doc) -> doc.body.style.overflowY = originalBodyOverflowY win.scrollTo(originalX, originalY) +validateNumScreenshots = (numScreenshots, automationOptions) -> + if numScreenshots < 1 + $utils.throwErrByPath("screenshot.invalid_height", { + log: automationOptions.log + }) + takeScrollingScreenshots = (scrolls, win, state, automationOptions) -> scrollAndTake = ({ y, clip, afterScroll }, index) -> win.scrollTo(0, y) @@ -108,6 +114,8 @@ takeFullPageScreenshot = (state, automationOptions) -> viewportHeight = getViewportHeight(state) numScreenshots = Math.ceil(docHeight / viewportHeight) + validateNumScreenshots(numScreenshots, automationOptions) + scrolls = _.map _.times(numScreenshots), (index) -> y = viewportHeight * index clip = if index + 1 is numScreenshots @@ -126,21 +134,48 @@ takeFullPageScreenshot = (state, automationOptions) -> takeScrollingScreenshots(scrolls, win, state, automationOptions) .finally(resetScrollOverrides) +applyPaddingToElementPositioning = (elPosition, automationOptions) -> + if not automationOptions.padding + return elPosition + + [ paddingTop, paddingRight, paddingBottom, paddingLeft ] = automationOptions.padding + + return { + width: elPosition.width + paddingLeft + paddingRight + height: elPosition.height + paddingTop + paddingBottom + fromViewport: { + top: elPosition.fromViewport.top - paddingTop + left: elPosition.fromViewport.left - paddingLeft + bottom: elPosition.fromViewport.bottom + paddingBottom + } + fromWindow: { + top: elPosition.fromWindow.top - paddingTop + } + } + takeElementScreenshot = ($el, state, automationOptions) -> win = state("window") doc = state("document") resetScrollOverrides = scrollOverrides(win, doc) - elPosition = $dom.getElementPositioning($el) + elPosition = applyPaddingToElementPositioning( + $dom.getElementPositioning($el), + automationOptions + ) viewportHeight = getViewportHeight(state) viewportWidth = getViewportWidth(state) numScreenshots = Math.ceil(elPosition.height / viewportHeight) + validateNumScreenshots(numScreenshots, automationOptions) + scrolls = _.map _.times(numScreenshots), (index) -> y = elPosition.fromWindow.top + (viewportHeight * index) afterScroll = -> - elPosition = $dom.getElementPositioning($el) + elPosition = applyPaddingToElementPositioning( + $dom.getElementPositioning($el), + automationOptions + ) x = Math.min(viewportWidth, elPosition.fromViewport.left) width = Math.min(viewportWidth - x, elPosition.width) @@ -188,6 +223,7 @@ getBlackout = ({ capture, blackout }) -> takeScreenshot = (Cypress, state, screenshotConfig, options = {}) -> { capture + padding clip disableTimersAndAnimations onBeforeScreenshot @@ -236,6 +272,7 @@ takeScreenshot = (Cypress, state, screenshotConfig, options = {}) -> width: getViewportWidth(state) height: getViewportHeight(state) } + padding userClip: clip viewport: { width: window.innerWidth @@ -313,7 +350,7 @@ module.exports = (Commands, Cypress, cy, state, config) -> isWin = $dom.isWindow(subject) - screenshotConfig = _.pick(options, "capture", "scale", "disableTimersAndAnimations", "blackout", "waitForCommandSynchronization", "clip", "onBeforeScreenshot", "onAfterScreenshot") + screenshotConfig = _.pick(options, "capture", "scale", "disableTimersAndAnimations", "blackout", "waitForCommandSynchronization", "padding", "clip", "onBeforeScreenshot", "onAfterScreenshot") screenshotConfig = $Screenshot.validate(screenshotConfig, "cy.screenshot", options._log) screenshotConfig = _.extend($Screenshot.getConfig(), screenshotConfig) diff --git a/packages/driver/src/cypress/error_messages.coffee b/packages/driver/src/cypress/error_messages.coffee index f0e08b6829e..388be1c40d3 100644 --- a/packages/driver/src/cypress/error_messages.coffee +++ b/packages/driver/src/cypress/error_messages.coffee @@ -780,7 +780,9 @@ module.exports = { invalid_capture: "{{cmd}}() 'capture' option must be one of the following: 'fullPage', 'viewport', or 'runner'. You passed: {{arg}}" invalid_boolean: "{{cmd}}() '{{option}}' option must be a boolean. You passed: {{arg}}" invalid_blackout: "{{cmd}}() 'blackout' option must be an array of strings. You passed: {{arg}}" - invalid_clip: "{{cmd}}() 'clip' option must be an object of with the keys { width, height, x, y } and number values. You passed: {{arg}}" + invalid_clip: "{{cmd}}() 'clip' option must be an object with the keys { width, height, x, y } and number values. You passed: {{arg}}" + invalid_height: "#{cmd('screenshot')} only works with a screenshot area with a height greater than zero." + invalid_padding: "{{cmd}}() 'padding' option must be either a number or an array of numbers with a maximum length of 4. You passed: {{arg}}" invalid_callback: "{{cmd}}() '{{callback}}' option must be a function. You passed: {{arg}}" multiple_elements: "#{cmd('screenshot')} only works for a single element. You attempted to screenshot {{numElements}} elements." timed_out: "#{cmd('screenshot')} timed out waiting '{{timeout}}ms' to complete." diff --git a/packages/driver/src/cypress/screenshot.coffee b/packages/driver/src/cypress/screenshot.coffee index 541523b5223..5e78cb0dbb3 100644 --- a/packages/driver/src/cypress/screenshot.coffee +++ b/packages/driver/src/cypress/screenshot.coffee @@ -16,6 +16,37 @@ defaults = reset() validCaptures = ["fullPage", "viewport", "runner"] +normalizePadding = (padding) -> + padding ||= 0 + + if _.isArray(padding) + # CSS shorthand + # See: https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties#Tricky_edge_cases + switch padding.length + when 1 + top = right = bottom = left = padding[0] + when 2 + top = bottom = padding[0] + right = left = padding[1] + when 3 + top = padding[0] + right = left = padding[1] + bottom = padding[2] + when 4 + top = padding[0] + right = padding[1] + bottom = padding[2] + left = padding[3] + else + top = right = bottom = left = padding + + return [ + top + right + bottom + left + ] + validateAndSetBoolean = (props, values, cmd, log, option) -> value = props[option] if not value? @@ -94,6 +125,21 @@ validate = (props, cmd, log) -> values.clip = clip + if padding = props.padding + isShorthandPadding = (value) -> ( + (_.isArray(value) and + value.length >= 1 and + value.length <= 4 and + _.every(value, _.isFinite)) + ) + if not (_.isFinite(padding) or isShorthandPadding(padding)) + $utils.throwErrByPath("screenshot.invalid_padding", { + log: log + args: { cmd: cmd, arg: $utils.stringify(padding) } + }) + + values.padding = normalizePadding(padding) + validateAndSetCallback(props, values, cmd, log, "onBeforeScreenshot") validateAndSetCallback(props, values, cmd, log, "onAfterScreenshot") diff --git a/packages/driver/test/cypress/fixtures/screenshots.html b/packages/driver/test/cypress/fixtures/screenshots.html index 7fcd2c413f6..50cd98fc549 100644 --- a/packages/driver/test/cypress/fixtures/screenshots.html +++ b/packages/driver/test/cypress/fixtures/screenshots.html @@ -13,6 +13,12 @@ border: solid 1px black; margin: 20px; } + .empty-element { + height: 0px; + width: 0px; + border: 0px; + margin: 0px; + } .short-element { height: 100px; margin-left: 40px; @@ -20,6 +26,8 @@ } .tall-element { height: 320px; + + background: linear-gradient(red, yellow, blue); } .multiple { border: none; @@ -28,6 +36,7 @@
+ diff --git a/packages/driver/test/cypress/integration/commands/screenshot_spec.coffee b/packages/driver/test/cypress/integration/commands/screenshot_spec.coffee index ab38685c9ec..1bb6c2dc747 100644 --- a/packages/driver/test/cypress/integration/commands/screenshot_spec.coffee +++ b/packages/driver/test/cypress/integration/commands/screenshot_spec.coffee @@ -4,6 +4,9 @@ _ = Cypress._ Promise = Cypress.Promise Screenshot = Cypress.Screenshot +getViewportHeight = () -> + Math.min(cy.state("viewportHeight"), $(cy.state("window")).height()) + describe "src/cy/commands/screenshot", -> beforeEach -> cy.stub(Cypress, "automation").callThrough() @@ -132,7 +135,7 @@ describe "src/cy/commands/screenshot", -> .then -> expect(Cypress.automation).to.be.calledWith("take:screenshot") args = Cypress.automation.withArgs("take:screenshot").args[0][1] - args = _.omit(args, "clip", "userClip", "viewport", "takenPaths", "startTime") + args = _.omit(args, "padding", "clip", "userClip", "viewport", "takenPaths", "startTime") expect(args).to.eql({ testId: runnable.id titles: [ @@ -168,7 +171,7 @@ describe "src/cy/commands/screenshot", -> .then -> expect(Cypress.automation.withArgs("take:screenshot")).to.be.calledOnce args = Cypress.automation.withArgs("take:screenshot").args[0][1] - args = _.omit(args, "clip", "userClip", "viewport", "takenPaths", "startTime") + args = _.omit(args, "padding", "clip", "userClip", "viewport", "takenPaths", "startTime") expect(args).to.eql({ testId: runnable.id titles: [ @@ -201,7 +204,7 @@ describe "src/cy/commands/screenshot", -> .then -> expect(Cypress.automation).to.be.calledWith("take:screenshot") args = Cypress.automation.withArgs("take:screenshot").args[0][1] - args = _.omit(args, "clip", "userClip", "viewport", "takenPaths", "startTime") + args = _.omit(args, "padding", "clip", "userClip", "viewport", "takenPaths", "startTime") expect(args).to.eql({ testId: runnable.id titles: [ @@ -516,18 +519,71 @@ describe "src/cy/commands/screenshot", -> expect(scrollTo.getCall(2).args.join(",")).to.equal("0,100") it "sends the right clip values for elements that need scrolling", -> + scrollTo = cy.spy(cy.state("window"), "scrollTo") + cy.get(".tall-element").screenshot() .then -> + expect(scrollTo.getCall(0).args).to.eql([0, 140]) + take = Cypress.automation.withArgs("take:screenshot") expect(take.args[0][1].clip).to.eql({ x: 20, y: 0, width: 560, height: 200 }) expect(take.args[1][1].clip).to.eql({ x: 20, y: 60, width: 560, height: 120 }) it "sends the right clip values for elements that don't need scrolling", -> + scrollTo = cy.spy(cy.state("window"), "scrollTo") + cy.get(".short-element").screenshot() .then -> + # even though we don't need to scroll, the implementation behaviour is to + # try to scroll until the element is at the top of the viewport. + expect(scrollTo.getCall(0).args).to.eql([0, 20]) + take = Cypress.automation.withArgs("take:screenshot") expect(take.args[0][1].clip).to.eql({ x: 40, y: 0, width: 200, height: 100 }) + it "applies padding to clip values for elements that need scrolling", -> + padding = 10 + + scrollTo = cy.spy(cy.state("window"), "scrollTo") + + cy.get(".tall-element").screenshot({ padding }) + .then -> + viewportHeight = getViewportHeight() + expect(scrollTo.getCall(0).args).to.eql([0, 140 - padding]) + expect(scrollTo.getCall(1).args).to.eql([0, 140 + viewportHeight - padding ]) + + take = Cypress.automation.withArgs("take:screenshot") + + expect(take.args[0][1].clip).to.eql({ + x: 20 - padding, + y: 0, + width: 560 + padding * 2, + height: viewportHeight + }) + expect(take.args[1][1].clip).to.eql({ + x: 20 - padding, + y: 60 - padding, + width: 560 + padding * 2, + height: 120 + padding * 2 + }) + + it "applies padding to clip values for elements that don't need scrolling", -> + padding = 10 + + scrollTo = cy.spy(cy.state("window"), "scrollTo") + + cy.get(".short-element").screenshot({ padding }) + .then -> + expect(scrollTo.getCall(0).args).to.eql([0, padding]) + + take = Cypress.automation.withArgs("take:screenshot") + expect(take.args[0][1].clip).to.eql({ + x: 30, + y: 0, + width: 220, + height: 120 + }) + it "works with cy.within()", -> cy.get(".short-element").within -> cy.screenshot() @@ -647,20 +703,42 @@ describe "src/cy/commands/screenshot", -> @assertErrorMessage("cy.screenshot() 'blackout' option must be an array of strings. You passed: true", done) cy.screenshot({ blackout: [true] }) + it "throws if there is a 0px tall element height", (done) -> + @assertErrorMessage("cy.screenshot() only works with a screenshot area with a height greater than zero.", done) + cy.visit("/fixtures/screenshots.html") + cy.get('.empty-element').screenshot() + + it "throws if padding is not a number", (done) -> + @assertErrorMessage("cy.screenshot() 'padding' option must be either a number or an array of numbers with a maximum length of 4. You passed: 50px", done) + cy.screenshot({ padding: '50px' }) + + it "throws if padding is not an array of numbers", (done) -> + @assertErrorMessage("cy.screenshot() 'padding' option must be either a number or an array of numbers with a maximum length of 4. You passed: bad, bad, bad, bad", done) + cy.screenshot({ padding: ['bad', 'bad', 'bad', 'bad'] }) + + it "throws if padding is not an array with a length between 1 and 4", (done) -> + @assertErrorMessage("cy.screenshot() 'padding' option must be either a number or an array of numbers with a maximum length of 4. You passed: 20, 10, 20, 10, 50", done) + cy.screenshot({ padding: [20, 10, 20, 10, 50] }) + + it "throws if padding is a large negative number that causes a 0px tall element height", (done) -> + @assertErrorMessage("cy.screenshot() only works with a screenshot area with a height greater than zero.", done) + cy.visit("/fixtures/screenshots.html") + cy.get('.tall-element').screenshot({ padding: -161 }) + it "throws if clip is not an object", (done) -> - @assertErrorMessage("cy.screenshot() 'clip' option must be an object of with the keys { width, height, x, y } and number values. You passed: true", done) + @assertErrorMessage("cy.screenshot() 'clip' option must be an object with the keys { width, height, x, y } and number values. You passed: true", done) cy.screenshot({ clip: true }) it "throws if clip is lacking proper keys", (done) -> - @assertErrorMessage("cy.screenshot() 'clip' option must be an object of with the keys { width, height, x, y } and number values. You passed: {x: 5}", done) + @assertErrorMessage("cy.screenshot() 'clip' option must be an object with the keys { width, height, x, y } and number values. You passed: {x: 5}", done) cy.screenshot({ clip: { x: 5 } }) it "throws if clip has extraneous keys", (done) -> - @assertErrorMessage("cy.screenshot() 'clip' option must be an object of with the keys { width, height, x, y } and number values. You passed: Object{5}", done) + @assertErrorMessage("cy.screenshot() 'clip' option must be an object with the keys { width, height, x, y } and number values. You passed: Object{5}", done) cy.screenshot({ clip: { width: 100, height: 100, x: 5, y: 5, foo: 10 } }) it "throws if clip has non-number values", (done) -> - @assertErrorMessage("cy.screenshot() 'clip' option must be an object of with the keys { width, height, x, y } and number values. You passed: Object{4}", done) + @assertErrorMessage("cy.screenshot() 'clip' option must be an object with the keys { width, height, x, y } and number values. You passed: Object{4}", done) cy.screenshot({ clip: { width: 100, height: 100, x: 5, y: "5" } }) it "throws if element capture with multiple elements", (done) -> diff --git a/packages/driver/test/cypress/integration/cypress/screenshot_spec.coffee b/packages/driver/test/cypress/integration/cypress/screenshot_spec.coffee index 69d277ba174..5723acd6392 100644 --- a/packages/driver/test/cypress/integration/cypress/screenshot_spec.coffee +++ b/packages/driver/test/cypress/integration/cypress/screenshot_spec.coffee @@ -59,7 +59,31 @@ describe "src/cypress/screenshot", -> Screenshot.defaults({ clip: { width: 200, height: 100, x: 0, y: 0 } }) - expect(Screenshot.getConfig().clip).to.eql({ width: 200, height: 100, x: 0, y:0 }) + expect( + Screenshot.getConfig().clip + ).to.eql( + { width: 200, height: 100, x: 0, y:0 } + ) + + it "sets and normalizes padding if specified", -> + tests = [ + [ 50, [50, 50, 50, 50] ] + [ [15], [15, 15, 15, 15] ] + [ [30, 20], [30, 20, 30, 20] ] + [ [10, 20, 30], [10, 20, 30, 20] ] + [ [20, 10, 20, 10], [20, 10, 20, 10] ] + ] + + for test in tests + [ input, expected ] = test + Screenshot.defaults({ + padding: input + }) + expect( + Screenshot.getConfig().padding + ).to.eql( + expected + ) it "sets onBeforeScreenshot if specified", -> onBeforeScreenshot = cy.stub() @@ -114,25 +138,31 @@ describe "src/cypress/screenshot", -> Screenshot.defaults({ blackout: [true] }) .to.throw("Cypress.Screenshot.defaults() 'blackout' option must be an array of strings. You passed: true") - it "throws if clip is not an object", -> + it "throws if padding is not a number or an array of numbers with a length between 1 and 4", -> expect => - Screenshot.defaults({ clip: true }) - .to.throw("Cypress.Screenshot.defaults() 'clip' option must be an object of with the keys { width, height, x, y } and number values. You passed: true") + Screenshot.defaults({ padding: '50px' }) + .to.throw("Cypress.Screenshot.defaults() 'padding' option must be either a number or an array of numbers with a maximum length of 4. You passed: 50px") + expect => + Screenshot.defaults({ padding: ['bad', 'bad', 'bad', 'bad'] }) + .to.throw("Cypress.Screenshot.defaults() 'padding' option must be either a number or an array of numbers with a maximum length of 4. You passed: bad, bad, bad, bad") + expect => + Screenshot.defaults({ padding: [20, 10, 20, 10, 50] }) + .to.throw("Cypress.Screenshot.defaults() 'padding' option must be either a number or an array of numbers with a maximum length of 4. You passed: 20, 10, 20, 10, 50") it "throws if clip is lacking proper keys", -> expect => Screenshot.defaults({ clip: { x: 5 } }) - .to.throw("Cypress.Screenshot.defaults() 'clip' option must be an object of with the keys { width, height, x, y } and number values. You passed: {x: 5}") + .to.throw("Cypress.Screenshot.defaults() 'clip' option must be an object with the keys { width, height, x, y } and number values. You passed: {x: 5}") it "throws if clip has extraneous keys", -> expect => Screenshot.defaults({ clip: { width: 100, height: 100, x: 5, y: 5, foo: 10 } }) - .to.throw("Cypress.Screenshot.defaults() 'clip' option must be an object of with the keys { width, height, x, y } and number values. You passed: Object{5}") + .to.throw("Cypress.Screenshot.defaults() 'clip' option must be an object with the keys { width, height, x, y } and number values. You passed: Object{5}") it "throws if clip has non-number values", -> expect => Screenshot.defaults({ clip: { width: 100, height: 100, x: 5, y: "5" } }) - .to.throw("Cypress.Screenshot.defaults() 'clip' option must be an object of with the keys { width, height, x, y } and number values. You passed: Object{4}") + .to.throw("Cypress.Screenshot.defaults() 'clip' option must be an object with the keys { width, height, x, y } and number values. You passed: Object{4}") it "throws if onBeforeScreenshot is not a function", -> expect => diff --git a/packages/server/__snapshots__/5_screenshots_spec.coffee.js b/packages/server/__snapshots__/5_screenshots_spec.coffee.js index e4c60cd0d3a..f94fe4816fb 100644 --- a/packages/server/__snapshots__/5_screenshots_spec.coffee.js +++ b/packages/server/__snapshots__/5_screenshots_spec.coffee.js @@ -33,6 +33,8 @@ exports['e2e screenshots passes 1'] = ` 2) ensures unique paths when there's a non-named screenshot and a failure ✓ properly resizes the AUT iframe - does not take a screenshot for a pending test + ✓ adds padding to element screenshot when specified + ✓ does not add padding to non-element screenshot clipping ✓ can clip app screenshots ✓ can clip runner screenshots @@ -48,7 +50,7 @@ exports['e2e screenshots passes 1'] = ` ✓ takes another screenshot - 18 passing + 20 passing 1 pending 5 failing @@ -84,12 +86,12 @@ Because this error occurred during a 'after each' hook we are skipping the remai (Results) ┌───────────────────────────────────┐ - │ Tests: 23 │ - │ Passing: 18 │ + │ Tests: 25 │ + │ Passing: 20 │ │ Failing: 4 │ │ Pending: 1 │ │ Skipped: 0 │ - │ Screenshots: 26 │ + │ Screenshots: 28 │ │ Video: true │ │ Duration: X seconds │ │ Spec Ran: screenshots_spec.js │ @@ -115,6 +117,8 @@ Because this error occurred during a 'after each' hook we are skipping the remai - /foo/bar/.projects/e2e/cypress/screenshots/screenshots_spec.js/taking screenshots -- ensures unique paths when there's a non-named screenshot and a failure.png (1000x660) - /foo/bar/.projects/e2e/cypress/screenshots/screenshots_spec.js/taking screenshots -- ensures unique paths when there's a non-named screenshot and a failure (failed).png (1280x720) - /foo/bar/.projects/e2e/cypress/screenshots/screenshots_spec.js/aut-resize.png (1000x2000) + - /foo/bar/.projects/e2e/cypress/screenshots/screenshots_spec.js/element-padding.png (420x320) + - /foo/bar/.projects/e2e/cypress/screenshots/screenshots_spec.js/non-element-padding.png (600x200) - /foo/bar/.projects/e2e/cypress/screenshots/screenshots_spec.js/app-clip.png (100x50) - /foo/bar/.projects/e2e/cypress/screenshots/screenshots_spec.js/runner-clip.png (120x60) - /foo/bar/.projects/e2e/cypress/screenshots/screenshots_spec.js/fullPage-clip.png (140x70) @@ -139,9 +143,9 @@ Because this error occurred during a 'after each' hook we are skipping the remai Spec Tests Passing Failing Pending Skipped ┌────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ ✖ screenshots_spec.js XX:XX 23 18 4 1 - │ + │ ✖ screenshots_spec.js XX:XX 25 20 4 1 - │ └────────────────────────────────────────────────────────────────────────────────────────────────┘ - 1 of 1 failed (100%) XX:XX 23 18 4 1 - + 1 of 1 failed (100%) XX:XX 25 20 4 1 - ` diff --git a/packages/server/test/support/fixtures/projects/e2e/cypress/integration/screenshots_spec.js b/packages/server/test/support/fixtures/projects/e2e/cypress/integration/screenshots_spec.js index 1ecadf9f36c..5a3f34a2948 100644 --- a/packages/server/test/support/fixtures/projects/e2e/cypress/integration/screenshots_spec.js +++ b/packages/server/test/support/fixtures/projects/e2e/cypress/integration/screenshots_spec.js @@ -264,6 +264,37 @@ describe('taking screenshots', () => { this.skip() }) + it('adds padding to element screenshot when specified', () => { + cy.visit('http://localhost:3322/element') + cy.get('.element') + .screenshot('element-padding', { + padding: 10, + }) + + cy.task('check:screenshot:size', { + name: `${path.basename(__filename)}/element-padding.png`, + width: 420, + height: 320, + devicePixelRatio, + }) + }) + + it('does not add padding to non-element screenshot', () => { + cy.viewport(600, 200) + cy.visit('http://localhost:3322/color/yellow') + cy.screenshot('non-element-padding', { + capture: 'viewport', + padding: 10, + }) + + cy.task('check:screenshot:size', { + name: `${path.basename(__filename)}/non-element-padding.png`, + width: 600, + height: 200, + devicePixelRatio, + }) + }) + context('before hooks', () => { before(() => { // failure 2