-
Notifications
You must be signed in to change notification settings - Fork 3
chore: Add option to capture screenshots without cropping #237
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
base: main
Are you sure you want to change the base?
Changes from all commits
84382bd
130c87a
5ed8d1f
e0c5343
268b39f
8ad4cf9
cf21b5e
9f991ec
61bdd9c
83ad3a6
e2d1175
87eccf5
cf5e9b9
206b462
3ab2995
a828495
1a4f41e
a60a974
5703781
cd40a26
9db6fb0
3811698
ae893b4
335790b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,8 @@ | ||
| // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| export { default as BasePageObject } from './base'; | ||
| export { default as ScreenshotBasePageObject } from './screenshot-base'; | ||
| export { default as ScreenshotPageObject, PermutationScreenshot } from './screenshot'; | ||
| export { default as RawScreenshotPageObject, RawPermutationScreenshot } from './raw-screenshot'; | ||
| export { default as EventsSpy } from './events-spy'; | ||
| export { ScreenshotWithOffset, ElementSize, ElementRect, ElementOffset } from './types'; | ||
| export { ScreenshotWithOffset, RawScreenshot, ElementSize, ElementRect, ElementOffset } from './types'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| import ScreenshotBasePageObject from './screenshot-base'; | ||
| import { RawScreenshot } from './types'; | ||
|
|
||
| /** | ||
| * Raw permutation screenshot captured via takeElementScreenshot. | ||
| * No decoded image, no offset — just the raw base64 PNG per element. | ||
| */ | ||
| export interface RawPermutationScreenshot extends RawScreenshot { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Initially I attempted to let the existing resources such as ScreenshotPageObject provide both options (raw screenshots vs decode), but it became way too complicated to do it in a backwards compatible way respecting the types that each consumer expects. So I added a sibling class RawScreenshotPageObject instead, which the visual regression action on Github will use. |
||
| id: string; | ||
| } | ||
|
|
||
| /** | ||
| * A page object that captures screenshots using takeElementScreenshot. | ||
| * Returns raw base64 PNGs without decoding, cropping, or re-encoding — | ||
| * significantly faster when pixel-level comparison can be done on raw bytes. | ||
| */ | ||
| export default class RawScreenshotPageObject extends ScreenshotBasePageObject { | ||
| async captureBySelector(selector: string): Promise<RawScreenshot> { | ||
| await this.waitForVisible(selector); | ||
| const { pixelRatio } = await this.getViewportSize(); | ||
| const box = await this.getBoundingBox(selector); | ||
|
|
||
| const originalWindowSize = await this.fitWindowHeightToContent(); | ||
| const element = this.browser.$(selector); | ||
| const rawBase64 = await this.browser.takeElementScreenshot(await element.elementId); | ||
| await this.safeSetWindowSize(originalWindowSize.width, originalWindowSize.height); | ||
|
|
||
| return { rawBase64, pixelRatio, height: box.height, width: box.width }; | ||
| } | ||
|
|
||
| async captureViewport(): Promise<RawScreenshot> { | ||
| const { height, width } = await this.getViewportSize(); | ||
| const rawBase64 = await this.browser.takeScreenshot(); | ||
| return { rawBase64, height, width }; | ||
| } | ||
|
|
||
| async capturePermutations(): Promise<RawPermutationScreenshot[]> { | ||
| await this.windowScrollTo({ top: 0, left: 0 }); | ||
|
|
||
| // Adapt viewport height to fit all elements before taking screenshots | ||
| const originalWindowSize = await this.fitWindowHeightToContent(); | ||
|
|
||
| const elements = await this.browser.$$('[data-permutation]').map(el => el); | ||
| if (elements.length === 0) { | ||
| await this.safeSetWindowSize(originalWindowSize.width, originalWindowSize.height); | ||
| throw new Error('No permutations found on current page.'); | ||
| } | ||
|
|
||
| const { pixelRatio } = await this.getViewportSize(); | ||
| const results: RawPermutationScreenshot[] = []; | ||
| for (const element of elements) { | ||
| const id = (await element.getAttribute('data-permutation')) || ''; | ||
| const rawBase64 = await this.browser.takeElementScreenshot(await element.elementId); | ||
| const size = await element.getSize(); | ||
| results.push({ | ||
| id, | ||
| rawBase64, | ||
| width: size.width * pixelRatio, | ||
| height: size.height * pixelRatio, | ||
| }); | ||
| } | ||
|
|
||
| await this.safeSetWindowSize(originalWindowSize.width, originalWindowSize.height); | ||
| return results; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| import { ScrollAction, scrollAction, getPageDimensions } from '../browser-scripts'; | ||
| import BasePageObject from './base'; | ||
| import fullPageScreenshot from './full-page-screenshot'; | ||
|
|
||
| /** | ||
| * Base class for screenshot page objects. Provides scroll helpers, | ||
| * full-page screenshot support, and window size management. | ||
| */ | ||
| export default class ScreenshotBasePageObject extends BasePageObject { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Base class which the old ScreenshotPageObject and the new RawScreenshotPageObject extend. Its methods are taken from ScreenshotPageObject. |
||
| constructor(browser: WebdriverIO.Browser, public readonly forceScrollAndMerge: boolean = false) { | ||
| super(browser); | ||
| } | ||
|
|
||
| async focusNextElement() { | ||
| return this.keys('Tab'); | ||
| } | ||
|
|
||
| async scrollToBottom(selector: string) { | ||
| const action: ScrollAction = 'scrollToBottom'; | ||
| await this.browser.execute(scrollAction, { action, selector }); | ||
| } | ||
|
|
||
| async scrollToRight(selector: string) { | ||
| const action: ScrollAction = 'scrollToRight'; | ||
| await this.browser.execute(scrollAction, { action, selector }); | ||
| } | ||
|
|
||
| async fullPageScreenshot() { | ||
| // preserve scroll position in order to avoid side effects after screenshot taking | ||
| const scrollPosition = await this.getWindowScroll(); | ||
| // Wait for the page to settle before taking a screenshot | ||
| await this.waitForJsTimers(); | ||
| const screenshot = await fullPageScreenshot(this.browser, this.forceScrollAndMerge); | ||
| // restore scroll position | ||
| await this.windowScrollTo(scrollPosition); | ||
| return screenshot; | ||
| } | ||
|
|
||
| protected async fitWindowHeightToContent(): Promise<{ width: number; height: number }> { | ||
| const originalWindowSize = await this.browser.getWindowSize(); | ||
| const { viewportHeight, pageHeight } = await this.browser.execute(getPageDimensions); | ||
| const windowUIHeight = originalWindowSize.height - viewportHeight; | ||
| await this.safeSetWindowSize(originalWindowSize.width, pageHeight + windowUIHeight); | ||
| return originalWindowSize; | ||
| } | ||
|
|
||
| /* istanbul ignore next -- setWindowSize is unsupported on some mobile browsers, not testable in CI */ | ||
| protected async safeSetWindowSize(width: number, height: number): Promise<void> { | ||
| try { | ||
| await this.browser.setWindowSize(width, height); | ||
| } catch (error) { | ||
| if (error instanceof Error && error.message.includes('Method has not yet been implemented')) { | ||
| console.log('setWindowSize is not supported on this device'); | ||
| } else { | ||
| throw error; | ||
| } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cropAndComparenow supports both approaches: comparing screenshots with raw PNG and with decoded image + cropping data.