diff --git a/docs/src/api/class-locator.md b/docs/src/api/class-locator.md index e93d02d9d8090..38a3546e41b9f 100644 --- a/docs/src/api/class-locator.md +++ b/docs/src/api/class-locator.md @@ -1717,16 +1717,21 @@ var banana = await page.GetByRole(AriaRole.Listitem).Nth(2); Creates a locator matching all elements that match one or both of the two locators. -Note that when both locators match something, the resulting locator will have multiple matches and violate [locator strictness](../locators.md#strictness) guidelines. +Note that when both locators match something, the resulting locator will have multiple matches, potentially causing a [locator strictness](../locators.md#strictness) violation. **Usage** Consider a scenario where you'd like to click on a "New email" button, but sometimes a security settings dialog shows up instead. In this case, you can wait for either a "New email" button, or a dialog and act accordingly. +:::note +If both "New email" button and security dialog appear on screen, the "or" locator will match both of them, +possibly throwing the ["strict mode violation" error](../locators.md#strictness). In this case, you can use [`method: Locator.first`] to only match one of them. +::: + ```js const newEmail = page.getByRole('button', { name: 'New' }); const dialog = page.getByText('Confirm security settings'); -await expect(newEmail.or(dialog)).toBeVisible(); +await expect(newEmail.or(dialog).first()).toBeVisible(); if (await dialog.isVisible()) await page.getByRole('button', { name: 'Dismiss' }).click(); await newEmail.click(); @@ -1735,7 +1740,7 @@ await newEmail.click(); ```java Locator newEmail = page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("New")); Locator dialog = page.getByText("Confirm security settings"); -assertThat(newEmail.or(dialog)).isVisible(); +assertThat(newEmail.or(dialog).first()).isVisible(); if (dialog.isVisible()) page.getByRole(AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Dismiss")).click(); newEmail.click(); @@ -1744,7 +1749,7 @@ newEmail.click(); ```python async new_email = page.get_by_role("button", name="New") dialog = page.get_by_text("Confirm security settings") -await expect(new_email.or_(dialog)).to_be_visible() +await expect(new_email.or_(dialog).first).to_be_visible() if (await dialog.is_visible()): await page.get_by_role("button", name="Dismiss").click() await new_email.click() @@ -1753,7 +1758,7 @@ await new_email.click() ```python sync new_email = page.get_by_role("button", name="New") dialog = page.get_by_text("Confirm security settings") -expect(new_email.or_(dialog)).to_be_visible() +expect(new_email.or_(dialog).first).to_be_visible() if (dialog.is_visible()): page.get_by_role("button", name="Dismiss").click() new_email.click() @@ -1762,7 +1767,7 @@ new_email.click() ```csharp var newEmail = page.GetByRole(AriaRole.Button, new() { Name = "New" }); var dialog = page.GetByText("Confirm security settings"); -await Expect(newEmail.Or(dialog)).ToBeVisibleAsync(); +await Expect(newEmail.Or(dialog).First).ToBeVisibleAsync(); if (await dialog.IsVisibleAsync()) await page.GetByRole(AriaRole.Button, new() { Name = "Dismiss" }).ClickAsync(); await newEmail.ClickAsync(); diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index ba83204e7e596..28304331e9ec8 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -13853,18 +13853,22 @@ export interface Locator { /** * Creates a locator matching all elements that match one or both of the two locators. * - * Note that when both locators match something, the resulting locator will have multiple matches and violate - * [locator strictness](https://playwright.dev/docs/locators#strictness) guidelines. + * Note that when both locators match something, the resulting locator will have multiple matches, potentially causing + * a [locator strictness](https://playwright.dev/docs/locators#strictness) violation. * * **Usage** * * Consider a scenario where you'd like to click on a "New email" button, but sometimes a security settings dialog * shows up instead. In this case, you can wait for either a "New email" button, or a dialog and act accordingly. * + * **NOTE** If both "New email" button and security dialog appear on screen, the "or" locator will match both of them, + * possibly throwing the ["strict mode violation" error](https://playwright.dev/docs/locators#strictness). In this case, you can use + * [locator.first()](https://playwright.dev/docs/api/class-locator#locator-first) to only match one of them. + * * ```js * const newEmail = page.getByRole('button', { name: 'New' }); * const dialog = page.getByText('Confirm security settings'); - * await expect(newEmail.or(dialog)).toBeVisible(); + * await expect(newEmail.or(dialog).first()).toBeVisible(); * if (await dialog.isVisible()) * await page.getByRole('button', { name: 'Dismiss' }).click(); * await newEmail.click();