Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add types to screenshots #22768

Merged
merged 11 commits into from
Jul 18, 2022
101 changes: 64 additions & 37 deletions packages/driver/src/cy/commands/screenshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,50 @@ import $dom from '../../dom'
import $errUtils from '../../cypress/error_utils'
import $utils from '../../cypress/utils'
import type { Log } from '../../cypress/log'
import type { StateFunc } from '../../cypress/state'

const getViewportHeight = (state) => {
interface InternalScreenshotOptions extends Partial<Cypress.Loggable & Cypress.Timeoutable & Cypress.ScreenshotOptions> {
_log?: Log
}

type Scroll = {
y: number
clip?: Cypress.ScreenshotOptions['clip']
afterScroll?: () => Cypress.Dimensions
}

type TakeScreenshotOptions = {
name?: string
subject?: JQuery<HTMLElement>
simple?: boolean
testFailure?: boolean
runnable: (Mocha.Test | Mocha.Hook) & {
id: string
}
log?: Log
timeout?: number
}

type AutomationOptions = TakeScreenshotOptions & Omit<Cypress.ScreenshotOptions, 'onBeforeScreenshot'| 'onAfterScreenshot' | 'disableTimersAndAnimations' | 'scale' | 'padding'> & Partial<Cypress.ScreenshotOptions>

const getViewportHeight = (state: StateFunc) => {
// TODO this doesn't seem correct
return Math.min(state('viewportHeight'), window.innerHeight)
}

const getViewportWidth = (state) => {
const getViewportWidth = (state: StateFunc) => {
return Math.min(state('viewportWidth'), window.innerWidth)
}

const automateScreenshot = (state, options: TakeScreenshotOptions = {}) => {
const automateScreenshot = (state: StateFunc, options: TakeScreenshotOptions) => {
const { runnable, timeout } = options

const titles: string[] = []

// if this a hook then push both the current test title
// and our own hook title
if (runnable.type === 'hook') {
let ct = runnable.ctx.currentTest
let ct = runnable.ctx?.currentTest

if (runnable.ctx && ct) {
titles.push(ct.title, runnable.title)
Expand Down Expand Up @@ -56,6 +81,7 @@ const automateScreenshot = (state, options: TakeScreenshotOptions = {}) => {
titles,
testId: runnable.id,
takenPaths: state('screenshotPaths'),
// @ts-ignore
testAttemptIndex: $utils.getTestFromRunnable(runnable)._currentRetry,
}, _.omit(options, 'runnable', 'timeout', 'log', 'subject'))

Expand Down Expand Up @@ -84,7 +110,7 @@ const automateScreenshot = (state, options: TakeScreenshotOptions = {}) => {
})
}

const scrollOverrides = (win, doc) => {
const scrollOverrides = (win: Window, doc: Document) => {
const originalOverflow = doc.documentElement.style.overflow
const originalBodyOverflowY = doc.body.style.overflowY
const originalX = win.scrollX
Expand All @@ -108,6 +134,9 @@ const scrollOverrides = (win, doc) => {
// since we scroll down the page in takeScrollingScreenshots
// and don't want the page size to change once we start
// https://github.com/cypress-io/cypress/issues/6099

// Window.Event is private and also deprecated. See https://developer.mozilla.org/en-US/docs/Web/API/Window/event.
// @ts-ignore
win.dispatchEvent(new win.Event('scroll'))
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe the link you referenced is for window.event not the Event interface.

Suggested change
// Window.Event is private and also deprecated. See https://developer.mozilla.org/en-US/docs/Web/API/Window/event.
// @ts-ignore
win.dispatchEvent(new win.Event('scroll'))
win.dispatchEvent(new Event('scroll'))

or you can add Event to:

Copy link
Contributor Author

Choose a reason for hiding this comment

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

whoops 😅. Updated in 5257d7e and also checked the tests for #6099 and looks good.


return () => {
Expand All @@ -122,16 +151,16 @@ const scrollOverrides = (win, doc) => {
}
}

const validateNumScreenshots = (numScreenshots, automationOptions) => {
const validateNumScreenshots = (numScreenshots: number, automationOptions: AutomationOptions) => {
if (numScreenshots < 1) {
$errUtils.throwErrByPath('screenshot.invalid_height', {
log: automationOptions.log,
})
}
}

const takeScrollingScreenshots = (scrolls, win, state, automationOptions) => {
const scrollAndTake = ({ y, clip, afterScroll }, index) => {
const takeScrollingScreenshots = (scrolls: Scroll[], win: Window, state: StateFunc, automationOptions: AutomationOptions) => {
const scrollAndTake = ({ y, clip, afterScroll }: Scroll, index) => {
win.scrollTo(0, y)
if (afterScroll) {
clip = afterScroll()
Expand All @@ -151,7 +180,7 @@ const takeScrollingScreenshots = (scrolls, win, state, automationOptions) => {
.then(_.last)
}

const takeFullPageScreenshot = (state, automationOptions) => {
const takeFullPageScreenshot = (state: StateFunc, automationOptions: AutomationOptions) => {
const win = state('window')
const doc = state('document')

Expand Down Expand Up @@ -187,11 +216,12 @@ const takeFullPageScreenshot = (state, automationOptions) => {
.finally(resetScrollOverrides)
}

const applyPaddingToElementPositioning = (elPosition, automationOptions) => {
const applyPaddingToElementPositioning = (elPosition: Cypress.ElementPositioning, automationOptions: AutomationOptions) => {
if (!automationOptions.padding) {
return elPosition
}

// @ts-ignore
const [paddingTop, paddingRight, paddingBottom, paddingLeft] = automationOptions.padding

return {
Expand All @@ -208,7 +238,7 @@ const applyPaddingToElementPositioning = (elPosition, automationOptions) => {
}
}

const takeElementScreenshot = ($el, state, automationOptions) => {
const takeElementScreenshot = ($el: JQuery<HTMLElement>, state: StateFunc, automationOptions: AutomationOptions) => {
const win = state('window')
const doc = state('document')

Expand All @@ -224,7 +254,7 @@ const takeElementScreenshot = ($el, state, automationOptions) => {

validateNumScreenshots(numScreenshots, automationOptions)

const scrolls = _.map(_.times(numScreenshots), (index) => {
const scrolls: Scroll[] = _.map(_.times(numScreenshots), (index) => {
const y = elPosition.fromElWindow.top + (viewportHeight * index)

const afterScroll = () => {
Expand Down Expand Up @@ -274,30 +304,30 @@ const takeElementScreenshot = ($el, state, automationOptions) => {
}

// "app only" means we're hiding the runner UI
const isAppOnly = ({ capture }) => {
const isAppOnly = ({ capture }: { capture: Cypress.ScreenshotOptions['capture']}) => {
return (capture === 'viewport') || (capture === 'fullPage')
}

const getShouldScale = ({ capture, scale }) => {
const getShouldScale = ({ capture, scale }: {
capture: Cypress.ScreenshotOptions['capture']
scale: Cypress.ScreenshotOptions['scale']
}) => {
return isAppOnly({ capture }) ? scale : true
}

const getBlackout = ({ capture, blackout }) => {
const getBlackout = ({ capture, blackout }: {
capture: Cypress.ScreenshotOptions['capture']
blackout: Cypress.ScreenshotOptions['blackout']
}) => {
return isAppOnly({ capture }) ? blackout : []
}

// TODO: anys should be removed.
type TakeScreenshotOptions = {
name?: string
subject?: any
simple?: boolean
testFailure?: boolean
runnable?: any
log?: any
timeout?: number
}

const takeScreenshot = (Cypress, state, screenshotConfig, options: TakeScreenshotOptions = {}) => {
const takeScreenshot = (
Cypress: Cypress.Cypress,
state: StateFunc,
screenshotConfig: Partial<Cypress.ScreenshotOptions> & Pick<Cypress.ScreenshotOptions, 'capture' | 'scale' | 'blackout' | 'overwrite'>,
options: TakeScreenshotOptions,
) => {
const {
capture,
padding,
Expand Down Expand Up @@ -326,7 +356,8 @@ const takeScreenshot = (Cypress, state, screenshotConfig, options: TakeScreensho
const getOptions = (isOpen) => {
return {
id: runnable.id,
testAttemptIndex: $utils.getTestFromRunnable(runnable)._currentRetry,
// @ts-ignore
testAttemptIndex: $utils.getTestFromRunnable(runnable)?._currentRetry,
isOpen,
appOnly: isAppOnly(screenshotConfig),
scale: getShouldScale(screenshotConfig),
Expand All @@ -337,7 +368,7 @@ const takeScreenshot = (Cypress, state, screenshotConfig, options: TakeScreensho
}
}

const before = ($body, $container) => {
const before = ($body: JQuery<HTMLBodyElement>, $container: JQuery<HTMLElement>) => {
return Promise.try(() => {
if (disableTimersAndAnimations) {
return cy.pauseTimers(true)
Expand Down Expand Up @@ -366,7 +397,7 @@ const takeScreenshot = (Cypress, state, screenshotConfig, options: TakeScreensho
})
}

const after = ($body) => {
const after = ($body: JQuery<HTMLBodyElement>) => {
// could fail if iframe is cross-origin, so fail gracefully
try {
if (disableTimersAndAnimations) {
Expand All @@ -392,7 +423,7 @@ const takeScreenshot = (Cypress, state, screenshotConfig, options: TakeScreensho
})
}

const automationOptions = _.extend({}, options, {
const automationOptions: AutomationOptions = _.extend({}, options, {
capture,
clip: {
x: 0,
Expand All @@ -413,13 +444,13 @@ const takeScreenshot = (Cypress, state, screenshotConfig, options: TakeScreensho
})

// use the subject as $el or yield the wrapped documentElement
const $el = $dom.isElement(subject)
const $el: JQuery<HTMLElement> = $dom.isElement(subject)
? subject
: $dom.wrap(state('document').documentElement)

// 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')
const $body: JQuery<HTMLBodyElement> = Cypress.$('body')

return before($body, $el)
.then(() => {
Expand Down Expand Up @@ -451,10 +482,6 @@ const takeScreenshot = (Cypress, state, screenshotConfig, options: TakeScreensho
.finally(() => after($body))
}

interface InternalScreenshotOptions extends Partial<Cypress.Loggable & Cypress.Timeoutable & Cypress.ScreenshotOptions> {
_log?: Log
}

export default function (Commands, Cypress, cy, state, config) {
// failure screenshot when not interactive
Cypress.on('runnable:after:run:async', (test, runnable) => {
Expand Down
1 change: 1 addition & 0 deletions packages/driver/src/cypress/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ const defaults = function (state: StateFunc, config, obj) {

const t = $utils.getTestFromRunnable(runnable)

// @ts-ignore
return t._currentRetry || 0
}

Expand Down
2 changes: 1 addition & 1 deletion packages/driver/src/cypress/screenshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import _ from 'lodash'
import $utils from './utils'
import $errUtils from './error_utils'

const _reset = () => {
const _reset = (): Pick<Cypress.ScreenshotDefaultsOptions, 'capture' | 'scale' | 'disableTimersAndAnimations' | 'screenshotOnRunFailure' | 'blackout' | 'overwrite' | 'onBeforeScreenshot' | 'onAfterScreenshot'> => {
return {
capture: 'fullPage',
scale: false,
Expand Down
4 changes: 2 additions & 2 deletions packages/driver/src/cypress/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,8 @@ export default {
return Math.sqrt((deltaX * deltaX) + (deltaY * deltaY))
},

getTestFromRunnable (r) {
return r.ctx.currentTest || r
getTestFromRunnable (r: Mocha.Runnable) {
return r.ctx?.currentTest || r
},

memoize (func, cacheInstance = new Map()) {
Expand Down
4 changes: 2 additions & 2 deletions packages/driver/src/dom/animation.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import $ from 'jquery'

function addCssAnimationDisabler ($body) {
function addCssAnimationDisabler ($body: JQuery<HTMLBodyElement>) {
$(`
<style id="__cypress-animation-disabler">
*, *:before, *:after {
Expand All @@ -11,7 +11,7 @@ function addCssAnimationDisabler ($body) {
`).appendTo($body)
}

function removeCssAnimationDisabler ($body) {
function removeCssAnimationDisabler ($body: JQuery<HTMLBodyElement>) {
$body.find('#__cypress-animation-disabler').remove()
}

Expand Down
8 changes: 4 additions & 4 deletions packages/driver/src/dom/blackout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const styles = (styleString) => {
return styleString.replace(/\s*\n\s*/g, '')
}

function addBlackoutForElement ($body, $el) {
function addBlackoutForElement ($body: JQuery<HTMLBodyElement>, $el: JQuery<HTMLElement>) {
const dimensions = $dimensions.getElementDimensions($el)
const width = dimensions.widthWithBorder
const height = dimensions.heightWithBorder
Expand All @@ -32,8 +32,8 @@ function addBlackoutForElement ($body, $el) {
$(`<div class="__cypress-blackout" style="${style}">`).appendTo($body)
}

function addBlackouts ($body, $container, selector) {
let $el
function addBlackouts ($body: JQuery<HTMLBodyElement>, $container: JQuery<HTMLElement>, selector: string) {
let $el: JQuery<HTMLElement>

try {
// only scope blacked out elements to to screenshotted element, not necessarily the whole body
Expand All @@ -49,7 +49,7 @@ function addBlackouts ($body, $container, selector) {
})
}

function removeBlackouts ($body) {
function removeBlackouts ($body: JQuery<HTMLBodyElement>) {
$body.find('.__cypress-blackout').remove()
}

Expand Down