PlatformPricingDocsBlogSign In
Sign InGet Started Free
PlatformPricingDocsBlogSign In

Stay up to date

Get the latest on the Euriqa platform, product updates, and test automation best practices.

Platform

  • Platform
  • Services
  • Pricing
  • Docs

Developers

  • Getting Started
  • API Reference
  • SDK
  • CI/CD Integration

Company

  • About
  • Blog
  • Contact
  • Security

Legal

  • Privacy Policy
  • Terms of Service
© 2026 Euriqa. All rights reserved.
  • Getting Started

    • Introduction
    • Quickstart
    • Reporter Configuration
    • CI/CD Setup
  • Platform Features

    • Test Orchestration
    • AI Features
    • Flakiness Detection
    • Artifacts & Uploads
    • Webhooks
    • Teams & Projects
  • API Reference

    • Authentication
    • API Reference
    • Data Model
  • Security

    • Security Overview
  • Playwright

    • Playwright Docs
    • Playwright API
    • Playwright Test Reporters

Data Model

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.

Overview

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.


Entity Hierarchy

The following tree shows how entities relate from the top-level team down to individual test artifacts.

text
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_view

Core Types

These 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.

TestRun

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.

typescript
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
}

Test (Definition)

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.

typescript
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
}

TestResult

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).

typescript
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
}

TestExecutionResult

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.

typescript
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
}

TestSuite

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.

typescript
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
}

PlaywrightConfig

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.

typescript
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
}

Attachment

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.

typescript
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
}

FlakinessRecord

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.

typescript
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
}

Team & Access Types

These types manage team organization, membership, authentication, and project configuration. All team data is isolated via row-level security policies at the database level.

Team

typescript
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
}

TeamMember

typescript
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
}

ApiKey

typescript
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
}

Webhook

typescript
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
}

ProjectSettings

typescript
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
}

Invitation

typescript
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
}

Database Views

Euriqa uses materialized database views to provide fast access to commonly queried data without requiring expensive joins at query time.

tests_with_latest_results

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.

typescript
// 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
}

tests_flakiness_view

Aggregates flakiness data for each test in the project. Powers the flakiness dashboard, including the flaky tests table, trend charts, and summary statistics.

typescript
// View columns
interface TestFlakinessView {
  testId: string
  testTitle: string
  filePath: string
  flakinessScore: number
  totalRuns: number
  flakyRuns: number
  lastFlakyAt: string | null
  isQuarantined: boolean
  quarantinedAt: string | null
}

Key Relationships

The following relationships define how entities connect across the data model:

  • A Team has many TeamMembers, Invitations, and Projects.
  • A Project has many TestRuns, ApiKeys, Webhooks, and one ProjectSettings.
  • A TestRun has one PlaywrightConfig, many TestSuites, and many TestResults.
  • A Test (definition) has many TestResults across runs and one FlakinessRecord.
  • A TestResult has many TestExecutionResults (one per attempt) and many Attachments.
  • A TestSuite can have a parent TestSuite (self-referencing for nested describe blocks).
  • ApiKeys are scoped to a single project and cannot access data from other projects or teams.
  • Webhooks are project-scoped and fire only for events within that project.
  • All entities include a projectId foreign key, enabling row-level security isolation.

Flaky Test Detection

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.

Detection Logic

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.

Scoring Formula

The flakiness score is calculated as the ratio of flaky runs to total runs for each test:

typescript
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.0

The 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.

The flakiness score only considers runs where the test was actually executed. Runs where the test was skipped or not included do not affect the score.

Test Key ID Generation

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:

  1. Extract the full title path-- The test's full title is constructed by joining the suite hierarchy and test name (e.g., Auth > Login > should login successfully).
  2. Extract the file path -- The relative path to the test file (e.g., tests/auth/login.spec.ts).
  3. Concatenate with a separator -- The full title and file path are joined with a delimiter to create a unique input string.
  4. Hash the input -- The concatenated string is hashed to produce a deterministic, fixed-length identifier. This ensures the same test always produces the same key ID regardless of when or where it runs.
typescript
// 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.