satya-qa-playwright-framework
v1.2.0
Published
A production-ready npm initializer CLI tool that scaffolds a hybrid UI + API test automation project using Playwright and TypeScript. v1.2.0 includes advanced wait strategies, retry patterns, enhanced error handling, and environment variable configuration
Downloads
31
Maintainers
Readme
satya-qa-playwright-framework
A production-ready npm initializer CLI tool that scaffolds a hybrid UI + API test automation project using Playwright and TypeScript.
Features
Core Framework (v1.0.0)
- Hybrid Testing: Unified framework for both UI and API test automation
- Page Object Model: BasePage class with reusable UI interaction helpers
- Three-Tier API Architecture: BaseApi → BaseClient → ServiceClient hierarchy
- Environment Switching: Switch between dev/uat/prod via TEST_ENV variable
- Credential Management: Centralized user credentials keyed by environment and role
- Allure Reporting: Auto-configured with rich metadata and screenshots
- TypeScript: Fully typed with strict configuration
- Fixtures & Hooks: Automatic dependency injection and lifecycle management
- Artifact Cleanup: Automatic cleanup of stale test results before each run
Enhanced Reliability (v1.2.0)
- Advanced Wait Strategies: Custom condition waits, API response waits, business logic waits, and element stability waits
- Retry Patterns: Exponential backoff, custom retry conditions, circuit breaker pattern, and retry statistics
- Enhanced Error Handling: Rich error context with automatic capture of page state, network requests, and recovery suggestions
- Environment Variable Configuration: Override any configuration value via environment variables for flexible CI/CD integration
- Backward Compatibility: 100% compatible with v1.0.0 - all existing tests work unchanged
Installation
Create a new test automation project:
npx satya-qa-playwright-framework <project-name>Replace <project-name> with your desired project directory name.
Usage
1. Scaffold a New Project
npx satya-qa-playwright-framework my-test-projectThis creates a new directory my-test-project/ with the complete framework structure.
2. Install Dependencies
Navigate into your project and run the setup script:
cd my-test-project
node setup.jsThe setup script will:
- Install all npm dependencies from package.json
- Install Playwright browsers via
npx playwright install
3. Run Tests
After setup completes, you can run tests:
# Run all tests (UI + API)
npm test
# Run only UI tests
npm run test:ui
# Run only API tests
npm run test:api
# Generate Allure report
npm run report
# Open Allure report in browser
npm run report:open4. Switch Environments
Use the TEST_ENV environment variable to switch between environments:
# Run tests against UAT environment
TEST_ENV=uat npm run test:ui
# Run tests against production environment
TEST_ENV=prod npm run test:apiSupported environments: dev (default), uat, prod
5. Environment Variable Configuration (v1.2.0)
Override any configuration value using environment variables for flexible CI/CD integration:
# Environment-specific URL overrides
DEV_BASE_URL=https://dev-custom.example.com npm run test
UAT_BASE_URL=https://uat-custom.example.com npm run test
PROD_BASE_URL=https://prod-custom.example.com npm run test
# Global configuration overrides
BASE_URL=https://global-override.example.com npm run test
API_BASE_URL=https://api-override.example.com npm run test
TEST_TIMEOUT=60000 npm run test
TEST_RETRIES=5 npm run test
# Playwright-specific overrides
HEADLESS=false npm run test:ui
SLOW_MO=1000 npm run test:uiAll configuration values can be overridden without modifying code files.
Project Structure
The scaffolded project follows this structure:
my-test-project/
├── setup.js # Dependency installation script
├── package.json # npm dependencies and scripts
├── playwright.config.ts # Playwright configuration
├── tsconfig.json # TypeScript configuration
│
├── src/
│ ├── api/
│ │ ├── base.api.ts # HTTP transport layer (enhanced v1.2.0)
│ │ └── clients/
│ │ ├── base.client.ts # Shared client utilities
│ │ └── service.client.ts # Service-specific endpoints
│ │
│ ├── config/
│ │ ├── config.ts # Environment switching (enhanced v1.2.0)
│ │ └── users.json # Credentials by env + role
│ │
│ ├── fixtures/
│ │ └── base.fixture.ts # Playwright fixtures
│ │
│ ├── hooks/
│ │ └── base.hook.ts # Lifecycle hooks
│ │
│ ├── pages/
│ │ └── base.page.ts # BasePage for POM (enhanced v1.2.0)
│ │
│ ├── specs/
│ │ ├── spec.base.ts # Unified test + step export
│ │ ├── specs-ui/ # UI test specs
│ │ └── specs-api/ # API test specs
│ │
│ ├── types/ # Type definitions (v1.2.0)
│ │ ├── config.types.ts # Configuration types
│ │ ├── error.types.ts # Error handling types
│ │ ├── retry.types.ts # Retry pattern types
│ │ └── wait.types.ts # Wait strategy types
│ │
│ └── utils/
│ ├── cleanup.util.ts # Artifact cleanup utility
│ ├── config.util.ts # Configuration validation (v1.2.0)
│ ├── error.util.ts # Enhanced error context (v1.2.0)
│ ├── retry.util.ts # Retry patterns (v1.2.0)
│ └── wait.util.ts # Advanced wait strategies (v1.2.0)
│
├── test-results/ # Screenshots, videos (auto-generated)
├── allure-results/ # Raw Allure data (auto-generated)
└── allure-report/ # HTML report (auto-generated)Writing Tests
UI Tests (Page Object Model)
- Create a page object in
src/pages/:
import { Page } from '@playwright/test';
import { BasePage } from './base.page';
export class LoginPage extends BasePage {
private usernameInput = this.getByTestId('username');
private passwordInput = this.getByTestId('password');
private loginButton = this.getByRole('button', { name: 'Login' });
constructor(page: Page) {
super(page);
}
async open() {
await this.navigate('/login');
}
async login(username: string, password: string) {
await this.fill(this.usernameInput, username);
await this.fill(this.passwordInput, password);
await this.click(this.loginButton);
}
}- Create a spec in
src/specs/specs-ui/:
import { test, step } from '../spec.base';
import { expect } from '@playwright/test';
import { LoginPage } from '../../pages/login.page';
test('User can log in', async ({ page }) => {
const loginPage = new LoginPage(page);
await step('Given user opens login page', async () => {
await loginPage.open();
});
await step('When user enters valid credentials', async () => {
await loginPage.login('admin', 'password');
});
await step('Then dashboard should appear', async () => {
await expect(page).toHaveURL(/dashboard/);
});
});API Tests (Service Client)
- Create a service client in
src/api/clients/:
import { BaseClient } from './base.client';
import { APIResponse } from '@playwright/test';
export class UserServiceClient extends BaseClient {
async getUser(userId: string): Promise<APIResponse> {
return this.get(`/api/users/${userId}`);
}
async createUser(payload: object): Promise<APIResponse> {
return this.post('/api/users', payload);
}
}- Create a spec in
src/specs/specs-api/:
import { test, step } from '../spec.base';
import { expect } from '@playwright/test';
import { UserServiceClient } from '../../api/clients/user.client';
test('API: Create and retrieve user', async ({ request, config }) => {
const client = new UserServiceClient(request, config.apiBaseUrl);
let userId: string;
await step('When creating a new user', async () => {
const res = await client.createUser({ name: 'John Doe' });
expect(res.status()).toBe(201);
userId = (await res.json()).id;
});
await step('Then user can be retrieved', async () => {
const res = await client.getUser(userId);
expect(res.status()).toBe(200);
expect((await res.json()).name).toBe('John Doe');
});
});v1.2.0 Enhanced Features
Retry Patterns
Use retry patterns for flaky interactions and API calls:
// UI interactions with retry
import { BasePage } from '../pages/base.page';
const loginPage = new LoginPage(page);
// Click with exponential backoff retry
await loginPage.clickWithRetry('#flaky-button', {
maxAttempts: 5,
initialDelay: 500
});
// Fill input with retry
await loginPage.fillWithRetry('#dynamic-input', 'value', {
maxAttempts: 3,
initialDelay: 1000
});
// API requests with retry
import { UserServiceClient } from '../api/clients/user.client';
const client = new UserServiceClient(request, config.apiBaseUrl);
// GET with retry
const response = await client.getWithRetry('/api/users', {}, {
maxAttempts: 3,
initialDelay: 1000
});
// POST with retry
const createResponse = await client.postWithRetry('/api/users', payload, {
maxAttempts: 5,
initialDelay: 500
});Advanced Wait Strategies
Use advanced wait utilities for complex scenarios:
import {
waitForCondition,
waitForApiResponse,
waitForBusinessLogic,
waitForElementStable
} from '../utils/wait.util';
// Wait for custom business logic
await waitForCondition(() => {
return someBusinessLogic() === 'completed';
}, { timeout: 30000, pollInterval: 1000 });
// Wait for API response with specific conditions
await waitForApiResponse(() => fetch('/api/status'), {
status: 200,
bodyContains: { ready: true },
timeout: 15000
});
// Wait for business process completion
await waitForBusinessLogic(page, {
type: 'data-loading',
loadingIndicator: '.spinner',
completionIndicator: '.data-ready',
timeout: 20000
});
// Wait for element to stop moving (animations)
await waitForElementStable(page.locator('#animated-element'), {
stableFor: 1000,
timeout: 10000
});Enhanced Error Context
Automatic error enhancement provides rich debugging information:
// Errors automatically include:
// - Action context ('clicking element #login-button')
// - Page URL and title
// - Screenshot (if available)
// - Network request logs
// - Console error logs
// - Recovery suggestions
try {
await basePage.click('#problematic-element');
} catch (error) {
// Error now contains rich context for debugging
console.log(error.context); // Page state, network logs, etc.
console.log(error.suggestions); // Recovery suggestions
}Configuration Validation
Validate and debug configuration issues:
import {
validateCurrentConfig,
getFullConfig,
getEnvMappings
} from '../config/config';
// Validate current configuration
const validation = validateCurrentConfig();
if (!validation.isValid) {
console.log('Config errors:', validation.errors);
}
// Get full configuration with all overrides applied
const fullConfig = getFullConfig();
console.log('Current config:', fullConfig);
// See environment variable mappings
const mappings = getEnvMappings();
console.log('Available env vars:', mappings);Configuration
Environment Configuration
Edit src/config/config.ts to add or modify environments:
const environments: Record<Environment, EnvConfig> = {
dev: {
baseURL: 'https://dev.example.com',
apiBaseUrl: 'https://api.dev.example.com'
},
uat: {
baseURL: 'https://uat.example.com',
apiBaseUrl: 'https://api.uat.example.com'
},
prod: {
baseURL: 'https://prod.example.com',
apiBaseUrl: 'https://api.prod.example.com'
}
};Credential Management
Edit src/config/users.json to add test credentials:
{
"dev": {
"admin": {
"username": "[email protected]",
"password": "dev-password"
}
},
"uat": {
"admin": {
"username": "[email protected]",
"password": "uat-password"
}
}
}Retrieve credentials in tests:
import { getUser } from '../config/config';
const user = getUser('admin');
// user.username, user.passwordRequirements
- Node.js >= 18.0.0
- npm >= 9.0.0
License
MIT
Support
For issues and questions, please refer to the inline usage guides in each scaffolded file.
