| name | playwright |
| description | Playwright E2E testing with Page Object pattern, web-first assertions, and proper locators. Triggers on playwright, e2e, page object, getByRole. |
| triggers | playwright, e2e, page object, getByRole, getByLabel, getByTestId |
MCPSearch({ query: "select:mcp__plugin_devtools_context7__query-docs" })
// Locator patterns
mcp__context7__query_docs({
libraryId: "/microsoft/playwright",
query: "How do I use getByRole, getByLabel, getByTestId, and locator?",
});
// Assertions
mcp__context7__query_docs({
libraryId: "/microsoft/playwright",
query: "How do I use expect with toBeVisible, toHaveText, and toContainText?",
});
// Page interactions
mcp__context7__query_docs({
libraryId: "/microsoft/playwright",
query: "How do I use click, fill, check, and waitFor?",
});
Note: Context7 v2 uses server-side filtering. Use descriptive natural language queries.
// pages/login.page.ts
class LoginPage extends BasePage {
async login(email: string, password: string) {
await this.page.getByLabel("Email").fill(email);
await this.page.getByLabel("Password").fill(password);
await this.page.getByRole("button", { name: "Sign in" }).click();
}
async verifyLoggedIn() {
await expect(
this.page.getByRole("heading", { name: "Dashboard" }),
).toBeVisible();
}
}
Test structure:
import { test, expect } from "@playwright/test";
import { Pages } from "../pages/pages";
test.describe.serial("Login flow", () => {
let pages: ReturnType<typeof Pages>;
test.beforeEach(async ({ page }) => {
pages = Pages(page);
});
test("should login successfully", async () => {
await pages.loginPage.goto();
await pages.loginPage.login("user@example.com", "password");
await pages.loginPage.verifyLoggedIn();
});
});
getByRole()- Accessible namegetByLabel()- Form labelsgetByText()- Visible textgetByTestId()- Test IDs- CSS selectors - Last resort
// ✅ Preferred
page.getByRole("button", { name: "Submit" });
page.getByLabel("Email");
page.getByText("Welcome");
// ⚠️ Use when needed
page.getByTestId("submit-button");
// ❌ Avoid
page.locator(".btn-primary");
page.locator("#submit");
await expect(locator).toBeVisible();
await expect(locator).toHaveText("expected");
await expect(locator).toContainText("partial");
await expect(locator).toHaveValue("value");
await expect(locator).toBeEnabled();
await expect(locator).toBeChecked();
await expect(page).toHaveURL(/pattern/);
await expect(page).toHaveTitle("Title");
Required:
- Page Objects extend
BasePage test.describe.serial()for related tests- Web-first assertions (auto-wait/retry)
- Extended timeouts for slow operations
Locator Priority: getByRole() → getByLabel() → getByText() → getByTestId() → CSS
- Context7 docs fetched for current API
- Page objects encapsulate selectors
- Web-first assertions used
- Tests are isolated
- Proper locator strategy