Campaigns

Comprehensive manual QA testing plan for the fundraising campaigns feature (~300 tests).

Context#

Comprehensive manual QA testing plan for the fundraising campaigns feature. The goal is to verify that every setting, toggle, field, and interaction works correctly — and that saved values are reflected both in the dashboard and on the public-facing campaign page/devices.


How to Use This Plan#

  • Work through each section sequentially
  • For every setting: change it, save, refresh the page, and confirm it persisted
  • Where indicated, also check the public campaign page to confirm the setting is reflected to donors
  • Mark each item pass/fail as you go

1. Campaign List Page#

Route: /dashboard/{org}/fundraising/campaigns

1.1 Data Table Display#

  • Page loads and displays all campaigns in a table
  • Columns visible by default: Name, Status, Fund, Raised, % of Goal, Goal, Supporters, Created
  • Column data matches expected values for each campaign
  • Status chips show correct colors (Published = green, Draft = yellow, Archived = grey)
  • Fund chips display correctly with fund color
  • Currency formatting is correct (e.g., $1,234.56)
  • Dates are formatted correctly

1.2 Optional Columns#

  • Click column settings and toggle on each optional column one by one:
    • ID
    • Fund ID
    • Campaign Slug
    • End Date
    • Most Recent Donation Date
    • Zakat Raised
    • Average Donation Amount
    • Recurring Supporters
    • 1 Month Recurring
    • 1 Year Recurring
  • Each column shows correct data
  • Column preferences persist after page refresh

1.3 Sorting#

  • Click each column header to sort ascending
  • Click again to sort descending
  • Sorting persists in URL params
  • Sort order survives page refresh

1.4 Filtering#

  • Created Date: Set a date range filter — table filters correctly
  • Status: Toggle Published / Draft / Archived — table filters correctly
  • Fund: Select a specific fund — only campaigns in that fund show
  • Amount Raised: Set min/max range — filters correctly
  • Fundraising Goal: Set min/max range — filters correctly
  • Supporter Count: Set min/max range — filters correctly
  • Combining multiple filters works correctly
  • Clearing filters restores full list
  • Filter state persists in URL params
  • Search by campaign name — matches correctly
  • Search by slug — matches correctly
  • Search by ID (prefix match) — matches correctly
  • Clear search restores full list

1.6 Pagination#

  • Pagination controls appear when campaigns exceed page size
  • Navigating pages works correctly
  • Page state persists in URL

1.7 Mobile Responsive#

  • Resize to mobile width — table switches to card view
  • Card view shows: raised amount, progress bar (if goal exists), fund chip, status chip
  • Actions are accessible on mobile

1.8 Row Actions#

  • On a Draft campaign:
    • "Publish" action appears and works (campaign becomes Published)
    • "Edit" navigates to campaign detail page
    • "Copy Link" copies URL to clipboard
    • "Duplicate" opens DuplicateCampaignModal
    • "Delete" appears (if raised === 0) and works
    • "Archive" appears (if raised > 0) and works
  • On a Published campaign:
    • "View" appears and opens public campaign URL in new tab
    • "Edit" navigates to detail page
    • "Copy Link" works
    • "Duplicate" works
    • Publish action is NOT shown (already published)
  • On an Archived campaign:
    • "Publish" action appears (re-publish)
    • "Delete" appears if raised === 0

2. Campaign Creation#

2.1 New Campaign Modal#

  • Click "New Campaign" button — modal opens
  • Internal Name field is required — cannot submit empty
  • Title field is present
  • Fund dropdown shows available funds, defaults to General Fund
  • Zakat Eligible toggle is present
  • Submit creates campaign with status DRAFT
  • After creation, navigates to the new campaign's detail page
  • New campaign appears in the campaigns list
  • Cancel button closes modal without creating

2.2 Duplicate Campaign Modal#

  • Open duplicate modal from row actions
  • Pre-fills name as "Copy of {original name}"
  • Creates a new DRAFT campaign with all settings copied
  • Verify duplicated campaign has:
    • Same goal settings
    • Same content (title, message, media)
    • Same theme color
    • Same email receipt configs
    • Same giving amounts/levels
    • Same fee/zakat settings
    • Fresh slug (not duplicated)
    • No external_id (stripped)
    • raised = 0 (not copied)
    • Giving levels have no product_id or claimed (stripped)

