Reference for all core types, database views, and entity relationships in the Euriqa platform. Understanding the data model helps you work effectively with the REST API and interpret dashboard data.
Euriqa uses a relational data model built on Supabase (PostgreSQL). Every entity is scoped to a project, which belongs to a team. Test data flows from the Playwright reporter through the API and into a normalized schema designed for fast querying, historical analysis, and flakiness detection.
The model captures the full lifecycle of test execution: from the configuration used to run the tests, through individual test definitions, results, execution attempts, and attached artifacts. Flakiness scoring and quarantine status are maintained as separate records that update with each new run.
The following tree shows how entities relate from the top-level team down to individual test artifacts.
Team
├── TeamMember (role: owner | admin | member | viewer)
├── Invitation (status: pending | accepted | declined | expired)
├── Project
│ ├── ProjectSettings (retention, flakiness threshold, auto-quarantine)
│ ├── ApiKey (scoped to project)
│ ├── Webhook (event subscriptions)
│ ├── TestRun
│ │ ├── PlaywrightConfig (workers, timeout, browser, viewport, shard)
│ │ ├── TestSuite (hierarchical: file > describe > nested describe)
│ │ ├── Test (definition: title, file, location, tags)
│ │ │ ├── TestResult (per-run outcome)
│ │ │ │ ├── TestExecutionResult (per-attempt: retry tracking)
│ │ │ │ └── Attachment (inline or URL-based)
│ │ │ └── FlakinessRecord (score, flaky run count, quarantine status)
│ │ └── Artifact (screenshots, videos, traces, reports)
│ └── Database Views
│ ├── tests_with_latest_results
│ └── tests_flakiness_viewThese are the primary types that represent test execution data. They are created by the reporter SDK during test runs and are accessible via the REST API at https://app.euriqa.dev/api.
Represents a single execution of your test suite. Created when the reporter starts and updated when it finishes. Contains aggregate statistics and links to the CI build that triggered it.
interface TestRun {
id: string // Unique run identifier (e.g., "run_abc123")
projectId: string // Project this run belongs to
status: 'running' | 'passed' | 'failed' | 'cancelled'
title: string | null // Optional custom run title
startedAt: string // ISO 8601 timestamp
completedAt: string | null // ISO 8601 timestamp (null while running)
duration: number | null // Total duration in milliseconds
// Test counts
totalTests: number
passedTests: number
failedTests: number
skippedTests: number
flakyTests: number
// Git metadata
branch: string | null
commitSha: string | null
commitMessage: string | null
author: string | null
// CI metadata
ciProvider: string | null // "github-actions", "gitlab-ci", etc.
ciBuildId: string | null
ciBuildUrl: string | null
hostname: string | null
os: string | null
// Playwright metadata
playwrightVersion: string | null
workers: number | null
createdAt: string
updatedAt: string
}A test definition represents a unique test across all runs. It is identified by a deterministic keyId generated from the test title and file path, so the same test is tracked consistently even as runs change.
interface Test {
id: string // Unique test identifier
projectId: string // Project this test belongs to
keyId: string // Deterministic ID from title + file path
title: string // Test name (e.g., "should login successfully")
fullTitle: string // Full title path (e.g., "Auth > Login > should login successfully")
filePath: string // Source file (e.g., "tests/auth/login.spec.ts")
location: { // Exact source location
file: string
line: number
column: number
} | null
tags: string[] // Playwright tags (e.g., ["@smoke", "@auth"])
suiteId: string | null // Parent test suite
createdAt: string
updatedAt: string
}The outcome of a test within a specific run. Links a test definition to a run and captures the overall status, including whether it was detected as flaky (passed on retry after initial failure).
interface TestResult {
id: string
testId: string // References Test.id
runId: string // References TestRun.id
projectId: string
status: 'passed' | 'failed' | 'skipped' | 'flaky'
duration: number // Total duration in milliseconds
retries: number // Number of retry attempts
isFlaky: boolean // true if test passed after retries
workerIndex: number | null // Playwright worker that ran this test
parallelIndex: number | null // Parallel execution index
// Error details (for failures)
errorMessage: string | null
errorStack: string | null
errorLocation: {
file: string
line: number
column: number
} | null
errorSnippet: string | null // Source code around the error
expected: string | null // Expected value (assertions)
actual: string | null // Actual value (assertions)
// Output
stdout: string | null
stderr: string | null
createdAt: string
}Represents a single attempt within a test result. When retries are enabled, a test may have multiple execution results -- one for each attempt. This is the key data structure for flakiness detection: if attempt 0 fails but attempt 1 passes, the test is marked as flaky.
interface TestExecutionResult {
id: string
testResultId: string // References TestResult.id
projectId: string
attempt: number // Attempt index (0 = first try, 1 = first retry, etc.)
status: 'passed' | 'failed' | 'skipped' | 'timedOut' | 'interrupted'
duration: number // Duration of this attempt in milliseconds
startedAt: string // ISO 8601 timestamp
// Error details for this specific attempt
errorMessage: string | null
errorStack: string | null
// Steps executed in this attempt
steps: TestStep[]
createdAt: string
}
interface TestStep {
title: string // Step description (e.g., "page.click('#submit')")
category: 'action' | 'assertion' | 'hook' | 'fixture'
status: 'passed' | 'failed' | 'skipped'
duration: number // Step duration in milliseconds
error: string | null // Error message if step failed
steps: TestStep[] // Nested sub-steps
}Represents a grouping of tests, typically mapping to a describe block in Playwright. Suites are hierarchical -- a file contains top-level suites, which can contain nested suites.
interface TestSuite {
id: string
projectId: string
runId: string // References TestRun.id
title: string // Suite name (e.g., "Login Page")
filePath: string // Source file path
parentSuiteId: string | null // Parent suite for nesting (null = top-level)
createdAt: string
}Captures the Playwright configuration used for a specific test run. This metadata is extracted automatically by the reporter and stored alongside the run for debugging and analysis.
interface PlaywrightConfig {
id: string
runId: string // References TestRun.id
projectId: string
workers: number // Number of parallel workers
maxFailures: number // Stop after N failures (0 = unlimited)
timeout: number // Default test timeout in milliseconds
retries: number // Number of retries configured
repeatEach: number // Times to repeat each test
fullyParallel: boolean // Whether tests run fully in parallel
// Browser & environment
browser: string | null // "chromium", "firefox", "webkit"
viewport: {
width: number
height: number
} | null
headless: boolean | null
// Sharding (for distributed testing)
shardTotal: number | null // Total number of shards
shardCurrent: number | null // Current shard index
// Filtering
grep: string | null // Test name filter pattern
grepInvert: string | null // Inverted filter pattern
projects: string[] // Playwright project names
createdAt: string
}Represents a file or inline content attached to a test result. Small attachments (under 10 KB) are stored inline as base64-encoded content. Larger attachments are uploaded to storage and referenced by URL.
interface Attachment {
id: string
testResultId: string // References TestResult.id
projectId: string
name: string // Original file name
contentType: string // MIME type (e.g., "image/png", "application/zip")
path: string | null // Storage path (for uploaded files)
body: string | null // Inline content (base64, for small files)
url: string | null // Signed URL for accessing the file
fileSize: number | null // File size in bytes
createdAt: string
}Tracks the flakiness score and quarantine status for a test definition. Updated after each run that includes the test. The score is a value between 0 and 1 representing the ratio of flaky runs to total runs.
interface FlakinessRecord {
id: string
testId: string // References Test.id
projectId: string
flakinessScore: number // 0.0 to 1.0
totalRuns: number // Total number of runs including this test
flakyRuns: number // Number of runs where this test was flaky
lastFlakyAt: string | null // ISO 8601 timestamp of last flaky occurrence
isQuarantined: boolean // Whether this test is currently quarantined
quarantinedAt: string | null // When quarantine was applied
createdAt: string
updatedAt: string
}These types manage team organization, membership, authentication, and project configuration. All team data is isolated via row-level security policies at the database level.
interface Team {
id: string
name: string // Team display name
slug: string // URL-friendly identifier
createdBy: string // User ID of the team creator
createdAt: string
updatedAt: string
}interface TeamMember {
id: string
teamId: string // References Team.id
userId: string // References the authenticated user
role: 'owner' | 'admin' | 'member' | 'viewer'
joinedAt: string // ISO 8601 timestamp
createdAt: string
}interface ApiKey {
id: string
projectId: string // Scoped to a single project
name: string // Descriptive label (e.g., "GitHub Actions")
keyHash: string // Hashed key value (never stored in plaintext)
keyPrefix: string // First 8 characters for identification
isActive: boolean // Toggle without deleting
expiresAt: string | null // Optional expiration date
lastUsedAt: string | null // Automatically updated on each API call
createdAt: string
}interface Webhook {
id: string
projectId: string // References Project.id
url: string // Delivery endpoint
events: string[] // Subscribed events: ["run.completed", "run.failed"]
secret: string | null // Shared secret for signature verification
headers: Record<string, string> // Custom headers sent with each delivery
isActive: boolean // Toggle without deleting
createdAt: string
updatedAt: string
}interface ProjectSettings {
id: string
projectId: string // References Project.id
dataRetentionDays: number // How long to keep test data (default: 90)
flakinessThreshold: number // Score threshold for flagging (default: 0.3)
autoQuarantine: boolean // Auto-quarantine tests exceeding threshold
createdAt: string
updatedAt: string
}interface Invitation {
id: string
teamId: string // References Team.id
email: string // Invitee email address
role: 'admin' | 'member' | 'viewer' // Role assigned on acceptance
status: 'pending' | 'accepted' | 'declined' | 'expired'
invitedBy: string // User ID of the inviter
expiresAt: string // 7-day expiration from creation
createdAt: string
updatedAt: string
}Euriqa uses materialized database views to provide fast access to commonly queried data without requiring expensive joins at query time.
Joins each test definition with its most recent result. Used by the test listing pages to show current status, duration, and flakiness for every test without loading full run data.
// View columns
interface TestWithLatestResult {
// From Test
id: string
keyId: string
title: string
fullTitle: string
filePath: string
tags: string[]
// From latest TestResult
latestStatus: 'passed' | 'failed' | 'skipped' | 'flaky'
latestDuration: number
latestRunId: string
latestRunAt: string
// From FlakinessRecord
flakinessScore: number | null
isQuarantined: boolean
}Aggregates flakiness data for each test in the project. Powers the flakiness dashboard, including the flaky tests table, trend charts, and summary statistics.
// View columns
interface TestFlakinessView {
testId: string
testTitle: string
filePath: string
flakinessScore: number
totalRuns: number
flakyRuns: number
lastFlakyAt: string | null
isQuarantined: boolean
quarantinedAt: string | null
}The following relationships define how entities connect across the data model:
Flakiness is detected automatically by the reporter and scored on the platform. A test is considered flaky when it fails on one or more attempts but ultimately passes on a retry within the same run.
During a test run, the reporter tracks each attempt via TestExecutionResult. If a test has multiple attempts and at least one attempt fails while the final attempt passes, the test result is marked with status: 'flaky' and isFlaky: true.
The flakiness score is calculated as the ratio of flaky runs to total runs for each test:
flakinessScore = flakyRuns / totalRuns
// Examples:
// 3 flaky out of 10 runs → score = 0.3
// 7 flaky out of 10 runs → score = 0.7
// 0 flaky out of 50 runs → score = 0.0The score is stored on the FlakinessRecord and updated after each run. Scores above the project's configured threshold (default: 0.3) are flagged in the dashboard. When auto-quarantine is enabled, tests exceeding the threshold are automatically quarantined.
Every test definition has a deterministic keyId that uniquely identifies it across runs. This allows Euriqa to track the same test over time, even when test IDs change between runs. The key ID is generated using the following process:
Auth > Login > should login successfully).tests/auth/login.spec.ts).// Pseudocode for key ID generation
const input = `${fullTitle}::${filePath}`
const keyId = hash(input)
// Example:
// fullTitle: "Auth > Login > should login successfully"
// filePath: "tests/auth/login.spec.ts"
// input: "Auth > Login > should login successfully::tests/auth/login.spec.ts"
// keyId: "k_8f3a2b1c..." (deterministic hash)This approach means that renaming a test or moving it to a different file will produce a new key ID, effectively treating it as a new test. This is intentional -- it ensures flakiness history and other per-test tracking resets when a test fundamentally changes.