This is a complete project structure for frontend UI testing using Playwright, including best practices and common configurations.
├── core/ # Generic Logic Layer - No business logic
│ ├── BasePage.ts # Base page class, encapsulates Playwright core API
│ ├── helpers.ts # Generic utility functions
│ └── test-data-factory.ts # Generic test data factory
├── tests/ # Business Logic Layer - Contains business-related code
│ ├── pages/ # Page Layer - Page Object Model (POM)
│ │ ├── LoginPage.ts # Login page object model
│ │ ├── DashboardPage.ts # Dashboard page object model
│ │ └── HomePage.ts # Home page object model
│ ├── services/ # Business Layer - Business process encapsulation
│ │ ├── AuthService.ts # Authentication service
│ │ └── UserService.ts # User service
│ ├── dsl/ # DSL Layer - Custom test DSL
│ │ └── UserDSL.ts # User domain-specific language
│ ├── data/ # Test Data Layer
│ │ └── test-data.ts # Business-related test data
│ ├── config/ # Configuration Management Layer
│ │ ├── environment.ts # Environment configuration
│ │ ├── test-config.ts # Test configuration
│ │ └── index.ts # Configuration unified export
│ ├── fixtures/ # Test fixtures
│ │ └── auth.fixture.ts # Authentication-related fixtures
│ ├── api/ # API testing
│ │ └── api.spec.ts # API test examples
│ ├── examples/ # Test examples
│ │ ├── dsl-example.spec.ts # DSL test examples
│ │ └── data-driven-example.spec.ts # Data-driven test examples
│ ├── example.spec.ts # Basic test examples
│ └── auth.spec.ts # Authentication test examples
├── playwright.config.ts # Playwright configuration
├── package.json # Project dependencies
├── tsconfig.json # TypeScript configuration
├── .eslintrc.js # ESLint configuration
├── .prettierrc # Prettier configuration
├── .gitignore # Git ignore file
├── env.example # Environment variables example
└── README.md # Project documentation
- âś… Layered Architecture - Base layer, page layer, business layer, DSL layer
- âś… TypeScript Support - Complete type safety
- âś… Page Object Model - Page Object Model pattern
- âś… Business Process Encapsulation - Cross-page business operation encapsulation
- âś… Custom DSL - Business-oriented test methods
- âś… Data-Driven Testing - Test data factory and parameterized testing
- âś… Test Fixtures - Reusable test setup
- âś… Multi-Browser Support - Chrome, Firefox, Safari
- âś… Mobile Testing - Mobile device viewport testing
- âś… API Testing - HTTP request testing
- âś… Environment Configuration - Multi-environment support
- âś… Code Quality - ESLint + Prettier
- âś… Test Reporting - HTML, JSON, JUnit reports
- âś… MCP Server Integration - AI-powered test automation through Cursor IDE
This framework now includes a built-in MCP (Model Context Protocol) Server that enables AI-powered test automation through IDE integrations like Cursor.
MCP allows AI assistants to:
- 🤖 Execute tests using natural language commands
- 📊 Analyze results automatically
- đź§Ş Generate tests based on your requirements
- 🔍 Debug failures with intelligent assistance
-
Start MCP Server:
npm run mcp-server
-
Configure Cursor IDE: Add to
~/.cursor/mcp.json:{ "mcpServers": { "playwright-e2e": { "command": "node", "args": ["--loader", "ts-node/esm", "/path/to/your/project/core/mcp/server.ts"], "cwd": "/path/to/your/project", "env": { "BASE_URL": "http://localhost:3000" } } } } -
Use Natural Language:
"Run all E2E tests" "Run login tests in headed mode" "Show me the latest test results" "Generate a test for the dashboard page"
See the MCP integration in action:
Simply ask in natural language: "Run tests in headed mode" and the AI executes the command for you.
The tests run in the browser with full visibility of the test execution process.
- run_tests - Execute tests with various options (headed, debug, grep, etc.)
- generate_test - Generate test code using Playwright codegen
- view_report - View test reports in various formats
- debug_test - Launch tests in debug mode with Playwright Inspector
- e2e://test-results/latest - Latest test execution results (JSON)
- e2e://test-reports/summary - Human-readable test summary
- e2e://screenshots/list - List of failure screenshots
- MCP Server Guide - Complete setup and usage guide
- MCP vs Direct Commands - Why use MCP and comparison
- Example Project - Working example with MCP integration
Traditional Approach:
# You need to remember commands
npm test auth/login.spec.ts --headed --project=chromiumWith MCP:
"Run login tests in headed mode with Chrome"
→ AI understands and executes automatically
See MCP vs Direct Commands for detailed comparison.
npm installnpm run install:browserscp env.example .env
# Edit .env file to set your application URL# Run all tests
npm test
# Run tests with browser visible
npm run test:headed
# Run tests in UI mode
npm run test:ui
# Debug mode
npm run test:debug
# Generate test reports
npm run test:report| Command | Description |
|---|---|
npm test |
Run all tests |
npm run test:headed |
Run tests with browser visible |
npm run test:ui |
Run Playwright UI mode |
npm run test:debug |
Run tests in debug mode |
npm run test:report |
Show test reports |
npm run test:codegen |
Generate test code |
npm run install:browsers |
Install browsers |
Our test framework adopts a clear layered architecture designed for multi-team collaboration:
- BasePage.ts - Encapsulates Playwright core API, provides unified element operations, waiting, and validation methods
- helpers.ts - Generic utility functions with no business logic
- test-data-factory.ts - Generic test data factory for dynamic data generation
- Page Layer (pages/) - Page Object Model pattern, encapsulates page elements and operations
- Service Layer (services/) - Business process encapsulation, provides cross-page business operations
- DSL Layer (dsl/) - Custom domain-specific language, business-oriented test methods
- Data Layer (data/) - Business-related test data
- Config Layer (config/) - Test environment and test configuration management
For comprehensive information about using the core framework, see:
- Core Framework Guide - Complete guide to core framework usage, components, and best practices
For team-specific implementations and examples:
- TTAM Team Guide - TikTok Ads Management team's test automation guide
This framework is designed to support multiple teams using the same core functionality:
- Core layer is published as an npm package:
@your-org/playwright-test-core - Teams install the core framework as a dependency
- Core updates are automatically available to all teams
Each team maintains their own test project with:
- Team-specific page objects
- Team-specific business services
- Team-specific test data
- Team-specific configuration
- Team-specific test specifications
- Shared Core: All teams benefit from core framework improvements
- Independent Development: Teams can work independently on their business logic
- Consistent Standards: All teams follow the same patterns and conventions
- Easy Updates: Core framework updates are automatically available
- Reduced Duplication: Common functionality is shared across teams
import { test, expect } from '@playwright/test';
test('should display correct title', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveTitle(/My App/);
});import { test, expect } from '@playwright/test';
import { LoginPage } from './pages/LoginPage';
test('should login successfully', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.navigateToLoginPage();
await loginPage.login('testuser', 'password123');
await loginPage.verifyLoginSuccess();
});import { test, expect } from '@playwright/test';
import { AuthService } from './services/AuthService';
test('should login as admin', async ({ page }) => {
const authService = new AuthService(page);
await authService.loginAsAdmin();
await authService.verifyUserLoggedIn('admin');
});import { test, expect } from '@playwright/test';
import { UserDSL } from './dsl/UserDSL';
test('User login and update profile', async ({ page }) => {
const user = new UserDSL(page);
await user.loginAs('testuser');
await user.updateProfile({
firstName: 'John',
lastName: 'Doe',
email: 'johndoe@example.com'
});
await user.verifyProfile({
firstName: 'John',
lastName: 'Doe',
email: 'johndoe@example.com'
});
});import { test, expect } from '@playwright/test';
import { UserDSL } from './dsl/UserDSL';
import { TestDataFactory } from '../../core/test-data-factory';
test('Data-driven user registration test', async ({ page }) => {
const user = new UserDSL(page);
const registrationData = TestDataFactory.createRegistrationFormData({
username: 'newuser',
email: 'newuser@example.com'
});
await user.registerAndLogin(registrationData);
await user.verifyProfile({
firstName: registrationData.firstName,
lastName: registrationData.lastName,
email: registrationData.email
});
});import { test, expect } from './fixtures/auth.fixture';
test('should access protected page', async ({ authenticatedPage }) => {
await authenticatedPage.goto('/profile');
await expect(authenticatedPage).toHaveURL('/profile');
});- Multi-browser Support: Chrome, Firefox, Safari
- Mobile Testing: Pixel 5, iPhone 12
- Parallel Execution: Support for parallel testing
- Retry Mechanism: Automatic retry in CI environment
- Report Generation: HTML, JSON, JUnit formats
Support for multiple environments:
development- Development environmentstaging- Staging environmentproduction- Production environment
- BasePage.ts - Encapsulate Playwright core API, provide unified element operations, waiting, and validation methods
- helpers.ts - Provide generic utility functions with no business logic
- test-data-factory.ts - Provide generic data generation capabilities for various data types
- Page Layer (pages/) - Use Page Object Model to encapsulate page elements and operations in classes
- Service Layer (services/) - Encapsulate cross-page business processes, provide high-level business operations
- DSL Layer (dsl/) - Create business-oriented test methods to make test code more readable
- Data Layer (data/) - Include business-related test data
- Config Layer (config/) - Manage test environment configuration and test configuration parameters
- Version Control: Use semantic versioning for core framework releases
- Backward Compatibility: Maintain backward compatibility when possible
- Migration Guides: Provide clear migration guides for breaking changes
- Testing: Thoroughly test core framework changes before release
- Naming Conventions: Use team prefixes for page objects, services, and DSL
- Module Organization: Group related functionality in subdirectories
- Configuration: Each team maintains their own environment configuration
- Documentation: Each team maintains their own README and documentation
- Unified Locator Encapsulation - Use semantic getter methods, avoid hardcoded selectors
- Test Fixtures - Use fixtures to set up test environment, reduce duplicate code
- Data-Driven - Use test data factory to separate test data from test logic
- Wait Strategy - Use smart wait mechanisms, avoid hardcoded wait times
- Error Handling - Add appropriate error handling and retry mechanisms
- Test Isolation - Ensure tests are independent of each other
- Business Language - Use test method names and comments that are close to business language
A: Add new browser configuration in the projects array in playwright.config.ts.
A: Use environment variables NODE_ENV and BASE_URL to configure different environments.
A: Run npm run test:codegen to start the Playwright code generator.
- Fork the project
- Create a feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request
MIT License