2.3 Delete Campaign Modal#

  • Only available when raised === 0
  • Shows warning about breaking links/widgets
  • Confirming deletes the campaign
  • Campaign disappears from list
  • Navigates back to campaigns list

2.4 Archive Campaign#

  • Available when raised > 0 and not already archived
  • Campaign status changes to ARCHIVED
  • Campaign remains in list with Archived status

3. Campaign Detail — Layout & Header#

Route: /dashboard/{org}/fundraising/campaigns/{id}

3.1 Header#

  • Breadcrumb shows "Campaigns" and links back to list
  • Campaign internal name displays in header
  • Status chip shows next to name (Published/Draft/Archived)
  • Copy Link icon button copies public URL to clipboard
  • Draft: "Publish" button appears and works
  • Published: "View" button appears and opens public URL in new tab
  • Loading spinner shows while fetching campaign data
  • Error state displays if fetch fails
  • Shows when campaign has raised > 0 and goal is set
  • Displays: "Raised ${amount} | [progress bar] | ${goal} Goal"
  • Progress percentage is visually correct
  • Hidden when no goal is set

3.3 Tabs#

  • All 8 tabs visible: Essentials, Campaign Page, Devices, Amounts, Post Donation, Sharing, Settings, Emails
  • Clicking each tab navigates to correct route
  • Active tab is visually highlighted
  • Default tab is Essentials

4. Essentials Tab#

4.1 Main Details Card#

  • Internal Name field shows current name
  • Change name → Save → Refresh → Name persisted
  • Name updates in header after save
  • Empty name is rejected (validation)
  • Customize Theme Color toggle:
    • OFF: No color picker shown, uses org default
    • ON: Color picker appears with hex input
    • Pick a color → Save → Refresh → Color persisted
    • Toggle OFF → Save → theme_color is cleared
  • Public page check: Theme color reflects on public campaign page

4.2 Goal Card#

  • Display Goal toggle:
    • OFF: No goal amount input shown
    • ON: Goal amount input appears
  • Enter goal amount (e.g., 50000) → Save → Refresh → Amount persisted
  • Goal shows correct currency symbol
  • Footer progress bar updates with new goal
  • Edge cases:
    • Amount = 0 when goal enabled → validation error
    • Amount > 99,999,999 → validation error
    • Negative amount → validation error
  • Public page check: Goal and progress bar display on campaign page
  • Public page check: Toggling goal OFF hides it from public page

4.3 Content Card#

  • Media Mode segmented control:
    • "Landscape" mode: Shows message + image/video below
    • "Portrait" mode: Shows image only with title text overlay
  • Image Upload:
    • Upload an image → Save → Refresh → Image persisted
    • Replace image → Save → New image shown
    • Remove image → Save → Image removed
  • Video URL:
    • Enter valid YouTube URL → Save → Refresh → URL persisted
    • Enter valid Vimeo URL → works
    • Enter invalid URL → validation error
  • Display Organization Logo toggle:
    • ON → logo shows on campaign page
    • OFF → logo hidden
    • Save → Refresh → persisted
  • Title (required):
    • Enter title → Save → Refresh → persisted
    • Clear title → validation error (required)
    • Max 50 characters enforced
  • Message (rich text, landscape mode only):
    • Type message with bold, italic, underline → formatting preserved
    • Add bullet list → preserved
    • Add link → preserved
    • Insert merge tag variable → variable tag shows
    • Max 250 characters enforced
    • Message hidden in portrait mode
  • Title Text Color (portrait mode only):
    • Color picker appears for text overlay color
    • Pick color → Save → Refresh → persisted
  • Public page check: Content reflects on campaign page (title, message, media, logo)
  • Public page check: Portrait vs landscape modes display differently

5. Campaign Page Tab#

Route: /dashboard/{org}/fundraising/campaigns/{id}/campaign-page

5.1 Supporter Feed Card#

  • Display Supporter Feed toggle:
    • ON → Save → Refresh → persisted
    • OFF → Save → Refresh → persisted
  • Public page check: Supporter feed shows/hides on the campaign page based on toggle

