UNPKG

playwright-json-runner

Version:

Extends Playwright to run tests using JSON-based test definitions.

720 lines (710 loc) 33.4 kB
import { z } from 'zod'; import { Page, Locator } from 'playwright'; /** Selector strategy */ declare const selectorStrategyParamsSchema: z.ZodObject<{ type: z.ZodLiteral<"selector">; value: z.ZodString; }, "strip", z.ZodTypeAny, { value: string; type: "selector"; }, { value: string; type: "selector"; }>; type SelectorStrategyParams = z.infer<typeof selectorStrategyParamsSchema>; /** Role strategy */ declare const roleStrategyParamsSchema: z.ZodObject<{ type: z.ZodLiteral<"role">; value: z.ZodObject<{ role: z.ZodEnum<["alert", "alertdialog", "application", "article", "banner", "blockquote", "button", "caption", "cell", "checkbox", "code", "columnheader", "combobox", "complementary", "contentinfo", "definition", "deletion", "dialog", "directory", "document", "emphasis", "feed", "figure", "form", "generic", "grid", "gridcell", "group", "heading", "img", "insertion", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "meter", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "navigation", "none", "note", "option", "paragraph", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "strong", "subscript", "superscript", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "time", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"]>; options: z.ZodOptional<z.ZodObject<{ checked: z.ZodOptional<z.ZodBoolean>; disabled: z.ZodOptional<z.ZodBoolean>; exact: z.ZodOptional<z.ZodBoolean>; expanded: z.ZodOptional<z.ZodBoolean>; includeHidden: z.ZodOptional<z.ZodBoolean>; level: z.ZodOptional<z.ZodNumber>; name: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodType<RegExp, z.ZodTypeDef, RegExp>]>>; pressed: z.ZodOptional<z.ZodBoolean>; selected: z.ZodOptional<z.ZodBoolean>; }, "strip", z.ZodTypeAny, { exact?: boolean | undefined; checked?: boolean | undefined; disabled?: boolean | undefined; expanded?: boolean | undefined; includeHidden?: boolean | undefined; level?: number | undefined; name?: string | RegExp | undefined; pressed?: boolean | undefined; selected?: boolean | undefined; }, { exact?: boolean | undefined; checked?: boolean | undefined; disabled?: boolean | undefined; expanded?: boolean | undefined; includeHidden?: boolean | undefined; level?: number | undefined; name?: string | RegExp | undefined; pressed?: boolean | undefined; selected?: boolean | undefined; }>>; }, "strip", z.ZodTypeAny, { role: "code" | "status" | "alert" | "alertdialog" | "application" | "article" | "banner" | "blockquote" | "button" | "caption" | "cell" | "checkbox" | "columnheader" | "combobox" | "complementary" | "contentinfo" | "definition" | "deletion" | "dialog" | "directory" | "document" | "emphasis" | "feed" | "figure" | "form" | "generic" | "grid" | "gridcell" | "group" | "heading" | "img" | "insertion" | "link" | "list" | "listbox" | "listitem" | "log" | "main" | "marquee" | "math" | "meter" | "menu" | "menubar" | "menuitem" | "menuitemcheckbox" | "menuitemradio" | "navigation" | "none" | "note" | "option" | "paragraph" | "presentation" | "progressbar" | "radio" | "radiogroup" | "region" | "row" | "rowgroup" | "rowheader" | "scrollbar" | "search" | "searchbox" | "separator" | "slider" | "spinbutton" | "strong" | "subscript" | "superscript" | "switch" | "tab" | "table" | "tablist" | "tabpanel" | "term" | "textbox" | "time" | "timer" | "toolbar" | "tooltip" | "tree" | "treegrid" | "treeitem"; options?: { exact?: boolean | undefined; checked?: boolean | undefined; disabled?: boolean | undefined; expanded?: boolean | undefined; includeHidden?: boolean | undefined; level?: number | undefined; name?: string | RegExp | undefined; pressed?: boolean | undefined; selected?: boolean | undefined; } | undefined; }, { role: "code" | "status" | "alert" | "alertdialog" | "application" | "article" | "banner" | "blockquote" | "button" | "caption" | "cell" | "checkbox" | "columnheader" | "combobox" | "complementary" | "contentinfo" | "definition" | "deletion" | "dialog" | "directory" | "document" | "emphasis" | "feed" | "figure" | "form" | "generic" | "grid" | "gridcell" | "group" | "heading" | "img" | "insertion" | "link" | "list" | "listbox" | "listitem" | "log" | "main" | "marquee" | "math" | "meter" | "menu" | "menubar" | "menuitem" | "menuitemcheckbox" | "menuitemradio" | "navigation" | "none" | "note" | "option" | "paragraph" | "presentation" | "progressbar" | "radio" | "radiogroup" | "region" | "row" | "rowgroup" | "rowheader" | "scrollbar" | "search" | "searchbox" | "separator" | "slider" | "spinbutton" | "strong" | "subscript" | "superscript" | "switch" | "tab" | "table" | "tablist" | "tabpanel" | "term" | "textbox" | "time" | "timer" | "toolbar" | "tooltip" | "tree" | "treegrid" | "treeitem"; options?: { exact?: boolean | undefined; checked?: boolean | undefined; disabled?: boolean | undefined; expanded?: boolean | undefined; includeHidden?: boolean | undefined; level?: number | undefined; name?: string | RegExp | undefined; pressed?: boolean | undefined; selected?: boolean | undefined; } | undefined; }>; }, "strip", z.ZodTypeAny, { value: { role: "code" | "status" | "alert" | "alertdialog" | "application" | "article" | "banner" | "blockquote" | "button" | "caption" | "cell" | "checkbox" | "columnheader" | "combobox" | "complementary" | "contentinfo" | "definition" | "deletion" | "dialog" | "directory" | "document" | "emphasis" | "feed" | "figure" | "form" | "generic" | "grid" | "gridcell" | "group" | "heading" | "img" | "insertion" | "link" | "list" | "listbox" | "listitem" | "log" | "main" | "marquee" | "math" | "meter" | "menu" | "menubar" | "menuitem" | "menuitemcheckbox" | "menuitemradio" | "navigation" | "none" | "note" | "option" | "paragraph" | "presentation" | "progressbar" | "radio" | "radiogroup" | "region" | "row" | "rowgroup" | "rowheader" | "scrollbar" | "search" | "searchbox" | "separator" | "slider" | "spinbutton" | "strong" | "subscript" | "superscript" | "switch" | "tab" | "table" | "tablist" | "tabpanel" | "term" | "textbox" | "time" | "timer" | "toolbar" | "tooltip" | "tree" | "treegrid" | "treeitem"; options?: { exact?: boolean | undefined; checked?: boolean | undefined; disabled?: boolean | undefined; expanded?: boolean | undefined; includeHidden?: boolean | undefined; level?: number | undefined; name?: string | RegExp | undefined; pressed?: boolean | undefined; selected?: boolean | undefined; } | undefined; }; type: "role"; }, { value: { role: "code" | "status" | "alert" | "alertdialog" | "application" | "article" | "banner" | "blockquote" | "button" | "caption" | "cell" | "checkbox" | "columnheader" | "combobox" | "complementary" | "contentinfo" | "definition" | "deletion" | "dialog" | "directory" | "document" | "emphasis" | "feed" | "figure" | "form" | "generic" | "grid" | "gridcell" | "group" | "heading" | "img" | "insertion" | "link" | "list" | "listbox" | "listitem" | "log" | "main" | "marquee" | "math" | "meter" | "menu" | "menubar" | "menuitem" | "menuitemcheckbox" | "menuitemradio" | "navigation" | "none" | "note" | "option" | "paragraph" | "presentation" | "progressbar" | "radio" | "radiogroup" | "region" | "row" | "rowgroup" | "rowheader" | "scrollbar" | "search" | "searchbox" | "separator" | "slider" | "spinbutton" | "strong" | "subscript" | "superscript" | "switch" | "tab" | "table" | "tablist" | "tabpanel" | "term" | "textbox" | "time" | "timer" | "toolbar" | "tooltip" | "tree" | "treegrid" | "treeitem"; options?: { exact?: boolean | undefined; checked?: boolean | undefined; disabled?: boolean | undefined; expanded?: boolean | undefined; includeHidden?: boolean | undefined; level?: number | undefined; name?: string | RegExp | undefined; pressed?: boolean | undefined; selected?: boolean | undefined; } | undefined; }; type: "role"; }>; type RoleStrategyParams = z.infer<typeof roleStrategyParamsSchema>; /** Test ID strategy */ declare const testIdStrategyParamsSchema: z.ZodObject<{ type: z.ZodLiteral<"testId">; value: z.ZodString; }, "strip", z.ZodTypeAny, { value: string; type: "testId"; }, { value: string; type: "testId"; }>; type TestIdStrategyParams = z.infer<typeof testIdStrategyParamsSchema>; /** Text strategy */ declare const textStrategyParamsSchema: z.ZodObject<{ type: z.ZodLiteral<"text">; value: z.ZodString; }, "strip", z.ZodTypeAny, { value: string; type: "text"; }, { value: string; type: "text"; }>; type TextStrategyParams = z.infer<typeof textStrategyParamsSchema>; /** * 1) First define the TypeScript union type manually. * This represents the union of all strategies, including the "nested" type * that references itself recursively. */ type LocatorStrategyParams = z.infer<typeof selectorStrategyParamsSchema> | z.infer<typeof roleStrategyParamsSchema> | z.infer<typeof testIdStrategyParamsSchema> | z.infer<typeof textStrategyParamsSchema> | { type: "nested"; parent: LocatorStrategyParams; child: LocatorStrategyParams; }; /** * 3) Define the nested locator strategy schema. We use z.lazy to handle * the self-referencing union. */ declare const nestedStrategyParamsSchema: z.ZodLazy<z.ZodObject<{ type: z.ZodLiteral<"nested">; parent: z.ZodType<LocatorStrategyParams, z.ZodTypeDef, LocatorStrategyParams>; child: z.ZodType<LocatorStrategyParams, z.ZodTypeDef, LocatorStrategyParams>; }, "strip", z.ZodTypeAny, { type: "nested"; parent: LocatorStrategyParams; child: LocatorStrategyParams; }, { type: "nested"; parent: LocatorStrategyParams; child: LocatorStrategyParams; }>>; type NestedStrategyParams = z.infer<typeof nestedStrategyParamsSchema>; declare const testRunSchema: z.ZodObject<z.objectUtil.extendShape<z.objectUtil.extendShape<{ label: z.ZodOptional<z.ZodString>; }, { description: z.ZodOptional<z.ZodString>; }>, { browser: z.ZodEnum<["chrome", "firefox", "webkit"]>; host: z.ZodString; scenarios: z.ZodArray<z.ZodObject<z.objectUtil.extendShape<z.objectUtil.extendShape<{ label: z.ZodOptional<z.ZodString>; }, { description: z.ZodOptional<z.ZodString>; }>, { name: z.ZodString; steps: z.ZodArray<z.ZodObject<z.objectUtil.extendShape<z.objectUtil.extendShape<{ label: z.ZodOptional<z.ZodString>; }, { description: z.ZodOptional<z.ZodString>; }>, { description: z.ZodString; actions: z.ZodArray<z.ZodObject<z.objectUtil.extendShape<z.objectUtil.extendShape<{ label: z.ZodOptional<z.ZodString>; }, { description: z.ZodOptional<z.ZodString>; }>, { type: z.ZodEnum<["setfieldvalue", "click", "navigate", "expect", "assertFieldValueEquals", "assertFieldValueContains", "assertElementExists", "sleep"]>; value: z.ZodOptional<z.ZodString>; playwrightFunction: z.ZodOptional<z.ZodString>; locator: z.ZodOptional<z.ZodType<LocatorStrategyParams, z.ZodTypeDef, LocatorStrategyParams>>; selector: z.ZodOptional<z.ZodString>; }>, "strip", z.ZodTypeAny, { type: "setfieldvalue" | "click" | "navigate" | "expect" | "assertFieldValueEquals" | "assertFieldValueContains" | "assertElementExists" | "sleep"; label?: string | undefined; value?: string | undefined; description?: string | undefined; selector?: string | undefined; playwrightFunction?: string | undefined; locator?: LocatorStrategyParams | undefined; }, { type: "setfieldvalue" | "click" | "navigate" | "expect" | "assertFieldValueEquals" | "assertFieldValueContains" | "assertElementExists" | "sleep"; label?: string | undefined; value?: string | undefined; description?: string | undefined; selector?: string | undefined; playwrightFunction?: string | undefined; locator?: LocatorStrategyParams | undefined; }>, "many">; }>, "strip", z.ZodTypeAny, { description: string; actions: { type: "setfieldvalue" | "click" | "navigate" | "expect" | "assertFieldValueEquals" | "assertFieldValueContains" | "assertElementExists" | "sleep"; label?: string | undefined; value?: string | undefined; description?: string | undefined; selector?: string | undefined; playwrightFunction?: string | undefined; locator?: LocatorStrategyParams | undefined; }[]; label?: string | undefined; }, { description: string; actions: { type: "setfieldvalue" | "click" | "navigate" | "expect" | "assertFieldValueEquals" | "assertFieldValueContains" | "assertElementExists" | "sleep"; label?: string | undefined; value?: string | undefined; description?: string | undefined; selector?: string | undefined; playwrightFunction?: string | undefined; locator?: LocatorStrategyParams | undefined; }[]; label?: string | undefined; }>, "many">; }>, "strip", z.ZodTypeAny, { name: string; steps: { description: string; actions: { type: "setfieldvalue" | "click" | "navigate" | "expect" | "assertFieldValueEquals" | "assertFieldValueContains" | "assertElementExists" | "sleep"; label?: string | undefined; value?: string | undefined; description?: string | undefined; selector?: string | undefined; playwrightFunction?: string | undefined; locator?: LocatorStrategyParams | undefined; }[]; label?: string | undefined; }[]; label?: string | undefined; description?: string | undefined; }, { name: string; steps: { description: string; actions: { type: "setfieldvalue" | "click" | "navigate" | "expect" | "assertFieldValueEquals" | "assertFieldValueContains" | "assertElementExists" | "sleep"; label?: string | undefined; value?: string | undefined; description?: string | undefined; selector?: string | undefined; playwrightFunction?: string | undefined; locator?: LocatorStrategyParams | undefined; }[]; label?: string | undefined; }[]; label?: string | undefined; description?: string | undefined; }>, "many">; }>, "strip", z.ZodTypeAny, { browser: "chrome" | "firefox" | "webkit"; host: string; scenarios: { name: string; steps: { description: string; actions: { type: "setfieldvalue" | "click" | "navigate" | "expect" | "assertFieldValueEquals" | "assertFieldValueContains" | "assertElementExists" | "sleep"; label?: string | undefined; value?: string | undefined; description?: string | undefined; selector?: string | undefined; playwrightFunction?: string | undefined; locator?: LocatorStrategyParams | undefined; }[]; label?: string | undefined; }[]; label?: string | undefined; description?: string | undefined; }[]; label?: string | undefined; description?: string | undefined; }, { browser: "chrome" | "firefox" | "webkit"; host: string; scenarios: { name: string; steps: { description: string; actions: { type: "setfieldvalue" | "click" | "navigate" | "expect" | "assertFieldValueEquals" | "assertFieldValueContains" | "assertElementExists" | "sleep"; label?: string | undefined; value?: string | undefined; description?: string | undefined; selector?: string | undefined; playwrightFunction?: string | undefined; locator?: LocatorStrategyParams | undefined; }[]; label?: string | undefined; }[]; label?: string | undefined; description?: string | undefined; }[]; label?: string | undefined; description?: string | undefined; }>; type TestRun = z.infer<typeof testRunSchema>; declare const actionTypeSchema: z.ZodEnum<["setfieldvalue", "click", "navigate", "expect", "assertFieldValueEquals", "assertFieldValueContains", "assertElementExists", "sleep"]>; type ActionType = z.infer<typeof actionTypeSchema>; declare const testActionSchema: z.ZodObject<z.objectUtil.extendShape<z.objectUtil.extendShape<{ label: z.ZodOptional<z.ZodString>; }, { description: z.ZodOptional<z.ZodString>; }>, { type: z.ZodEnum<["setfieldvalue", "click", "navigate", "expect", "assertFieldValueEquals", "assertFieldValueContains", "assertElementExists", "sleep"]>; value: z.ZodOptional<z.ZodString>; playwrightFunction: z.ZodOptional<z.ZodString>; locator: z.ZodOptional<z.ZodType<LocatorStrategyParams, z.ZodTypeDef, LocatorStrategyParams>>; selector: z.ZodOptional<z.ZodString>; }>, "strip", z.ZodTypeAny, { type: "setfieldvalue" | "click" | "navigate" | "expect" | "assertFieldValueEquals" | "assertFieldValueContains" | "assertElementExists" | "sleep"; label?: string | undefined; value?: string | undefined; description?: string | undefined; selector?: string | undefined; playwrightFunction?: string | undefined; locator?: LocatorStrategyParams | undefined; }, { type: "setfieldvalue" | "click" | "navigate" | "expect" | "assertFieldValueEquals" | "assertFieldValueContains" | "assertElementExists" | "sleep"; label?: string | undefined; value?: string | undefined; description?: string | undefined; selector?: string | undefined; playwrightFunction?: string | undefined; locator?: LocatorStrategyParams | undefined; }>; type TestAction = z.infer<typeof testActionSchema>; type LocatorStrategies = Partial<{ selector: (page: Page, param: SelectorStrategyParams) => Promise<Locator>; role: (page: Page, param: RoleStrategyParams) => Promise<Locator>; testId: (page: Page, param: TestIdStrategyParams) => Promise<Locator>; text: (page: Page, param: TextStrategyParams) => Promise<Locator>; nested: (page: Page, param: NestedStrategyParams) => Promise<Locator>; } & Record<string, (page: Page, param: any) => Promise<Locator>>>; declare function resolveLocator<T extends LocatorStrategyParams["type"]>(locatorStrategies: LocatorStrategies, page: Page, strategy: Extract<LocatorStrategyParams, { type: T; }>): Promise<Locator>; declare const baseConfig: Configuration; type RuleKeys = 'input.datepicker' | 'select' | 'text'; type RuleType = (params: ConditionParams) => boolean; type SetterStrategyType = (param: StrategyParam) => Promise<void>; type GetterStrategyType = (param: StrategyParam) => Promise<string | null>; type ActionTypeHandler = (locator: Locator, action: TestAction) => Promise<void>; interface RuleMatch { id: string; xpath?: string; matchedChild: boolean; } interface ConditionParams { document: Document; element: Element; xpathEval: (xpath: string) => boolean; } interface StrategyParam { locator: Locator; ruleMatch: RuleMatch; value?: string; } /** * **usage** * * * the below config enables you to have the following functionalities in any json test: * - ```locator: {type: textWaitForDom}``` within any action (because of ```locatorStrategies``` property) * - ```clear``` action (because of ```actionTypeHandlers``` property) * - ```assertClear``` action (because of ```actionTypeHandlers``` property) * - ```setFieldValue``` on an input that's editable using our ```setterStrategies``` entry ```myCustomInput``` * - ```assertFieldValue``` on an input that's editable ```getterStrategies``` entry ```myCustomInput``` * * * playwright-json.config.ts example: * ``` * import { expect } from "@playwright/test"; * import { extendConfig } from "playwright-json-runner"; * import {getLocatorValue, setLocatorValue} from "playwright-json-runner" * * const userConfig = extendConfig({ * jsonTestDir: "json-tests", * locatorStrategies: { * textWaitForDom: async (page, strategy) => * { * page.waitForLoadState("domcontentloaded"); * return page.getByText(strategy.value) * } * }, * actionTypeHandlers:{ * "clear": async (locator)=>{ * setLocatorValue(locator, "") * }, * "assertClear": async (locator)=>{ * expect(getLocatorValue(locator)).toBe("") * } * }, * //define when it applies * rules: { * "myCustomInput": ({ xpathEval }) => xpathEval("//div[@contenteditable=true") * }, * //strategy for getting the value used when actionTypeHandlers call setLocatorValue * getterStrategies: { * "myCustomInput": async ({locator}) => await locator.inputValue() * }, * //strategy for setting the value used when actionTypeHandlers call getLoctorvalue * setterStrategies: { * "myCustomInput": async ({locator, value}) => await locator.fill(value??"") * } * * }); * * export default userConfig; * ``` * * then you can layout your playwright.json test like this: * * ``` * { * "driver": "Playwright", * "browser": "chrome", * "host": "https://www.github.com/", * "scenarios": [ * { * "name": "Signup Test", * "steps": [ * { * "description": "Navigate to create account", * "actions": [ * { * "type": "clear", * "selector":"[id='name']" * }, * { * "type": "assertclear", * "selector":"[id='name']" * }, * { * "type": "click", * "locator":{type: "textWaitForDom": value: "Check Box Label"} * }, * { * "type": "setFieldValue", * "selector": "//div[@id='blog-body' and contenteditable=true]" * "value": "some blog writing" * }, * { * "type": "assertFieldValueEquals", * "selector": "//div[@id='blog-body' and contenteditables=true]" * "value": "some blog writing" * } * ] * } * ] * } * ] * } * ``` */ declare function extendConfig(extensions: Partial<Configuration>): Configuration; /** * **usage** * * * the below config enables you to have the following functionalities in any json test: * - ```locator: {type: textWaitForDom}``` within any action (because of ```locatorStrategies``` property) * - ```clear``` action (because of ```actionTypeHandlers``` property) * - ```assertClear``` action (because of ```actionTypeHandlers``` property) * - ```setFieldValue``` on an input that's editable using our ```setterStrategies``` entry ```myCustomInput``` * - ```assertFieldValue``` on an input that's editable ```getterStrategies``` entry ```myCustomInput``` * * * playwright-json.config.ts example: * ``` * import { expect } from "@playwright/test"; * import { extendConfig } from "playwright-json-runner"; * import {getLocatorValue, setLocatorValue} from "playwright-json-runner" * * const userConfig = extendConfig({ * jsonTestDir: "json-tests", * locatorStrategies: { * textWaitForDom: async (page, strategy) => * { * page.waitForLoadState("domcontentloaded"); * return page.getByText(strategy.value) * } * }, * actionTypeHandlers:{ * "clear": async (locator)=>{ * setLocatorValue(locator, "") * }, * "assertClear": async (locator)=>{ * expect(getLocatorValue(locator)).toBe("") * } * }, * //define when it applies * rules: { * "myCustomInput": ({ xpathEval }) => xpathEval("//div[@contenteditable=true") * }, * //strategy for getting the value used when actionTypeHandlers call setLocatorValue * getterStrategies: { * "myCustomInput": async ({locator}) => await locator.inputValue() * }, * //strategy for setting the value used when actionTypeHandlers call getLoctorvalue * setterStrategies: { * "myCustomInput": async ({locator, value}) => await locator.fill(value??"") * } * * }); * * export default userConfig; * ``` * * then you can layout your playwright.json test like this: * * ``` * { * "driver": "Playwright", * "browser": "chrome", * "host": "https://www.github.com/", * "scenarios": [ * { * "name": "Signup Test", * "steps": [ * { * "description": "Navigate to create account", * "actions": [ * { * "type": "clear", * "selector":"[id='name']" * }, * { * "type": "assertclear", * "selector":"[id='name']" * }, * { * "type": "click", * "locator":{type: "textWaitForDom": value: "Check Box Label"} * }, * { * "type": "setFieldValue", * "selector": "//div[@id='blog-body' and contenteditable=true]" * "value": "some blog writing" * }, * { * "type": "assertFieldValueEquals", * "selector": "//div[@id='blog-body' and contenteditables=true]" * "value": "some blog writing" * } * ] * } * ] * } * ] * } * ``` */ interface Configuration<TActionType extends string = ActionType | string, TRuleKeys extends string = RuleKeys | string> { locatorStrategies: LocatorStrategies; actionTypeHandlers: Record<TActionType, ActionTypeHandler>; /** * **What**: `xpathEval` is a function that evaluates an XPath expression within a locator's context. * * **Why**: This allows users to dynamically resolve elements **relative to a known locator**, ensuring more flexible and adaptable test strategies. * * **How It Works**: * - The **locator** points to an element (e.g., a `div` with `id="name"`). * - `xpathEval` runs an **XPath query within that element's context**. * - xpathEval loads the locator's HTML upfront, then runs the XPath query against it. this allows lighting fast checking of the xpath query * * --- * * **📌 Example Scenario** * * **HTML Structure:** * ```html * <body> * <div id="name"> * <customInputTag></customInputTag> * </div> * </body> * ``` * * **JSON Configuration (Example Action for `setFieldValue`):** * ```json * { * "selector": "[id='name']", * "type": "setFieldValue", * "value": "first name" * } * ``` * * **📌 How `xpathEval` Works Here** * - The **locator** initially references the `div` (`id="name"`). * - `xpathEval("//customInputTag")` **runs inside the `div`'s context**, returning the `<customInputTag>` element. * - The `setFieldValue` action is **executed on `<customInputTag>`** instead of the `div` itself. * * --- * * **🚀 Why This Matters** * - Lets you **refine targeting** within an existing locator instead of writing full XPath queries. * - Avoids brittle full-document XPath lookups. * - Works well for **nested custom elements**. */ rules: Record<TRuleKeys, RuleType>; /** * **What**: strategies for **setting** the value * * **Why** this allows the user to implement custom ways of handling setting a value * * **When**: ```actionTypeHandlers``` call ```setLocatorValue``` and rule with the **key** returns true * * **Requirement**: must add an entry in rules for it to apply * * **IMPORTANT**: rules order matters! notice how the new rule (often most complex) is defined on top, because whatever rule matches first, is the one that will be used * * usage * * ``` * const userConfig = extendConfig({ * rules: { * "contentEditableDiv": ({ xpathEval }) => xpathEval("//div[@contenteditable=true]") * }, * setterStrategies: { * "myCustomInput": async ({ locator, value }) => { * await locator.fill(value ?? ""); * } * } * }); * export default userConfig * ``` */ setterStrategies: Record<TRuleKeys, SetterStrategyType>; /** * * **What**: strategies for **getting** the value * * **Why** this allows the user to implement custom ways of handling getting a value * * **When**: ```actionTypeHandlers``` call ```getLocatorValue``` and rule with the **key** returns true * * **Requirement**: must add an entry in rules for it to apply * * **IMPORTANT**: rules order matters! notice how the new rule (often most complex) is defined on top, because whatever rule matches first, is the one that will be used * usage * * ``` * * const userConfig = extendConfig({ * rules: { * "contentEditableDiv": ({ xpathEval }) => xpathEval("//div[@contenteditable=true]") * }, * setterStrategies: { * "contentEditableDiv": async ({locator}) => await locator.inputValue() * } * }); * export default userConfig * * ``` */ getterStrategies: Record<TRuleKeys, GetterStrategyType>; /** * Directory that will be recursively scanned for test files. Defaults to the directory of the configuration file. */ jsonTestDir: string; /** * Only the files matching one of these patterns are executed as test files. Matching is performed against the * absolute file path. Strings are treated as glob patterns. * * By default, the runner looks for files matching the following glob pattern: `**\/*.playwright.json` * This means Json files with `".playwright.json"` suffix, for example * `github.playwright.json`. */ jsonTestMatch: string | RegExp; } declare function getConfiguration(): Configuration; declare function loadConfiguration(): Configuration; declare function runTests(testRun: TestRun): Promise<void>; declare function executeAction(config: Configuration, page: Page, action: TestAction): Promise<void>; /** * Finds the best matching locator and sets its value. * @param locator Locator representing the field * @param value Value to be set. * @param waitSeconds Timeout in seconds (default: 30s). */ declare function setLocatorValue(locator: Locator, value: string | undefined): Promise<void>; /** * Finds the best matching locator and gets its value. * @param locator Locator representing the field * @param value Value to be set. * @param waitSeconds Timeout in seconds (default: 30s). */ declare function getLocatorValue(locator: Locator): Promise<string | null>; export { type ActionTypeHandler, type ConditionParams, type Configuration, type GetterStrategyType, type LocatorStrategies, type RuleKeys, type RuleMatch, type RuleType, type SetterStrategyType, type StrategyParam, type TestRun, baseConfig, executeAction, extendConfig, getConfiguration, getLocatorValue, loadConfiguration, resolveLocator, runTests, setLocatorValue, testRunSchema };