A robust end-to-end testing framework built with Playwright and Cucumber BDD, designed for scalable, maintainable, and efficient test automation.
This framework combines the power of Playwright's modern browser automation capabilities with Cucumber's Behavior-Driven Development approach, enabling both technical and non-technical team members to collaborate effectively on test automation.
- Hybrid Testing: Combined UI and API testing in a single framework
- BDD Approach: Uses Cucumber for behavior-driven development
- Page Object Model: Clean separation of test logic and UI interactions
- Factory Patterns: Efficient resource management for both UI and API components
- Multi-Browser Support: Tests run on Chrome, Firefox, and Safari
- Test Hooks: Flexible before/after hooks for setup and teardown operations
- Structured Logging: Configurable logging levels with detailed insights
- Comprehensive Reporting: Built-in HTML reporting with screenshots and traces
- Allure Reporting: Enhanced reporting with detailed visualizations and analytics
- Test Organization: Clear separation between UI and API tests
- Node.js (v14 or higher)
- npm (v6 or higher)
# Clone the repository
git clone https://github.com/your-org/playwright-bdd-hybrid-framework.git
# Install dependencies
npm install
Create a .env
file in the root directory with the following structure:
# User Credentials for Test Authentication
TEST_USERNAME=tomsmith
TEST_PASSWORD=SuperSecretPassword!
# Test Environment Configuration
TEST_ENVIRONMENT=staging
TEST_TYPE=UI
# Logging Configuration
LOG_LEVEL=debug
# Execution Settings
HEADLESS=false
DEFAULT_TIMEOUT=30000
# Reporting Settings
SCREENSHOT_ON_FAILURE=true
VIDEO_RECORDING=false
# Run all tests in headless mode
npm test
# Run tests with visible browser
npm run test:headed
# Run tests in UI mode for interactive debugging
npm run test:ui
# Run tests in debug mode with detailed logging
npm run test:debug
# Run UI tests in specific browsers
npm run test:ui-chrome
npm run test:ui-firefox
npm run test:ui-safari
# Run UI tests in all browsers
npm run test:ui-all
# Run API-only tests
npm run test:api
# Clean Allure results and run tests with fresh reporting
npm run test:clean-run
# Run tests with custom log level
LOG_LEVEL=debug npm run test:api
LOG_LEVEL=warn npm run test:ui-chrome
The framework is built with a layered architecture that separates concerns and promotes reusability:
Feature files written in Gherkin syntax describe application behavior from a user's perspective.
Feature: Sample Login
Scenario: Successful Login
Given I am on the login page
When I enter valid credentials
Then I should be redirected to the dashboard
Step definition files connect Gherkin steps to actual implementations using:
- Page objects for UI interactions
- API clients for backend calls
- Custom assertions for verification
Page objects provide a clean abstraction over UI elements and interactions:
- BasePage: Common utilities and methods
- Specialized Pages: Specific page interactions
- PageFactory: Efficient page object management
API clients handle backend service calls:
- BaseApiClient: Core HTTP functionality
- Specialized Clients: Domain-specific API operations
- ServiceFactory: Centralized client management
Cross-cutting concerns that support the entire framework:
- Logger: Structured logging system
- Configuration: Environment-based configuration
├── features/ # Cucumber feature files
│ └── login.feature # Login functionality scenarios
├── src/ # Source code
│ ├── pages/ # Page Object Model classes
│ │ ├── BasePage.ts # Base page with common utilities
│ │ ├── DashboardPage.ts # Dashboard/secure area page interactions
│ │ ├── LoginPage.ts # Login page interactions
│ │ └── PageFactory.ts # Factory for managing page objects
│ ├── services/ # API Services
│ │ ├── api/ # API Clients
│ │ │ ├── AuthApiClient.ts # Authentication API methods
│ │ │ ├── BaseApiClient.ts # Base API client with HTTP methods
│ │ │ └── ServiceFactory.ts # Factory for managing API clients
│ │ └── models/ # API Data Models
│ │ └── ApiModels.ts # Interface definitions for API requests/responses
│ ├── utils/ # Utilities and Helpers
│ │ ├── hooks.ts # Test lifecycle hooks for setup/teardown
│ │ └── logger.ts # Configurable logging utility
│ └── steps/ # Step definitions
│ └── sampleSteps.ts # Implementation of test steps
├── test-results/ # Test execution artifacts
├── playwright-report/ # HTML test reports
├── allure-results/ # Allure test results data
├── allure-report/ # Generated Allure reports
├── package.json # Project dependencies and scripts
├── playwright.config.ts # Playwright configuration
└── tsconfig.json # TypeScript configuration
The Page Object Model architecture separates UI interactions from test logic:
Abstract base class with common utilities for all pages:
// Page loading utilities
async waitForPageLoad() {
log.debug('Waiting for page to reach networkidle state');
await this.page.waitForLoadState('networkidle');
log.debug('Page load completed');
}
// Verification methods
async verifyPageTitle(title: string) {
log.debug(`Verifying page title matches "${title}"`);
await expect(this.page).toHaveTitle(new RegExp(title));
log.debug('Page title verified successfully');
}
Manages page object creation and lifecycle:
public getLoginPage(): LoginPage {
if (!this.pageInstances.has('loginPage')) {
log.debug('Creating new LoginPage instance');
this.pageInstances.set('loginPage', new LoginPage(this.page));
} else {
log.debug('Returning cached LoginPage instance');
}
return this.pageInstances.get('loginPage');
}
The API testing architecture provides a clean, modular approach to service interaction:
Foundation class for all API operations:
async post(url: string, options?: any): Promise<APIResponse> {
if (!this.apiContext) {
log.debug('API context not initialized for POST request, initializing now');
await this.init();
}
log.debug(`POST request to ${url}`, options);
const response = await this.apiContext.post(url, options);
log.debug(`POST response from ${url}: status ${response.status()}`);
return response;
}
Manages API client instances:
public async getAuthApiClient(): Promise<AuthApiClient> {
if (!this.serviceInstances.has('authApiClient')) {
log.debug('Creating new AuthApiClient instance');
const client = new AuthApiClient();
await client.init();
this.serviceInstances.set('authApiClient', client);
log.info('AuthApiClient instance created and initialized');
}
return this.serviceInstances.get('authApiClient');
}
The framework includes a robust logging system for detailed test execution insights:
export enum LogLevel {
DEBUG = 1, // Most verbose - detailed debug information
INFO = 2, // General information about test progress
WARN = 3, // Warnings that don't fail tests but require attention
ERROR = 4, // Error conditions that affect test execution
NONE = 5 // No logging
}
The logging level can be configured in multiple ways:
- Environment Variable: Set
LOG_LEVEL
in your environment - Command Line: Override for specific runs (
LOG_LEVEL=debug npm run test:api
) - .env File: Default setting in your project's
.env
file - Configuration: Handled in
playwright.config.ts
with fallback to 'info'
The framework includes built-in HTML reporting for test results:
# Generate and view HTML report
npm run report
Allure provides enhanced reporting capabilities with rich visualizations:
# Clean previous Allure results
npm run allure:clean
# Generate Allure report from test results
npm run allure:generate
# Open Allure report in browser
npm run allure:open
# Generate and open Allure report (combined command)
npm run allure:report
# Clean results, run tests, and generate report in one command
npm run test:clean-run
The Allure reporting system offers several advantages:
- Interactive Dashboard: Overview of test execution with pass/fail statistics
- Detailed Test Cases: Step-by-step test execution with screenshots and traces
- Timeline View: Chronological representation of test execution
- Categorized Tests: Tests organized by UI and API categories
- Categorized Failures: Group failures by type for easier troubleshooting
- Environment Details: Capture test environment information
- Attachments: View screenshots, videos, and logs directly in the report
- BDD Integration: Cucumber steps are properly displayed in the report hierarchy
The framework uses a simplified binary approach to test categorization:
- UI Tests: Execute browser-based interactions
- API Tests: Execute HTTP requests without browser UI
This categorization is controlled by the TEST_TYPE
environment variable, which can be:
UI
: For browser-based testsAPI
: For API-only tests
The project configuration in playwright.config.ts
defines separate projects for UI tests with different browsers and API tests, making it easy to run them separately or together.
This framework is designed to work seamlessly with CI/CD pipelines, supporting:
- GitHub Actions
- Jenkins
- Azure DevOps
- CircleCI
- Travis CI
The framework can be easily extended with:
- Visual regression testing
- Performance testing
- Accessibility testing
- Data-driven testing
The framework includes a powerful test hooks implementation for flexible test lifecycle management:
// Hooks for test setup and teardown
beforeFeature(async (feature) => {
log.info(`Starting feature: ${feature.name}`);
// Setup operations before each feature
});
afterFeature(async (feature) => {
log.info(`Completed feature: ${feature.name}`);
// Cleanup operations after each feature
});
beforeScenario(async (scenario) => {
log.info(`Starting scenario: ${scenario.name}`);
// Setup operations before each scenario
});
afterScenario(async (scenario) => {
log.info(`Completed scenario: ${scenario.name} with status: ${scenario.result}`);
// Cleanup operations after each scenario, handle different outcomes
});