5.2 Content Override Card#

  • Override fields are optional — empty means fallback to Essentials content
  • Image Override: Upload different image → Save → page uses override image
  • Video Override: Enter different video URL → page uses override
  • Title Override: Enter custom title → page uses override title
  • Message Override: Enter custom message (rich text) → page uses override
  • Clear all overrides → Save → page falls back to main content
  • Public page check: Campaign page shows override content when set, main content when not

6. Devices Tab#

Route: /dashboard/{org}/fundraising/campaigns/{id}/devices

6.1 StandPro Device Media Card#

  • Display Media toggle:
    • ON: Image upload area visible
    • OFF: Media hidden on StandPro devices
  • Upload StandPro-specific image → Save → Refresh → persisted
  • Enter StandPro video URL → Save → Refresh → persisted
  • If no StandPro media set: falls back to main content image (vertical/portrait orientation)
  • Device check: StandPro device displays correct media

6.2 Handheld Device Media Card#

  • Display Media toggle works same as StandPro
  • Upload Handheld-specific image → Save → Refresh → persisted
  • Enter Handheld video URL → Save → Refresh → persisted
  • If no Handheld media set: falls back to main content image (landscape orientation)
  • Device check: Handheld device displays correct media

7. Amounts Tab#

Route: /dashboard/{org}/fundraising/campaigns/{id}/amounts

7.1 Intelligence Card#

  • Suggest Personalized Upsells toggle:
    • ON → "Nizam Intelligence" badge appears → Save → Refresh → persisted
    • OFF → Save → Refresh → persisted
  • Suggest Personalized Amounts toggle:
    • ON → "Nizam Intelligence" badge appears → Save → Refresh → persisted
    • OFF → Save → Refresh → persisted
  • Suggest Personalized Intervals toggle:
    • ON → Save → Refresh → persisted
    • OFF → Save → Refresh → persisted

7.2 Frequencies Card#

  • Frequency 1 dropdown:
    • Options: One-time, Daily, Weekly, Biweekly, Monthly, Quarterly, Semiannually, Annually
    • Select a frequency → Save → Refresh → persisted
  • Frequency 2 dropdown (optional):
    • Enable second frequency → Save → Refresh → persisted
    • Cannot remove a frequency if it has giving levels configured (confirmation modal)
  • Default Frequency (when 2 frequencies selected):
    • Adaptive (Nizam Intelligence) option available
    • Manual select Frequency 1 or 2
    • Save → Refresh → persisted
  • Public page check: Available frequencies match configuration on donation form

7.3 Giving Amounts Card#

Amounts Mode#

  • Select "Amounts" giving type
  • Preset Amounts:
    • Multiple amount buttons shown (e.g., $25, $50, $100, $250)
    • Edit an amount → value updates
    • Amount = 0 → validation error
    • Amount > 100,000 → validation error
    • Duplicate amount → validation error
    • Add new amount → Save → Refresh → persisted
    • Remove an amount → Save → Refresh → persisted
  • Default Amount:
    • Select one amount as default (radio button)
    • "Default" badge shows on selected amount
    • Save → Refresh → default persisted
  • Allow Custom Amount toggle:
    • ON: Minimum amount input appears
    • Set minimum (e.g., $5) → Save → Refresh → persisted
    • Min < 1 → validation
    • Min > 100,000 → validation
    • OFF: Custom amount disabled for donors
  • Public page check: Donation amounts show correctly, default is pre-selected
  • Public page check: Custom amount field shows/hides based on toggle

Giving Levels Mode#

  • Switch to "Giving Levels" type
  • Core Levels (1-3):
    • Add a core level → opens level editor modal
    • Set Amount (required), Title (required, max 50 chars), Description (max 150 chars), Goal (0-9999)
    • One core level must be set as default
    • Max 3 core levels enforced
    • Min 1 core level enforced
  • Additional Levels (0-7):
    • Add additional levels beyond core
    • Max 10 total levels enforced
  • Drag and Drop:
    • Drag level from Core to Additional → updates is_core
    • Drag level from Additional to Core → updates is_core (if under 3 limit)
    • Reorder levels within a section
  • Display Claimed toggle:
    • ON: Shows claimed count per level on public page
    • OFF: Hides claimed count
    • Save → Refresh → persisted
  • Allow Custom Amount toggle (same as amounts mode)
  • Public page check: Giving levels display with titles, descriptions, and amounts
  • Public page check: Core levels are prominently displayed vs additional

