Tổng hợp Test Inventory
Snapshot lần ăn khớp với code thực tế. Cập nhật mỗi khi test counts thay đổi đáng kể. Xem
features.mdcho phạm vi feature,changelog.mdcho lịch sử release.
Cập nhật lần cuối: 2026-05-05 (coverage audit lần đầu + branding.resolver.spec.ts lấp 0% gap)
Coverage snapshot 2026-05-05 (
yarn test:covtừ rootbooking-api):
- Lines 83.66 % · Statements 82.33 % · Functions 78.32 % · Branches 68.81 %
- 0 % files (top gaps):
core/email/{branding,booking-payload,branded-email,recipient}.{resolver,builder,processor}.ts,core/booking/on-{booking,payment}-email.listener.ts,auth-customer/auth-customer.{controller,service}.ts,common/filters/http-exception.filter.ts,core/loyalty/.../prisma-loyalty-redemption.repository.ts.- Đã đóng:
core/email/branding.resolver.ts(0 % → 100 % L · 89 % B · 100 % F) bằngbranding.resolver.spec.ts(+21 tests covering tenant-not-found, ConfigService default, salonUrl trailing-slash strip, logo signing, branding fallbacks, address formatting, locale inference). Suite: 1577 → 1596 pass.- Web: Vitest 4 chưa cấu hình
@vitest/coverage-v8→ cầnyarn add -D @vitest/coverage-v8+coverage: { provider: 'v8' }trongvitest.config.tsmới đọc được số. Files mới ship 2026-05-02 (BrandingSection, FocalPointPicker, page.tsx, HomeContent.tsx, InvoiceClient.tsx) đều chưa có spec.
Tổng quan
| Repo | Framework | Files | Tests | Pass | Skip | Fail | Wall time (local) |
|---|---|---|---|---|---|---|---|
booking-api |
Jest (unit + integration) | 112 | 1332 | 1332 | 0 | 0 | ~6 s |
booking-api |
Jest (e2e, real DB + Redis) | 3 | 59 | 59 | 0 | 0 | ~45 s |
booking-web |
Vitest (unit + component) | 18 | 137 | 137 | 0 | 0 | ~4 s |
booking-web |
Playwright (E2E smoke + regression) | 4 | 25 | 24 | 0 | 1 fixme | ~34 s |
booking-web |
Playwright (@integration, live PSP) |
1 | 1 | — | 1 ‡ | — | — |
booking-mobile |
— | 0 | 0 | 0 | 0 | 0 | — |
| Tổng (repo-wide, non-integration) | 138 | 1553 | 1552 | 1 | 0 | — |
‡ payment-settings-integration.spec.ts: hit live Bambora sandbox, gate bằng env E2E_BAMBORA_*, mặc định test.skip() ở local.
Lệnh chạy
Chạy từ root của mỗi repo (yarn-only).
# booking-api — Jest
yarn test # unit + integration, ~6s
yarn test:e2e # e2e against docker-compose DB + Redis
yarn test:cov # coverage report
# booking-web — Vitest + Playwright
yarn test # vitest (unit/component), ~4s
yarn test:watch # vitest watch mode
yarn test:e2e # playwright, exclude @integration
yarn test:e2e:integration # playwright, chỉ @integration (cần live PSP)
yarn test:e2e:ui # playwright UI mode
booking-api — Jest
Unit + integration (yarn test)
Coverage chia theo bounded context / module. Số đếm dưới đây gom từ *.spec.ts thực tế trong src/**.
| Khu vực | Suites | Tests | Notes |
|---|---|---|---|
| Auth (login, refresh, guards, password reset) | 4 | 38 | Token versioning, tenant isolation, multi-tenant AUTH_TENANT_REQUIRED, rate-limit guard |
| Tenant + Onboarding | 6 | 26 | Settings merge, slug uniqueness, industry resolution, validateSettingsCombination (P1-5/P1-11) |
| Resource management | 9 | 32 | Schedule, time-off, skill assignment, soft-delete, STAFF self time-off (P3 audit) |
| Service catalog | 7 | 26 | Categories, VAT, active-flag, skill links |
| Booking engine (multi-service + status) | 14 | 67 | Conflict detection, cross-item availability, snapshot on create, P1-1/P1-3/P1-8 cancel-preview, deposit-required guard P0-2 |
| Availability calculator | 5 | 20 | Business hours, buffer, time-off overlap, weekday edge cases |
| Public booking API | 6 | 25 | Slug resolution, guest path, resource filter, skill scoping, P1-1 self-cancel, P1-4 retry endpoint |
| TenantCustomer | 4 | 12 | Dedup phone/email, per-tenant scope, STAFF whitelist (notes/tags) |
| Loyalty (stamps + points) | 8 | 28 | Discount compute, redemption ledger, idempotency |
| Loyalty Track L2–L3 (DDD layered) | 5 | 58 | computeLoyaltyDiscount pure helper + DDD reservations + backfill guards |
| Payment — domain + application (Track A–C) | 18 | 280 | State machine, intent creation, dual-write outbox, refund/capture/void |
| Payment — provider adapters | 12 | 155 | Bambora Classic (adapter, MD5, mapper, http-client, retry) — Worldline Direct kept cho enterprise migration |
| Payment — expiry cron + metrics | 5 | 25 | 15-min sweep job, findExpirable, Prometheus labels |
| Booking → Outbox listeners (Phase 6B) | 4 | 27 | OnBookingCreated/Confirmed/Cancelled/Completed/NoShow payload builders + listeners |
| Payment lifecycle listeners (P1-9 + P1-10) | 5 | 39 | OnPaymentStateProjectionListener (8 events → 8 depositStatus), OnPaymentNotificationListener (refund/void SMS), OnPaymentFailedRetryNotificationListener (P1-4) |
| Authorization expiry cron contract | 1 | 27 | Cron sweep + PaymentExpired listener invariant test (audit P1-11) |
| Status-matrix shipped — P0/P1 series | 8 | 110 | P0-1 force cancel, P0-2 deposit guard, P0-3 walk-in IN_PERSON, P1-1 customer cancel, P1-3 phone IN_PERSON, P1-4 retry classification, P1-5 combo guard, P1-8 cancel preview, P1-9 projection, P1-10 notifications, P1-11 7-day cap + auto-cancel |
| Role-based audit Phase 2–4 | 6 | 24 | STAFF scoping (booking/resource/tenant-customer/payment), web STAFF support + /auth/me resourceId, multi-tenant cache leak fix |
Shared primitives (src/shared/) |
6 | 30 | Clock, UUIDv7, event-bus, outbox port + Prisma impl |
| Infrastructure (Prisma adapter, logger, …) | 4 | 25 | PrismaService constructor DI, request-id interceptor |
| Tổng | 112 | 1332 | Coverage ≥ 80% trên mọi module touched bởi PR (verify yarn test:cov) |
End-to-end (yarn test:e2e)
Boot Nest app trên Postgres + Redis sạch từ docker-compose.yml, seed fixtures per spec, assert HTTP responses.
| Spec | Scenarios | Pass | Mục đích |
|---|---|---|---|
booking-outbox.e2e-spec.ts |
6 | 6 | BookingCreated → outbox row shape, publish marks published, Completed triggers TC+Loyalty DB effects, idempotent re-publish, guest booking flags false |
customer-auth.e2e-spec.ts |
19 | 19 | Google OAuth callback, token version, profile update, refresh cookie, password reset |
public-booking.e2e-spec.ts |
34 | 34 | Guest flow, deposit branch, skill enforcement, conflict, P1-1 cancel, multi-day time-off (weekday-flake-fixed) |
| Tổng | 59 | 59 |
booking-web — Vitest
Unit + component test chạy với jsdom + @testing-library/react. Colocated src/**/*.test.{ts,tsx}.
| File | Tests | Concern |
|---|---|---|
lib/timezone.test.ts |
22 | Salon-tz formatters (formatDateTimeInZone), DST edges, month/day boundaries, single-digit pad |
lib/booking-display.test.ts |
12 | Booking detail formatting, status labels |
lib/payment/bambora-schema.test.ts |
17 | Zod validation Bambora Classic credentials (T/P prefix → test/prod) |
lib/payment/deposit-calc.test.ts |
8 | Percentage vs fixed, zero-price guard, rounding |
lib/payment/provider-metadata.test.ts |
4 | Provider label lookup |
lib/payment/public-payment-api.test.ts |
6 | redirectToCheckout polling, timeout, failure mapping |
components/settings/payment/BamboraConfigForm.test.tsx |
5 | Merchant-mode badge gating, required fields |
components/settings/payment/ProviderCard.test.tsx |
7 | Active / inactive / Coming-soon, mode-badge gated by health-check OK |
components/settings/payment/HealthCheckBadge.test.tsx |
3 | OK / FAILED / not-yet với hover timestamp |
components/payments/refund-schema.test.ts |
8 | Refund amount bounds, partial vs full |
components/payments/capture-schema.test.ts |
5 | Capture ≤ authorized amount |
components/payments/collect-remaining-schema.test.ts |
7 | Remaining > 0, ≤ max, integer minor-units |
components/staff/StaffFormModal.test.tsx |
8 | Tạo staff kèm login (email/phone + password + role), unique-per-tenant validate |
components/auth/__tests__/SignInForm.test.tsx |
7 | Multi-tenant AUTH_TENANT_REQUIRED payload, tenant picker (avatar + role + name) |
components/auth/__tests__/SignUpForm.test.tsx |
5 | Owner register flow, validation rules |
context/__tests__/AuthContext.test.tsx |
6 | clear() query cache trên login/logout/register, refreshUser → resolved user |
hooks/usePayments.test.tsx |
2 | Query terminal-status polling stop |
hooks/usePaymentConfigs.test.tsx |
8 | Active config resolution + invalidation |
| Tổng | 137 |
booking-web — Playwright
Headless Chromium mặc định (playwright.config.ts). Serial execution (workers: 1, fullyParallel: false) vì nhiều test PATCH shared studio-nordic tenant; finally block restore.
ownerApi / staffApi fixtures worker-scoped; loggedInPage / loggedInAsStaffPage load pre-saved storageState (login 1 lần per worker per role) — tránh trip 10-req/min login rate-limiter.
Smoke (admin)
| Spec | Scenarios | Phạm vi |
|---|---|---|
payment-settings.spec.ts |
3 | Provider list render, Vipps disabled, Bambora drawer mở |
payments.spec.ts |
2 | Payments index render + xuất hiện trong sidebar |
staff-role.spec.ts |
7 | STAFF signin form + sidebar filter (4 visible / 4 hidden) + OwnerOnlyGuard bounce (/admin/staff, /admin/settings, /admin/services) + /admin/bookings accessible + OWNER→STAFF role switch giữ tenantId |
Regression — Public booking (public-booking.spec.ts)
13 tests, chia 4 tranches (P1-3 đã thêm 2 case DateStrip clamp).
Tranche 0 — DateStrip clamp maxBookingDaysInAdvance
| # | Scenario |
|---|---|
| 0.1 | Cap ≥ 28 → strip saturate đúng 28 cells |
| 0.2 | Cap = 5 → strip render đúng 6 cells (today → today+5), boundary check |
Tranche 1 — Service & staff selection
| # | Scenario |
|---|---|
| 1.1 | Happy path: 1 service → confirmed booking (deposit off) |
| 1.2 | Multi-service: 2 services → totals aggregate trong summary |
| 1.3 | Unassigned booking (bookingMode=allow_unassigned) — test.fixme pending API fix: first slot 09:00 land trên BOOKING_OUTSIDE_BUSINESS_HOURS ở unassigned path (1.1 same slot + assigned passes) |
| 1.4 | Assigned-only: submit không staff → inline error trên staff field |
Tranche 2 — Settings enforcement
| # | Scenario |
|---|---|
| 2.3 | Closed day (Sunday seed) render disabled cell trong DateStrip |
| 2.4 | Time slots bound bởi business hours (no post-21:00 leakage) |
| 2.5 | depositEnabled=true → deposit notice visible + submit redirect tới PSP (Bambora checkout URL) |
Tranche 3 — Validation & error states
| # | Scenario |
|---|---|
| 3.1 | Submit với blank customerName → inline error, no redirect |
| 3.2 | Staff dropdown count match /resources?serviceId=… API (skill filter correctness) |
| 3.3 | Invalid serviceId → "no-service-selected" state, booking form không mount |
Tranche 4 — Book again
| # | Scenario |
|---|---|
| 4.1 | ?from=<bookingId> pre-fill service item + notes từ seeded booking |
data-testid catalogue
| TestID | File | Used by |
|---|---|---|
booking-form |
BookingPage.tsx |
Reach form root; assert it didn't mount on invalid deep-link |
booking-submit |
BookingPage.tsx |
Click submit |
booking-error |
BookingPage.tsx |
Top-level submit banner |
booking-summary |
BookingPage.tsx |
Side panel — assert totals appear |
booking-success |
BookingPage.tsx |
Render khi requiresPayment=false; data-status mirror booking status |
deposit-notice |
BookingPage.tsx |
Amber banner; data-deposit-amount cho numeric assertion |
no-service-selected |
BookingPage.tsx |
Empty-state khi deep-link → 0 items |
service-item |
ServiceItem.tsx |
Per-service row (data-service-id cùng node) |
service-item-remove |
ServiceItem.tsx |
Remove button (chỉ khi canRemove=true) |
service-item-staff |
ServiceItem.tsx |
Staff SearchSelect wrapper (scope option queries) |
service-item-time |
ServiceItem.tsx |
Time-slot SearchSelect wrapper |
service-picker-add |
ServicePicker.tsx |
Add-service CTA |
service-picker-search |
ServicePicker.tsx |
Expanded search mode |
date-strip |
DateStrip.tsx |
Scrollable day container |
date-strip-day |
DateStrip.tsx |
Each day button; data-date=YYYY-MM-DD |
booking-mobile
Chưa có automated tests. Khi mobile scaffold start (sau Role-audit Phase 5), section này sẽ mirror layout của web.
Conventions
- TDD bắt buộc — viết failing test trước, rồi implement. Xem
development-rules.md. - E2E luôn restore settings trong
finallyblock —studio-nordictenant share giữa specs, serial execution depend trên known state. - Worker-scoped fixtures cho auth — tránh rate-limiter false positive khi run > 5 tests / worker.
- Scope dropdown locators tới SearchSelect wrapper (qua
data-testid) — dropdown render fixed-position<div>trong component root,page.locator('li')không scope sẽ leak sang lists khác. - Playwright tạo bookings thật trên
studio-nordic— intentional để dev spot-check trong admin sau run. Để discoverable marker trongcustomerName/notes(e.g.E2E Happy Path,E2E rebook seed <ts>).
Known Gaps (target tranche tiếp)
- Auth flows: chưa Playwright cover customer login / Google OAuth, admin login form, forgot-password round-trip.
- Admin calendar: drag-and-drop, conflict warning, booking drawer — vẫn manual-test only.
- Loyalty: unit coverage tốt nhưng chưa E2E redeem-on-checkout flow.
- Customer portal: booking list pagination + "my bookings" cancel flow chưa có E2E.
- Settings: chỉ Payments tab có E2E touch.
- Mobile: zero automated tests (xem trên).
- Status-matrix P1-4 retry: 26 unit tests cover backend path, FE chưa có Playwright assert UI hiển thị "Retry payment" button.
Track trong features.md ở mỗi module tương ứng.