Workflow ID: _bmad/bmm/testarch/framework
Version: 4.0 (BMad v6)
Initialize a production-ready test framework architecture (Playwright or Cypress) with fixtures, helpers, configuration, and best practices. This workflow scaffolds the complete testing infrastructure for modern web applications.
Critical: Verify these requirements before proceeding. If any fail, HALT and notify the user.
package.json exists in project rootplaywright.config.* or cypress.config.*)Validate package.json
{project-root}/package.jsonCheck for Existing Framework
playwright.config.*, cypress.config.*, cypress.jsonpackage.json for @playwright/test or cypress dependenciesupgrade-framework instead."Gather Context
architecture.md, tech-spec*.md)Halt Condition: If preflight checks fail, stop immediately and report which requirement failed.
Default Logic:
Playwright (recommended for):
Cypress (recommended for):
Detection Strategy:
package.json for existing preferenceproject_size variable from workflow configframework_preference variable if setCreate Directory Structure
{project-root}/
├── tests/ # Root test directory
│ ├── e2e/ # Test files (users organize as needed)
│ ├── support/ # Framework infrastructure (key pattern)
│ │ ├── fixtures/ # Test fixtures (data, mocks)
│ │ ├── helpers/ # Utility functions
│ │ └── page-objects/ # Page object models (optional)
│ └── README.md # Test suite documentation
Note: Users organize test files (e2e/, api/, integration/, component/) as needed. The support/ folder is the critical pattern for fixtures and helpers used across tests.
For Playwright (playwright.config.ts or playwright.config.js):
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
timeout: 60 * 1000, // Test timeout: 60s
expect: {
timeout: 15 * 1000, // Assertion timeout: 15s
},
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
actionTimeout: 15 * 1000, // Action timeout: 15s
navigationTimeout: 30 * 1000, // Navigation timeout: 30s
},
reporter: [['html', { outputFolder: 'test-results/html' }], ['junit', { outputFile: 'test-results/junit.xml' }], ['list']],
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
],
});
For Cypress (cypress.config.ts or cypress.config.js):
import { defineConfig } from 'cypress';
export default defineConfig({
e2e: {
baseUrl: process.env.BASE_URL || 'http://localhost:3000',
specPattern: 'tests/e2e/**/*.cy.{js,jsx,ts,tsx}',
supportFile: 'tests/support/e2e.ts',
video: false,
screenshotOnRunFailure: true,
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
retries: {
runMode: 2,
openMode: 0,
},
defaultCommandTimeout: 15000,
requestTimeout: 30000,
responseTimeout: 30000,
pageLoadTimeout: 60000,
});
Create .env.example:
# Test Environment Configuration
TEST_ENV=local
BASE_URL=http://localhost:3000
API_URL=http://localhost:3001/api
# Authentication (if applicable)
TEST_USER_EMAIL=test@example.com
TEST_USER_PASSWORD=
# Feature Flags (if applicable)
FEATURE_FLAG_NEW_UI=true
# API Keys (if applicable)
TEST_API_KEY=
Create .nvmrc:
20.11.0
(Use Node version from existing .nvmrc or default to current LTS)
Knowledge Base Reference: testarch/knowledge/fixture-architecture.md
Create tests/support/fixtures/index.ts:
import { test as base } from '@playwright/test';
import { UserFactory } from './factories/user-factory';
type TestFixtures = {
userFactory: UserFactory;
};
export const test = base.extend<TestFixtures>({
userFactory: async ({}, use) => {
const factory = new UserFactory();
await use(factory);
await factory.cleanup(); // Auto-cleanup
},
});
export { expect } from '@playwright/test';
Knowledge Base Reference: testarch/knowledge/data-factories.md
Create tests/support/fixtures/factories/user-factory.ts:
import { faker } from '@faker-js/faker';
export class UserFactory {
private createdUsers: string[] = [];
async createUser(overrides = {}) {
const user = {
email: faker.internet.email(),
name: faker.person.fullName(),
password: faker.internet.password({ length: 12 }),
...overrides,
};
// API call to create user
const response = await fetch(`${process.env.API_URL}/users`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user),
});
const created = await response.json();
this.createdUsers.push(created.id);
return created;
}
async cleanup() {
// Delete all created users
for (const userId of this.createdUsers) {
await fetch(`${process.env.API_URL}/users/${userId}`, {
method: 'DELETE',
});
}
this.createdUsers = [];
}
}
Create tests/e2e/example.spec.ts:
import { test, expect } from '../support/fixtures';
test.describe('Example Test Suite', () => {
test('should load homepage', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveTitle(/Home/i);
});
test('should create user and login', async ({ page, userFactory }) => {
// Create test user
const user = await userFactory.createUser();
// Login
await page.goto('/login');
await page.fill('[data-testid="email-input"]', user.email);
await page.fill('[data-testid="password-input"]', user.password);
await page.click('[data-testid="login-button"]');
// Assert login success
await expect(page.locator('[data-testid="user-menu"]')).toBeVisible();
});
});
Add minimal test script to package.json:
{
"scripts": {
"test:e2e": "playwright test"
}
}
Note: Users can add additional scripts as needed (e.g., --ui, --headed, --debug, show-report).
Generate Documentation
Create tests/README.md with setup instructions (see Step 3 deliverables).
Configuration File
playwright.config.ts or cypress.config.tsDirectory Structure
tests/ with e2e/, api/, support/ subdirectoriessupport/fixtures/ for test fixturessupport/helpers/ for utility functionsEnvironment Configuration
.env.example with TEST_ENV, BASE_URL, API_URL.nvmrc with Node versionTest Infrastructure
mergeTests pattern)Documentation
tests/README.md with setup instructionsThe generated tests/README.md should include:
Critical: Check configuration and load appropriate fragments.
Read {config_source} and check config.tea_use_playwright_utils.
If config.tea_use_playwright_utils: true (Playwright Utils Integration):
Consult {project-root}/_bmad/bmm/testarch/tea-index.csv and load:
overview.md - Playwright utils installation and design principlesfixtures-composition.md - mergeTests composition with playwright-utilsauth-session.md - Token persistence setup (if auth needed)api-request.md - API testing utilities (if API tests planned)burn-in.md - Smart test selection for CI (recommend during framework setup)network-error-monitor.md - Automatic HTTP error detection (recommend in merged fixtures)data-factories.md - Factory patterns with faker (498 lines, 5 examples)Recommend installing playwright-utils:
npm install -D @seontechnologies/playwright-utils
Recommend adding burn-in and network-error-monitor to merged fixtures for enhanced reliability.
If config.tea_use_playwright_utils: false (Traditional Patterns):
Consult {project-root}/_bmad/bmm/testarch/tea-index.csv and load:
fixture-architecture.md - Pure function → fixture → mergeTests composition with auto-cleanup (406 lines, 5 examples)data-factories.md - Faker-based factories with overrides, nested factories, API seeding, auto-cleanup (498 lines, 5 examples)network-first.md - Network-first testing safeguards: intercept before navigate, HAR capture, deterministic waiting (489 lines, 5 examples)playwright-config.md - Playwright-specific configuration: environment-based, timeout standards, artifact output, parallelization, project config (722 lines, 5 examples)test-quality.md - Test design principles: deterministic, isolated with cleanup, explicit assertions, length/time limits (658 lines, 5 examples)Playwright Advantages:
Cypress Advantages:
Avoid Cypress when:
Always recommend:
data-testid attributes for UI elementsdata-cy attributes if Cypress is chosenFor microservices architectures, recommend Pact for consumer-driven contract testing alongside E2E tests.
Configure failure-only capture:
This reduces storage overhead while maintaining debugging capability.
After completing this workflow, provide a summary:
## Framework Scaffold Complete
**Framework Selected**: Playwright (or Cypress)
**Artifacts Created**:
- ✅ Configuration file: `playwright.config.ts`
- ✅ Directory structure: `tests/e2e/`, `tests/support/`
- ✅ Environment config: `.env.example`
- ✅ Node version: `.nvmrc`
- ✅ Fixture architecture: `tests/support/fixtures/`
- ✅ Data factories: `tests/support/fixtures/factories/`
- ✅ Sample tests: `tests/e2e/example.spec.ts`
- ✅ Documentation: `tests/README.md`
**Next Steps**:
1. Copy `.env.example` to `.env` and fill in environment variables
2. Run `npm install` to install test dependencies
3. Run `npm run test:e2e` to execute sample tests
4. Review `tests/README.md` for detailed setup instructions
**Knowledge Base References Applied**:
- Fixture architecture pattern (pure functions + mergeTests)
- Data factories with auto-cleanup (faker-based)
- Network-first testing safeguards
- Failure-only artifact capture
After completing all steps, verify:
Refer to checklist.md for comprehensive validation criteria.