Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Partner Cover Image Self-Serve — QA Test-Readiness Findings (Rough Draft)

Companion to:

OwnerQA
SourceClickUp PRD + Figma design, reviewed Sep 09 2026
StatusRough draft — to be refined by QA team; menu-item image edit (Flow 2) design pending

1. Resolved decisions

TopicDecisionWhy
Coverage target80% overall, 100% on critical paths (per project standard)Standard CI gate. Critical paths: image upload success, ownership rejection, audit trail, toggle permission tightening.
Zero-price bugN/A — not applicable to this feature—
Cover image is optional & removableRemove flow ships with set/change flow in v1Design includes it; endpoint supports it; must be tested in all flows.
Status gate (Pending Review / Rejected)UI-only for v1; endpoint remains permissiveSimplest path; follow-up can add endpoint-level gate if needed. Test the UI hides the action.
Factory additionsMinimal — reuse existing HhPackage::Package factories, add a trait for has_cover_image: true/falseNo new table; no new model.

2. Test matrix scope (v1)

Backend (hh-server)

LayerWhat to testCoverage target
Flow 1: cover-image setHappy path: upload valid image → cover_image updated, audit entry created, TouchWorker/search-sync fires100%
Flow 1: cover-image removeHappy path: remove existing image → cover_image cleared, audit entry, fallback to placeholder100%
Flow 1: validationsFormat rejection (PDF, TIFF, etc.), size rejection (>10MB), wrong restaurant → rejection100%
Flow 1: no review queueConfirm PackageSubmission / PackageDraftVersion is not created or modified100%
Flow 1: pending draft clobberIf partner has a pending whole-package edit_request, then makes a cover image edit, confirm the pending draft’s stale image does not overwrite the newer image when approved (or flag if it does)Critical path
Flow 1: audit trailAudit record created for both set and remove; record has restaurant_id, package_id, actor, timestamp100%
Flow 2: menu-item image setHappy path: upload valid image → PackageMenu#image updated; MenuSectionGeneratorWorker fires100% (when design lands)
Flow 2: menu-item validationWrong format (TIFF, WEBP), wrong restaurant, soft-deleted item → rejection100% (when design lands)
Flow 4: change_active ownership checkAdmin-created packages: toggle now blocked (permission tightening); partner-created packages: toggle works100%
Flow 4: :partner_full_package_control flagFlag on: Admin-created packages become toggleable/editable; flag off: reverts to default100%
Flow 5: serializercreated_by_admin = !package_created_by_partner?(package); can_toggle / can_edit_other_fields computed correctly with and without Flipper override100%
change_active behavior changeTests confirming previously-toggleable Admin-created packages now fail (behavior change from pre-existing state)Critical path

Frontend (book-bite, partner portal)

LayerWhat to testCoverage target
Modal (Flow 1)Add mode (no image) vs Edit mode (image present) — correct title, correct dropzone state, correct image overlay (“Change Image” / “Remove image”)100%
Modal: validationClient-side format validation (JPEG, JPG, PNG, HEIC, TIFF, WEBP) + size validation (≤10MB), inline error display, Save disabled when invalid100%
Modal: successUpload → saving → success toast (“Great success!” / “Your cover image is updated.”) → modal closes, row updatedCritical path
Modal: remove flow“Remove image” → confirm modal (“Are you sure…”) → Back / Remove → row returns to “Add Cover Image”Critical path
Modal: Save disabled stateSave gray/disabled when no image selected; enabled after valid image80%
Row action visibilityRows with Active/Inactive status → show Add/Edit Cover Image action; rows with Pending Review/Rejected → no image action100%
Row action labelPackage with image → “Edit Cover Image”; package without → “Add Cover Image”100%
“Created by Admin” badgeBadge visible on Admin-created row, not on partner-created; tooltip content matches design100%
Toggle / edit permissionsToggle hidden when can_toggle === false; non-image edit hidden when can_edit_other_fields === false100%
Network error handlingUpload failure → inline error “Upload failed…”; retry works; package retains previous image80%
i18nAll partnerCoverImage.* keys render in en/th/cn; no missing-key underlineSmoke test

Out of v1 / design pending

LayerWhat to test (deferred)
Flow 2: menu-item image edit (no design yet)Blocked on design. Same test patterns apply once UI lands.

3. Critical test cases (priority order)

P0 — Blockers (must pass before Sprint 1 end)

IDNameWhat it asserts
QA-CI-001Cover image set happy path (endpoint)Upload valid image → cover_image updated, audit record created, no PackageSubmission created
QA-CI-002Cover image remove happy path (endpoint)Remove image → cover_image cleared, audit record created, TouchWorker/search-sync fires
QA-CI-003Ownership rejectionRequest from wrong restaurant → 403 rejection
QA-CI-004Format validation (endpoint)Upload TIFF (valid) and PDF (invalid for cover) → PDF rejected, TIFF accepted
QA-CI-005Size validation (endpoint)Upload 10.1MB file → rejected with size error
QA-CI-006change_active permission tighteningAdmin-created package toggle now blocked (wasn’t before this feature); partner-created toggle still works
QA-CI-007Toggle permission with Flipper overrideFlag :partner_full_package_control on for a restaurant → Admin-created packages become toggleable; off → reverts

P1 — High (must pass before rollout)

