Writing Tests with Raiken
Learn how to create effective test cases using Raiken's AI-powered test generation and advanced editor features.
Overview
Raiken provides multiple ways to create tests:
- AI-Generated Tests - Describe tests in natural language
- Manual Test Writing - Use the Monaco editor with TypeScript support
- Hybrid Approach - Combine AI generation with manual refinement
AI-Powered Test Generation
Basic Test Generation
Describe your test scenario in plain English:
Test the product search functionality with filters
Raiken will generate a complete Playwright test:
import { test, expect } from '@playwright/test'
test('product search with filters', async ({ page }) => {
await page.goto('/products')
await page.fill('[data-testid="search-input"]', 'laptop')
await page.click('[data-testid="search-button"]')
await page.selectOption('[data-testid="category-filter"]', 'electronics')
await page.check('[data-testid="in-stock-filter"]')
await expect(page.locator('[data-testid="product-card"]')).toHaveCount({ min: 1 })
await expect(page.locator('[data-testid="results-count"]')).toContainText('results found')
})
Advanced Test Scenarios
For complex scenarios, provide more detailed descriptions:
Test e-commerce checkout flow:
1. Add product to cart
2. Proceed to checkout
3. Fill shipping information
4. Select payment method
5. Complete purchase
6. Verify order confirmation
Best Practices for AI Prompts
- Be specific about the actions you want to test
- Include expected outcomes and assertions
- Mention specific UI elements when possible
- Describe edge cases and error scenarios
Manual Test Writing
Using the Monaco Editor
The built-in Monaco editor provides:
- TypeScript support with autocomplete
- Syntax highlighting for Playwright APIs
- Error detection and inline suggestions
- Code formatting and organization
Test Structure
Follow this structure for maintainable tests:
import { test, expect } from '@playwright/test'
test.describe('Feature Name', () => {
test.beforeEach(async ({ page }) => {
// Setup code
await page.goto('/app')
})
test('should perform specific action', async ({ page }) => {
// Test implementation
await page.click('[data-testid="button"]')
await expect(page.locator('[data-testid="result"]')).toBeVisible()
})
})
Element Selection Strategies
Recommended Selectors
-
Data Test IDs (Preferred)
await page.click('[data-testid="submit-button"]') -
Semantic Selectors
await page.click('button:has-text("Submit")') -
CSS Selectors (When necessary)
await page.click('.submit-btn')
Avoiding Fragile Selectors
❌ Avoid these selectors:
// Too specific, will break with style changes
await page.click('.btn.btn-primary.px-4.py-2')
// Position-dependent, fragile
await page.click('div:nth-child(3) > button:first-child')
✅ Prefer these approaches:
// Semantic and stable
await page.click('button:has-text("Submit")')
// Data attributes designed for testing
await page.click('[data-testid="submit-button"]')
Test Organization
File Structure
Organize tests by feature or page:
tests/
├── auth/
│ ├── login.spec.ts
│ ├── registration.spec.ts
│ └── password-reset.spec.ts
├── dashboard/
│ ├── overview.spec.ts
│ └── settings.spec.ts
└── e2e/
└── user-journey.spec.ts
Test Naming
Use descriptive names that explain the test purpose:
// ❌ Poor naming
test('test1', async ({ page }) => { ... })
test('login', async ({ page }) => { ... })
// ✅ Good naming
test('should successfully log in with valid credentials', async ({ page }) => { ... })
test('should display error message for invalid email format', async ({ page }) => { ... })
Assertions and Expectations
Common Assertions
// Visibility assertions
await expect(page.locator('[data-testid="element"]')).toBeVisible()
await expect(page.locator('[data-testid="element"]')).toBeHidden()
// Text assertions
await expect(page.locator('[data-testid="title"]')).toHaveText('Expected Title')
await expect(page.locator('[data-testid="message"]')).toContainText('Success')
// URL assertions
await expect(page).toHaveURL('/dashboard')
await expect(page).toHaveURL(/.*\/user\/\d+/)
// Attribute assertions
await expect(page.locator('[data-testid="input"]')).toHaveAttribute('disabled')
await expect(page.locator('[data-testid="link"]')).toHaveAttribute('href', '/target')
Wait Strategies
// Wait for element to be visible
await page.waitForSelector('[data-testid="loading"]', { state: 'hidden' })
// Wait for network request
await page.waitForResponse(response =>
response.url().includes('/api/data') && response.status() === 200
)
// Wait for function to return truthy value
await page.waitForFunction(() => window.app.isReady)
Advanced Techniques
Page Object Model
Create reusable page objects:
export class LoginPage {
constructor(private page: Page) {}
async login(email: string, password: string) {
await this.page.fill('[data-testid="email"]', email)
await this.page.fill('[data-testid="password"]', password)
await this.page.click('[data-testid="login-button"]')
}
async expectLoginError(message: string) {
await expect(this.page.locator('[data-testid="error"]')).toHaveText(message)
}
}
Test Data Management
// Use test data files
import { users } from '../fixtures/users.json'
test('login with admin user', async ({ page }) => {
const adminUser = users.admin
await loginPage.login(adminUser.email, adminUser.password)
})
Parameterized Tests
const testCases = [
{ email: 'invalid-email', password: 'valid123', error: 'Invalid email format' },
{ email: 'valid@email.com', password: '123', error: 'Password too short' },
]
testCases.forEach(({ email, password, error }) => {
test(`should show error "${error}" for email "${email}"`, async ({ page }) => {
await loginPage.login(email, password)
await loginPage.expectLoginError(error)
})
})
Debugging Tests
Using Raiken's Debug Features
- Video Recording - Automatically captures test execution
- Screenshots - Taken on test failures
- Console Logs - Captured and displayed in reports
- Network Monitoring - Track API calls and responses
Debug Mode
Run tests in debug mode:
npx playwright test --debug
Adding Debug Information
test('complex user flow', async ({ page }) => {
console.log('Starting user registration flow')
await page.goto('/register')
await page.screenshot({ path: 'debug-register-page.png' })
// Continue with test...
})
Advanced Features
Page Object Model Support
Raiken can generate tests using the Page Object Model pattern for better maintainability:
// Generated Page Object
class ProfilePage {
constructor(private page: Page) {}
async updateProfile(name: string, email: string) {
await this.page.getByLabel('Full Name').fill(name);
await this.page.getByLabel('Email').fill(email);
await this.page.getByRole('button', { name: 'Save Changes' }).click();
}
}
// Generated Test
test('user profile update', async ({ page }) => {
const profilePage = new ProfilePage(page);
await profilePage.updateProfile('John Doe', 'john@example.com');
await expect(page.getByText('Profile updated successfully')).toBeVisible();
});
Multi-Language Support
Raiken handles internationalized applications with intelligent selector generation:
// Supports multiple languages automatically
await page.getByRole('button', { name: /login|connexion|iniciar sesión/i });
Error Handling and Recovery
Built-in resilience features:
- Automatic Retries: Configurable retry mechanisms for flaky tests
- Graceful Degradation: Continues operation even when some features fail
- Comprehensive Logging: Detailed logs for debugging and analysis
- Recovery Strategies: Intelligent error recovery and alternative approaches
Best Practices
Test Organization
- Use descriptive test names that explain what is being tested
- Group related tests using
test.describe()blocks - Keep tests independent - each test should be able to run in isolation
- Use semantic selectors - Prefer data-testid and ARIA attributes
- Add meaningful assertions to verify expected behavior
Writing Maintainable Tests
// Good: Descriptive and focused
test('should add item to cart and update counter', async ({ page }) => {
await page.goto('/products/laptop-123')
await page.getByTestId('add-to-cart-button').click()
await expect(page.getByTestId('cart-counter')).toContainText('1')
await expect(page.getByTestId('success-message')).toContainText('Added to cart')
})
// Avoid: Vague and testing multiple things
test('cart stuff', async ({ page }) => {
// Multiple unrelated assertions...
})
Next Steps
- Learn about AI Test Generation in detail
- Explore Browser Testing capabilities
- Set up CI/CD Integration
- View Test Examples for inspiration