Skip to content

Commit 977dcbd

Browse files
committed
feat(e2e): wire CI job + docker-compose for smoke suite
- docker-compose.e2e.yml: db + api + dashboard + wiremock carrier stub, tmpfs postgres, no redis (worker runs in-process), healthchecks drive `docker compose up --wait`. - WireMock mappings for rate/shipment/tracking — no live carrier calls. - scripts/seed.ts: idempotent REST-based seed (address + parcel). - New `e2e` GitHub Actions job: boots the stack, installs browsers, seeds, runs specs/, uploads report + traces + videos on failure. - README refresh covering layout, env vars, local vs CI invocation. refs #1065
1 parent 8f02f54 commit 977dcbd

7 files changed

Lines changed: 332 additions & 43 deletions

File tree

.github/workflows/tests.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,3 +237,76 @@ jobs:
237237
- name: Build
238238
working-directory: packages/mcp
239239
run: npm run build
240+
241+
e2e:
242+
# Required gate on any `refs/tags/2026.*` push. PRs and branch pushes
243+
# also run the suite so regressions surface before tag cuts.
244+
runs-on: ubuntu-latest
245+
timeout-minutes: 40
246+
247+
steps:
248+
- uses: actions/checkout@v4
249+
with:
250+
submodules: false
251+
252+
- name: Checkout submodules (shallow)
253+
run: git submodule update --init --depth=1 community
254+
255+
- uses: actions/setup-node@v4
256+
with:
257+
node-version: 22.x
258+
cache: 'npm'
259+
cache-dependency-path: package-lock.json
260+
261+
- name: Install workspace dependencies
262+
run: npm ci
263+
264+
- name: Start stack (api + dashboard + stubbed carrier + db)
265+
run: docker compose -f docker-compose.e2e.yml up -d --wait
266+
env:
267+
KARRIO_TAG: ${{ github.ref_name == 'main' && 'latest' || 'latest' }}
268+
269+
- name: Install Playwright browsers
270+
working-directory: packages/e2e
271+
run: npx playwright install --with-deps chromium firefox
272+
273+
- name: Seed test tenant
274+
working-directory: packages/e2e
275+
run: npx tsx scripts/seed.ts
276+
env:
277+
KARRIO_API_URL: http://localhost:5002
278+
KARRIO_DASHBOARD_URL: http://localhost:3002
279+
280+
- name: Run Playwright smoke suite
281+
working-directory: packages/e2e
282+
run: npx playwright test specs/
283+
env:
284+
CI: "true"
285+
KARRIO_API_URL: http://localhost:5002
286+
KARRIO_DASHBOARD_URL: http://localhost:3002
287+
288+
- name: Collect docker logs on failure
289+
if: failure()
290+
run: docker compose -f docker-compose.e2e.yml logs --no-color > docker-logs.txt || true
291+
292+
- name: Upload Playwright report
293+
if: always()
294+
uses: actions/upload-artifact@v4
295+
with:
296+
name: playwright-report-${{ github.run_attempt }}
297+
path: packages/e2e/playwright-report
298+
retention-days: 14
299+
300+
- name: Upload traces + videos on failure
301+
if: failure()
302+
uses: actions/upload-artifact@v4
303+
with:
304+
name: playwright-artifacts-${{ github.run_attempt }}
305+
path: |
306+
packages/e2e/test-results
307+
docker-logs.txt
308+
retention-days: 14
309+
310+
- name: Tear down stack
311+
if: always()
312+
run: docker compose -f docker-compose.e2e.yml down -v

