chore: simplify types handling in toHaveScreenshot (#29374)
This commit is contained in:
parent
fb29d90052
commit
20699c36ba
|
|
@ -62,14 +62,13 @@ type PDFOptions = Omit<channels.PagePdfParams, 'width' | 'height' | 'margin'> &
|
|||
path?: string,
|
||||
};
|
||||
|
||||
type ExpectScreenshotOptions = Omit<channels.PageExpectScreenshotOptions, 'screenshotOptions' | 'locator' | 'expected'> & {
|
||||
export type ExpectScreenshotOptions = Omit<channels.PageExpectScreenshotOptions, 'locator' | 'expected' | 'mask'> & {
|
||||
expected?: Buffer,
|
||||
locator?: Locator,
|
||||
locator?: api.Locator,
|
||||
isNot: boolean,
|
||||
screenshotOptions: Omit<channels.PageExpectScreenshotOptions['screenshotOptions'], 'mask'> & { mask?: Locator[] }
|
||||
mask?: api.Locator[],
|
||||
};
|
||||
|
||||
|
||||
export class Page extends ChannelOwner<channels.PageChannel> implements api.Page {
|
||||
private _browserContext: BrowserContext;
|
||||
_ownedContext: BrowserContext | undefined;
|
||||
|
|
@ -547,22 +546,19 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
|
|||
}
|
||||
|
||||
async _expectScreenshot(options: ExpectScreenshotOptions): Promise<{ actual?: Buffer, previous?: Buffer, diff?: Buffer, errorMessage?: string, log?: string[]}> {
|
||||
const mask = options.screenshotOptions?.mask ? options.screenshotOptions?.mask.map(locator => ({
|
||||
frame: locator._frame._channel,
|
||||
selector: locator._selector,
|
||||
const mask = options?.mask ? options?.mask.map(locator => ({
|
||||
frame: (locator as Locator)._frame._channel,
|
||||
selector: (locator as Locator)._selector,
|
||||
})) : undefined;
|
||||
const locator = options.locator ? {
|
||||
frame: options.locator._frame._channel,
|
||||
selector: options.locator._selector,
|
||||
frame: (options.locator as Locator)._frame._channel,
|
||||
selector: (options.locator as Locator)._selector,
|
||||
} : undefined;
|
||||
return await this._channel.expectScreenshot({
|
||||
...options,
|
||||
isNot: !!options.isNot,
|
||||
locator,
|
||||
screenshotOptions: {
|
||||
...options.screenshotOptions,
|
||||
mask,
|
||||
}
|
||||
mask,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1059,26 +1059,22 @@ scheme.PageExpectScreenshotParams = tObject({
|
|||
frame: tChannel(['Frame']),
|
||||
selector: tString,
|
||||
})),
|
||||
comparatorOptions: tOptional(tObject({
|
||||
comparator: tOptional(tString),
|
||||
maxDiffPixels: tOptional(tNumber),
|
||||
maxDiffPixelRatio: tOptional(tNumber),
|
||||
threshold: tOptional(tNumber),
|
||||
})),
|
||||
screenshotOptions: tOptional(tObject({
|
||||
fullPage: tOptional(tBoolean),
|
||||
clip: tOptional(tType('Rect')),
|
||||
omitBackground: tOptional(tBoolean),
|
||||
caret: tOptional(tEnum(['hide', 'initial'])),
|
||||
animations: tOptional(tEnum(['disabled', 'allow'])),
|
||||
scale: tOptional(tEnum(['css', 'device'])),
|
||||
mask: tOptional(tArray(tObject({
|
||||
frame: tChannel(['Frame']),
|
||||
selector: tString,
|
||||
}))),
|
||||
maskColor: tOptional(tString),
|
||||
style: tOptional(tString),
|
||||
})),
|
||||
comparator: tOptional(tString),
|
||||
maxDiffPixels: tOptional(tNumber),
|
||||
maxDiffPixelRatio: tOptional(tNumber),
|
||||
threshold: tOptional(tNumber),
|
||||
fullPage: tOptional(tBoolean),
|
||||
clip: tOptional(tType('Rect')),
|
||||
omitBackground: tOptional(tBoolean),
|
||||
caret: tOptional(tEnum(['hide', 'initial'])),
|
||||
animations: tOptional(tEnum(['disabled', 'allow'])),
|
||||
scale: tOptional(tEnum(['css', 'device'])),
|
||||
mask: tOptional(tArray(tObject({
|
||||
frame: tChannel(['Frame']),
|
||||
selector: tString,
|
||||
}))),
|
||||
maskColor: tOptional(tString),
|
||||
style: tOptional(tString),
|
||||
});
|
||||
scheme.PageExpectScreenshotResult = tObject({
|
||||
diff: tOptional(tBinary),
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
|
|||
}
|
||||
|
||||
async expectScreenshot(params: channels.PageExpectScreenshotParams, metadata: CallMetadata): Promise<channels.PageExpectScreenshotResult> {
|
||||
const mask: { frame: Frame, selector: string }[] = (params.screenshotOptions?.mask || []).map(({ frame, selector }) => ({
|
||||
const mask: { frame: Frame, selector: string }[] = (params.mask || []).map(({ frame, selector }) => ({
|
||||
frame: (frame as FrameDispatcher)._object,
|
||||
selector,
|
||||
}));
|
||||
|
|
@ -190,14 +190,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
|
|||
return await this._page.expectScreenshot(metadata, {
|
||||
...params,
|
||||
locator,
|
||||
comparatorOptions: {
|
||||
...params.comparatorOptions,
|
||||
_comparator: params.comparatorOptions?.comparator,
|
||||
},
|
||||
screenshotOptions: {
|
||||
...params.screenshotOptions,
|
||||
mask,
|
||||
},
|
||||
mask,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ type EmulatedMedia = {
|
|||
forcedColors: types.ForcedColors;
|
||||
};
|
||||
|
||||
type ExpectScreenshotOptions = {
|
||||
type ExpectScreenshotOptions = ImageComparatorOptions & ScreenshotOptions & {
|
||||
timeout?: number,
|
||||
expected?: Buffer,
|
||||
isNot?: boolean,
|
||||
|
|
@ -118,8 +118,6 @@ type ExpectScreenshotOptions = {
|
|||
frame: frames.Frame,
|
||||
selector: string,
|
||||
},
|
||||
comparatorOptions?: ImageComparatorOptions,
|
||||
screenshotOptions?: ScreenshotOptions,
|
||||
};
|
||||
|
||||
export class Page extends SdkObject {
|
||||
|
|
@ -537,11 +535,11 @@ export class Page extends SdkObject {
|
|||
async expectScreenshot(metadata: CallMetadata, options: ExpectScreenshotOptions = {}): Promise<{ actual?: Buffer, previous?: Buffer, diff?: Buffer, errorMessage?: string, log?: string[] }> {
|
||||
const locator = options.locator;
|
||||
const rafrafScreenshot = locator ? async (progress: Progress, timeout: number) => {
|
||||
return await locator.frame.rafrafTimeoutScreenshotElementWithProgress(progress, locator.selector, timeout, options.screenshotOptions || {});
|
||||
return await locator.frame.rafrafTimeoutScreenshotElementWithProgress(progress, locator.selector, timeout, options || {});
|
||||
} : async (progress: Progress, timeout: number) => {
|
||||
await this.performLocatorHandlersCheckpoint(progress);
|
||||
await this.mainFrame().rafrafTimeout(timeout);
|
||||
return await this._screenshotter.screenshotPage(progress, options.screenshotOptions || {});
|
||||
return await this._screenshotter.screenshotPage(progress, options || {});
|
||||
};
|
||||
|
||||
const comparator = getComparator('image/png');
|
||||
|
|
@ -549,7 +547,7 @@ export class Page extends SdkObject {
|
|||
if (!options.expected && options.isNot)
|
||||
return { errorMessage: '"not" matcher requires expected result' };
|
||||
try {
|
||||
const format = validateScreenshotOptions(options.screenshotOptions || {});
|
||||
const format = validateScreenshotOptions(options || {});
|
||||
if (format !== 'png')
|
||||
throw new Error('Only PNG screenshots are supported');
|
||||
} catch (error) {
|
||||
|
|
@ -562,7 +560,7 @@ export class Page extends SdkObject {
|
|||
diff?: Buffer,
|
||||
} | undefined = undefined;
|
||||
const areEqualScreenshots = (actual: Buffer | undefined, expected: Buffer | undefined, previous: Buffer | undefined) => {
|
||||
const comparatorResult = actual && expected ? comparator(actual, expected, options.comparatorOptions) : undefined;
|
||||
const comparatorResult = actual && expected ? comparator(actual, expected, options) : undefined;
|
||||
if (comparatorResult !== undefined && !!comparatorResult === !!options.isNot)
|
||||
return true;
|
||||
if (comparatorResult)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { compare } from '../image_tools/compare';
|
|||
const { diff_match_patch, DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL } = require('../third_party/diff_match_patch');
|
||||
import { PNG } from '../utilsBundle';
|
||||
|
||||
export type ImageComparatorOptions = { threshold?: number, maxDiffPixels?: number, maxDiffPixelRatio?: number, _comparator?: string };
|
||||
export type ImageComparatorOptions = { threshold?: number, maxDiffPixels?: number, maxDiffPixelRatio?: number, comparator?: string };
|
||||
export type ComparatorResult = { diff?: Buffer; errorMessage: string; } | null;
|
||||
export type Comparator = (actualBuffer: Buffer | string, expectedBuffer: Buffer, options?: any) => ComparatorResult;
|
||||
|
||||
|
|
@ -65,18 +65,18 @@ function compareImages(mimeType: string, actualBuffer: Buffer | string, expected
|
|||
}
|
||||
const diff = new PNG({ width: size.width, height: size.height });
|
||||
let count;
|
||||
if (options._comparator === 'ssim-cie94') {
|
||||
if (options.comparator === 'ssim-cie94') {
|
||||
count = compare(expected.data, actual.data, diff.data, size.width, size.height, {
|
||||
// All ΔE* formulae are originally designed to have the difference of 1.0 stand for a "just noticeable difference" (JND).
|
||||
// See https://en.wikipedia.org/wiki/Color_difference#CIELAB_%CE%94E*
|
||||
maxColorDeltaE94: 1.0,
|
||||
});
|
||||
} else if ((options._comparator ?? 'pixelmatch') === 'pixelmatch') {
|
||||
} else if ((options.comparator ?? 'pixelmatch') === 'pixelmatch') {
|
||||
count = pixelmatch(expected.data, actual.data, diff.data, size.width, size.height, {
|
||||
threshold: options.threshold ?? 0.2,
|
||||
});
|
||||
} else {
|
||||
throw new Error(`Configuration specifies unknown comparator "${options._comparator}"`);
|
||||
throw new Error(`Configuration specifies unknown comparator "${options.comparator}"`);
|
||||
}
|
||||
|
||||
const maxDiffPixels1 = options.maxDiffPixels;
|
||||
|
|
|
|||
|
|
@ -15,12 +15,10 @@
|
|||
*/
|
||||
|
||||
import type { Locator, Page } from 'playwright-core';
|
||||
import type { Page as PageEx } from 'playwright-core/lib/client/page';
|
||||
import type { Locator as LocatorEx } from 'playwright-core/lib/client/locator';
|
||||
import type { ExpectScreenshotOptions, Page as PageEx } from 'playwright-core/lib/client/page';
|
||||
import { currentTestInfo, currentExpectTimeout } from '../common/globals';
|
||||
import type { ImageComparatorOptions, Comparator } from 'playwright-core/lib/utils';
|
||||
import { getComparator, sanitizeForFilePath, zones } from 'playwright-core/lib/utils';
|
||||
import type { PageScreenshotOptions } from 'playwright-core/types/types';
|
||||
import {
|
||||
addSuffixToFilePath,
|
||||
trimLongString, callLogText,
|
||||
|
|
@ -32,6 +30,7 @@ import { mime } from 'playwright-core/lib/utilsBundle';
|
|||
import type { TestInfoImpl } from '../worker/testInfo';
|
||||
import type { ExpectMatcherContext } from './expect';
|
||||
import type { MatcherResult } from './matcherHint';
|
||||
import type { FullProjectInternal } from '../common/config';
|
||||
|
||||
type NameOrSegments = string | string[];
|
||||
const snapshotNamesSymbol = Symbol('snapshotNames');
|
||||
|
|
@ -43,7 +42,36 @@ type SnapshotNames = {
|
|||
|
||||
type ImageMatcherResult = MatcherResult<string, string> & { diff?: string };
|
||||
|
||||
class SnapshotHelper<T extends ImageComparatorOptions> {
|
||||
type ToHaveScreenshotConfigOptions = NonNullable<NonNullable<FullProjectInternal['expect']>['toHaveScreenshot']> & {
|
||||
_comparator?: string;
|
||||
};
|
||||
|
||||
type ToHaveScreenshotOptions = ToHaveScreenshotConfigOptions & {
|
||||
clip?: {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
fullPage?: boolean;
|
||||
mask?: Array<Locator>;
|
||||
maskColor?: string;
|
||||
omitBackground?: boolean;
|
||||
timeout?: number;
|
||||
};
|
||||
|
||||
// Keep in sync with above (begin).
|
||||
const NonConfigProperties: (keyof ToHaveScreenshotOptions)[] = [
|
||||
'clip',
|
||||
'fullPage',
|
||||
'mask',
|
||||
'maskColor',
|
||||
'omitBackground',
|
||||
'timeout',
|
||||
];
|
||||
// Keep in sync with above (end).
|
||||
|
||||
class SnapshotHelper {
|
||||
readonly testInfo: TestInfoImpl;
|
||||
readonly snapshotName: string;
|
||||
readonly legacyExpectedPath: string;
|
||||
|
|
@ -54,9 +82,8 @@ class SnapshotHelper<T extends ImageComparatorOptions> {
|
|||
readonly mimeType: string;
|
||||
readonly kind: 'Screenshot'|'Snapshot';
|
||||
readonly updateSnapshots: 'all' | 'none' | 'missing';
|
||||
readonly comparatorOptions: ImageComparatorOptions;
|
||||
readonly comparator: Comparator;
|
||||
readonly allOptions: T;
|
||||
readonly options: Omit<ToHaveScreenshotOptions, '_comparator'> & { comparator?: string };
|
||||
readonly matcherName: string;
|
||||
readonly locator: Locator | undefined;
|
||||
|
||||
|
|
@ -66,19 +93,18 @@ class SnapshotHelper<T extends ImageComparatorOptions> {
|
|||
locator: Locator | undefined,
|
||||
snapshotPathResolver: (...pathSegments: string[]) => string,
|
||||
anonymousSnapshotExtension: string,
|
||||
configOptions: ImageComparatorOptions,
|
||||
nameOrOptions: NameOrSegments | { name?: NameOrSegments } & T,
|
||||
optOptions: T,
|
||||
configOptions: ToHaveScreenshotConfigOptions,
|
||||
nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ToHaveScreenshotOptions,
|
||||
optOptions: ToHaveScreenshotOptions,
|
||||
) {
|
||||
let options: T;
|
||||
let name: NameOrSegments | undefined;
|
||||
if (Array.isArray(nameOrOptions) || typeof nameOrOptions === 'string') {
|
||||
name = nameOrOptions;
|
||||
options = optOptions;
|
||||
this.options = { ...optOptions };
|
||||
} else {
|
||||
name = nameOrOptions.name;
|
||||
options = { ...nameOrOptions };
|
||||
delete (options as any).name;
|
||||
this.options = { ...nameOrOptions };
|
||||
delete (this.options as any).name;
|
||||
}
|
||||
|
||||
let snapshotNames = (testInfo as any)[snapshotNamesSymbol] as SnapshotNames;
|
||||
|
|
@ -116,15 +142,24 @@ class SnapshotHelper<T extends ImageComparatorOptions> {
|
|||
}
|
||||
}
|
||||
|
||||
options = {
|
||||
...configOptions,
|
||||
...options,
|
||||
const filteredConfigOptions = { ...configOptions };
|
||||
for (const prop of NonConfigProperties)
|
||||
delete (filteredConfigOptions as any)[prop];
|
||||
this.options = {
|
||||
...filteredConfigOptions,
|
||||
...this.options,
|
||||
};
|
||||
|
||||
if (options.maxDiffPixels !== undefined && options.maxDiffPixels < 0)
|
||||
// While comparator is not a part of the public API, it is translated here.
|
||||
if ((this.options as any)._comparator) {
|
||||
this.options.comparator = (this.options as any)._comparator;
|
||||
delete (this.options as any)._comparator;
|
||||
}
|
||||
|
||||
if (this.options.maxDiffPixels !== undefined && this.options.maxDiffPixels < 0)
|
||||
throw new Error('`maxDiffPixels` option value must be non-negative integer');
|
||||
|
||||
if (options.maxDiffPixelRatio !== undefined && (options.maxDiffPixelRatio < 0 || options.maxDiffPixelRatio > 1))
|
||||
if (this.options.maxDiffPixelRatio !== undefined && (this.options.maxDiffPixelRatio < 0 || this.options.maxDiffPixelRatio > 1))
|
||||
throw new Error('`maxDiffPixelRatio` option value must be between 0 and 1');
|
||||
|
||||
// sanitizes path if string
|
||||
|
|
@ -145,13 +180,6 @@ class SnapshotHelper<T extends ImageComparatorOptions> {
|
|||
this.comparator = getComparator(this.mimeType);
|
||||
|
||||
this.testInfo = testInfo;
|
||||
this.allOptions = options;
|
||||
this.comparatorOptions = {
|
||||
maxDiffPixels: options.maxDiffPixels,
|
||||
maxDiffPixelRatio: options.maxDiffPixelRatio,
|
||||
threshold: options.threshold,
|
||||
_comparator: options._comparator,
|
||||
};
|
||||
this.kind = this.mimeType.startsWith('image/') ? 'Screenshot' : 'Snapshot';
|
||||
}
|
||||
|
||||
|
|
@ -284,7 +312,7 @@ export function toMatchSnapshot(
|
|||
if (this.isNot) {
|
||||
if (!fs.existsSync(helper.snapshotPath))
|
||||
return helper.handleMissingNegated();
|
||||
const isDifferent = !!helper.comparator(received, fs.readFileSync(helper.snapshotPath), helper.comparatorOptions);
|
||||
const isDifferent = !!helper.comparator(received, fs.readFileSync(helper.snapshotPath), helper.options);
|
||||
return isDifferent ? helper.handleDifferentNegated() : helper.handleMatchingNegated();
|
||||
}
|
||||
|
||||
|
|
@ -292,7 +320,7 @@ export function toMatchSnapshot(
|
|||
return helper.handleMissing(received);
|
||||
|
||||
const expected = fs.readFileSync(helper.snapshotPath);
|
||||
const result = helper.comparator(received, expected, helper.comparatorOptions);
|
||||
const result = helper.comparator(received, expected, helper.options);
|
||||
if (!result)
|
||||
return helper.handleMatching();
|
||||
|
||||
|
|
@ -306,11 +334,9 @@ export function toMatchSnapshot(
|
|||
return helper.handleDifferent(received, expected, undefined, result.diff, result.errorMessage, undefined);
|
||||
}
|
||||
|
||||
type HaveScreenshotOptions = ImageComparatorOptions & Omit<PageScreenshotOptions, 'type' | 'quality' | 'path' | 'style'> & { stylePath?: string | string[] };
|
||||
|
||||
export function toHaveScreenshotStepTitle(
|
||||
nameOrOptions: NameOrSegments | { name?: NameOrSegments } & HaveScreenshotOptions = {},
|
||||
optOptions: HaveScreenshotOptions = {}
|
||||
nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ToHaveScreenshotOptions = {},
|
||||
optOptions: ToHaveScreenshotOptions = {}
|
||||
): string {
|
||||
let name: NameOrSegments | undefined;
|
||||
if (typeof nameOrOptions === 'object' && !Array.isArray(nameOrOptions))
|
||||
|
|
@ -323,8 +349,8 @@ export function toHaveScreenshotStepTitle(
|
|||
export async function toHaveScreenshot(
|
||||
this: ExpectMatcherContext,
|
||||
pageOrLocator: Page | Locator,
|
||||
nameOrOptions: NameOrSegments | { name?: NameOrSegments } & HaveScreenshotOptions = {},
|
||||
optOptions: HaveScreenshotOptions = {}
|
||||
nameOrOptions: NameOrSegments | { name?: NameOrSegments } & ToHaveScreenshotOptions = {},
|
||||
optOptions: ToHaveScreenshotOptions = {}
|
||||
): Promise<MatcherResult<NameOrSegments | { name?: NameOrSegments }, string>> {
|
||||
const testInfo = currentTestInfo();
|
||||
if (!testInfo)
|
||||
|
|
@ -334,47 +360,45 @@ export async function toHaveScreenshot(
|
|||
return { pass: !this.isNot, message: () => '', name: 'toHaveScreenshot', expected: nameOrOptions };
|
||||
|
||||
expectTypes(pageOrLocator, ['Page', 'Locator'], 'toHaveScreenshot');
|
||||
const [page, locator] = pageOrLocator.constructor.name === 'Page' ? [(pageOrLocator as PageEx), undefined] : [(pageOrLocator as Locator).page() as PageEx, pageOrLocator as LocatorEx];
|
||||
const config = (testInfo._projectInternal.expect as any)?.toHaveScreenshot;
|
||||
const [page, locator] = pageOrLocator.constructor.name === 'Page' ? [(pageOrLocator as PageEx), undefined] : [(pageOrLocator as Locator).page() as PageEx, pageOrLocator as Locator];
|
||||
const configOptions = testInfo._projectInternal.expect?.toHaveScreenshot || {};
|
||||
const snapshotPathResolver = testInfo.snapshotPath.bind(testInfo);
|
||||
const helper = new SnapshotHelper(
|
||||
testInfo, 'toHaveScreenshot', locator, snapshotPathResolver, 'png',
|
||||
{
|
||||
_comparator: config?._comparator,
|
||||
maxDiffPixels: config?.maxDiffPixels,
|
||||
maxDiffPixelRatio: config?.maxDiffPixelRatio,
|
||||
threshold: config?.threshold,
|
||||
},
|
||||
nameOrOptions, optOptions);
|
||||
configOptions, nameOrOptions, optOptions);
|
||||
if (!helper.snapshotPath.toLowerCase().endsWith('.png'))
|
||||
throw new Error(`Screenshot name "${path.basename(helper.snapshotPath)}" must have '.png' extension`);
|
||||
expectTypes(pageOrLocator, ['Page', 'Locator'], 'toHaveScreenshot');
|
||||
return await zones.preserve(async () => {
|
||||
// Loading from filesystem resets zones.
|
||||
const style = await loadScreenshotStyles(helper.allOptions.stylePath || config?.stylePath);
|
||||
return toHaveScreenshotContinuation.call(this, helper, page, locator, config, style);
|
||||
const style = await loadScreenshotStyles(helper.options.stylePath);
|
||||
return toHaveScreenshotContinuation.call(this, helper, page, locator, style);
|
||||
});
|
||||
}
|
||||
|
||||
async function toHaveScreenshotContinuation(
|
||||
this: ExpectMatcherContext,
|
||||
helper: SnapshotHelper<HaveScreenshotOptions>,
|
||||
helper: SnapshotHelper,
|
||||
page: PageEx,
|
||||
locator: LocatorEx | undefined,
|
||||
config?: HaveScreenshotOptions,
|
||||
locator: Locator | undefined,
|
||||
style?: string) {
|
||||
const screenshotOptions: any = {
|
||||
animations: config?.animations ?? 'disabled',
|
||||
scale: config?.scale ?? 'css',
|
||||
caret: config?.caret ?? 'hide',
|
||||
const expectScreenshotOptions: ExpectScreenshotOptions = {
|
||||
locator,
|
||||
animations: helper.options.animations ?? 'disabled',
|
||||
caret: helper.options.caret ?? 'hide',
|
||||
clip: helper.options.clip,
|
||||
fullPage: helper.options.fullPage,
|
||||
mask: helper.options.mask,
|
||||
maskColor: helper.options.maskColor,
|
||||
omitBackground: helper.options.omitBackground,
|
||||
scale: helper.options.scale ?? 'css',
|
||||
style,
|
||||
...helper.allOptions,
|
||||
mask: (helper.allOptions.mask || []) as LocatorEx[],
|
||||
maskColor: helper.allOptions.maskColor,
|
||||
name: undefined,
|
||||
threshold: undefined,
|
||||
maxDiffPixels: undefined,
|
||||
maxDiffPixelRatio: undefined,
|
||||
isNot: !!this.isNot,
|
||||
timeout: currentExpectTimeout(helper.options),
|
||||
comparator: helper.options.comparator,
|
||||
maxDiffPixels: helper.options.maxDiffPixels,
|
||||
maxDiffPixelRatio: helper.options.maxDiffPixelRatio,
|
||||
threshold: helper.options.threshold,
|
||||
};
|
||||
|
||||
const hasSnapshot = fs.existsSync(helper.snapshotPath);
|
||||
|
|
@ -385,17 +409,8 @@ async function toHaveScreenshotContinuation(
|
|||
// Having `errorMessage` means we timed out while waiting
|
||||
// for screenshots not to match, so screenshots
|
||||
// are actually the same in the end.
|
||||
const isDifferent = !(await page._expectScreenshot({
|
||||
expected: await fs.promises.readFile(helper.snapshotPath),
|
||||
isNot: true,
|
||||
locator,
|
||||
comparatorOptions: {
|
||||
...helper.comparatorOptions,
|
||||
comparator: helper.comparatorOptions._comparator,
|
||||
},
|
||||
screenshotOptions,
|
||||
timeout: currentExpectTimeout(helper.allOptions),
|
||||
})).errorMessage;
|
||||
expectScreenshotOptions.expected = await fs.promises.readFile(helper.snapshotPath);
|
||||
const isDifferent = !(await page._expectScreenshot(expectScreenshotOptions)).errorMessage;
|
||||
return isDifferent ? helper.handleDifferentNegated() : helper.handleMatchingNegated();
|
||||
}
|
||||
|
||||
|
|
@ -405,15 +420,7 @@ async function toHaveScreenshotContinuation(
|
|||
|
||||
if (!hasSnapshot) {
|
||||
// Regenerate a new screenshot by waiting until two screenshots are the same.
|
||||
const timeout = currentExpectTimeout(helper.allOptions);
|
||||
const { actual, previous, diff, errorMessage, log } = await page._expectScreenshot({
|
||||
expected: undefined,
|
||||
isNot: false,
|
||||
locator,
|
||||
comparatorOptions: { ...helper.comparatorOptions, comparator: helper.comparatorOptions._comparator },
|
||||
screenshotOptions,
|
||||
timeout,
|
||||
});
|
||||
const { actual, previous, diff, errorMessage, log } = await page._expectScreenshot(expectScreenshotOptions);
|
||||
// We tried re-generating new snapshot but failed.
|
||||
// This can be due to e.g. spinning animation, so we want to show it as a diff.
|
||||
if (errorMessage)
|
||||
|
|
@ -427,15 +434,8 @@ async function toHaveScreenshotContinuation(
|
|||
// - snapshot exists
|
||||
// - regular matcher (i.e. not a `.not`)
|
||||
// - perhaps an 'all' flag to update non-matching screenshots
|
||||
const expected = await fs.promises.readFile(helper.snapshotPath);
|
||||
const { actual, diff, errorMessage, log } = await page._expectScreenshot({
|
||||
expected,
|
||||
isNot: false,
|
||||
locator,
|
||||
comparatorOptions: { ...helper.comparatorOptions, comparator: helper.comparatorOptions._comparator },
|
||||
screenshotOptions,
|
||||
timeout: currentExpectTimeout(helper.allOptions),
|
||||
});
|
||||
expectScreenshotOptions.expected = await fs.promises.readFile(helper.snapshotPath);
|
||||
const { actual, diff, errorMessage, log } = await page._expectScreenshot(expectScreenshotOptions);
|
||||
|
||||
if (!errorMessage)
|
||||
return helper.handleMatching();
|
||||
|
|
@ -448,7 +448,7 @@ async function toHaveScreenshotContinuation(
|
|||
return helper.createMatcherResult(helper.snapshotPath + ' running with --update-snapshots, writing actual.', true);
|
||||
}
|
||||
|
||||
return helper.handleDifferent(actual, expected, undefined, diff, errorMessage, log);
|
||||
return helper.handleDifferent(actual, expectScreenshotOptions.expected, undefined, diff, errorMessage, log);
|
||||
}
|
||||
|
||||
function writeFileSync(aPath: string, content: Buffer | string) {
|
||||
|
|
|
|||
|
|
@ -1939,26 +1939,22 @@ export type PageExpectScreenshotParams = {
|
|||
frame: FrameChannel,
|
||||
selector: string,
|
||||
},
|
||||
comparatorOptions?: {
|
||||
comparator?: string,
|
||||
maxDiffPixels?: number,
|
||||
maxDiffPixelRatio?: number,
|
||||
threshold?: number,
|
||||
},
|
||||
screenshotOptions?: {
|
||||
fullPage?: boolean,
|
||||
clip?: Rect,
|
||||
omitBackground?: boolean,
|
||||
caret?: 'hide' | 'initial',
|
||||
animations?: 'disabled' | 'allow',
|
||||
scale?: 'css' | 'device',
|
||||
mask?: {
|
||||
frame: FrameChannel,
|
||||
selector: string,
|
||||
}[],
|
||||
maskColor?: string,
|
||||
style?: string,
|
||||
},
|
||||
comparator?: string,
|
||||
maxDiffPixels?: number,
|
||||
maxDiffPixelRatio?: number,
|
||||
threshold?: number,
|
||||
fullPage?: boolean,
|
||||
clip?: Rect,
|
||||
omitBackground?: boolean,
|
||||
caret?: 'hide' | 'initial',
|
||||
animations?: 'disabled' | 'allow',
|
||||
scale?: 'css' | 'device',
|
||||
mask?: {
|
||||
frame: FrameChannel,
|
||||
selector: string,
|
||||
}[],
|
||||
maskColor?: string,
|
||||
style?: string,
|
||||
};
|
||||
export type PageExpectScreenshotOptions = {
|
||||
expected?: Binary,
|
||||
|
|
@ -1967,26 +1963,22 @@ export type PageExpectScreenshotOptions = {
|
|||
frame: FrameChannel,
|
||||
selector: string,
|
||||
},
|
||||
comparatorOptions?: {
|
||||
comparator?: string,
|
||||
maxDiffPixels?: number,
|
||||
maxDiffPixelRatio?: number,
|
||||
threshold?: number,
|
||||
},
|
||||
screenshotOptions?: {
|
||||
fullPage?: boolean,
|
||||
clip?: Rect,
|
||||
omitBackground?: boolean,
|
||||
caret?: 'hide' | 'initial',
|
||||
animations?: 'disabled' | 'allow',
|
||||
scale?: 'css' | 'device',
|
||||
mask?: {
|
||||
frame: FrameChannel,
|
||||
selector: string,
|
||||
}[],
|
||||
maskColor?: string,
|
||||
style?: string,
|
||||
},
|
||||
comparator?: string,
|
||||
maxDiffPixels?: number,
|
||||
maxDiffPixelRatio?: number,
|
||||
threshold?: number,
|
||||
fullPage?: boolean,
|
||||
clip?: Rect,
|
||||
omitBackground?: boolean,
|
||||
caret?: 'hide' | 'initial',
|
||||
animations?: 'disabled' | 'allow',
|
||||
scale?: 'css' | 'device',
|
||||
mask?: {
|
||||
frame: FrameChannel,
|
||||
selector: string,
|
||||
}[],
|
||||
maskColor?: string,
|
||||
style?: string,
|
||||
};
|
||||
export type PageExpectScreenshotResult = {
|
||||
diff?: Binary,
|
||||
|
|
|
|||
|
|
@ -1367,19 +1367,13 @@ Page:
|
|||
properties:
|
||||
frame: Frame
|
||||
selector: string
|
||||
comparatorOptions:
|
||||
type: object?
|
||||
properties:
|
||||
comparator: string?
|
||||
maxDiffPixels: number?
|
||||
maxDiffPixelRatio: number?
|
||||
threshold: number?
|
||||
screenshotOptions:
|
||||
type: object?
|
||||
properties:
|
||||
fullPage: boolean?
|
||||
clip: Rect?
|
||||
$mixin: CommonScreenshotOptions
|
||||
comparator: string?
|
||||
maxDiffPixels: number?
|
||||
maxDiffPixelRatio: number?
|
||||
threshold: number?
|
||||
fullPage: boolean?
|
||||
clip: Rect?
|
||||
$mixin: CommonScreenshotOptions
|
||||
returns:
|
||||
diff: binary?
|
||||
errorMessage: string?
|
||||
|
|
|
|||
|
|
@ -22,5 +22,5 @@ type ImageComparatorOptions = { threshold?: number, maxDiffPixels?: number, maxD
|
|||
|
||||
export function comparePNGs(actual: Buffer, expected: Buffer, options: ImageComparatorOptions = {}): ComparatorResult {
|
||||
// Strict threshold by default in our tests.
|
||||
return pngComparator(actual, expected, { _comparator: 'ssim-cie94', threshold: 0, ...options });
|
||||
return pngComparator(actual, expected, { comparator: 'ssim-cie94', threshold: 0, ...options });
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue