# Testing Strategies for Open-SEO: A Layered Unit and E2E Approach

> Explore Open-SEO testing strategies: Vitest for unit/integration and Playwright for E2E. Ensure schema parity and validate full-stack user flows with this layered approach.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: testing-strategies
- Published: 2026-08-13

---

**Open-SEO uses a dual-layer testing strategy combining Vitest for fast unit and integration checks with Playwright for realistic end-to-end browser testing, ensuring schema parity across SQLite and PostgreSQL while validating full-stack user flows.**

The every-app/open-seo repository implements a comprehensive testing strategy that prioritizes both developer velocity and production reliability. By orchestrating Vitest for rapid feedback on business logic and database contracts alongside Playwright for critical user journey validation, the codebase maintains strict quality gates across its Cloudflare Workers deployment target.

## Layered Testing Architecture

Open-SEO divides its test suite into two distinct execution layers that run sequentially in CI. This separation allows quick validation of pure functions and data contracts before exercising the full browser-based stack.

### Unit and Integration Tests with Vitest

The Vitest configuration in [`vitest.config.ts`](https://github.com/every-app/open-seo/blob/main/vitest.config.ts) restricts test discovery to `src/**/*.test.ts`, isolating repository-specific tests from dependencies in `node_modules`. These tests validate pure functions, type-safety, and database schema contracts in milliseconds, providing immediate feedback on commits.

A critical integration test in [`src/db/schema-parity.test.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema-parity.test.ts) ensures Drizzle ORM schema compatibility across database dialects. This test programmatically generates both SQLite and PostgreSQL schemas from the Drizzle definitions and asserts structural equality, guaranteeing that migrations work on either backend without divergence:

```ts
test('SQLite & Postgres schemas stay in sync', async () => {
  const sqlite = await generateSQLiteSchema();
  const pg = await generatePostgresSchema();

  // Deep structural comparison (ignores dialect-specific defaults)
  expect(normalize(sqlite)).toEqual(normalize(pg));
});

```

### End-to-End Tests with Playwright

Configured in [`playwright.config.ts`](https://github.com/every-app/open-seo/blob/main/playwright.config.ts), the E2E suite resides in the `e2e/` directory and exercises the full stack—including the frontend UI, API endpoints, and database—within a browser-like environment. These tests catch regressions that span multiple services by simulating real user interactions.

The [`e2e/keyword-research-navigation.spec.ts`](https://github.com/every-app/open-seo/blob/main/e2e/keyword-research-navigation.spec.ts) file validates core user flows such as navigating to the keyword research page, entering search terms, and verifying results tables render correctly:

```ts
test('navigate to keyword research and verify UI components', async ({ page }) => {
  await page.goto('/keyword-research');
  await expect(page.getByRole('heading', { name: /Keyword Research/ })).toBeVisible();
  await page.getByLabel('Search term').fill('open-seo');
  await page.getByRole('button', { name: /Search/ }).click();
  await expect(page.getByTestId('results-table')).toBeVisible();
});

```

## Performance Guardrails

Beyond functional correctness, Open-SEO enforces performance budgets through dedicated Playwright specs. The [`e2e/domain-overview-filters.perf.spec.ts`](https://github.com/every-app/open-seo/blob/main/e2e/domain-overview-filters.perf.spec.ts) file measures response times for critical user interactions, ensuring filter operations on the domain overview page remain within acceptable thresholds:

```ts
test('filter performance stays under threshold', async ({ page }) => {
  await page.goto('/domain-overview');
  const start = Date.now();
  await page.getByLabel('Country').selectOption('US');
  await page.waitForResponse(resp => resp.url().includes('/api/domain-overview') && resp.status() === 200);
  const duration = Date.now() - start;
  expect(duration).toBeLessThan(2000); // 2 seconds max
});

```

Functional filter behavior is separately validated in [`e2e/domain-overview-filters.spec.ts`](https://github.com/every-app/open-seo/blob/main/e2e/domain-overview-filters.spec.ts), maintaining separation between performance and functional concerns.

## CI Pipeline Orchestration

The testing strategy relies on strict sequencing within the CI pipeline (defined in the repository’s GitHub Actions workflow). **Vitest unit tests** execute first on every commit; only upon passage do **Playwright E2E tests** trigger against the built Cloudflare Workers environment. If either layer fails, the commit is blocked, preserving release quality by preventing broken code from reaching production.

## Summary

- Open-SEO employs **Vitest** for fast unit and integration tests scoped to `src/**/*.test.ts`, ensuring rapid feedback on schema parity and business logic.
- **Playwright** E2E tests in the `e2e/` directory validate full-stack user flows including keyword research navigation and domain overview filtering.
- The [`src/db/schema-parity.test.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema-parity.test.ts) file guarantees database agnosticism by asserting structural equality between generated SQLite and PostgreSQL schemas.
- Performance thresholds (such as the 2-second maximum for filter responses) are enforced through dedicated [`.perf.spec.ts`](https://github.com/every-app/open-seo/blob/main/.perf.spec.ts) files.
- CI orchestration runs Vitest before Playwright, blocking commits if either layer fails.

## Frequently Asked Questions

### What testing frameworks does Open-SEO use?

Open-SEO uses **Vitest** for unit and integration testing and **Playwright** for end-to-end browser testing. This combination allows the repository to validate both isolated business logic and complete user workflows across the Cloudflare Workers stack.

### How does Open-SEO ensure compatibility between SQLite and PostgreSQL?

The repository includes a specialized integration test in [`src/db/schema-parity.test.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema-parity.test.ts) that generates schemas for both database dialects from the Drizzle ORM definitions. It performs deep structural comparisons to ensure migrations remain compatible across SQLite and PostgreSQL backends without manual drift detection.

### Where are the end-to-end tests located in the repository?

All E2E tests reside in the `e2e/` directory at the project root, keeping them separate from production source code and unit tests. Key files include [`e2e/keyword-research-navigation.spec.ts`](https://github.com/every-app/open-seo/blob/main/e2e/keyword-research-navigation.spec.ts), [`e2e/domain-overview-filters.spec.ts`](https://github.com/every-app/open-seo/blob/main/e2e/domain-overview-filters.spec.ts), and [`e2e/domain-overview-filters.perf.spec.ts`](https://github.com/every-app/open-seo/blob/main/e2e/domain-overview-filters.perf.spec.ts).

### What performance standards does Open-SEO enforce through testing?

Open-SEO enforces specific timing thresholds in Playwright performance tests. For example, domain overview filter operations must complete within **2 seconds** from user interaction to API response, as validated in [`e2e/domain-overview-filters.perf.spec.ts`](https://github.com/every-app/open-seo/blob/main/e2e/domain-overview-filters.perf.spec.ts).