Eddy Dev Handbook
Technical reference

Testing

Testing strategies, types of tests, and conventions used in the Eddy project

Testing in Eddy

Source: This page is based on docs/TESTING.md

Last Updated: January 8, 2026

This document outlines the testing strategies, types of tests, and conventions used in the Eddy project. Our goal is to ensure code quality, prevent regressions, and maintain a stable application through a multi-layered testing approach.

Overview

Our testing suite is primarily built using Jest for unit and integration tests, and Playwright for end-to-end tests. We categorize our tests into several types, each with a specific purpose and scope.


Test Categories

1. Unit Tests

Unit tests focus on individual functions or components in isolation. They are designed to be fast and specific, verifying that a single piece of logic works as expected.

Location: app/__tests__/utils/ and app/__tests__/zod/

Purpose: To test pure functions, business logic, algorithms, and data validation schemas.

Key Areas Covered:

  • Algorithmic Logic: Graph and workflow structure helpers like calculating distances between stages (workflowDistance.test.ts), identifying loops (getLoopSets.test.ts), and preparing edges for layouting (dagreLayoutHelpers.test.ts)
  • Business Logic & Rules: Core application logic such as workflow validation (validateWorkflowNew.test.ts) and calculating UI state (canRoleTransitionBeOpen.test.ts)
  • Data Validation: Zod schemas are tested to ensure they correctly parse and reject expected data structures, such as webhook payloads (zod/hooks/workflowRuns.test.ts)
  • Utility Functions: General-purpose helpers for data manipulation (innerJoin.test.ts) and color calculations (colours.test.ts)

2. Integration Tests

Integration tests verify the interaction between different parts of the system, primarily focusing on the service layer's communication with the database.

Location: app/__tests__/integration/

Purpose: To ensure that services correctly execute database queries, handle transactions, and maintain data integrity for key application features.

Setup: These tests run against a real database connection (managed via knex). Each test case is wrapped in a transaction that is rolled back after completion to ensure test isolation and prevent side effects.

Key Features Covered:

  • Workflow Import/Export: End-to-end testing of the workflow serialization and deserialization logic, including all related entities like pages, blocks, sheets, and rules (workflowExport.test.ts, workflowImport.test.ts)
  • Workflow Copying: Verifying that workflows can be duplicated correctly, with special attention to how sheet/column relationships and rules are handled (workflowCopy.test.ts, rulesCopy.test.ts)
  • Data Reordering: Testing services that perform complex reordering operations on ordered entities like workflow sections/blocks (reorderWorkflow.test.ts) and sheet columns (columnReordering.test.ts)

3. End-to-End (E2E) Tests

Our E2E tests are built using Playwright to simulate real user interactions in a browser environment. They are crucial for verifying that entire features and user flows work correctly from the UI to the database.

Location: app/__e2es__/

Purpose: To validate complete user journeys, ensuring the frontend, backend APIs, and database are all integrated correctly. These tests catch regressions in critical paths like user authentication, workflow creation, and session execution.

Setup & Teardown:

  • Authentication: A global setup file (auth.setup.ts) handles user registration before the test suite runs. It creates a new user and saves the authenticated state to a file (playwright/.auth/user.json). This allows each test file to start with a logged-in user, speeding up execution and isolating authentication from feature tests.
  • Cleanup: A global teardown script (global.teardown.ts) runs after all tests are complete. It cleans up by deleting the test user's account via the UI, ensuring a clean state for subsequent test runs.
  • Helpers: Reusable functions for sign-up, sign-in, and creating test-specific user data are centralized in spec.helpers.ts.

Key Flows Covered:

  • Core User Actions: Signing up, signing in, editing a user profile (profile.spec.ts), and creating/managing workspaces (workspace.spec.ts)
  • Workspace Collaboration: Inviting new members to a workspace and managing invitations (members.spec.ts)
  • Workflow Authoring:
    • Creating workflows from scratch and adding/configuring various blocks like text inputs, date pickers, and select lists (workflow.spec.ts)
    • Defining and managing roles within a workflow (workflow.spec.ts)
    • Reordering sections and blocks via drag-and-drop, ensuring order persists (workflow-reorder.spec.ts)
    • Copying workflows with different options for their associated sheets (copyWorkflow.spec.ts)
  • Data Management (Sheets): Creating sheets, adding, editing, and deleting columns (sheet.spec.ts)
  • Session Execution:
    • Running through a workflow as a participant, filling out various input types, and progressing between stages (session.spec.ts)
    • Testing dynamic UI changes based on conditional rules, such as showing/hiding sections or blocking progression (rules.spec.ts)
    • Interacting with embedded sheet sections to add, edit, delete, filter, and search records within a session (sheetSection.spec.ts)

For comprehensive documentation on E2E testing, including detailed setup instructions, the createTestScenario service, workflow import strategies, and best practices, see End-to-End Testing.


Running Tests

Unit Tests

To run unit tests, use Jest directly:

# Run a specific test file
npx jest __tests__/utils/validateWorkflowNew.test.ts

# Run all unit tests
npx jest __tests__/utils/

Integration Tests

Integration tests require a test database. Make sure your test database is configured in envs/.env.test:

npx jest __tests__/integration/

E2E Tests

E2E tests use Playwright and require the development server to be running:

yarn e2e

