Skip to content

Acceptance Tests: run locally (AI runbook)

This document captures the exact, working setup used by AI on 2026-01-22 to run the Gherkin/Playwright acceptance tests locally against a locally running API + web app.

It exists so future AI (or humans) can re-run acceptance tests without rediscovering the same environment pitfalls.

Prerequisites

  • Rancher Desktop installed (used to provide a Docker daemon on this machine).
  • Docker Desktop was not available on this machine (open -a Docker failed), so Rancher Desktop was used instead.
  • Node + pnpm installed.
  • Verified on this machine: node v24.3.0, pnpm 9.0.0.
  • curl, nc available.
  • This repo checked out (Nx workspace).

High-level overview

  1. Start a Docker daemon (Rancher Desktop).
  2. Ensure Postgres exists and is reachable.
  3. Apply Prisma migrations to a dedicated schema (important workaround).
  4. Start API with required env vars (important: some webhook secrets are getOrThrow()).
  5. Start web (Vite) configured to call local API.
  6. Run acceptance tests (Playwright-BDD). The suite will:
  7. verify /health and the web URL
  8. seed data through /api/v1/test-seeding/*
  9. run tests
  10. cleanup through /api/v1/test-seeding/cleanup

1) Start Rancher Desktop (Docker daemon)

Start Rancher Desktop:

open -a "Rancher Desktop"

Wait until Docker is ready:

for i in $(seq 1 90); do
  docker ps >/dev/null 2>&1 && echo "Docker is ready" && exit 0
  sleep 2
done
echo "Docker did not become ready in time" && exit 1

2) PostgreSQL

Important note: database name vs schema

On this machine there was already a host Postgres reachable on localhost:5432 with a database named forma3d_connect.

When trying to use a separate database like forma3d_connect_test, Prisma commands failed with:

  • permission denied to create database
  • and/or P1003: Database ... does not exist

To avoid needing CREATE DATABASE, we applied migrations into a separate schema inside the existing DB:

  • DB: forma3d_connect
  • schema: forma3d_connect_test

This is the main “gotcha” that future runs should keep.

Optional: verify port is open

nc -zv 127.0.0.1 5432

If you need to run Postgres in Docker (fallback)

If you do not already have a working Postgres on localhost:5432, run one in Docker:

docker rm -f forma3d-connect-postgres >/dev/null 2>&1 || true

docker run -d \
  --name forma3d-connect-postgres \
  -e POSTGRES_PASSWORD=postgres \
  -e POSTGRES_DB=forma3d_connect \
  -p 5432:5432 \
  postgres:16

Wait until it’s ready:

for i in $(seq 1 60); do
  docker exec forma3d-connect-postgres pg_isready -U postgres -h localhost >/dev/null 2>&1 && echo "Postgres is ready" && exit 0
  sleep 1
done
echo "Postgres not ready" && exit 1

3) Prisma migrations (schema-based)

Run migrations into a dedicated schema:

export DATABASE_URL='postgresql://postgres@localhost:5432/forma3d_connect?schema=forma3d_connect_test'

pnpm prisma generate
pnpm prisma migrate deploy

Notes:

  • prisma migrate deploy is used (same approach as CI), and it worked with the schema-based DATABASE_URL.
  • The acceptance tests do their own test data seeding via API endpoints; you don’t need pnpm prisma db seed for acceptance tests.

4) Start the API locally

Critical env vars

The API uses ConfigService.getOrThrow() for some webhook secrets. Even if you don’t use these integrations locally, the app will crash without them.

This set worked:

export DATABASE_URL='postgresql://postgres@localhost:5432/forma3d_connect?schema=forma3d_connect_test'
export APP_PORT='3000'
export APP_URL='http://localhost:3000'
export FRONTEND_URL='http://localhost:4200'
export NODE_ENV='development'
export RATE_LIMIT_DISABLED='true'

# API key required (the API is fail-closed when INTERNAL_API_KEY is missing)
export INTERNAL_API_KEY='local-dev-key'

# Required due to getOrThrow() usage
export SIMPLYPRINT_WEBHOOK_SECRET='local-simplyprint-webhook-secret'
export SHOPIFY_WEBHOOK_SECRET='local-shopify-webhook-secret'
export SHOPIFY_SHOP_DOMAIN='example.myshopify.com'
export SHOPIFY_API_VERSION='2024-01'
export SHOPIFY_ACCESS_TOKEN='local-shopify-access-token'

Start command

In this run, starting the API via Nx had issues with “waiting for api:serve:development in another nx process”, so the built output was started directly:

node dist/apps/api/main.js

Verify it’s up:

curl -s -o /dev/null -w '%{http_code}\n' http://localhost:3000/health

Expected: 200

5) Start the web app locally (Vite)

Web runs on port 4200 (configured in apps/web/vite.config.mts).

export VITE_API_URL='http://localhost:3000'
pnpm nx dev web

Verify it’s up:

curl -s -o /dev/null -w '%{http_code}\n' http://localhost:4200/

Expected: 200 (or another non-5xx response if the app serves HTML)

6) Run acceptance tests locally

The acceptance-tests Nx project defines a local configuration that targets:

  • STAGING_API_URL=http://localhost:3000
  • STAGING_WEB_URL=http://localhost:4200

Run:

export STAGING_API_URL='http://localhost:3000'
export STAGING_WEB_URL='http://localhost:4200'
export INTERNAL_API_KEY='local-dev-key'

# Optional: force local-like behavior (no CI retries)
export CI=''

pnpm nx run acceptance-tests:test

Expected output ends with:

  • 80 passed
  • global teardown cleanup via API

What “seeding” means in this context

Acceptance tests seed data in Playwright global setup (apps/acceptance-tests/src/support/global-setup.ts) by calling API endpoints:

  • GET /api/v1/test-seeding/status
  • DELETE /api/v1/test-seeding/cleanup
  • POST /api/v1/test-seeding/seed-all

So as long as the API is running and INTERNAL_API_KEY matches, test data will be created automatically before the suite starts.

Troubleshooting

API fails to start with “Configuration key ... does not exist”

This happens when a module/guard calls configService.getOrThrow(...).

Fix: set the missing env var(s). In this run, the minimum needed were:

  • SIMPLYPRINT_WEBHOOK_SECRET
  • SHOPIFY_WEBHOOK_SECRET
  • SHOPIFY_SHOP_DOMAIN
  • SHOPIFY_API_VERSION
  • SHOPIFY_ACCESS_TOKEN

Prisma migrate deploy fails with “permission denied to create database”

Avoid creating a database; use a schema in an existing DB instead:

export DATABASE_URL='postgresql://postgres@localhost:5432/forma3d_connect?schema=forma3d_connect_test'
pnpm prisma migrate deploy

Acceptance tests time out on Orders/Mappings UI elements

In this repo, most API endpoints are protected by the API key guard. The web app must include the API key for read calls too (orders/mappings/logs/dashboard/shipping).

If UI tests fail with missing rows/filters/search inputs while API tests pass, the likely cause is “web app is calling protected endpoints without X-API-Key”.

Quick “one shot” copy/paste (assuming Postgres already exists)

# 0) Docker (Rancher Desktop)
open -a "Rancher Desktop"
for i in $(seq 1 90); do docker ps >/dev/null 2>&1 && break; sleep 2; done

# 1) Prisma schema migrations
export DATABASE_URL='postgresql://postgres@localhost:5432/forma3d_connect?schema=forma3d_connect_test'
pnpm prisma generate
pnpm prisma migrate deploy

# 2) API
export APP_PORT='3000' APP_URL='http://localhost:3000' FRONTEND_URL='http://localhost:4200' NODE_ENV='development' RATE_LIMIT_DISABLED='true'
export INTERNAL_API_KEY='local-dev-key'
export SIMPLYPRINT_WEBHOOK_SECRET='local-simplyprint-webhook-secret'
export SHOPIFY_WEBHOOK_SECRET='local-shopify-webhook-secret' SHOPIFY_SHOP_DOMAIN='example.myshopify.com' SHOPIFY_API_VERSION='2024-01' SHOPIFY_ACCESS_TOKEN='local-shopify-access-token'
node dist/apps/api/main.js

# 3) Web (in another terminal)
export VITE_API_URL='http://localhost:3000'
pnpm nx dev web

# 4) Acceptance tests (in another terminal)
export STAGING_API_URL='http://localhost:3000' STAGING_WEB_URL='http://localhost:4200' INTERNAL_API_KEY='local-dev-key' CI=''
pnpm nx run acceptance-tests:test