Skip to content

Locators

Use this guide when you want readable, stable element lookup patterns.

  • CSS: querySelector, querySelectorAll
  • Role: getByRole(role, options?)
  • Text: getByText(text, options?)
  • Label: getByLabel(label, options?)
  • Placeholder: getByPlaceholder(text, options?)
  • Alt text: getByAltText(text, options?)
  • Title: getByTitle(text, options?)
  • Data attribute: getByDataAttribute(name, value, options?)
  • XPath: getByXPath(xpath)
  • Frame chaining: getFrame(selector)
  • Raw frame handle: getFrameElement(selector)
await view.navigate("https://example.com/login");
await view.waitForDocumentReady({ state: "complete", timeoutMs: 15000 });
const username = await view.getByLabel("Username", { exact: true });
const password = await view.getByPlaceholder("Password", { exact: true });
const signIn = await view.getByRole("button", { name: "Sign in", exact: true });
if (!username || !password || !signIn) {
throw new Error("Missing login controls");
}
await view.click(username);
await view.type("demo-user", { perCharacterDelayMs: 0 });
await view.click(password);
await view.type("demo-password", { perCharacterDelayMs: 0 });
await view.click(signIn);
const releaseNote = await view.getByText("Release notes", { exact: false });
const logo = await view.getByAltText("Company logo", { exact: true });
const helpIcon = await view.getByTitle("Help", { exact: true });
const panel = await view.getByDataAttribute("testid", "settings-panel", { exact: true });
const saveByXPath = await view.getByXPath("//button[@id='save']");
if (!releaseNote || !logo || !helpIcon || !panel || !saveByXPath) {
throw new Error("Expected content not found");
}
const rows = await view.querySelectorAll("table#orders tbody tr");
for (const row of rows) {
const status = await view.getAttribute(row, "data-status");
if (status === "pending") {
await view.click(row);
}
await view.disposeElement(row);
}

getFrame() returns a chainable frame locator that supports the same locator family.

const frame = await view.getFrame("iframe#payment-frame");
if (!frame) throw new Error("payment frame not found");
const cardNumber = await frame.getByLabel("Card number", { exact: true });
const payNow = await frame.getByRole("button", { name: "Pay now", exact: true });
if (!cardNumber || !payNow) throw new Error("payment controls not found");
await view.click(cardNumber);
await view.type("4242 4242 4242 4242", { perCharacterDelayMs: 0 });
await view.click(payNow);

Use getFrameElement() when you specifically need an ElementHandle for low-level handle-scoped work.

Open shadow roots are traversed by the locator engine automatically.

const openMenu = await view.getByRole("button", { name: "Open menu", exact: true });
const shadowItem = await view.getByText("Shadow menu item", { exact: true });
if (!openMenu || !shadowItem) throw new Error("shadow controls not found");
await view.click(openMenu);
await view.click(shadowItem);
  • Use { exact: true } for strict, deterministic checks.
  • Use partial text matching (exact: false or omitted) for flexible content.
  • Prefer role and label locators for UI actions.
  • Use data attributes for test-only hooks.
  • Use XPath when CSS and semantic locators are awkward.