docker-compose.e2e.yml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Minimised compose for the Playwright smoke suite.
2+
#
3+
# Goals:
4+
# - Fastest possible cold-start: Postgres + API + dashboard + carrier stub.
5+
# - No Redis: API runs the worker in-process (DETACHED_WORKER=false).
6+
# - Carrier stub: WireMock serving canned rate/label/tracking JSON so
7+
# specs never touch real carrier endpoints.
8+
# - Healthchecks drive `docker compose up --wait` in CI.
9+
#
10+
# Image tags default to the `latest` published build on Scarf; override with
11+
# `KARRIO_TAG=<tag>` to pin a commit-specific image.
12+
#
13+
# Usage:
14+
# docker compose -f docker-compose.e2e.yml up -d --wait
15+
# docker compose -f docker-compose.e2e.yml down -v
16+
17+
services:
18+
db:
19+
image: postgres:16-alpine
20+
environment:
21+
POSTGRES_DB: karrio
22+
POSTGRES_USER: postgres
23+
POSTGRES_PASSWORD: postgres
24+
tmpfs:
25+
- /var/lib/postgresql/data
26+
healthcheck:
27+
test: ["CMD-SHELL", "pg_isready -U postgres -d karrio"]
28+
interval: 3s
29+
timeout: 3s
30+
retries: 20
31+
32+
carrier-stub:
33+
# WireMock stands in for real carrier endpoints. Mappings live in
34+
# packages/e2e/fixtures/wiremock and are mounted read-only.
35+
image: wiremock/wiremock:3.9.1
36+
command: ["--port", "8080", "--disable-banner"]
37+
volumes:
38+
- ./packages/e2e/fixtures/wiremock:/home/wiremock/mappings:ro
39+
healthcheck:
40+
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/__admin/health || exit 1"]
41+
interval: 3s
42+
timeout: 3s
43+
retries: 10
44+
45+
api:
46+
image: karrio.docker.scarf.sh/karrio/server:${KARRIO_TAG:-latest}
47+
depends_on:
48+
db:
49+
condition: service_healthy
50+
carrier-stub:
51+
condition: service_healthy
52+
environment:
53+
SECRET_KEY: "e2e-smoke-test-secret-key-not-for-production"
54+
DEBUG_MODE: "True"
55+
DETACHED_WORKER: "False"
56+
DATABASE_ENGINE: "postgresql_psycopg2"
57+
DATABASE_HOST: db
58+
DATABASE_PORT: "5432"
59+
DATABASE_NAME: karrio
60+
DATABASE_USERNAME: postgres
61+
DATABASE_PASSWORD: postgres
62+
KARRIO_HTTP_PORT: "5002"
63+
ADMIN_EMAIL: admin@example.com
64+
ADMIN_PASSWORD: demo
65+
ENABLE_ALL_PLUGINS_BY_DEFAULT: "True"
66+
LOG_LEVEL: "30"
67+
KARRIO_E2E_CARRIER_STUB_URL: "http://carrier-stub:8080"
68+
ports:
69+
- "5002:5002"
70+
healthcheck:
71+
test: ["CMD-SHELL", "curl -fsS http://localhost:5002/ || exit 1"]
72+
interval: 5s
73+
timeout: 5s
74+
retries: 40
75+
start_period: 30s
76+
77+
dashboard:
78+
image: karrio.docker.scarf.sh/karrio/dashboard:${KARRIO_TAG:-latest}
79+
depends_on:
80+
api:
81+
condition: service_healthy
82+
environment:
83+
AUTH_TRUST_HOST: "true"
84+
NEXTAUTH_SECRET: "e2e-smoke-test-nextauth-secret"
85+
DASHBOARD_PORT: "3002"
86+
NEXT_PUBLIC_DASHBOARD_URL: "http://localhost:3002"
87+
NEXT_PUBLIC_KARRIO_PUBLIC_URL: "http://localhost:5002"
88+
KARRIO_URL: "http://api:5002"
89+
ports:
90+
- "3002:3002"
91+
healthcheck:
92+
test: ["CMD-SHELL", "wget -qO- http://localhost:3002/signin || exit 1"]
93+
interval: 5s
94+
timeout: 5s
95+
retries: 40
96+
start_period: 30s

packages/e2e/README.md

Lines changed: 50 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,71 @@
11
# @karrio/e2e — Playwright E2E Tests
22

3-
End-to-end tests for the Karrio dashboard at `localhost:3002`.
4-
5-
> **Note:** `node_modules` are not committed. Run `npm install` before executing
6-
> tests, and `npx playwright install chromium` on first run.
3+
End-to-end tests for the Karrio dashboard (default: `localhost:3002`) and
4+
API (default: `localhost:5002`).
5+
6+
> **Note:** `node_modules` are not committed. Run `npm install` at the repo
7+
> root before executing tests, and `npx playwright install chromium firefox`
8+
> on first run.
9+
10+
## Layout
11+
12+
| Path | Purpose |
13+
|------|---------|
14+
| `playwright.config.ts` | Chromium + Firefox, retries on CI, trace/video capture |
15+
| `fixtures/auth.ts` | Extended Playwright test with a REST `api` fixture |
16+
| `fixtures/wiremock/` | Canned carrier JSON served by WireMock in CI |
17+
| `helpers/env.ts` | Centralised env-var resolution |
18+
| `helpers/api.ts` | Thin REST client (JWT bearer auth) |
19+
| `helpers/selectors.ts` | Shared role-based locators |
20+
| `helpers/wait-for-stack.ts` | Polls API + dashboard until healthy |
21+
| `tests/auth.setup.ts` | Persists NextAuth session to storageState |
22+
| `tests/rate-sheet-editor.spec.ts` | Legacy rate-sheet editor suite |
23+
| `specs/*.spec.ts` | Golden-path smoke suite (auth, shipment, tracking, order, settings) |
24+
| `scripts/seed.ts` | CI-only data seeding via REST |
725

826
## Setup
927

1028
```bash
11-
cd karrio/packages/e2e
29+
cd karrio
1230
npm install
13-
npx playwright install chromium # first time only
31+
cd packages/e2e
32+
npx playwright install chromium firefox # first time only
1433
```
1534

16-
## Run (requires dev server on localhost:3002)
35+
## Run against a running dev stack
1736

1837
```bash
19-
npm test # headless
20-
npm run test:headed # headed (see browser)
21-
npm run test:ui # Playwright UI / trace viewer
38+
npm test # full suite (chromium + firefox)
39+
npm run test:smoke # specs/ only (chromium)
40+
npm run test:headed # see the browser
41+
npm run test:ui # Playwright UI / trace viewer
2242
```
2343

24-
## Auth
44+
## Run against the CI compose stack
2545

