8.6 KiB
Playwright Playbook Testing
This project uses Playwright for end-to-end testing with test playbooks defined in features.json for reusable test scenarios.
Getting Started
Running Playwright Tests
# Run all tests
npm run test:e2e
# Run in UI mode (interactive)
npx playwright test --ui
# Run specific test file
npx playwright test tests/e2e/Playbooks.e2e.ts
# Run tests in headed mode (see browser)
npx playwright test --headed
Playbook Runner Utility
The playbook runner (tests/utils/playbookRunner.ts) executes test scenarios defined in the playwrightPlaybooks section of features.json.
Why Use Playbooks?
- Reusability - Define common workflows once, use in multiple tests
- Consistency - Ensure tests follow the same patterns
- Maintainability - Update test steps in one place
- Documentation - Playbooks serve as living documentation
- Configuration-driven - Non-developers can update test scenarios
Using the Playbook Runner
Basic Usage
import { test } from '@playwright/test';
import { runPlaybook } from '../utils/playbookRunner';
test('should execute login workflow', async ({ page }) => {
await runPlaybook(page, 'adminLogin', {
username: 'admin',
password: 'password123',
});
});
With Variables
Playbooks support variable substitution using {{variableName}} syntax:
await runPlaybook(page, 'createTable', {
tableName: 'users',
columnName: 'id',
dataType: 'INTEGER',
});
With Cleanup
Some playbooks include cleanup steps:
await runPlaybook(page, 'createTable',
{ tableName: 'test_table' },
{ runCleanup: true } // Runs cleanup steps after main steps
);
Available Utilities
runPlaybook(page, playbookName, variables?, options?)
Executes a complete playbook from features.json.
Parameters:
page- Playwright Page objectplaybookName- Name of the playbook in features.jsonvariables- Object with variable values for substitutionoptions.runCleanup- Whether to run cleanup steps
executeStep(page, step, variables?)
Executes a single playbook step.
getPlaybooksByTag(tag)
Returns all playbooks with a specific tag.
const adminPlaybooks = getPlaybooksByTag('admin');
listPlaybooks()
Returns names of all available playbooks.
const playbooks = listPlaybooks();
console.log('Available playbooks:', playbooks);
Defining Playbooks in features.json
Playbooks are defined in the playwrightPlaybooks section:
{
"playwrightPlaybooks": {
"playbookName": {
"name": "Human-Readable Name",
"description": "What this playbook does",
"tags": ["admin", "crud"],
"steps": [
{
"action": "goto",
"url": "/admin/dashboard"
},
{
"action": "click",
"selector": "button:has-text('Create')"
},
{
"action": "fill",
"selector": "input[name='name']",
"value": "{{name}}"
},
{
"action": "expect",
"selector": "text={{name}}",
"text": "visible"
}
],
"cleanup": [
{
"action": "click",
"selector": "button:has-text('Delete')"
}
]
}
}
}
Supported Actions
| Action | Description | Parameters |
|---|---|---|
goto |
Navigate to URL | url |
click |
Click element | selector |
fill |
Fill input | selector, value |
select |
Select dropdown option | selector, value |
wait |
Wait for timeout | timeout (ms) |
expect |
Assert condition | selector, text or url |
screenshot |
Take screenshot | selector (optional) |
Variable Substitution
Use {{variableName}} in any string field:
{
"action": "fill",
"selector": "input[name='{{fieldName}}']",
"value": "{{fieldValue}}"
}
When running the playbook:
await runPlaybook(page, 'myPlaybook', {
fieldName: 'username',
fieldValue: 'admin',
});
Pre-defined Playbooks
The following playbooks are available in features.json:
adminLogin
Complete admin login flow.
- Tags: admin, auth, login
- Variables: username, password
createTable
Create a new database table through UI.
- Tags: admin, table, crud
- Variables: tableName
- Cleanup: Yes (drops the table)
addColumn
Add a column to an existing table.
- Tags: admin, column, crud
- Variables: tableName, columnName, dataType
createIndex
Create a database index.
- Tags: admin, index, performance
- Variables: tableName, indexName, columnName
queryBuilder
Build and execute a query.
- Tags: admin, query, select
- Variables: tableName, columnName
securityCheck
Verify API endpoints require authentication.
- Tags: security, api, auth
- Variables: None
Best Practices
1. Tag Your Playbooks
Use tags for organization and filtering:
{
"tags": ["admin", "crud", "table"]
}
2. Use Meaningful Names
Make playbook names descriptive:
- ✅
createUserAndVerifyEmail - ❌
test1
3. Add Cleanup Steps
Clean up test data to keep tests independent:
{
"cleanup": [
{
"action": "click",
"selector": "button:has-text('Delete')"
}
]
}
4. Make Playbooks Composable
Break complex workflows into smaller playbooks:
// Login first
await runPlaybook(page, 'adminLogin', { username, password });
// Then run specific test
await runPlaybook(page, 'createTable', { tableName });
5. Use Descriptive Selectors
Prefer text selectors and test IDs:
- ✅
button:has-text('Create') - ✅
[data-testid="create-button"] - ❌
.btn-primary
Example Tests
Simple Playbook Test
import { test } from '@playwright/test';
import { runPlaybook } from '../utils/playbookRunner';
test('create and delete table', async ({ page }) => {
const tableName = `test_${Date.now()}`;
await runPlaybook(page, 'createTable',
{ tableName },
{ runCleanup: true }
);
});
Multiple Playbooks
test('complete workflow', async ({ page }) => {
// Step 1: Login
await runPlaybook(page, 'adminLogin', {
username: 'admin',
password: 'password',
});
// Step 2: Create table
const tableName = 'users';
await runPlaybook(page, 'createTable', { tableName });
// Step 3: Add column
await runPlaybook(page, 'addColumn', {
tableName,
columnName: 'email',
dataType: 'VARCHAR',
});
// Step 4: Create index
await runPlaybook(page, 'createIndex', {
tableName,
indexName: 'idx_email',
columnName: 'email',
});
});
Tag-based Testing
import { getPlaybooksByTag } from '../utils/playbookRunner';
test.describe('Admin CRUD operations', () => {
const crudPlaybooks = getPlaybooksByTag('crud');
for (const [name, playbook] of Object.entries(crudPlaybooks)) {
test(playbook.name, async ({ page }) => {
// Run each CRUD playbook
await runPlaybook(page, name, {
/* variables */
});
});
}
});
Debugging
View Test Results
# Show test report
npx playwright show-report
# Open trace viewer
npx playwright show-trace trace.zip
Debug Mode
# Run in debug mode
npx playwright test --debug
# Run specific test in debug mode
npx playwright test tests/e2e/Playbooks.e2e.ts --debug
Screenshots
Playbooks can take screenshots:
{
"action": "screenshot",
"selector": ".query-results"
}
Screenshots are saved to screenshots/ directory.
Continuous Integration
In CI environments, tests run automatically:
# .github/workflows/test.yml
- name: Run Playwright tests
run: npm run test:e2e
The playwright.config.ts is configured to:
- Use different settings for CI vs local
- Record videos on failure
- Generate test reports
Troubleshooting
Playbook not found
Make sure the playbook name matches exactly in features.json:
const playbooks = listPlaybooks();
console.log('Available:', playbooks);
Timeout errors
Increase wait times in playbook steps:
{
"action": "wait",
"timeout": 5000
}
Or configure global timeout in playwright.config.ts.
Variable substitution not working
Check variable names match exactly:
// In features.json: {{tableName}}
// In test:
await runPlaybook(page, 'createTable', {
tableName: 'users', // Must match: tableName
});