Files
postgres/docs/PLAYWRIGHT_PLAYBOOKS.md

423 lines
8.6 KiB
Markdown

# 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
```bash
# 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
```typescript
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:
```typescript
await runPlaybook(page, 'createTable', {
tableName: 'users',
columnName: 'id',
dataType: 'INTEGER',
});
```
#### With Cleanup
Some playbooks include cleanup steps:
```typescript
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 object
- `playbookName` - Name of the playbook in features.json
- `variables` - Object with variable values for substitution
- `options.runCleanup` - Whether to run cleanup steps
#### `executeStep(page, step, variables?)`
Executes a single playbook step.
#### `getPlaybooksByTag(tag)`
Returns all playbooks with a specific tag.
```typescript
const adminPlaybooks = getPlaybooksByTag('admin');
```
#### `listPlaybooks()`
Returns names of all available playbooks.
```typescript
const playbooks = listPlaybooks();
console.log('Available playbooks:', playbooks);
```
## Defining Playbooks in features.json
Playbooks are defined in the `playwrightPlaybooks` section:
```json
{
"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:
```json
{
"action": "fill",
"selector": "input[name='{{fieldName}}']",
"value": "{{fieldValue}}"
}
```
When running the playbook:
```typescript
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:
```json
{
"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:
```json
{
"cleanup": [
{
"action": "click",
"selector": "button:has-text('Delete')"
}
]
}
```
### 4. Make Playbooks Composable
Break complex workflows into smaller playbooks:
```typescript
// 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
```typescript
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
```typescript
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
```typescript
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
```bash
# Show test report
npx playwright show-report
# Open trace viewer
npx playwright show-trace trace.zip
```
### Debug Mode
```bash
# 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:
```json
{
"action": "screenshot",
"selector": ".query-results"
}
```
Screenshots are saved to `screenshots/` directory.
## Continuous Integration
In CI environments, tests run automatically:
```yaml
# .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:
```typescript
const playbooks = listPlaybooks();
console.log('Available:', playbooks);
```
### Timeout errors
Increase wait times in playbook steps:
```json
{
"action": "wait",
"timeout": 5000
}
```
Or configure global timeout in playwright.config.ts.
### Variable substitution not working
Check variable names match exactly:
```typescript
// In features.json: {{tableName}}
// In test:
await runPlaybook(page, 'createTable', {
tableName: 'users', // Must match: tableName
});
```
## Additional Resources
- [Playwright Documentation](https://playwright.dev/)
- [Playwright Best Practices](https://playwright.dev/docs/best-practices)
- [Test Examples](/tests/e2e/)