This guide will help you understand how to write and run Playwright tests for the FlowInquiry frontend application.
Introduction to Playwright
We picked Playwright for FlowInquiry’s frontend tests because it just makes life easier. It works with Chromium, Firefox, and WebKit, so we can quickly check that everything
behaves the same no matter what browser you’re using. Its API is super intuitive, letting us mimic clicks, type stuff into forms, and navigate around the app just like a real
person would. That means we catch weird bugs sooner, our CI runs faster, and we can focus on building cool features instead of chasing down flaky tests
Project Setup
Installation
Playwright is already configured in the FlowInquiry frontend project. The main configuration files and directories are:
playwright.config.ts
home.spec.ts
home-page.ts
Configuration
The Playwright configuration is defined in apps/frontend/playwright.config.ts. This file specifies:
FlowInquiry uses the Page Object Model (POM) pattern for organizing test code. This pattern creates an abstraction layer between test code and page-specific details, making tests more maintainable.
The Page Object Model pattern separates the test logic from the page
implementation details, making tests more maintainable and easier to update
when the UI changes.
Directory Structure
Tests are organized as follows:
tests/ - Contains all test files (*.spec.ts)
tests/pages/ - Contains page object models
Creating a Page Object
Page objects should be created in the tests/pages/ directory. Here’s an example of a page object for the home page:
// tests/pages/home-page.tsimport { expect, Locator, Page } from "@playwright/test";/** * Page Object Model for the Home page * This class encapsulates the selectors and actions for the home page */export class HomePage { readonly page: Page; readonly emailInput: Locator; readonly passwordInput: Locator; readonly signInButton: Locator; readonly errorMessage: Locator; constructor(page: Page) { this.page = page; this.emailInput = page.getByLabel("Email"); this.passwordInput = page.getByLabel("Password"); this.signInButton = page.getByRole("button", { name: /sign in/i }); this.errorMessage = page.locator(".text-red-700"); } /** * Navigate to the home page */ async goto() { await this.page.goto("/"); } /** * Verify that the home page has loaded correctly */ async expectPageLoaded() { try { // In production, we should be redirected to dashboard await expect(this.page).toHaveURL("/portal/dashboard"); } catch (error) { // In test environment without proper auth setup, we might stay at login console.log( "[DEBUG_LOG] Not redirected to dashboard, checking if still on login page", ); await expect(this.page).toHaveURL("/login"); } } /** * Login with the provided credentials */ async login(email: string, password: string) { await this.emailInput.fill(email); await this.passwordInput.fill(password); await this.signInButton.click(); } // Additional methods...}
Writing Test Cases
Test cases should be created in the tests/ directory with the .spec.ts extension. Here’s an example of a test file:
// tests/home.spec.tsimport { test } from "@playwright/test";import { HomePage } from "./pages/home-page";test.describe("Home Page", () => { test("should navigate to home page and login successfully", async ({ page, }) => { const homePage = new HomePage(page); // Navigate to the home page await homePage.goto(); // Verify redirection to login page await homePage.expectRedirectToLogin(); // Login with admin credentials await homePage.login("admin@flowinquiry.io", "admin"); // Verify redirection to dashboard after login await homePage.expectPageLoaded(); }); test("should stay on login page when using incorrect credentials", async ({ page, }) => { const homePage = new HomePage(page); // Navigate to the home page await homePage.goto(); // Login with incorrect credentials await homePage.login("wrong@example.com", "wrongpassword"); // Verify error message is displayed await homePage.expectLoginError(); });});
Best Practices
Locator Strategies
Playwright provides several ways to locate elements. Here are the recommended strategies in order of preference:
Use Accessibility Locators
These are the most reliable and resilient to UI changes: