Skip to main content
Logo, a drawing of a warm coffee vgagaleski

Back to all blogs

A complete guide to organizing your Playwright project

Published on by Viktor Gagaleski · 9 min read

As teams adopt test automation, we often focus on writing tests, but overlook how to structure them for long-term maintainability. Just as we wouldn’t build a house without proper architectural plans, we shouldn’t build our test automation without a solid foundation. Whether you’re a developer bringing your code organization expertise to testing, or an SDET looking for ideas on how to better structure your test project, this guide will provide several approaches and ideas on creating maintainable and scalable Playwright test framework.

Defining the criteria

Before diving into specific structures, let’s establish the key criteria for a good Playwright project:

  1. Maintainability - How easy is it to understand, modify, and extend the test code over time? This focuses on the long-term health of the codebase. A well-maintained project is easy to adapt to changing requirements and fix bugs.

  2. Scalability - Can the project handle a growing number of tests and features without significant performance degradation or management overhead? This is about how well the project adapts to increased size and complexity.

  3. Collaboration - Does the structure allow for multiple developers and teams to work on the project efficiently? A well-defined structure minimizes conflicts and promotes collaboration.

  4. Reusability - How easily can common test components and utilities be reused across different tests to avoid code duplication? This focuses on efficiency through shared resources.

By clarifying these distinctions with concrete examples, you should now have a better understanding of how each criterion contributes to the overall quality of your Playwright project. This will help you make informed decisions about your project structure and ensure its long-term success.

It will be hard to optimize for all criteria at the same time, but with good understanding and analysis you should be able to start small and evolve depending on the project requirements. In the following text I will go over different structures, from basic to more complex, and I will explain the pros and cons in detail.

Basic project structure

When you initialize a new Playwright project, you’ll typically get a structure similar to this:

