quickpickle
Version:
Plugin for Vitest to run tests written in Gherkin Syntax.
283 lines (282 loc) • 12.6 kB
TypeScript
import type { TestContext } from 'vitest';
import type { QuickPickleConfig } from '.';
import sanitize from './shims/path-sanitizer';
import { type PixelmatchOptions } from 'pixelmatch';
import { type AriaRole } from '@a11y-tools/aria-roles';
export type AriaRoleExtended = AriaRole | 'element' | 'input';
import { Buffer } from 'buffer';
export declare function isAriaRoleExtended(role: string): role is AriaRoleExtended;
interface Common {
info: {
feature: string;
tags: string[];
};
[key: string]: any;
}
export interface QuickPickleWorldInterface {
info: {
config: QuickPickleConfig;
feature: string;
scenario: string;
tags: string[];
steps: string[];
stepIdx?: number;
rule?: string;
step?: string;
line?: number;
explodedIdx?: number;
errors: any[];
};
context: TestContext;
isComplete: boolean;
config: QuickPickleConfig;
worldConfig: QuickPickleConfig['worldConfig'];
data: Record<string, any>;
common: Common;
init: () => Promise<void>;
tagsMatch(tags: string[]): string[] | null;
sanitizePath: typeof sanitize;
fullPath(path: string): string;
wait(ms: number): Promise<void>;
}
export type InfoConstructor = Omit<QuickPickleWorldInterface['info'], 'errors'> & {
common: Common;
};
export declare class QuickPickleWorld implements QuickPickleWorldInterface {
private _projectRoot;
info: QuickPickleWorldInterface['info'];
common: QuickPickleWorldInterface['common'];
context: TestContext;
data: Record<string, any>;
sanitizePath: typeof sanitize;
constructor(context: TestContext, info: InfoConstructor);
init(): Promise<void>;
get config(): QuickPickleConfig;
get worldConfig(): Partial<{
[key: string]: any;
}>;
get isComplete(): boolean;
get projectRoot(): string;
/**
* Checks the tags of the Scenario against a provided list of tags,
* and returns the shared tags, with the "@" prefix character.
*
* @param tags tags to check
* @returns string[]|null
*/
tagsMatch(tags: string[]): string[] | null;
/**
* Given a provided path-like string, returns a full path that:
*
* 1. contains no invalid characters.
* 2. is a subdirectory of the project root.
*
* This is intended for security when retrieving and saving files;
* it does not slugify filenames or check for a file's existence.
*
* @param path string the path to sanitize
* @return string the sanitized path, including the project root
*/
fullPath(path: string): string;
/**
* A helper function for when you really just need to wait.
*
* @deprecated Waiting for arbitrary amounts of time makes your tests flaky! There are
* usually better ways to wait for something to happen, and this functionality will be
* removed from the API as soon we're sure nobody will **EVER** want to use it again.
* (That may be a long time.)
*
* @param ms milliseconds to wait
*/
wait(ms: number): Promise<void>;
toString(): string;
}
export type WorldConstructor = new (context: TestContext, info: InfoConstructor) => QuickPickleWorldInterface;
export declare function getWorldConstructor(): WorldConstructor;
export declare function setWorldConstructor(constructor: WorldConstructor): void;
export type VisualDiffResult = {
diff: Buffer;
pixels: number;
pct: number;
};
export type ScreenshotComparisonOptions = any & Partial<PixelmatchOptions> & {
/**
* The maximum difference between the actual and expected images, as a percentage of the total number of pixels.
*/
maxDiffPercentage?: number;
/**
* The maximum difference between the actual and expected images, as the number of pixels.
*/
maxDiffPixels?: number;
/**
* Whether to compare the images even if they are of different sizes by resizing them as necessary.
* @default false
*/
resizeEnabled?: boolean;
/**
* Whether to ignore the resized area when comparing images.
*/
resizeIgnored?: boolean;
/**
* The color to use for resizing images, in [r,g,b] format, with values from 0 to 255.
* If this number is too similar to the color of the larger image, the resized area
* will be ignored by the pixelmatch algorithm.
* @default [255,0,128]
*/
resizeColor?: [number, number, number];
/**
* The anchor point to use if the images are of different size.
* @default "top left"
*/
resizeAnchor?: 'top left' | 'top right' | 'bottom left' | 'bottom right' | 'center' | 'top' | 'bottom' | 'left' | 'right';
};
export declare const defaultScreenshotComparisonOptions: ScreenshotComparisonOptions;
export interface VisualConfigSetting {
screenshotDir?: string;
screenshotOpts?: Partial<ScreenshotComparisonOptions>;
}
interface StubVisualWorldInterface extends QuickPickleWorldInterface {
/**
* The directory where screenshots are saved, relative to the project root.
*/
screenshotDir: string;
/**
* The filename for a screenshot based on the current Scenario.
*/
screenshotFilename: string;
/**
* The full path to a screenshot file, from the root of the file system,
* based on the current Scenario.
*/
screenshotPath: string;
/**
* The options for the default screenshot comparisons.
*/
screenshotOptions: Partial<ScreenshotComparisonOptions>;
/**
* The full path to a screenshot file, from the root of the file system,
* based on the custom name provided, and including information on any
* exploded tags as necessary.
*
* @param name
*/
getScreenshotPath(name?: string): string;
/**
* A helper function to compare two screenshots, for visual regression testing.
* If the screenshots do not match, the difference should be returned as a Buffer.
*/
screenshotDiff(actual: Buffer, expected: Buffer, options?: any): Promise<VisualDiffResult>;
}
export interface VisualWorldInterface extends StubVisualWorldInterface {
/**
* A helper method for getting an element, which should work across different testing libraries.
* The "Locator" interface used should be whatever is compatible with the testing library
* being integrated by your World Constructor. This is intended as an 80% solution for
* behavioral tests, so that a single step definition can get an element based on a variety
* of factors, e.g. (in Playwright syntax):
*
* @example getLocator(page, 'Cancel', 'button') => page.getByRole('button', { name: 'Cancel' })
* @example getLocator(page, 'Search', 'input') => page.getByLabel('Search').or(page.getByPlaceholder('Search'))
* @example getLocator(page, 'ul.fourteen-points li', 'element', 'Open covenants of peace') => page.locator('ul.fourteen-points li').filter({ hasText: 'Open covenants of peace' })
*
* @param locator Locator
* The container inside which to search for the required element.
* @param identifier string
* A string that identifies the element to be found. For ARIA roles this is the "name" attribute,
* for role="input" it is the label or placeholder, and for role="element" it is the CSS selector.
* @param role string
* An ARIA role, or "input" to get an input by label or placeholder, or "element" to get an element by css selector.
* @param text string
* A string that the element must contain.
*/
getLocator(locator: any, identifier: string, role: AriaRoleExtended | string, text?: string | null): any;
/**
* Sets a value on a form element based on its type (select, checkbox/radio, or other input).
* The "Locator" interface used should be whatever is compatible with the testing library
* being integrated by your World Constructor. This is intended as an 80% solution for
* behavioral tests, so that a single step definition can get an element based on a variety
* of factors, e.g.:
*
* @example setValue(<SelectInput>, "Option 1, Option 2") => Selects multiple options in a select element
* @example setValue(<RadioInput>, "true") => Checks a checkbox/radio item
* @example setValue(<CheckboxInput>, "false") => Unchecks a checkbox item
* @example setValue(<TextInput>, "Some text") => Fills a text input with "Some text"
* @example setValue(<NumberInput>, 5) => Sets a number input to the number 5
*
* @param locator Locator
* The Locator for the form element
* @param value string|any
* The value to set can be string or other value type
*/
setValue(locator: any, value: string | any): Promise<void>;
/**
* Scrolls an element by a number of pixels in a given direction.
*
* @param locator Locator
* The locator that should be scrolled
* @param direction "up"|"down"|"left"|"right"
* The direction to scroll, i.e. "up", "down", "left", "right"
* @param px
* A number of pixels to scroll
*/
scroll(locator: any, direction: string, px: number): Promise<void>;
/**
* A helper method for parsing text on a page or in an element.
* Can be used to check for the presence OR absence of visible OR hidden text.
*
* Examples:
* @example expectText(locator, 'text', true, true) // expect that a locator with the text is visible (and there may be hidden ones)
* @example expectText(locator, 'text', false, true) // expect that NO locator with the text is visible (but there may be hidden ones)
* @example expectText(locator, 'text', true, false) // expect that a HIDDEN locator with the text IS FOUND on the page (but there may be visible ones)
* @example expectText(locator, 'text', false, false) // expect that NO hidden locator with the text is found on the page (but there may be visible ones)
*
* @param locator the locator to check
* @param text the text to be found
* @param toBePresent whether a locator with the text should be present
* @param toBeVisible whether the locator with the text should be visible
* @returns void
*/
expectText(locator: any, text: string, toBePresent: boolean, toBeVisible: boolean): Promise<void>;
/**
* A helper function for parsing elements on a page or in an element.
* Can be used to check for the presence OR absence of visible OR hidden elements.
* Examples:
* @example expectElement(locator, true) // expect that an element is visible (and there may be hidden ones)
* @example expectElement(locator, false) // expect that NO element is visible (but there may be hidden ones)
* @example expectElement(locator, true, false) // expect that a HIDDEN element IS FOUND on the page (but there may be visible ones)
* @example expectElement(locator, false, false) // expect that NO hidden element is found on the page (but there may be visible ones)
*
* @param locator the locator to check
* @param toBePresent whether an element should be present
* @param toBeVisible whether the element should be visible
*/
expectElement(locator: any, toBePresent: boolean, toBeVisible: boolean): Promise<void>;
/**
* A helper function to get a screenshot of the current page or an element.
* Depending on the implementation, it may also save a screenshot to disk.
*/
screenshot(opts?: {
name?: string;
locator?: any;
}): Promise<Buffer>;
/**
* A helper function to test whether two screenshots match. The "Locator" interface used
* should be whatever is compatible with the testing library being integrated by your World Constructor.
*
* @param locator the locator to check
* @param screenshotName the name of the screenshot to compare against
*/
expectScreenshotMatch(locator: any, screenshotName: string, options?: any): Promise<void>;
screenshotOptions: Partial<ScreenshotComparisonOptions>;
}
export declare class VisualWorld extends QuickPickleWorld implements StubVisualWorldInterface {
constructor(context: TestContext, info: InfoConstructor);
init(): Promise<void>;
get screenshotDir(): string;
get screenshotFilename(): string;
get screenshotPath(): string;
get screenshotOptions(): any;
getScreenshotPath(name?: string): string;
screenshotDiff(actual: Buffer, expected: Buffer, opts: any): Promise<VisualDiffResult>;
}
export {};