26-
Tests log in once via `helpers/auth.ts` and reuse the session via
27-
`playwright/.auth/user.json` (auto-created, git-ignored).
46+
```bash
47+
docker compose -f docker-compose.e2e.yml up -d --wait
48+
npx tsx packages/e2e/scripts/seed.ts
49+
npm --prefix packages/e2e test
50+
docker compose -f docker-compose.e2e.yml down -v
51+
```
2852

29-
Set credentials via env vars or use the defaults:
53+
## Env overrides
3054

31-
| Env var | Default |
32-
|-------------------|------------------------|
33-
| `KARRIO_EMAIL` | `admin@example.com` |
34-
| `KARRIO_PASSWORD` | `demo` |
55+
| Env var | Default |
56+
|---------|---------|
57+
| `KARRIO_EMAIL` | `admin@example.com` |
58+
| `KARRIO_PASSWORD` | `demo` |
3559
| `KARRIO_DASHBOARD_URL` | `http://localhost:3002` |
60+
| `KARRIO_API_URL` | `http://localhost:5002` |
61+
62+
## Carrier stubbing
3663

37-
## Test Targets
38-
39-
| Test # | Description | URL |
40-
|--------|-------------|-----|
41-
| 1 | Carrier Network page loads | `/admin/carriers` |
42-
| 2 | Rate Sheets tab switch | `/admin/carriers` |
43-
| 3 | Create Rate Sheet editor opens | `/admin/carriers` |
44-
| 4 | Mode buttons (edit/import/export) visible | editor panel |
45-
| 5 | Switch to import mode → file input | editor panel |
46-
| 6 | Upload valid xlsx → diff preview | editor import panel |
47-
| 7 | Upload error xlsx → validation errors | editor import panel |
48-
| 8 | Cancel import → returns to edit mode | editor import panel |
49-
| 9 | Export button triggers download | editor panel |
50-
| 10 | Connections rate-sheets page loads | `/connections/rate-sheets` |
51-
| 11 | Escape key closes editor | editor panel |
52-
| 12 | Close button dismisses editor | editor panel |
53-
54-
## Fixtures
55-
56-
| File | Description |
57-
|------|-------------|
58-
| `fixtures/rate-sheet-valid.xlsx` | Valid rate sheet — dry-run succeeds, diff preview shown |
59-
| `fixtures/rate-sheet-errors.xlsx` | Invalid data — validation errors shown |
60-
| `fixtures/rate-sheet-updated.xlsx` | Updated rates — for confirm-import flow |
64+
The smoke suite never touches real carrier APIs. In CI the compose stack
65+
launches a `wiremock/wiremock` container with mappings from
66+
`fixtures/wiremock/` returning canned rate/shipment/tracking JSON.
6167

6268
## CI
6369

64-
Set `CI=true` to enable GitHub reporter and 2 retries per test.
70+
Runs on every push + PR in `.github/workflows/tests.yml` under the `e2e` job.
71+
Reports, traces, and videos are uploaded as artifacts on failure.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"request": {
3+
"method": "POST",
4+
"urlPathPattern": "/(rate|rates|rating).*"
5+
},
6+
"response": {
7+
"status": 200,
8+
"headers": { "Content-Type": "application/json" },
9+
"jsonBody": {
10+
"rates": [
11+
{
12+
"service": "e2e_standard",
13+
"service_name": "E2E Standard",
14+
"total_charge": 12.5,
15+
"currency": "USD",
16+
"transit_days": 3,
17+
"carrier_id": "e2e-stub",
18+
"carrier_name": "e2e_stub"
19+
}
20+
]
21+
}
22+
}
23+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"request": {
3+
"method": "POST",
4+
"urlPathPattern": "/(ship|shipment|labels).*"
5+
},
6+
"response": {
7+
"status": 200,
8+
"headers": { "Content-Type": "application/json" },
9+
"jsonBody": {
10+
"tracking_number": "E2ESMOKE123456",
11+
"shipment_identification_number": "E2ESMOKE123456",
12+
"label": "JVBERi0xLjQKJcOkw7zDtsOfCjI=",
13+
"label_type": "PDF",
14+
"service": "e2e_standard",
15+
"total_charge": { "amount": 12.5, "currency": "USD" }
16+
}
17+
}
18+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"request": {
3+
"method": "GET",
4+
"urlPathPattern": "/(track|tracking|status).*"
5+
},
6+
"response": {
7+
"status": 200,
8+
"headers": { "Content-Type": "application/json" },
9+
"jsonBody": {
10+
"tracking_number": "E2ESMOKE123456",
11+
"status": "in_transit",
12+
"events": [
13+
{
14+
"date": "2026-04-18",
15+
"time": "10:00",
16+
"code": "IN_TRANSIT",
17+
"description": "Package scanned at origin facility",
18+
"location": "Los Angeles, CA"
19+
},
20+
{
21+
"date": "2026-04-19",
22+
"time": "08:00",
23+
"code": "OUT_FOR_DELIVERY",
24+
"description": "Out for delivery",
25+
"location": "New York, NY"
26+
}
27+
]
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)