Appearance
E2E Testing
End-to-end tests for the spread app using Playwright. Tests run against a real backend with a dedicated test workspace.
Prerequisites
| Service | How to start |
|---|---|
| PostgreSQL + RabbitMQ | cd schemastack-be && docker-compose up -d |
| Backend | cd schemastack-be && ./mvnw quarkus:dev |
| Frontend | cd schemastack-fe && npx ng serve app |
First-Time Setup
Run the setup script once to create the test database, user, organisation, and workspace:
bash
cd schemastack-fe
bash e2e/setup.shThis will:
- Create an
e2e_testPostgreSQL database (via docker exec) - Seed it with 5 tables covering all relationship types (authors, books, tags, book_tags, book_details)
- Register a test user (
e2e-test@schemastack.io) and verify their email - Create an organisation (
e2e-test-org) and workspace (e2e-workspace) - Trigger a schema sync (creates 5 views)
- Write
.env.e2ewith all configuration
The setup is idempotent — safe to re-run if the database or workspace already exists.
Running Tests
bash
# Load environment and run all E2E tests (Chromium only)
source .env.e2e && npx playwright test --project chromium
# Run a specific test suite
source .env.e2e && npx playwright test relationships.spec.ts --project chromium
# Run with browser visible
source .env.e2e && npx playwright test --project chromium --headed
# Run in interactive UI mode
source .env.e2e && npx playwright test --ui
# Run across all browsers (Chromium, Firefox, WebKit, Brave)
source .env.e2e && npx playwright test
# View the HTML test report
npx playwright show-reportWARNING
Always source .env.e2e before running tests. Without it, tests use fallback credentials that won't match your setup.
Test Suites
| Suite | File | Tests | What it covers |
|---|---|---|---|
| Relationships | relationships.spec.ts | 21 | ManyToOne, OneToMany, M2M, column types, sort, filter, add row, inline editing, selection, infinite scroll |
| Constraints | constraints.spec.ts | 11 | NOT_BLANK, MIN/MAX, EMAIL, URL, numeric, date, entity-level, unique |
| Permissions | permissions.spec.ts | 11 | View toggles (addable/editable/sortable), toolbar, selection, bulk bar, properties panel |
| Filter & Sort | filter-sort.spec.ts | 11 | ASC/DESC sort, add/remove filter, presets, conditional styles |
| Schema Ops | schema-ops.spec.ts | 10 | Column display, drag reorder, resize, properties, view tabs |
| Bulk Operations | bulk-operations.spec.ts | 5 | Selection, bulk delete, export, edit |
| Import/Export | import-export.spec.ts | 8 | CSV import, export formats, SSE, theme switching |
Test Data
The setup script seeds the e2e_test database with these tables:
authors (id, name, email, bio, active, created_at)
↑
books (id, title, isbn, author_id FK, pages, price, published_date, ...)
↕
book_tags (book_id, tag_id) — junction table
↓
tags (id, name, color)
book_details (id, book_id UNIQUE FK, edition, publisher, language, weight_grams)This covers:
- ManyToOne:
books.author_id → authors.id - OneToMany:
authors → books(reverse) - ManyToMany:
books ↔ tagsviabook_tags - OneToOne:
book_details.book_id → books.id(unique FK) - Nullable FK: one book has
author_id = NULL - All column types: VARCHAR, TEXT, INTEGER, DECIMAL, BOOLEAN, DATE, TIMESTAMP, UUID
Architecture
API Client (e2e/helpers/api.ts)
A Playwright APIRequestContext-based client that authenticates via POST /api/auth/login-with-org and provides methods for CRUD, schema ops, and test data management. Used in beforeAll to set up test state.
Spread Auth (e2e/helpers/spread-auth.ts)
Injects the JWT token into localStorage and navigates directly to a view URL, bypassing the login UI for speed. Each test gets a fresh page context.
Configuration (e2e/helpers/test-config.ts)
Reads from environment variables with sensible defaults:
| Variable | Default | Purpose |
|---|---|---|
E2E_API_URL | http://localhost:8080 | Backend API |
E2E_BASE_URL | http://localhost:4200 | Frontend app |
E2E_USER_EMAIL | e2e-test@schemastack.io | Test user |
E2E_USER_PASSWORD | E2eTestPassword123! | Test password |
E2E_ORG_SLUG | e2e-test-org | Organisation |
E2E_WORKSPACE_SLUG | e2e-workspace | Workspace |
Test Resilience
Tests use test.skip() when required features aren't available (e.g., no M2M columns, not enough rows for infinite scroll). This means the same tests work against any workspace — they skip gracefully instead of failing.
Writing New Tests
typescript
import { test, expect } from '@playwright/test';
import { TestApi } from './helpers/api';
import { loginToSpread } from './helpers/spread-auth';
import { testConfig } from './helpers/test-config';
let api: TestApi;
test.beforeAll(async () => {
api = new TestApi(testConfig.apiUrl, testConfig.user);
await api.login(testConfig.orgSlug);
});
test.afterAll(async () => {
await api.dispose();
});
test('my test', async ({ page }) => {
const views = await api.listViews(testConfig.orgSlug, testConfig.workspaceSlug);
test.skip(!views?.length, 'No views available');
await loginToSpread(page, api, {
orgSlug: testConfig.orgSlug,
workspaceSlug: testConfig.workspaceSlug,
viewSlug: views[0].slug,
});
// Wait for data to load
await page.waitForSelector('.data-row', { timeout: 15000 });
// Your assertions here
expect(await page.locator('.data-row').count()).toBeGreaterThan(0);
});Tips
- Don't use
networkidle— SSE connections keep the network active, causing timeouts - Use
test.skip()for features that depend on schema — makes tests portable across workspaces - Seed data via API in
beforeAll, not via UI — faster and more reliable - Use
--headedwhen debugging — watch the browser interact with the app - Use
--uifor interactive debugging — step through tests, inspect DOM snapshots - Check screenshots on failure — saved in
test-results/with traces