diff --git a/docs/visual-testing/_partials/_fullpage-description.md b/docs/visual-testing/_partials/_fullpage-description.md index 77731368de..9f21ab24d3 100644 --- a/docs/visual-testing/_partials/_fullpage-description.md +++ b/docs/visual-testing/_partials/_fullpage-description.md @@ -1,13 +1 @@ -By default, only the current viewport is captured when `.sauceVisualCheck` is used. You can opt in to capturing the entire page by using the `fullPage` option. It will capture everything by scrolling and stitching multiple screenshots together. - -:::note -It's recommended to use the `hideAfterFirstScroll` option for fixed or sticky position elements such as sticky headers or consent banners. -::: - -Options: - -- `delayAfterScrollMs`: Delay in ms after scrolling and before taking screenshots. The default value is 0. We recommend using this option for lazy loading content. -- `disableCSSAnimation`: Disable CSS animations and the input caret in the app. The default value is true. -- `hideAfterFirstScroll`: One or more CSS selectors that we should remove from the page after the first scroll. Useful for hiding fixed elements such as headers, cookie banners, etc. -- `hideScrollBars`: Hide all scrollbars in the app. The default value is true. -- `scrollLimit`: Limit the number of screenshots taken for scrolling and stitching. The default value is 10. The value needs to be between 1 and 10. +Full Page Screenshots capture the entire webpage, including content beyond the visible viewport, to ensure comprehensive visual testing. This feature helps teams identify layout or rendering issues across the full page and ensures consistency across devices and browsers. diff --git a/docs/visual-testing/_partials/_fullpage-js.md b/docs/visual-testing/_partials/_fullpage-js.md index abcc1b464d..cc8dd7b774 100644 --- a/docs/visual-testing/_partials/_fullpage-js.md +++ b/docs/visual-testing/_partials/_fullpage-js.md @@ -1,18 +1,39 @@ -import FullPageDescription from './_fullpage-description.md'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + import FullPageLimit from './_fullpage-limit.md'; +import FullPageLimitation from './_fullpage-limitation.md'; +import FullPageDescription from './_fullpage-description.md'; + + +By default, only the viewport is captured when `.sauceVisualCheck` is used. You can opt in to capturing the entire page by using the `fullPage` option. It will capture everything by scrolling and stitching multiple screenshots together. + + + +#### Web + +Options: - +- `delayAfterScrollMs`: Delay in ms after scrolling and before taking screenshots. The default value is 0. We recommend using this option for lazy loading content. +- `disableCSSAnimation`: Disable CSS animations and the input caret in the app. The default value is true. +- `hideAfterFirstScroll`: One or more CSS selectors that we should remove from the page after the first scroll. Useful for hiding fixed elements such as headers, cookie banners, etc. +- `hideScrollBars`: Hide all scrollbars in the app. The default value is true. +- `scrollLimit`: Limit the number of screenshots taken for scrolling and stitching. The default value is 10. The value needs to be between 1 and 10. + +:::note +It's recommended to use the `hideAfterFirstScroll` option for fixed or sticky position elements such as sticky headers or consent banners. +::: Example: ```ts await browser.sauceVisualCheck('Long content page', { - // Enable full page screenshots using the default options + // Enable full page screenshot using the default options fullPage: true, }); await browser.sauceVisualCheck('Long content page', { - // Enable full page screenshots and customize the behavior + // Enable full page screenshot and customize the behavior fullPage: { delayAfterScrollMs: 500, disableCSSAnimation: false, @@ -23,4 +44,118 @@ await browser.sauceVisualCheck('Long content page', { }); ``` - +#### Mobile Native (beta) + +Options: + +- `delayAfterScrollMs`: Delay in ms after scrolling and before taking screenshots. The default value is 0. We recommend using this option for lazy loading content. +- `nativeClipSelector`: Selector used to identify the first element to which clipping will be applied. +- `scrollElement`: Scrollable element used for scrolling. The default is root element. +- `scrollLimit`: Limit the number of screenshots taken for scrolling and stitching. The default value is 10. The value needs to be between 1 and 10. + +:::note +It is recommended to set `scrollElement` to the appropriate scrollable container. +::: + + + + ```ts + await browser.sauceVisualCheck('Long content page', { + // Enable full page screenshot and customize the behavior + fullPage: { + scrollElement: $('//XCUIElementTypeCollectionView'), + scrollLimit: 5 + }, + }); + ``` + + + ```ts + await browser.sauceVisualCheck('Long content page', { + // Enable full page screenshot and customize the behavior + fullPage: { + scrollElement: $('//androidx.recyclerview.widget.RecyclerView'), + scrollLimit: 5 + }, + }); + ``` + + + +Use only XPath selectors for ignore regions and clipping to an element. + +:::note +On iOS, selectors must be contained within the `scrollElement`. +::: + + + + ```ts + await browser.sauceVisualCheck('Ignore regions - Long content page', { + // Enable full page screenshot and ignore elements + ignore: [ + { + selector: { + value: '//XCUIElementTypeStaticText[@name="Product Price"]', + type: 'XPATH' + } + } + ], + fullPage: { + scrollElement: $('//XCUIElementTypeCollectionView'), + }, + }); + ``` + + + ```ts + await browser.sauceVisualCheck('Ignore regions - Long content page', { + // Enable full page screenshot and ignore elements + ignore: [ + { + selector: { + value: '//android.widget.TextView[@content-desc="Product Price"]', + type: 'XPATH' + } + } + ], + fullPage: { + scrollElement: $('//androidx.recyclerview.widget.RecyclerView'), + }, + }); + ``` + + + + + + ```ts + await browser.sauceVisualCheck('Clip - Long content page', { + // Enable full page screenshot and clip to an element + fullPage: { + scrollElement: $('//XCUIElementTypeCollectionView'), + nativeClipSelector: { + value: '//XCUIElementTypeCollectionView/XCUIElementTypeOther', + type: 'XPATH' + } + }, + }); + ``` + + + ```ts + await browser.sauceVisualCheck('Clip - Long content page', { + // Enable full page screenshot and clip to an element + fullPage: { + scrollElement: $('//androidx.recyclerview.widget.RecyclerView'), + nativeClipSelector: { + value: '//androidx.recyclerview.widget.RecyclerView[@content-desc="Displays all products of catalog"]', + type: 'XPATH' + } + }, + }); + ``` + + + + diff --git a/docs/visual-testing/_partials/_fullpage-limit.md b/docs/visual-testing/_partials/_fullpage-limit.md index f1417fc075..a39883c3a8 100644 --- a/docs/visual-testing/_partials/_fullpage-limit.md +++ b/docs/visual-testing/_partials/_fullpage-limit.md @@ -1,3 +1,5 @@ -:::note The maximum number of scrolls and stitches in a full page screenshot is 10. + +:::note +Use full page screenshots only when necessary, as they slow down test execution. ::: diff --git a/docs/visual-testing/_partials/_fullpage-limitation.md b/docs/visual-testing/_partials/_fullpage-limitation.md new file mode 100644 index 0000000000..20860702c8 --- /dev/null +++ b/docs/visual-testing/_partials/_fullpage-limitation.md @@ -0,0 +1,3 @@ +:::note +Full page screenshot for mobile native testing is in beta. Read more about [mobile native limitation](/visual-testing/mobile-native-testing/) +::: diff --git a/docs/visual-testing/integrations/csharp.md b/docs/visual-testing/integrations/csharp.md index 9e92813bfd..4680730784 100644 --- a/docs/visual-testing/integrations/csharp.md +++ b/docs/visual-testing/integrations/csharp.md @@ -399,7 +399,7 @@ VisualClient.CaptureDom = true; ### Full page screenshots -By default, only the current viewport is captured when `.VisualCheck` is used. You can opt in to capturing the entire page by using the `FullPage` option. It will capture everything by scrolling and stitching multiple screenshots together. +By default, only the viewport is captured when `.VisualCheck` is used. You can opt in to capturing the entire page by using the `FullPage` option. It will capture everything by scrolling and stitching multiple screenshots together. Additionally, you have the option to configure full page settings using the `FullPageConfig` option. :::note diff --git a/docs/visual-testing/integrations/java.md b/docs/visual-testing/integrations/java.md index b5b044b963..c75dd6eeae 100644 --- a/docs/visual-testing/integrations/java.md +++ b/docs/visual-testing/integrations/java.md @@ -10,6 +10,8 @@ import EnvironmentVariables from '../_partials/_environment-variables.md'; import SelectiveDiffing from '../_partials/_selective-diffing.md'; import SelectiveDiffingGlobal from '../_partials/_selective-diffing-global.md'; import SelectiveDiffingRegion from '../_partials/_selective-diffing-region.md'; +import FullPageLimitation from '../_partials/_fullpage-limitation.md'; +import FullPageDescription from '../_partials/_fullpage-description.md'; # Java WebDriver Integration @@ -252,7 +254,7 @@ When creating the service in `VisualApi`, extra fields can be set to define the It needs to be defined through the `VisualApi.Builder` object. -Methods available: +Available methods: - `withBuild(String build)`: Sets the name of the build - `withProject(String project)`: Sets the name of the project @@ -392,15 +394,16 @@ visual.sauceVisualCheck("Inventory Page", options); ### Full page screenshots -By default, only the current viewport is captured when `.sauceVisualCheck` is used. You can opt in to capturing the entire page by using the `enableFullPageScreenshots` option. It will capture everything by scrolling and stitching multiple screenshots together. - -:::note -It's recommended to use the `withHideAfterFirstScroll` method for fixed or sticky position elements such as sticky headers or consent banners. -::: + +By default, only the viewport is captured when `.sauceVisualCheck` is used. You can opt in to capturing the entire page by using the `enableFullPageScreenshots` option. It will capture everything by scrolling and stitching multiple screenshots together. Configuration should be specified using the `FullPageScreenshotConfig.Builder` object. -Methods available: + + +#### Web + +Available methods: - `withDelayAfterScrollMs(int delayAfterScrollMs)`: Delay in ms after scrolling and before taking screenshots. The default value is 0. We recommend using this option for lazy loading content. - `withDisableCSSAnimation(Boolean disableCSSAnimation)`: Disable CSS animations and the input caret in the app. The default value is true. @@ -408,6 +411,10 @@ Methods available: - `withHideScrollBars(Boolean hideScrollBars)`: Hide all scrollbars in the app. The default value is true. - `withScrollLimit(int scrollLimit)`: Limit the number of screenshots taken for scrolling and stitching. The default value is 10. The value needs to be between 1 and 10. +:::note +It's recommended to use the `withHideAfterFirstScroll` method for elements with a fixed or sticky position, such as sticky headers or consent banners. +::: + Examples: ```java @@ -424,17 +431,179 @@ import com.saucelabs.visual.model.FullPageScreenshotConfig; CheckOptions options = new CheckOptions(); FullPageScreenshotConfig config = new FullPageScreenshotConfig.Builder() - .withDelayAfterScrollMs(500) - .withDisableCSSAnimation(false) - .withHideAfterFirstScroll("#header") - .withHideScrollBars(false) - .withScrollLimit(5) - .build(); + .withDelayAfterScrollMs(500) + .withDisableCSSAnimation(false) + .withHideAfterFirstScroll("#header") + .withHideScrollBars(false) + .withScrollLimit(5) + .build(); options.enableFullPageScreenshots(config); visual.sauceVisualCheck("Long content page", options); ``` - +#### Mobile Native (beta) + +Available methods: +- `withDelayAfterScrollMs(int delayAfterScrollMs)`: Delay in ms after scrolling and before taking screenshots. The default value is 0. We recommend using this option for lazy loading content. +- `withNativeClipSelector(SelectorIn nativeClipSelector)`: Selector used to identify the first element to which clipping will be applied. +- `withScrollElement(WebElement scrollElement)`: Scrollable element used for scrolling. The default is root element. +- `withScrollLimit(int scrollLimit)`: Limit the number of screenshots taken for scrolling and stitching. The default value is 10. The value needs to be between 1 and 10. + +:::note +It is recommended to use the `withScrollElement` method to set the appropriate scrollable container. +::: + +Examples: + + + + ```java + import com.saucelabs.visual.CheckOptions; + import com.saucelabs.visual.model.FullPageScreenshotConfig; + + RemoteWebDriver driver; + ... + + WebElement scrollElement = driver.findElement(AppiumBy.xpath("//XCUIElementTypeCollectionView")); + CheckOptions options = new CheckOptions(); + FullPageScreenshotConfig config = new FullPageScreenshotConfig.Builder() + .withScrollElement(scrollElement) + .withScrollLimit(5) + .build(); + options.enableFullPageScreenshots(config); + visual.sauceVisualCheck("Long content page", options); + ``` + + + ```java + import com.saucelabs.visual.CheckOptions; + import com.saucelabs.visual.model.FullPageScreenshotConfig; + + RemoteWebDriver driver; + ... + + WebElement scrollElement = driver.findElement(AppiumBy.xpath("//androidx.recyclerview.widget.RecyclerView")); + CheckOptions options = new CheckOptions(); + FullPageScreenshotConfig config = new FullPageScreenshotConfig.Builder() + .withScrollElement(scrollElement) + .withScrollLimit(5) + .build(); + options.enableFullPageScreenshots(config); + visual.sauceVisualCheck("Long content page", options); + ``` + + + +Only XPath selectors can be used for ignore regions and clipping to an element. + +:::note +On iOS, selectors **must** be contained within the `scrollElement`. +::: + + + + + ```java + import com.saucelabs.visual.CheckOptions; + import com.saucelabs.visual.model.FullPageScreenshotConfig; + + RemoteWebDriver driver; + ... + + WebElement scrollElement = driver.findElement(AppiumBy.xpath("//XCUIElementTypeCollectionView")); + CheckOptions options = new CheckOptions(); + FullPageScreenshotConfig config = new FullPageScreenshotConfig.Builder() + .withScrollElement(scrollElement) + .build(); + options.enableFullPageScreenshots(config); + List ignoreSelectors = List.of( + new IgnoreSelectorIn.Builder() + .withSelector( + new SelectorIn.Builder() + .withValue("//XCUIElementTypeStaticText[@name="Product Price"]") + .withType(SelectorType.XPATH) + .build()) + .build()); + options.setIgnoreSelectors(ignoreSelectors); + visual.sauceVisualCheck("Long content page", options); + ``` + + + ```java + import com.saucelabs.visual.CheckOptions; + import com.saucelabs.visual.model.FullPageScreenshotConfig; + + RemoteWebDriver driver; + ... + + WebElement scrollElement = driver.findElement(AppiumBy.xpath("//androidx.recyclerview.widget.RecyclerView")); + CheckOptions options = new CheckOptions(); + FullPageScreenshotConfig config = new FullPageScreenshotConfig.Builder() + .withScrollElement(scrollElement) + .build(); + options.enableFullPageScreenshots(config); + List ignoreSelectors = List.of( + new IgnoreSelectorIn.Builder() + .withSelector( + new SelectorIn.Builder() + .withValue("//android.widget.TextView[@content-desc="Product Price"]") + .withType(SelectorType.XPATH) + .build()) + .build()); + options.setIgnoreSelectors(ignoreSelectors); + visual.sauceVisualCheck("Long content page", options); + ``` + + + + + + ```java + import com.saucelabs.visual.CheckOptions; + import com.saucelabs.visual.model.FullPageScreenshotConfig; + + RemoteWebDriver driver; + ... + + WebElement scrollElement = driver.findElement(AppiumBy.xpath("//XCUIElementTypeCollectionView")); + CheckOptions options = new CheckOptions(); + SelectorIn nativeClipSelector = new SelectorIn.Builder() + .withType(SelectorType.XPATH) + .withValue("//XCUIElementTypeCollectionView/XCUIElementTypeOther") + .build(); + FullPageScreenshotConfig config = new FullPageScreenshotConfig.Builder() + .withScrollElement(scrollElement) + .withNativeClipSelector(nativeClipSelector) + .build(); + options.enableFullPageScreenshots(config); + visual.sauceVisualCheck("Long content page", options); + ``` + + + ```java + import com.saucelabs.visual.CheckOptions; + import com.saucelabs.visual.model.FullPageScreenshotConfig; + + RemoteWebDriver driver; + ... + + WebElement scrollElement = driver.findElement(AppiumBy.xpath("//androidx.recyclerview.widget.RecyclerView")); + CheckOptions options = new CheckOptions(); + SelectorIn nativeClipSelector = new SelectorIn.Builder() + .withType(SelectorType.XPATH) + .withValue("//androidx.recyclerview.widget.RecyclerView[@content-desc='Displays all products of catalog']") + .build(); + FullPageScreenshotConfig config = new FullPageScreenshotConfig.Builder() + .withScrollElement(scrollElement) + .withNativeClipSelector(nativeClipSelector) + .build(); + options.enableFullPageScreenshots(config); + visual.sauceVisualCheck("Long content page", options); + ``` + + + + ### Clip to an Element diff --git a/docs/visual-testing/integrations/nightwatch.md b/docs/visual-testing/integrations/nightwatch.md index 4d13c926c2..dc3812e688 100644 --- a/docs/visual-testing/integrations/nightwatch.md +++ b/docs/visual-testing/integrations/nightwatch.md @@ -353,10 +353,6 @@ browser .end(); ``` -### Full page screenshots - - - ### Clip to an Element diff --git a/docs/visual-testing/mobile-native-testing.md b/docs/visual-testing/mobile-native-testing.md index e61cca7cae..f67c0cb30e 100644 --- a/docs/visual-testing/mobile-native-testing.md +++ b/docs/visual-testing/mobile-native-testing.md @@ -25,6 +25,21 @@ When writing a visual test for mobile apps, we recommend the following: ## Limitations The following features are not yet available for mobile app testing: -- Full page screenshots -- DOM capture and inspection - [Selective diffing](./selective-diffing.md) + +Native full-page screenshots are currently in beta and may have unexpected behavior. +Identified limitations: +- Slow screenshot capture +- 1 pixel shifts in the screenshot (iOS tablets only) +- Ignoring and clipping limited to elements within `scrollElement` (iOS only) +- Sticky elements missing at the bottom of the screen +- DOM capture is not available + + +## Integrations + +Learn more about native full-page options on the integration pages: +