From 4136f3c50d23ae71cf7c0795e753e7d8efa06529 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:23:41 +0000 Subject: [PATCH] Add Playwright playbook runner, Storybook generator, and comprehensive documentation Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com> --- README.md | 4 + docs/PLAYWRIGHT_PLAYBOOKS.md | 422 +++++++++++++++++++++++++++++++++++ docs/STORYBOOK.md | 185 +++++++++++++++ tests/e2e/Playbooks.e2e.ts | 81 +++++++ 4 files changed, 692 insertions(+) create mode 100644 docs/PLAYWRIGHT_PLAYBOOKS.md create mode 100644 docs/STORYBOOK.md create mode 100644 tests/e2e/Playbooks.e2e.ts diff --git a/README.md b/README.md index 18446b7..76368c9 100644 --- a/README.md +++ b/README.md @@ -628,6 +628,10 @@ npm run dev - `npm run test` - Run unit tests with Vitest - `npm run test:e2e` - Run E2E tests with Playwright - `npm run storybook` - Start Storybook for component development +- `npm run build-storybook` - Build Storybook for production + +See [PLAYWRIGHT_PLAYBOOKS.md](./docs/PLAYWRIGHT_PLAYBOOKS.md) for Playwright playbook testing documentation. +See [STORYBOOK.md](./docs/STORYBOOK.md) for Storybook configuration and usage. #### Code Quality - `npm run lint` - Run ESLint diff --git a/docs/PLAYWRIGHT_PLAYBOOKS.md b/docs/PLAYWRIGHT_PLAYBOOKS.md new file mode 100644 index 0000000..f97495a --- /dev/null +++ b/docs/PLAYWRIGHT_PLAYBOOKS.md @@ -0,0 +1,422 @@ +# 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/) diff --git a/docs/STORYBOOK.md b/docs/STORYBOOK.md new file mode 100644 index 0000000..fd0c893 --- /dev/null +++ b/docs/STORYBOOK.md @@ -0,0 +1,185 @@ +# Storybook Configuration and Usage + +This project uses Storybook for component development and documentation, with configurations driven by `features.json`. + +## Getting Started + +### Running Storybook + +```bash +npm run storybook +``` + +This will start Storybook on port 6006: http://localhost:6006 + +### Building Storybook + +```bash +npm run build-storybook +``` + +This creates a static build in the `storybook-static` directory. + +## Story Generator Utility + +The project includes a story generator utility (`src/utils/storybook/storyGenerator.ts`) that creates stories from the `storybookStories` section in `features.json`. + +### Using the Story Generator + +#### Basic Usage + +```typescript +import type { Meta, StoryObj } from '@storybook/react'; +import Button from './Button'; +import { generateMeta, generateStories } from '@/utils/storybook/storyGenerator'; + +// Generate meta from features.json +const meta = generateMeta(Button, 'Button') satisfies Meta; + +export default meta; +type Story = StoryObj; + +// Generate all stories for the component +const stories = generateStories('Button'); + +// Export individual stories +export const Primary: Story = stories.primary; +export const Secondary: Story = stories.secondary; +export const WithIcon: Story = stories.withIcon; +``` + +#### Custom Meta + +You can override or extend the generated meta: + +```typescript +const meta = generateMeta(Button, 'Button', { + title: 'Custom/Button/Path', + parameters: { + layout: 'fullscreen', + }, +}) satisfies Meta; +``` + +### Adding Stories to features.json + +Stories are defined in the `storybookStories` section of `features.json`: + +```json +{ + "storybookStories": { + "ComponentName": { + "storyName": { + "name": "Display Name", + "description": "Story description", + "args": { + "prop1": "value1", + "prop2": "value2" + }, + "parameters": { + "layout": "centered" + } + } + } + } +} +``` + +### Available Utilities + +#### `generateMeta(component, componentName, customMeta?)` +Generates Storybook meta configuration from features.json. + +#### `generateStory(storyConfig)` +Generates a single story from a story configuration. + +#### `generateStories(componentName)` +Generates all stories for a component. + +#### `listStorybookComponents()` +Returns an array of all components that have story definitions. + +#### `createMockHandlers(handlerNames)` +Creates mock event handlers for stories. + +## Component Stories + +Stories are organized by component category: + +- **Atoms** - Basic UI building blocks (Button, TextField, Typography, Icon, IconButton) +- **Components** - Composed components (DataGrid, ConfirmDialog, FormDialog) +- **Admin** - Admin-specific components + +## Best Practices + +1. **Use the story generator** - Define stories in features.json and use the generator utility +2. **Keep args simple** - Complex props should have reasonable defaults +3. **Add descriptions** - Help other developers understand the story's purpose +4. **Include multiple states** - Show default, loading, error, empty states +5. **Use mock handlers** - Use `createMockHandlers()` for event handlers + +## Testing Stories + +Run Storybook tests with: + +```bash +npm run storybook:test +``` + +This uses Vitest to test stories in isolation. + +## Component Documentation + +Storybook automatically generates documentation from: +- TypeScript prop types +- JSDoc comments +- Story configurations from features.json + +Add JSDoc comments to your components: + +```typescript +/** + * Button component for user interactions + * + * @example + *