playwright/
|-- tests/                 // Your test files
|   `-- example.test.ts    // Example test
|-- pages/                 // Page Objects (or functions)
|   `-- LoginPage.ts
|-- fixtures/              // Test fixtures
|   `-- auth.ts
|-- api/                   // API Tests or helpers
|   `-- user.ts
|-- playwright.config.ts   // Playwright configuration
|-- package.json           // Project dependencies
|-- .gitignore
|-- .npmignore
`-- README.md

This is a good starting point, playwright.config.ts holds the project configuration, package.json manages dependencies, and the tests directory contains your test files. There isn’t deep nesting, but rather flat structure.

PROSCONS
Simple and easy to understand for beginners.As the project grows, managing a large number of files within these folders can become challenging.
One layer of separation, tests, page objects, fixtures, and API interactions, improves code organization.Doesn’t inherently represent the application’s features or the teams responsible for them.
Suitable for smaller projects or teams just getting started with Playwright.May lead to duplication of code, especially for shared components or functionalities if not carefully managed.

Advanced project structures

The ideal project structure depends heavily on your team’s size, organization, and the complexity of the application under test. The provided examples are structures of something that I consider general for two different approaches.

Team-based structure

Organizes tests based on team ownership, with each team maintaining their own test files, page objects, and utilities. This approach works well for organizations where multiple teams work on different parts of the application and clear ownership boundaries exist.

For example, for an eCommerce website, let’s say Team Alpha, Shopping Cart team, owns all tests related to cart functionality, including adding/removing items and updating quantities, while Team Beta, Checkout team, would separately maintain tests for payment processing, shipping address forms, and order confirmation pages. Each team can work independently on their own test suite, reflecting team-specific development priorities and release schedules.

In a team-based structure, end-to-end flows that cross team boundaries, like completing a purchase from cart to checkout, would typically require coordination between teams. Either a dedicated QA team might own these cross-functional E2E tests, or there would be a shared test suite jointly maintained by both Team Alpha (Cart) and Team Beta (Checkout) with clear ownership boundaries.

playwright/
├── tests/
│   ├── team-alpha/
│   │   ├── tests/
│   │   │   └── cart.test.ts
│   │   ├── pages/
│   │   │   └── CartPage.ts
│   │   ├── components/
│   │   │   └── Table.ts
│   │   ├── fixtures/
│   │   │   └── cartFixture.ts
│   │   └── data/
│   │       └── user_data.ts
│   ├── team-beta/                          // Similar structure
│   │   └── ...
│   ├── user-journey/                       // E2E flows (user journeys)
│   │       └── add-prod-checkout.test.ts
│   └── utils/                              // Shared utilities
│       └── test_utils.ts
│   └── api/                                // API Helpers
│       └── api_helper.ts
├── ...
PROSCONS
Clear ownership, each team has its dedicated directory, promoting ownership and accountability.Cross-team dependencies, if Team Alpha needs a component from Team Beta, they need to access Team Beta’s directory. This can create dependencies and potential conflicts.
Facilitates collaboration within individual teams as all related code is together.Careful coordination is needed to avoid naming collisions and ensure consistency across teams.
Handles growth by adding more team-specific directories.

Feature-based structure

Organizes tests around functional features of the application rather than teams, with all tests related to a particular feature grouped together regardless of which team implements it. This structure requires clear feature boundaries and works better when multiple teams might contribute to the same feature area.

For example, a “search” feature would include tests for the search bar, search results page, filters, and sorting functionality, even if these components are developed by different teams. Team Alpha might own the search UI implementation while Team Beta owns the product catalog that appears in search results, but all search-related UI tests would still live in a single “search” directory for cohesive testing and maintenance.

In a feature-based structure, E2E flows that cross multiple features would typically be organized in their own dedicated directory structure, perhaps labeled “end-to-end-flows” or “user-journeys.” For example, a complete purchase process that starts with the homepage, search, product details, cart, and checkout features would be maintained as a distinct test suite that focuses on integration between these features. In Playwright, you can easily add test annotations and tags that will clearly point to the owners of a test.

playwright/
├── tests/
│   ├── search/
│   │   ├── tests/
│   │   |   ├── search.test.ts
│   │   │   └── search-results.test.ts
│   │   ├── pages/
│   │   │   └── SearchPage.ts
│   │   ├── components/
│   │   │   ├── SearchBox.ts
│   │   │   └── SearchResultsTable.ts
│   │   ├── fixtures/
│   │   │   └── search.ts
│   │   └── data/
│   │       └── user_data.ts
│   ├── product-catalog/                    // Similar structure
│   │   └── ...
│   │
│   ├── user-journey/                       // E2E flows (user journeys)
│   │       └── add-prod-checkout.test.ts
│   └── utils/                              // Shared utilities
│       └── test_utils.ts
│   └── api/                                // API Helpers
│       └── api_helper.ts
├── ...
PROSCONS
Tests are grouped by feature, reflecting the application’s functionality.Cross-feature usage, if a feature is used across multiple pages, it can be challenging to determine ownership and avoid duplication.
Easy to find tests related to a specific feature.Requires careful planning to identify features and their boundaries.
Handles growth by adding more feature-specific directories.

Monorepo structure

Monorepos (monolithic repositories) are increasingly popular for managing complex applications with multiple interconnected services and components. The monorepo is a great place for your Playwright code as well. Having the test code directly adjacent to the frontend code it is testing, dramatically reduces maintenance burden and creates faster feedback loops.

I would definitelly encourage SDETs and architects to always prioritize co-locating tests with implementation code to enable tests to run automatically with each PR, providing immediate validation before code merges. This proximity eliminates cross-repository dependencies, accelerates development cycles, and ensures testing happens early in the development process when fixes are least expensive. Within a monorepo, you can still organize using team-based or feature-based approaches while sharing utilities and configurations across the organization.

monorepo/
├── packages/
│   ├── team-alpha-ui/
│   │   ├── src/
│   │   │   ├── components/
│   │   │   └── pages/
│   │   └── tests/
│   │       ├── e2e/
│   │       │   └── search.test.ts
│   │       ├── pages/
│   │       │   └── SearchPage.ts
│   │       ├── component/
│   │       │   └── SearchBar.ts
│   │       └── utils/
│   │           └── test-helpers.ts
│   ├── team-beta-ui/                # Similar structure
│   │   └── ...
│   ├── shared-ui/
│   │   ├── src/
│   │   │   └── components/
│   │   └── tests/
│   │       └── component/
│   │           └── SharedButton.ts
│   └── api-services/
│       ├── schema/
│       └── generated/
├── playwright.config.ts             # Root config with project-specific overrides
├── package.json
└── .github/
    └── workflows/
        └── playwright.yml           # GitHub Actions workflow for Playwright
PROSCONS
Single source of truth - All code and tests exist in one repository, eliminating version mismatchesInitial setup complexity - Creating the right structure and configuration requires significant upfront planning
Simplified dependency management - Shared dependencies are managed in one placeBuild time - As the monorepo grows, build and test times can increase without proper optimization
Shared tooling - Test utilities, CI configs, and linting rules can be standardizedPermissions management - More challenging to restrict access to specific parts of the codebase
Atomic commits - Changes to both application and test code can be committed togetherLearning curve - Developers need to understand the entire repository structure
Easier refactoring - Changes across package boundaries are tracked in a single PRBuild tool requirements - Often requires specialized build tools like Nx
Improved discoverability - Tests for all packages are in one place, making it easier to find existing testsCI pipeline complexity - Requires more sophisticated CI setup to avoid running all tests for every change
Better collaboration - Teams can more easily collaborate on cross-package features

Conclusion

Organizing your Playwright project effectively is a critical but often overlooked aspect of test automation. A well-structured project leads to better maintainability, collaboration, and scalability. The right structure depends on your team’s size, organization, and the application’s complexity.

Start with a simple structure for smaller projects and evolve as needed. For larger teams or complex applications, consider team-based or feature-based structures. If you’re working within a monorepo, co-locate the tests with implementation code as the developers are used to working with that repository.

Remember that no structure is perfect, and what works for one team may not work for another. Be prepared to adapt your structure as your project evolves. Regularly refactor and improve your test organization to keep your automation suite healthy and effective.

  1. Prioritize maintainability and readability in your test structure
  2. Organize tests in a way that matches your team’s workflow
  3. Create clear boundaries and ownership for test code
  4. Leverage shared components and utilities to reduce duplication
  5. Be flexible and willing to evolve your structure as needed
  6. Communicate openly, make sure to have forums where multiple teams can sit together and discuss.

By following these guidelines, you’ll build a Playwright test framework that not only works effectively today but can grow with your project over time.