Recurring End Date Settings (if a recurring frequency exists)#

  • Enable Recurring End Date toggle:
    • ON: End date configuration appears
    • OFF: No end date for recurring plans
  • End Date Setting (when enabled):
    • Optional Off: Shows end date option, not pre-selected
    • Optional On: Shows end date option, pre-selected
    • Required: Forces end date selection
  • End Date Type:
    • "Length" mode: Count + Unit inputs
      • Days: max 1825
      • Weeks: max 260
      • Months: max 60
      • Years: max 5
    • "Date" mode: Date picker
      • Min date: Today
      • Max date: 5 years from today
  • Allow Custom End Date toggle (when not Required):
    • ON: Supporters can pick their own end date
    • OFF: Uses configured default
  • Save → Refresh → all recurring settings persisted
  • Public page check: Recurring end date options show correctly on donation form

Tab Switching (when 2 frequencies configured)#

  • Separate tab for each frequency
  • Each tab has independent amounts/levels configuration
  • Switching tabs preserves unsaved changes warning (if any)

8. Post Donation Tab#

Route: /dashboard/{org}/fundraising/campaigns/{id}/post-donation

8.1 Thank You Card#

  • Three default options: "JazakAllah Khair!", "JazakAllah!", "Thank you!"
  • Select each option → Save → Refresh → persisted
  • Custom values preserved if previously saved
  • Public page check: Correct thank you message shows after donation

