I want to check if an element is visible. If so, I want to click on it. If not, I want to check if another html element is available and click on it. How can I do this using playwright?
The html element in question has test id "create-btn"
It's a good idea to break your problem into distinct, easily searchable, steps:
page.getByTestId("create-btn")
)page.getByTestId("create-btn").click()
)As the docs state, clicking (and many other actions) auto-wait for actionability by default, which includes being visible in the viewport and not behind another element.
Here's a minimal example:
import {test} from "@playwright/test"; // ^1.42.1
const html = `<!DOCTYPE html><html><body>
<button style="visibility: hidden;" data-testid="create-btn">click me</button>
<script>
setTimeout(() => {
document.querySelector("button").style.visibility = "visible";
}, 4000);
</script>
</body></html>`;
test("create-btn can be clicked", async ({page}) => {
await page.setContent(html);
await page.getByTestId("create-btn").click();
});
If you want to click a different button if the first one never shows up, you can extend the code to do this with a try
/catch
:
const html = `<!DOCTYPE html><html><body>
<button style="visibility: hidden;" data-testid="menu-btn">click me</button>
<script>
setTimeout(() => {
document.querySelector("button").style.visibility = "visible";
}, 4000);
</script>
</body></html>`;
test("create-btn or menu-btn can be clicked", async ({page}) => {
await page.setContent(html);
try {
await page.getByTestId("create-btn").click({timeout: 5_000});
} catch (err) {
await page.getByTestId("menu-btn").click();
}
});
.or()
, the CSS comma operator (,
) and Promise.race
might also be useful if you want to click the first visible of multiple buttons:
test("create-btn or menu-btn can be clicked", async ({page}) => {
await page.setContent(html);
await page.getByTestId("create-btn")
.or(page.getByTestId("menu-btn")).click();
await page.locator(
'[data-testid="create-btn"],[data-testid="menu-btn"]'
).click();
await Promise.race([
page.getByTestId("create-btn").click(),
page.getByTestId("menu-btn").click(),
]);
});
If you don't want to auto-wait, you can use .isVisible()
to run an immediate check:
const createBtn = page.getByTestId("create-btn");
if (await createBtn.isVisible()) {
await createBtn.click();
}
const menuBtn = page.getByTestId("menu-btn");
if (await menuBtn.isVisible()) {
await menuBtn.click();
}
But conditions and nondeterminism aren't generally good practice in tests.