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

wdio - ignore selectors #158

Merged
merged 9 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions visual-js/.changeset/curvy-foxes-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@saucelabs/wdio-sauce-visual-service": patch
"@saucelabs/visual": patch
---

add ignore region selectors for appium native fullpage
32 changes: 22 additions & 10 deletions visual-js/visual-wdio/src/SauceVisualService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
VisualApi,
WebdriverSession,
getVisualResults,
isIgnoreSelectorType,
IgnoreSelectorIn,
} from '@saucelabs/visual';

import logger from '@wdio/logger';
Expand Down Expand Up @@ -114,6 +116,11 @@ type CucumberWorld = {
export type CheckOptions = {
ignore?: Array<Ignorable>;

/**
* XPath selectors to ignore changes (available only with full-page screenshots and mobile native apps).
*/
ignoreSelectors?: Array<string>;

regions?: Array<RegionType<Ignorable>>;
/**
* A querySelector compatible selector of an element that we should crop the screenshot to.
Expand Down Expand Up @@ -367,11 +374,12 @@ export default class SauceVisualService implements Services.ServiceInstance {

const resolveIgnorable = async (
element: Ignorable | Promise<RegionIn>,
): Promise<Array<RegionIn | ElementIn>> => {
): Promise<Array<RegionIn | ElementIn | IgnoreSelectorIn>> => {
if (isIgnoreRegion(element)) return [element];

const awaited = await element;
if (isIgnoreRegion(awaited)) return [awaited];
if (isIgnoreSelectorType(awaited)) return [awaited];

const wdioElements = isWdioElement(awaited) ? [awaited] : awaited;

Expand All @@ -381,14 +389,21 @@ export default class SauceVisualService implements Services.ServiceInstance {
}));
};

const { ignoreRegions, ignoreElements } = await parseRegionsForAPI(
[...(options.regions ?? []), ...(options.ignore ?? [])],
resolveIgnorable,
);
const { ignoreRegions, ignoreElements, ignoreSelectors } =
await parseRegionsForAPI(
[...(options.regions ?? []), ...(options.ignore ?? [])],
resolveIgnorable,
);

const sessionId = browser.sessionId;
const jobId = (browser.capabilities as any)['jobUuid'] || sessionId;

const fullPageConfig = await getFullPageConfig<WdioElement>(
this.fullPage,
options.fullPage,
(el) => el.elementId,
);

const clipSelector = options.clipSelector ?? this.clipSelector;
const clipElement = clipSelector
? await browser.$(clipSelector).elementId
Expand All @@ -405,6 +420,7 @@ export default class SauceVisualService implements Services.ServiceInstance {
buildUuid: buildId,
name: name,
ignoreRegions,
ignoreSelectors,
ignoreElements,
diffingOptions: selectiveRegionOptionsToDiffingOptions({
disableOnly: options.disable ?? [],
Expand All @@ -414,11 +430,7 @@ export default class SauceVisualService implements Services.ServiceInstance {
options.diffingMethod || this.diffingMethod || DiffingMethod.Balanced,
suiteName: this.test?.parent,
testName: this.test?.title,
fullPageConfig: await getFullPageConfig<WdioElement>(
this.fullPage,
options.fullPage,
(el) => el.elementId,
),
fullPageConfig,
baselineOverride: options.baselineOverride || this.baselineOverride,
});
uploadedDiffIds.push(...result.diffs.nodes.flatMap((diff) => diff.id));
Expand Down
4 changes: 3 additions & 1 deletion visual-js/visual-wdio/src/guarded-types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type } from 'arktype';
import {
FullPageScreenshotOptions,
IgnoreSelectorIn,
makeValidate,
RegionIn,
} from '@saucelabs/visual';
Expand Down Expand Up @@ -28,4 +29,5 @@ export type Ignorable =
| WdioElement[]
| Promise<WdioElement>
| Promise<WdioElement[]>
| RegionIn;
| RegionIn
| IgnoreSelectorIn;
59 changes: 55 additions & 4 deletions visual-js/visual/src/graphql/__generated__/graphql.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 38 additions & 9 deletions visual-js/visual/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import {
DiffStatus,
ElementIn,
FullPageConfigIn,
IgnoreSelectorIn,
InputMaybe,
RegionIn,
SelectorType,
} from './graphql/__generated__/graphql';
import { FullPageScreenshotOptions, RegionType, VisualEnvOpts } from './types';
import { selectiveRegionOptionsToDiffingOptions } from './common/selective-region';
Expand Down Expand Up @@ -85,6 +87,13 @@ const regionType: Type<RegionType<unknown>> = type([
]);
export const isRegionType = (item: unknown): item is RegionType<unknown> =>
typeof item === 'object' && regionType.allows(item);

// arktype has problem to check enums
export const isIgnoreSelectorType = (item: any): item is IgnoreSelectorIn =>
typeof item === 'object' &&
typeof item.selector === 'object' &&
Object.values(SelectorType).includes(item.selector.type) &&
typeof item.selector.value === 'string';
export const validateRegionType = makeValidate(elementIn);

export const getDiffingOptions = <T>(
Expand All @@ -103,24 +112,43 @@ export const getDiffingOptions = <T>(
* @param resolveItem A callback to resolve an element and gather the required data for the API.
*/
export const parseRegionsForAPI = async <T>(
ignore: (T | RegionIn | RegionType<T> | Promise<RegionIn>)[],
ignore: (
| T
| RegionIn
| RegionType<T>
| Promise<RegionIn>
| IgnoreSelectorIn
)[],
resolveItem: (
item: T | Promise<RegionIn>,
) => Promise<(RegionIn | ElementIn)[]>,
) => Promise<(RegionIn | ElementIn | IgnoreSelectorIn)[]>,
): Promise<{
ignoreRegions: RegionIn[];
ignoreElements: ElementIn[];
ignoreSelectors: IgnoreSelectorIn[];
}> => {
const promisedIgnorables: Promise<(RegionIn | ElementIn)[]>[] = ignore.map(
async (itemOrRegion): Promise<Array<RegionIn | ElementIn>> => {
const { item, diffingOptions } = isRegionType(itemOrRegion)
const promisedIgnorables: Promise<
(RegionIn | ElementIn | IgnoreSelectorIn)[]
>[] = ignore.map(
async (
itemOrRegionOrSelector,
): Promise<Array<RegionIn | ElementIn | IgnoreSelectorIn>> => {
const { item, diffingOptions } = isRegionType(itemOrRegionOrSelector)
paweltomaszewskisaucelabs marked this conversation as resolved.
Show resolved Hide resolved
? {
item: itemOrRegionOrSelector.element,
diffingOptions: getDiffingOptions(itemOrRegionOrSelector),
}
: isIgnoreSelectorType(itemOrRegionOrSelector)
? {
item: itemOrRegion.element,
diffingOptions: getDiffingOptions(itemOrRegion),
item: itemOrRegionOrSelector,
diffingOptions: itemOrRegionOrSelector.diffingOptions,
}
: { item: itemOrRegion, diffingOptions: undefined };
: { item: itemOrRegionOrSelector, diffingOptions: undefined };

const elements = isIgnoreRegion(item) ? [item] : await resolveItem(item);
const elements =
isIgnoreRegion(item) || isIgnoreSelectorType(item)
? [item]
: await resolveItem(item);
return elements.map((element) => ({
...element,
diffingOptions,
Expand All @@ -133,6 +161,7 @@ export const parseRegionsForAPI = async <T>(
return {
ignoreRegions: flattened.filter(isIgnoreRegion),
ignoreElements: flattened.filter(isElementIn),
ignoreSelectors: flattened.filter(isIgnoreSelectorType),
};
};

Expand Down
Loading