How do I locate an element that can appear in one of two forms in Playwright?
Written by Jatin Thacker, Automation Architect · Updated
This answer is one piece of Chapter 9: Locators I: What the User Sees.
- getByRole and the Accessible Name
- getByText, getByLabel, getByPlaceholder, getByTestId
- Strict Mode — One Locator, One Element
Some pages legitimately show one of two things: a banner for signed-in users or one for visitors, a result list or an empty-state message. Writing if logic before anything has loaded is a race. or() lets you wait for whichever turns up, then decide.
Playwright's documentation notes the catch. If both alternatives match something, the combined locator matches more than one element, and actions or single-element assertions can throw a strict mode violation. Its example adds .first().
The dashboard shows why .first() is not always enough. Both auth banners are always in the DOM, and the script hides one. Signed in, signedOut.or(signedIn) matches two elements, and .first() is the hidden signed-out banner, so toBeVisible() would fail. .visible() keeps only the banner that is shown, and it checks visibility each time the locator is used. The API reference lists it as added in v1.63; on an older version, filter({ visible: true }) does the same.
The test runs twice, once signed out and once with autologin=1. Each time it waits for exactly one visible banner, then uses isVisible() for the if. That is control flow, and the assertion has already waited. Each branch then asserts the text it expects.
import { test, expect } from '@playwright/test';
for (const [state, query] of [['signed out', ''], ['signed in', '&autologin=1']]) {
test(`dashboard banner when ${state}`, async ({ page }) => {
await page.goto(`/index.html?mock=static&seed=42${query}`);
const signedOut = page.getByText('You are browsing signed out');
const signedIn = page.getByText('Signed in. Revenue figures');
// Both banners are in the DOM; only one is ever shown
const banner = signedOut.or(signedIn).visible();
await expect(banner).toHaveCount(1);
if (await signedOut.isVisible()) {
await expect(banner).toContainText('Sign in to see revenue');
} else {
await expect(banner).toContainText('action queue are visible');
}
});
}2 passed (1.9s)
a.or(b) are visible on the page at the same time. What can happen when you click the combined locator?Practised in the full course: the drill Both banners are in the DOM.
See the chapterRelated questions
AI can write the test now. The job is knowing whether it checks anything. That's what the course teaches, one chapter at a time.
163 lessons · 226 drills · start free