End-to-End Testing
Apache Superset uses Playwright for end-to-end testing, migrating from the legacy Cypress tests.
Running Tests
Playwright (Recommended)
cd superset-frontend
# Run all tests
npm run playwright:test
# or: npx playwright test
# Run specific test file
npx playwright test tests/auth/login.spec.ts
# Run with UI mode for debugging
npm run playwright:ui
# or: npx playwright test --ui
# Run in headed mode (see browser)
npm run playwright:headed
# or: npx playwright test --headed
# Debug specific test file
npm run playwright:debug tests/auth/login.spec.ts
# or: npx playwright test --debug tests/auth/login.spec.ts
Cypress (Deprecated)
Cypress tests are being migrated to Playwright. For legacy tests:
cd superset-frontend/cypress-base
npm run cypress-run-chrome # Headless
npm run cypress-debug # Interactive UI
Project Architecture
superset-frontend/playwright/
├── components/core/ # Reusable UI components
├── pages/ # Page Object Models
├── tests/ # Test files organized by feature
├── utils/ # Shared constants and utilities
└── playwright.config.ts
Design Principles
We follow YAGNI (You Aren't Gonna Need It), DRY (Don't Repeat Yourself), and KISS (Keep It Simple, Stupid) principles:
- Build only what's needed now
- Reuse existing patterns and components
- Keep solutions simple and maintainable
Page Object Pattern
Each page object encapsulates:
- Actions: What you can do on the page
- Queries: Information you can get from the page
- Selectors: Centralized in private static SELECTORS constant
- NO Assertions: Keep assertions in test files
Example Page Object:
export class AuthPage {
// Selectors centralized in the page object
private static readonly SELECTORS = {
LOGIN_FORM: '[data-test="login-form"]',
USERNAME_INPUT: '[data-test="username-input"]',
} as const;
// Actions - what you can do
async loginWithCredentials(username: string, password: string) {}
// Queries - information you can get
async getCurrentUrl(): Promise<string> {}
// NO assertions - those belong in tests
}
Example Test:
import { test, expect } from '@playwright/test';
import { AuthPage } from '../../pages/AuthPage';
import { LOGIN } from '../../utils/urls';
test('should login with correct credentials', async ({ page }) => {
const authPage = new AuthPage(page);
await authPage.goto();
await authPage.loginWithCredentials('admin', 'general');
// Assertions belong in tests, not page objects
expect(await authPage.getCurrentUrl()).not.toContain(LOGIN);
});
Core Components
Reusable UI interaction classes for common elements (components/core/):
- Form: Container with properly scoped child element access
- Input: Supports
fill(),type(), andpressSequentially()methods - Button: Standard click, hover, focus interactions
Usage Example:
import { Form } from '../components/core';
const loginForm = new Form(page, '[data-test="login-form"]');
const usernameInput = loginForm.getInput('[data-test="username-input"]');
await usernameInput.fill('admin');
Test Reports
Playwright generates multiple reports for better visibility:
# View interactive HTML report (opens automatically on failure)
npm run playwright:report
# or: npx playwright show-report
# View test trace for debugging failures
npx playwright show-trace test-results/[test-name]/trace.zip
Report Types
- List Reporter: Shows progress and summary table in terminal
- HTML Report: Interactive web interface with screenshots, videos, and traces
- JSON Report: Machine-readable format in
test-results/results.json - GitHub Actions: Annotations in CI for failed tests
Debugging Failed Tests
When tests fail, Playwright automatically captures:
- Screenshots at the point of failure
- Videos of the entire test run
- Traces with timeline and network activity
- Error context with detailed debugging information
All debugging artifacts are available in the HTML report for easy analysis.
Configuration
- Config:
playwright.config.ts- matches Cypress settings - Base URL:
http://localhost:8088(assumes Superset running) - Browsers: Chrome only for Phase 1 (YAGNI)
- Retries: 2 in CI, 0 locally (matches Cypress)
Contributing Guidelines
Adding New Tests
- Check existing components before creating new ones
- Use page objects for page interactions
- Keep assertions in tests, not page objects
- Follow naming conventions:
feature.spec.ts
Adding New Components
- Follow YAGNI: Only build what's immediately needed
- Use Locator-based scoping for proper element isolation
- Support both string selectors and Locator objects via constructor overloads
- Add to
components/core/index.tsfor easy importing
Adding New Page Objects
- Centralize selectors in private static SELECTORS constant
- Import shared constants from
utils/urls.ts - Actions and queries only - no assertions
- Use existing components for DOM interactions
Migration from Cypress
When porting Cypress tests:
- Port the logic, not the implementation
- Use page objects instead of inline selectors
- Replace
cy.intercept/cy.waitwithpage.waitForRequest() - Use shared constants from
utils/urls.ts - Follow the established patterns shown in
tests/auth/login.spec.ts
Best Practices
- Centralize selectors in page objects
- Centralize URLs in
utils/urls.ts - Use meaningful test descriptions
- Keep page objects action-focused
- Put assertions in tests, not page objects
- Follow the existing patterns for consistency