IDNameWhat it asserts
QA-CI-008UI: Add Cover Image modal (no image)Modal opens with correct title “Add Cover Image”, dropzone, helper text, Save disabled
QA-CI-009UI: Edit Cover Image modal (image present)Modal opens with correct title, “Change Image” overlay, “Remove image” link, Save enabled
QA-CI-010UI: validation errors (cover)Upload >10MB file → “File too large…” inline error; upload PDF → “Unsupported format…” inline error
QA-CI-011UI: Remove confirm flow“Remove image” → confirm modal “Are you sure…” → Remove → row label returns to “Add Cover Image”
QA-CI-012UI: success toastUpload → success → toast “Great success! / Your cover image is updated.”
QA-CI-013Row action visibility by statusActive/Inactive → show Add/Edit; Pending Review/Rejected → no image action
QA-CI-014“Created by Admin” badge + tooltipBadge renders on Admin-created row; tooltip matches design copy
QA-CI-015Pending draft does not clobber newer imageIf partner has pending whole-package edit_request, then makes instant cover image edit, confirm the image stays as the newer version when/if the pending draft is later approved (or flag behavior)
QA-CI-016Audit trail: record created + correct fieldsAudit record has restaurant_id, package_id, actor_id, timestamp, action_type (set/remove)

P2 — Standard (must pass before full launch)

IDNameWhat it asserts
QA-CI-017Network error handling (FE)Upload failure → inline error “Upload failed…”; retry works; previous image retained
QA-CI-018i18n completenessAll partnerCoverImage.* keys render in en/th/cn; no missing-key underline
QA-CI-019Rate limit (10 per 24h per package)11th image change in 24h → rate-limited (429 or soft error)
QA-CI-020can_toggle / can_edit_other_fields serializer correctnessThese booleans match the backend logic with and without Flipper override; package types with no self-serve path have them as false
QA-CI-021Edit Cover on package with pending whole-package edit_requestImage change applied instantly; partner can still see the pending edit_request; no conflict state

P3 — Nice-to-have (defer to v2 if blocked)

  • Analytics events (partner_cover_image_updated, partner_cover_image_removed, partner_created_by_admin_badge_viewed) — verify they fire with correct payloads
  • Menu-item image edit (Flow 2) — blocked on design
  • Concurrency test: partner + Admin editing same image simultaneously

4. Factory additions

No new table. Extend existing HhPackage::Package factories:

# Within existing package factory
trait :with_cover_image do
  cover_image { Rack::Test::UploadedFile.new(Rails.root.join('spec/fixtures/files/valid_image.jpg'), 'image/jpeg') }
end

trait :without_cover_image do
  cover_image { nil }
end

trait :admin_created do
  # Simulate admin-created package (no partner approval record)
  after(:create) do |pkg|
    # ensure no PackageSubmission with package_created_by_partner? = true exists
  end
end

For FE Cypress tests, mock the endpoint responses — no backend fixture needed.


5. Definition of Done — ticket-level gate

Per ticket, the following must all be true before merge:

  1. Tests pass in CI (hh-server RSpec; book-bite Cypress component + Playwright e2e)
  2. Coverage not decreased (CI gate at 80% project-wide; 100% on the touched module)
  3. No new offenses in bundle exec rubocop (hh-server) or npm run lint (book-bite)
  4. API contract tests pass if the ticket touches a serialized endpoint (hh-server)
  5. Manual smoke documented in the PR description for any UI-facing change
  6. Analytics event verified end-to-end where applicable
  7. PR linked to spec (FE spec for FE tickets; BE spec for backend tickets; QA spec for critical-path tickets)

6. Pre-rollout QA checklist

CheckOwner
All P0 tests pass in CIBackend eng + QA
All P1 tests pass in CIBackend eng + FE eng + QA
Cover image set + remove manual smoke recorded (staging, all package types)QA
“Created by Admin” badge manual test recordedQA
change_active permission tightening documented in release notesPM + QA
Analytics events (partner_cover_image_updated) appearing in analytics dashboard (test cohort)FE eng + Analytics
Pending draft clobber test result recorded (pass or flagged)Backend eng + QA

7. Regression risk areas

  1. change_active behavior change — partners who could previously toggle Admin-created packages lose that ability by default. This is an intentional tightening, not a regression, but it must be explicitly communicated in release notes.
  2. Pending-draft image clobber — if the pending edit_request in the old review flow contains a stale cover_image snapshot, it could overwrite the newer instant edit when approved. Must be tested and either fixed or flagged before launch.
  3. Existing cover image on packages — removing the image from an existing package with active bookings should be verified to not break the booking confirmation display.
  4. TouchWorker / search-sync — cover image changes (set/remove) must trigger the same sync paths as Admin-side edits. Verify.
  5. i18n key collision — partnerCoverImage.* namespace should not collide with existing or future partner-portal i18n keys.

8. Out of scope for v1 QA (design pending)

ItemWhy deferred
Flow 2: menu-item image edit E2ENo Figma design yet
Analytics event verification for partner_menu_image_updatedBlocked on Flow 2
imenupro-only package empty stateBlocked on Flow 2 UI
Cross-browser E2E for menu-item editBlocked on Flow 2

9. Open questions

Tracked in PRD Still Open:

  • Menu-item image edit design — needed before Flow 2 QA
  • Status-based endpoint gate — is it UI-only or also backend? (Currently assumed UI-only)
  • Cover image removal clear behavior — confirm what image_cover_url returns after removal
  • Pending draft clobber — confirm fix or accepted behavior before launch