For detailed information on running E2E tests, configuration options, and writing new tests, see End-to-End Testing.


Writing Tests

Unit Test Conventions

  1. Test File Naming: Use .test.ts or .test.tsx extension
  2. Helper Functions: Create reusable helper functions (e.g., createWorkflow(), createPage()) to generate test data with proper types
  3. Descriptive Test Names: Use clear, descriptive test names that explain what is being tested and the expected behavior
  4. Arrange-Act-Assert: Structure tests with clear sections for setup, execution, and verification

Example from validateWorkflowNew.test.ts:

it('should error when workflow has no pages', () => {
  // Arrange
  const data: CheckContext = {
    workflow: createWorkflow(),
    pages: [],
    sections: [],
    blocks: [],
    pageTransitions: [],
    stageRoleAssignments: []
  }

  // Act
  const errors = validateWorkflowNew(data)

  // Assert
  expect(errors.length).toBeGreaterThanOrEqual(1)
  const noPagesError = errors.find(e => e.code === 'WORKFLOW_NO_PAGES')
  expect(noPagesError).toBeDefined()
  expect(noPagesError?.level).toBe('error')
})

Integration Test Conventions

  1. Database Transactions: Wrap tests in transactions that are rolled back to maintain isolation
  2. Real Data: Use actual database connections and real data structures
  3. Cleanup: Ensure proper cleanup after each test to avoid side effects

Example pattern:

describe('Workflow Export Service', () => {
  let trx: Knex.Transaction

  beforeEach(async () => {
    trx = await knex.transaction()
  })

  afterEach(async () => {
    await trx.rollback()
  })

  it('should export workflow with all related entities', async () => {
    // Test implementation using trx for all queries
  })
})

Areas for Test Coverage Expansion

Based on the application's documented features, this section identifies key areas where our testing strategy could be enhanced to improve coverage and prevent regressions in critical user-facing functionality.

1. Roles & Permissions Enforcement

While we test the creation of roles, we should add explicit tests to verify that permissions are correctly enforced during a session run. This is crucial for multi-user workflows.

E2E Tests:

  • Create a multi-stage workflow with two roles: "Submitter" and "Reviewer"
  • Assign a user to the "Reviewer" role on a stage where can_write is false. Log in as that user and assert that input blocks are disabled/read-only
  • Assign a user to a role where can_progress is false. Log in as that user and assert that the "Complete Stage" button is disabled or hidden
  • Test a "Handover" scenario: User A completes a stage, and an assignment is created for User B on the next stage. Log in as User B and verify they can access the new stage

The Start Link feature is a primary entry point for external users and involves a complex orchestration of user creation, session creation, and role assignment.

E2E Tests:

  • As a builder, create and publish a workflow
  • Create a Self-Service Start Link for it
  • Configure the link to assign the "Self-service user" to a specific role and a pre-existing workspace member to another
  • In a new incognito browser window, navigate to the start link URL
  • Sign up as a new user
  • Start the session and assert that you are redirected to the new workflow run
  • Verify that the new user has been correctly assigned to their role and the pre-assigned member is also correctly assigned
  • Test the "Resumable Sessions" flow by navigating back to the start link and ensuring the option to continue the existing session is presented

3. Advanced Sheet Functionality

The current sheet tests cover basic column management. We should expand this to cover the rich filtering and view management features.

E2E Tests:

  • Create a sheet with multiple records
  • Use the Query Builder UI to create a complex filter (e.g., Status = 'Completed' AND Priority > 2). Assert that the grid correctly filters the rows
  • Save this configuration as a new "Sheet View"
  • Clear the filter, then load the saved view and assert that the filter is correctly reapplied
  • Within a saved view, reorder and hide columns. Reload the view and assert that the column order and visibility are preserved

4. Workflow Lifecycle & Validation

We have unit tests for the validation logic, but we should add E2E tests that verify the user-facing validation experience.

E2E Tests:

  • Create a workflow with validation issues (e.g., no pages, unreachable pages, missing role assignments)
  • Attempt to publish the workflow
  • Assert that the validation panel appears with clear error messages
  • Fix the errors one by one and assert that the validation panel updates in real-time
  • Once all errors are resolved, successfully publish the workflow

5. Notifications

Notifications are a key part of the user experience for multi-user workflows, but they are not currently covered by E2E tests.

E2E Tests:

  • Create a workflow with two stages assigned to different roles
  • User A starts a session and completes the first stage
  • Log in as User B (assigned to the second stage)
  • Assert that User B has a new notification indicating they have been assigned to a stage
  • Click the notification and assert that it navigates to the correct workflow run

Best Practices

When Writing Tests

  1. Test behavior, not implementation - Focus on what the code does, not how it does it
  2. Keep tests independent - Each test should be able to run in isolation
  3. Use descriptive names - Test names should clearly explain what is being tested
  4. Avoid test interdependence - Don't rely on the order of test execution
  5. Mock external dependencies - Use mocks for external APIs, services, etc.
  6. Keep tests simple - Complex tests are hard to maintain and understand

When Running Tests

  1. Run tests frequently - Run relevant tests before committing code
  2. Fix failing tests immediately - Don't let broken tests accumulate
  3. Monitor test performance - Slow tests should be optimized or moved to integration/E2E
  4. Use watch mode during development - npx jest --watch for rapid feedback

On this page