8.2 Redirect Card#

  • Enable Redirect toggle:
    • OFF: No redirect settings shown
    • ON: URL input + Auto Redirect toggle appear
  • Redirect URL:
    • Enter valid URL (https://example.com) → Save → Refresh → persisted
    • Enter invalid URL → validation error
    • Empty URL when enabled → validation error
  • Auto Redirect toggle:
    • ON: Auto-redirects after donation
    • OFF: Shows "Continue" button for manual redirect
  • Public page check: After donation, redirect behavior matches setting

8.3 Sharing Channels Card#

  • Enable Sharing Channels toggle:
    • OFF: Sharing disabled after donation
    • ON: URL + Message inputs appear
  • Sharing URL:
    • Defaults to campaign URL
    • Can customize to different URL
    • Enter URL → Save → Refresh → persisted
  • Message:
    • Enter sharing message → Save → Refresh → persisted
  • Public page check: After donation, sharing options show with correct URL and message

9. Sharing Tab#

Route: /dashboard/{org}/fundraising/campaigns/{id}/sharing

9.1 Slug Card#

  • Shows current slug with full URL preview: nizam.co/{org}/campaigns/{slug}
  • Change slug to valid kebab-case value → Save → Refresh → persisted
  • Invalid characters → validation error
  • Empty slug → validation error
  • Duplicate slug (already exists for org) → error from server
  • Public page check: Campaign is accessible at the new slug URL
  • Public page check: Old slug URL no longer works (or redirects)

9.2 SEO Card#

  • SEO Title: Enter title → Save → Refresh → persisted
    • Falls back to campaign page_content.title or content.title if empty
  • SEO Description: Enter description → Save → Refresh → persisted
  • Social Image: Upload image → Save → Refresh → persisted
    • Falls back to campaign content image if not set
  • Public page check: Share campaign URL on social media (or use meta tag inspector):
    • og:title matches SEO title (or fallback)
    • og:description matches SEO description
    • og:image matches uploaded social image (or fallback)

10. Settings Tab#

Route: /dashboard/{org}/fundraising/campaigns/{id}/settings

10.1 General Card#

  • Enable Anonymous Supporters toggle:
    • ON → Save → Refresh → persisted
    • OFF → Save → Refresh → persisted
  • Enable Supporter Comments toggle:
    • ON → Save → Refresh → persisted
    • OFF → Save → Refresh → persisted
  • Public page check: Anonymous option shows/hides on donation form
  • Public page check: Comment field shows/hides on donation form

10.2 Financial Card#

  • Parent Fund dropdown:
    • Shows all available funds
    • Select different fund → Save → Refresh → persisted
    • Fund chip updates in campaign list
  • External ID:
    • Enter external ID → Save → Refresh → persisted
    • Clear external ID → Save → Refresh → cleared

10.3 End Date Card#

  • Has End Date toggle:
    • OFF: Campaign runs indefinitely
    • ON: Date picker appears
  • End Date:
    • Select future date → Save → Refresh → persisted
    • Cannot select past date (timezone-aware)
  • Accept Late Payments toggle:
    • ON: Donations accepted after end date
    • OFF: Donations blocked after end date
    • Save → Refresh → persisted
  • Public page check: Campaign stops accepting donations after end date (unless Accept Late is ON)

10.4 Zakat Card#

  • Is Zakat Eligible toggle:
    • OFF: No zakat options shown
    • ON: Zakat mode radio group appears
  • Zakat Mode (when eligible):
    • Optional On: Shows zakat checkbox, pre-checked → Save → Refresh → persisted
    • Optional Off: Shows zakat checkbox, unchecked → Save → Refresh → persisted
    • Required: Always zakat, no checkbox → Save → Refresh → persisted
    • Disabled: No zakat option → Save → Refresh → persisted
  • Public page check: Zakat option shows/hides based on eligibility
  • Public page check: Zakat checkbox default matches mode (on/off/required/disabled)

10.5 Fees Card#

  • Fees Mode radio options:
    • Smart (Nizam Intelligence badge) → Save → Refresh → persisted
    • Optional On → Save → Refresh → persisted
    • Optional Off → Save → Refresh → persisted
    • Required → Save → Refresh → persisted
    • Disabled → Save → Refresh → persisted
  • Public page check: Fee coverage option behavior matches selected mode

10.6 Other Section (Bottom of Settings)#

  • Duplicate Campaign button → Opens DuplicateCampaignModal
  • Delete/Archive buttons:
    • Delete shows when raised === 0
    • Archive shows when raised > 0 and not archived

11. Emails Tab#

Route: /dashboard/{org}/fundraising/campaigns/{id}/emails

11.1 One-Time Donation Receipt#

  • Enable Receipt toggle:
    • ON → Save → Refresh → persisted
    • OFF → No receipt sent for one-time donations
  • Customize Message toggle (when enabled):
    • OFF: Uses default receipt template
    • ON: Custom fields appear:
      • Subject (rich text, single line): Enter subject with merge tags → Save → Refresh → persisted
      • Image: Upload receipt image → Save → Refresh → persisted
      • Content Title (rich text, single line): Enter with merge tags → Save → Refresh → persisted
      • Content Message (rich text, multi-line): Enter with formatting + merge tags → Save → Refresh → persisted
  • Merge Tags Test:
    • Insert {supporter.first_name} → Save → tag persisted
    • Insert {supporter.last_name} → Save → tag persisted
    • Insert {supporter.email} → Save → tag persisted
    • Insert {transaction.amount} → Save → tag persisted
    • Insert {transaction.id} → Save → tag persisted
    • Insert {campaign.title} → Save → tag persisted
    • Insert {campaign.total_raised} → Save → tag persisted
  • Email check: Make a one-time donation → receipt email received with correct custom content and resolved merge tags

11.2 Recurring Plan Receipt#

  • Same test cases as One-Time Receipt above
  • Additional merge tags available:
    • {recurring.id} → Save → tag persisted
    • {recurring.frequency} → Save → tag persisted
    • {recurring.next_installment_date} → Save → tag persisted
  • Email check: Set up recurring plan → receipt email reflects custom content

11.3 Installment Receipt#

  • Same test cases as One-Time Receipt above
  • Recurring merge tags available (same as Recurring Plan Receipt)
  • Email check: Installment payment triggers → receipt email reflects custom content

12. Cross-Cutting Concerns#

12.1 Save Behavior#

  • Every card shows unsaved changes indicator when modified
  • Save button is disabled until changes are made
  • Saving shows loading state (spinner/disabled button)
  • Success toast appears after save
  • Error toast appears on failure with descriptive message
  • Concurrent saves are prevented (second save blocked while first is in-flight)
  • Navigating away with unsaved changes — expected behavior (warning or silent discard?)

12.2 Loading States#

  • Campaign detail shows loading spinner while fetching
  • Individual cards show skeleton/loading states
  • Campaign list shows loading state while fetching

12.3 Error Handling#

  • Network error during save → error toast, form data not lost
  • Network error during load → error state displayed with retry option
  • Server validation error → error message displayed to user

12.4 URL State Persistence#

  • Campaign list: filters, sort, pagination, search persist in URL params
  • Copy/paste URL with params → same view loads
  • Browser back/forward navigates between filter states

12.5 Campaign Status Lifecycle#

  • Create campaign → status is DRAFT
  • Publish draft campaign → status becomes PUBLISHED
  • Archive published campaign → status becomes ARCHIVED
  • Re-publish archived campaign → status becomes PUBLISHED
  • Delete campaign (raised = 0) → campaign removed entirely
  • Cannot delete campaign with donations (raised > 0) — only archive

12.6 Data Consistency (List ↔ Detail)#

  • Edit campaign name → campaign list reflects new name without full page refresh
  • Change campaign status → list status chip updates
  • Change fund → list fund chip updates
  • Change goal → list goal column + footer progress bar update

12.7 Rich Text Editor#

  • Bold, italic, underline formatting saves and loads correctly
  • Links save and load correctly (clickable in preview)
  • Bullet/numbered lists save and load correctly
  • Merge tag variables insert and display correctly as chips/tags
  • Character limits enforced where specified (50 for title, 250 for message)
  • Copy-paste from external sources doesn't break formatting

12.8 Media Upload#

  • Image upload works for all image fields (content, page, device, SEO, receipt)
  • Image preview displays after upload
  • Replace existing image works
  • Remove image works
  • Video URLs (YouTube, Vimeo) validate and display correctly
  • Invalid video URLs show validation error
  • Large file upload handles gracefully (progress indicator or size limit)

13. End-to-End Donation Flow#

13.1 Full Donor Journey (One-Time)#

  1. Create a new campaign with all settings configured
  2. Publish the campaign
  3. Open the public campaign URL
  4. Verify: Theme color, title, message, media, logo all match dashboard settings
  5. Verify: Goal progress bar shows (if enabled)
  6. Verify: Supporter feed shows (if enabled)
  7. Verify: Donation amounts match configured amounts
  8. Verify: Default amount is pre-selected
  9. Verify: Custom amount option shows (if enabled) with correct minimum
  10. Verify: Anonymous toggle shows (if enabled)
  11. Verify: Comment field shows (if enabled)
  12. Verify: Zakat option shows with correct default (if eligible)
  13. Verify: Fee coverage option matches fee mode
  14. Complete a one-time donation
  15. Verify: Thank you message matches setting
  16. Verify: Redirect behavior matches setting (auto or manual)
  17. Verify: Sharing options show (if enabled) with correct URL and message
  18. Verify: Receipt email received with correct content (if enabled)
  19. Verify: Campaign raised amount increments in dashboard
  20. Verify: Supporter count increments in dashboard
  21. Verify: Supporter appears in feed (if enabled and not anonymous)

13.2 Full Donor Journey (Recurring)#

  1. Configure a campaign with a recurring frequency (e.g., Monthly)
  2. Set recurring end date settings
  3. Open public campaign URL
  4. Verify: Frequency tabs/options match configuration
  5. Verify: Default frequency matches setting (adaptive or manual)
  6. Verify: Amounts on each frequency tab are independent and correct
  7. Verify: Recurring end date options match configuration (length vs date, optional vs required)
  8. Complete a recurring donation
  9. Verify: Recurring plan receipt email received with correct content
  10. Verify: Installment receipt email received on subsequent payments

13.3 Device-Specific Flows#

  1. Open campaign on StandPro device
  2. Verify: StandPro-specific media shows (or fallback to portrait content image)
  3. Open campaign on Handheld device
  4. Verify: Handheld-specific media shows (or fallback to landscape content image)
  5. Complete donation on each device type — verify full flow works

Verification Checklist#

AreaTotal TestsPassFailNotes
Campaign List~35
Campaign Creation/Duplication~20
Layout & Header~15
Essentials Tab~30
Campaign Page Tab~10
Devices Tab~10
Amounts Tab~50
Post Donation Tab~15
Sharing Tab~12
Settings Tab~25
Emails Tab~25
Cross-Cutting~25
E2E Flows~30
TOTAL~300