Funds
Comprehensive manual QA testing plan for funds — organizational buckets for campaigns (~160 tests).
Context#
Comprehensive manual QA testing plan for funds — organizational buckets that group campaigns for reporting and CRM integration. Every campaign belongs to a fund. A special General fund exists by default and cannot be edited or deleted. When a custom fund is deleted, its campaigns are automatically reassigned to General.
How to Use This Plan#
- Work through each section sequentially
- For every setting: change it, save, refresh the page, and confirm it persisted
- Mark each item pass/fail as you go
- Pre-requisites: At least 2-3 campaigns should exist to test linking/reassignment
Key Concepts#
| Concept | Details |
|---|---|
| General Fund | System default fund (ID: fund_0000000000000000). Cannot be edited or deleted. Auto-receives unassigned campaigns. Marked with lock icon |
| Custom Funds | User-created funds with name, color, internal note, and optional external ID. Fully editable and deletable |
| Campaign Link | Campaigns reference a fund via fund_id. Every campaign must belong to exactly one fund |
| Deletion Behavior | Deleting a custom fund moves all its campaigns to the General fund |
| FundChip | Colored chip component used in campaign lists showing fund name with fund color |
| External CRM ID | Optional tracking ID for syncing transactions to an external CRM (e.g., Salesforce) |
1. Funds List Page#
Route: /dashboard/{org}/finances/funds
1.1 Data Table Display#
- Page loads and displays all funds in a table
- Columns visible:
- Name — sortable, title variant. Shows lock icon next to "General" fund
- Raised — currency-formatted amount (e.g.,
$1,234.56), right-aligned, sortable - Campaigns — campaign count, right-aligned, sortable
- External ID — CRM identifier text, sortable
- Created — formatted date in org timezone, sortable
- ID — fund identifier, sortable
- All column data matches expected values for each fund
- General fund always present in list
- Lock icon visible only on General fund row
- Currency formatting is correct across all funds
1.2 Sorting#
- Default sort: Amount Raised (descending)
- Name — click to sort alphabetically asc/desc
- Amount Raised — click to sort by raised amount
- Campaigns — click to sort by campaign count
- External ID — click to sort
- Created Date — click to sort by creation date
- Sorting persists in URL params
- Sort order survives page refresh
1.3 Filtering#
- Amount Raised (range filter):
- Set min amount → filters correctly
- Set max amount → filters correctly
- Set both min and max → range filter works
- Campaigns (count range filter):
- Set min campaign count → filters correctly
- Set max campaign count → filters correctly
- Set both → range filter works
- Combining both filters works correctly
- Clearing filters restores full list
- Filter state persists in URL params
1.4 Search#
- Search by fund name — matches correctly (partial match)
- Search by fund ID — matches correctly (prefix match)
- Clear search restores full list
- Search works in combination with filters
1.5 Pagination#
- Pagination controls appear when funds exceed page size
- Page size options: 20, 50, 100
- Navigating pages works correctly
- Page state persists in URL
1.6 Mobile Responsive#
- Resize to mobile width — table switches to card view
- Mobile card shows:
- Fund name (with lock icon if General)
- Raised amount (formatted with currency)
- Campaign count chip (e.g., "3 Campaigns")
- Actions menu accessible
- Skeleton loading state shows while data fetches
- Card data matches table data
1.7 Row Actions#
- On a custom (non-General) fund:
- "Edit" → navigates to fund detail page
- "Delete" → opens DeleteFundModal
- On the General fund:
- "Edit" → navigates to fund detail page (shows read-only view)
- "Delete" action is NOT available (General cannot be deleted)
1.8 Empty States#
- No funds (except General): Shows empty state with "Create new fund" button
- Filters applied, no results: Shows filter empty state with "Clear Filters" button
- Error loading: Shows error state with retry button
1.9 Add Fund Button#
- "Add Fund" button visible at top of page
- Clicking opens NewFundModal
2. Fund Creation#
2.1 New Fund Modal#
- Click "Add Fund" button — modal opens
- Name field:
- Required — cannot submit empty
- Max 40 characters enforced
- Placeholder: "Enter fund name"
- Internal Note field:
- Optional textarea (3 rows)
- Placeholder: "Enter internal note"
- Can be left empty
- No color field in creation modal (set on detail page)
- No external ID field in creation modal (set on detail page)
- Create button:
- Disabled until name is provided
- Shows loading state during submission
- Cannot close modal during submission
- On success:
- Modal closes
- Funds list cache invalidated
- Navigates to new fund's detail page
- New fund appears in funds list
- Cancel button closes modal without creating
3. Fund Detail Page — General Fund#
Route: /dashboard/{org}/finances/funds/{generalFundId}
3.1 Header#
- Fund name "General" displays as title
- Lock icon visible next to name
- Breadcrumb links back to "Funds" list
- Footer shows: "${amount} raised" with fund currency
3.2 Read-Only Callout#
- Callout banner displayed at top: "General is your default fund. Unassigned campaigns go here automatically. This fund can't be edited or deleted."
- No editable form fields shown
- No color picker shown
- No internal note field shown
- No external ID field shown
3.3 Linked Campaigns Card#
- Card title shows: "{count} Linked Campaign(s)"
- Displays first 5 campaigns as clickable links to campaign detail pages
- Each link shows campaign name
- If > 5 campaigns: "See all" link appears → navigates to campaigns list filtered by this fund (
/dashboard/{org}/fundraising/campaigns?fund={fundId}) - If 0 campaigns: Empty state message: "Visit the Campaigns page to create a new campaign or edit an existing one to assign it to this fund"
- Campaign links navigate to correct campaign detail pages
3.4 No Delete Card#
- No "Delete Fund" card or button shown for General fund
4. Fund Detail Page — Custom Funds#
Route: /dashboard/{org}/finances/funds/{fundId}
4.1 Header#
- Fund name displays as title (no lock icon)
- Breadcrumb links back to "Funds" list
- Footer shows: "${amount} raised" with fund currency
4.2 Main Details Card (Editable)#
- Name field:
- Shows current fund name
- Change name → Save → Refresh → Name persisted
- Empty name → validation error (save disabled)
- Max 40 characters enforced
- Name updates in header after save
- Name updates in funds list
- Name updates in FundChip wherever displayed (campaign list, etc.)
- Color field (color picker):
- Shows current color swatch + hex input
- Click swatch → color picker opens
- Pick a color → hex value updates
- Type valid hex → color swatch updates
- Change color → Save → Refresh → Color persisted
- Color required — cannot save without color
- Invalid hex → validation error shown
- Color reflects in FundChip across all views (background + text color)
- Internal Note field (textarea):
- Shows current internal note (or empty)
- 3 rows visible
- Enter note → Save → Refresh → Note persisted
- Clear note → Save → Refresh → Note cleared
- Optional — can save with empty note
Save Behavior#
- Save button disabled until changes are made
- Save button disabled if validation fails (empty name, missing color)
- Both name AND color must be set for save to be enabled
- Saving shows loading state
- Success toast appears after save
- Error toast appears on failure
- Funds cache invalidated after save
4.3 Linked Campaigns Card (Read-Only)#
- Card title shows: "{count} Linked Campaign(s)"
- Displays first 5 campaigns as clickable links to campaign detail pages
- Each link shows campaign name
- If > 5 campaigns: "See all" link appears → navigates to campaigns list filtered by this fund (
/dashboard/{org}/fundraising/campaigns?fund={fundId}) - If 0 campaigns: Empty state message with link to Campaigns page
- Campaign links navigate to correct campaign detail pages
- Count matches actual number of campaigns in this fund
4.4 External CRM ID Card (Editable)#
- Description: "Add a tracking ID from your external CRM. All transactions from this fund's campaigns will be synced to this ID."
- External ID field:
- Text input
- Shows current external ID (or empty)
- Enter ID → Save → Refresh → ID persisted
- Clear ID → Save → Refresh → ID cleared
- Optional — can save with empty ID
- Save button independent from Main Details card (or same card — verify)
- Success toast appears after save
4.5 Delete Fund Card#
- Description: "Permanently delete this fund. All associated campaigns and transactions will be automatically re-assigned to your 'General' fund."
- Delete button is red/destructive
- Clicking opens DeleteFundModal
5. Fund Deletion#
5.1 Delete Fund Modal#
- Shows fund name (read-only)
- Destructive warning callout: "Campaigns assigned to this fund will be moved to the General fund."
- Delete button is red/destructive
- Shows loading state during deletion
- Cannot close modal during deletion
- On success:
- Success toast: "Fund deleted"
- Funds list cache invalidated
- Navigates back to funds list (or triggers onSuccess callback)
- Fund disappears from list
- Cancel button closes modal without deleting
5.2 Campaign Reassignment After Deletion#
- Create a custom fund with 2-3 campaigns assigned to it
- Delete the fund
- Verify: All campaigns now show "General" as their fund in campaigns list
- Verify: General fund's campaign count increased by the number of reassigned campaigns
- Verify: General fund's linked campaigns card shows the reassigned campaigns
- Verify: Each reassigned campaign's settings page shows "General" as parent fund
- Verify: FundChip for each campaign updated to General fund's appearance
5.3 Deletion Edge Cases#
- Delete fund with 0 campaigns → clean deletion (no reassignment needed)
- Delete fund with raised > 0 → still allowed (no raise restriction for funds)
- Try to navigate to a deleted fund's URL → appropriate error/redirect
- Delete fund while a campaign is open that belongs to it → campaign should show General fund on next load
6. Fund-Campaign Relationship#
6.1 New Campaign Default Fund#
- Create a new campaign via New Campaign Modal
- Fund dropdown shows all available funds
- Default selection is General fund
- Select a custom fund → Save → Campaign created with that fund
- Don't change fund → Campaign created with General fund
- Verify: Campaign appears in the selected fund's linked campaigns
6.2 Reassigning Campaigns Between Funds#
- Go to a campaign's detail page → Settings tab → Financial card
- Parent Fund dropdown shows all available funds
- Current fund is pre-selected
- Change to a different fund → Save → Refresh → Persisted
- Verify: Campaign now appears in the new fund's linked campaigns
- Verify: Campaign removed from the old fund's linked campaigns
- Verify: Campaign list FundChip updated with new fund's name and color
- Verify: Old fund's campaign count decremented
- Verify: New fund's campaign count incremented
6.3 FundChip Display in Campaign List#
- Campaign assigned to custom fund → FundChip shows fund name
- Chip background: fund color at 20% opacity
- Chip text: darkened version of fund color
- Campaign assigned to General fund → FundChip shows "General"
- Fund name change → chip text updates across campaigns list
- Fund color change → chip color updates across campaigns list
6.4 Campaign List Fund Filter#
- Go to campaigns list page
- Fund filter shows all available funds as toggle options
- Select one fund → only campaigns in that fund shown
- Select multiple funds → campaigns in any selected fund shown
- Clear fund filter → all campaigns shown
- Filter persists in URL params
7. Cross-Cutting Concerns#
7.1 Save Behavior#
- Save button disabled until changes are made
- Save button disabled if validation fails
- Saving shows loading state (spinner/disabled)
- Success toast appears after save
- Error toast appears on failure
- Form data not lost on error
7.2 Loading States#
- Funds list shows loading state while fetching
- Fund detail page shows loading state while fetching
- Linked campaigns card shows loading state
7.3 Error Handling#
- Network error during save → error toast
- Network error during load → error state with retry
- Server validation error → displayed to user
7.4 URL State Persistence#
- Funds list: filters, sort, pagination, search persist in URL params
- Copy/paste URL with params → same view loads
- Browser back/forward navigates between filter states
7.5 Data Consistency#
- Create fund → appears in funds list immediately
- Edit fund name → list reflects new name
- Edit fund color → list and campaign FundChips reflect new color
- Delete fund → disappears from list, campaigns reassigned
- Campaign assigned to fund → linked campaigns count increments
- Campaign unassigned from fund → linked campaigns count decrements
- Fund appears in campaign creation modal's fund dropdown
- Fund appears in campaign settings Financial card's fund dropdown
7.6 Mobile Responsiveness#
- List page renders as cards on mobile
- Detail page stacks cards vertically on mobile
- Color picker works on mobile
- Modal displays correctly on mobile
8. End-to-End Flows#
8.1 Create Fund and Assign Campaigns#
- Click "Add Fund" → modal opens
- Enter fund name (e.g., "Ramadan 2026")
- Optionally enter internal note (e.g., "Ramadan fundraising bucket")
- Submit → redirected to new fund detail page
- Set a color (e.g., green
#22C55E) → Save - Set external CRM ID (e.g., "CRM-RAM-2026") → Save
- Go to an existing campaign → Settings → Financial card
- Change parent fund to "Ramadan 2026" → Save
- Go back to "Ramadan 2026" fund detail page
- Verify: Campaign appears in linked campaigns card
- Verify: Campaign count is 1
- Go to campaigns list → verify campaign shows green "Ramadan 2026" FundChip
- Create a new campaign → select "Ramadan 2026" as fund
- Verify: Fund detail now shows 2 linked campaigns
8.2 Delete Fund and Verify Reassignment#
- Ensure a custom fund has 3 campaigns assigned
- Note the campaign names
- Delete the fund via detail page
- Verify: Fund no longer in funds list
- Verify: All 3 campaigns now show "General" fund in campaigns list
- Verify: General fund linked campaigns includes the 3 campaigns
- Verify: Each campaign's Settings → Financial card shows "General" as parent fund
- Verify: FundChip color updated to General fund's appearance
8.3 General Fund Immutability#
- Navigate to General fund detail page
- Verify: Read-only callout is shown
- Verify: No editable form fields
- Verify: No external ID card
- Verify: No delete button
- Go to funds list → verify no "Delete" action on General row
- Delete a custom fund → verify campaigns go to General
- Verify: General fund's campaign count reflects the reassignment
- Create a campaign without changing fund → verify it defaults to General
8.4 Color Propagation#
- Create a fund with a distinctive color (e.g., bright red
#FF0000) - Assign a campaign to this fund
- Go to campaigns list → verify FundChip shows red color (20% opacity background, darkened text)
- Go back to fund detail → change color to blue
#0000FF→ Save - Go to campaigns list → verify FundChip now shows blue color
- Verify: Color update propagated without editing any campaign
8.5 External CRM ID Workflow#
- Create a fund
- Go to fund detail → External CRM ID card
- Enter an external ID (e.g., "SF-FUND-001") → Save
- Refresh → verify ID persisted
- Assign campaigns to this fund
- Verify: Transactions from these campaigns should sync to this external ID (backend behavior — check if any UI indicator exists)
- Clear the external ID → Save → Refresh → verify cleared
- Verify: Funds list shows empty External ID column for this fund
8.6 Fund as Campaign Filter#
- Create 2 custom funds with different colors
- Assign 2 campaigns to Fund A, 3 campaigns to Fund B, keep some in General
- Go to campaigns list
- Filter by Fund A → verify only 2 campaigns shown
- Filter by Fund B → verify only 3 campaigns shown
- Filter by General → verify remaining campaigns shown
- Filter by Fund A + Fund B → verify 5 campaigns shown
- Clear filter → verify all campaigns shown
- Verify filter persists in URL → copy URL, open in new tab, same filter active
8.7 Edge Cases#
- Fund name at max length (40 chars) → saves and displays correctly (truncated if needed in chips)
- Fund with a very long internal note → saves and displays in textarea
- Delete a fund with 0 campaigns → clean deletion
- Delete a fund with raised > 0 → still works (no raise restriction)
- Create multiple funds and verify sorting works correctly across all columns
- Fund with special characters in name → saves and displays correctly
- Multiple funds with similar names → both distinct in list and dropdowns
- Assign all campaigns to one fund → General fund has 0 campaigns (still works)
Verification Checklist#
| Area | Total Tests | Pass | Fail | Notes |
|---|---|---|---|---|
| Funds List Page | ~30 | |||
| Fund Creation | ~12 | |||
| General Fund Detail | ~10 | |||
| Custom Fund Detail | ~25 | |||
| Fund Deletion | ~12 | |||
| Fund-Campaign Relationship | ~20 | |||
| Cross-Cutting | ~15 | |||
| E2E Flows | ~35 | |||
| TOTAL | ~160 |