The strategy table
Every builder offers the same ten locator methods, and every one of them must check, copy, patch and resolve a definition the same way for a given strategy. The strategy table is where those per-strategy facts live, so that one rule is written once. Which methods exist and what they take is on Registering locators.
One row per strategy
Section titled “One row per strategy”POMWright’s ten locator methods differ in a few facts: the Playwright method, the name of the first argument, whether
there are options, whether an empty string is a value Playwright matches. Those facts live in one table, STRATEGIES
in src/locators/utils.ts, one row per strategy. The row of getByRole:
role: { method: "getByRole", primary: "role", options: true, allowEmpty: false, create: (target, definition) => target.getByRole(definition.role, definition.options),},The fields:
| row field | what it says | example, role |
|---|---|---|
method |
the method name on every builder and in messages | getByRole |
primary |
the definition field of the first argument | role |
options |
whether the strategy takes Playwright’s options object | true |
allowEmpty |
whether "" is a value Playwright matches, so it is stored |
false |
optionalPrimary |
whether the first argument may be absent: only frameLocator, whose absence is the any-frame form |
|
create |
builds the element locator on a page, a locator or a frame | target.getByRole(role, options) |
Who reads it
Section titled “Who reads it”Every per-strategy rule reads the table; there is no switch on the strategy elsewhere:
assertStrategyPrimarychecks a first argument at every builder: present, a string (or a RegExp where Playwright takes one), and not empty unlessallowEmpty. A value Playwright cannot take,null, a number or an object, gets the missing-value message, so the message is the same on every builder.createLocatorresolves a segment with the row’screate.cloneLocatorStrategyDefinitionandassembleDefinitioncopy and build definitions field by field.applyDefinitionPatch(a seeded override),mergeLocatorDefinition(update) andbuildReplacementDefinition(replace) read which field is the first argument and whether options merge.- Messages name the method:
use replace("…").getByText(…), the list of locator methods in the step-before-strategy message.
Why a table
Section titled “Why a table”Before the table there were five switch statements over the strategies, in utils.ts and locatorUpdateBuilder.ts,
and a rule, such as which strategies keep an empty string, had to be kept in agreement across all of them. In the table
it is one cell. The types follow it: satisfies makes the compiler require a row for every definition type, and
StrategyMethod, the union of the method column, is the type the builders’ method sets are checked against.
Adding a strategy
Section titled “Adding a strategy”- A definition type in
src/locators/types.ts, added toLocatorStrategyDefinitionandLocatorDefinitionByType. - Its row in
STRATEGIES. - Its method on each builder: the registration, the update builder and
createReusable.
The type tests of the registration builder, the update builder and createReusable compare each builder’s methods
with StrategyMethod, so they fail until the method exists on every builder, and the unit tests of the table pin every
row.