# TrakQR Test Plan > Manual test plan for validating all features before release. - ## Test Plan Overview | Item | Details | |------|---------| | **Application** | TrakQR - QR-First URL Shortener SaaS | | **Version** | MVP | | **Test Type** | Functional / End-to-End | | **Environment** | Development (`localhost:5173` + `localhost:42001`) | - ## How to Use This Document 1. Work through each section sequentially 2. Check the box `[x]` when a test passes 3. Mark `[F]` for failures and add notes 4. Mark `[S]` for skipped tests with reason --- - ## 1. Authentication - ### 1.1 Registration | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 1.1.1 | Register with valid email | 1. Go to `/register`
2. Enter valid email and password (8+ chars)
3. Click Register | Account created, redirected to dashboard, default workspace created | [ ] | | 1.1.2 | Register with invalid email | 1. Go to `/register`
2. Enter invalid email format
3. Click Register | Error message shown, no account created | [ ] | | 1.1.3 | Register with short password | 1. Go to `/register`
2. Enter valid email, password < 8 chars
3. Click Register | Error message about password length | [ ] | | 1.1.4 | Register with existing email | 1. Register a new account
2. Try to register again with same email | Error message about email already in use | [ ] | | 1.1.5 | Registration rate limiting | 1. Attempt to register 15+ times in 1 minute | Rate limit error after ~10 attempts | [ ] | - ### 1.2 Login | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 1.2.1 | Login with valid credentials | 1. Go to `/login`
2. Enter registered email/password
3. Click Login | Logged in, redirected to dashboard | [ ] | | 1.2.2 | Login with wrong password | 1. Go to `/login`
2. Enter valid email, wrong password
3. Click Login | Error message, not logged in | [ ] | | 1.2.3 | Login with non-existent email | 1. Go to `/login`
2. Enter unregistered email
3. Click Login | Error message (generic, no email enumeration) | [ ] | | 1.2.4 | Login rate limiting | 1. Attempt failed logins 15+ times | Rate limit error after ~10 attempts | [ ] | | 1.2.5 | Redirect after login | 1. Try to access `/links` while logged out
2. Get redirected to login
3. Login successfully | Redirected back to `/links` | [ ] | - ### 1.3 Password Reset | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 1.3.1 | Request password reset | 1. Go to `/forgot-password`
2. Enter registered email
3. Click Submit | Success message shown (check console for email in dev) | [ ] | | 1.3.2 | Reset with valid token | 1. Get reset token from email/console
2. Go to `/reset-password?token=xxx`
3. Enter new password | Password changed, can login with new password | [ ] | | 1.3.3 | Reset with invalid token | 1. Go to `/reset-password?token=invalid`
2. Enter new password | Error message about invalid/expired token | [ ] | | 1.3.4 | Reset with expired token | 1. Wait for token to expire (or modify DB)
2. Try to use token | Error message about expired token | [ ] | - ### 1.4 Email Verification | # | Test Case | Steps | Expected Result | Status | |----------------|-------|-----------------|--------| | 1.4.1 | Verify with valid token | 1. Register new account
2. Get verification token from console
3. Go to `/verify-email?token=xxx` | Email verified, success message | [ ] | | 1.4.2 | Verify with invalid token | 1. Go to `/verify-email?token=invalid` | Error message about invalid token | [ ] | | 1.4.3 | Resend verification | 1. Login with unverified account
2. Go to Settings
3. Click "Resend verification" | New email sent (check console) | [ ] | - ### 1.5 Logout | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 1.5.1 | Logout | 1. Login
2. Click Logout in sidebar | Logged out, redirected to login, protected routes inaccessible | [ ] | | 1.5.2 | Session persistence | 1. Login
2. Close browser
3. Reopen and go to app | Still logged in (token in localStorage) | [ ] | --- - ## 2. Workspaces - ### 2.1 Workspace Management | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 2.1.1 | Default workspace exists | 1. Register new account
2. Check workspace selector | Default workspace present | [ ] | | 2.1.2 | Create workspace | 1. Click + button in workspace selector
2. Enter name
3. Click Create | New workspace created, switched to it | [ ] | | 2.1.3 | Switch workspace | 1. Have 2+ workspaces
2. Select different workspace from dropdown | Workspace switched, data refreshed | [ ] | | 2.1.4 | Update workspace name | 1. Click settings icon in workspace selector
2. Change name
3. Save | Name updated | [ ] | | 2.1.5 | Delete workspace | 1. Have 2+ workspaces
2. Open workspace settings
3. Click Delete | Workspace deleted, switched to another | [ ] | | 2.1.6 | Cannot delete only workspace | 1. Have only 1 workspace
2. Try to delete it | Delete button disabled or error shown | [ ] | | 2.1.7 | Workspace persistence | 1. Switch to workspace B
2. Refresh page | Still on workspace B (localStorage) | [ ] | --- - ## 3. Projects - ### 3.1 Project CRUD | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 3.1.1 | View projects page | 1. Go to `/projects` | Projects list shown (or empty state) | [ ] | | 3.1.2 | Create project | 1. Click "New Project"
2. Enter name and description
3. Save | Project created, appears in list | [ ] | | 3.1.3 | Edit project | 1. Click edit on a project
2. Change name/description
3. Save | Project updated | [ ] | | 3.1.4 | Delete project | 1. Click delete on a project
2. Confirm deletion | Project removed from list | [ ] | | 3.1.5 | Project shows link count | 1. Create project
2. Create links assigned to project
3. View projects | Link count displayed correctly | [ ] | --- - ## 4. Short Links - ### 4.1 Link Creation | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 4.1.1 | Create link with auto slug | 1. Click "Create Link"
2. Enter destination URL only
3. Save | Link created with auto-generated slug | [ ] | | 4.1.2 | Create link with custom slug | 1. Click "Create Link"
2. Enter URL and custom slug
3. Save | Link created with custom slug | [ ] | | 4.1.3 | Create link with title | 1. Create link with title filled in | Title shown in list | [ ] | | 4.1.4 | Create link with expiration | 1. Create link with expiration date set | Link shows expiration, stops working after date | [ ] | | 4.1.5 | Create link with password | 1. Create link with password set | Link requires password to access | [ ] | | 4.1.6 | Invalid destination URL | 1. Try to create link with invalid URL | Error message shown | [ ] | | 4.1.7 | Duplicate slug | 1. Create link with slug "test"
2. Try to create another with same slug | Error message about duplicate | [ ] | - ### 4.2 Link Management | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 4.2.1 | View links list | 1. Go to `/links` | Links listed with short URL, destination, clicks | [ ] | | 4.2.2 | Copy short URL | 1. Click copy icon on a link | URL copied to clipboard | [ ] | | 4.2.3 | Edit link | 1. Click edit on a link
2. Change destination URL
3. Save | Link updated, redirect goes to new URL | [ ] | | 4.2.4 | Delete link (soft) | 1. Click delete on a link
2. Confirm | Link moved to trash | [ ] | | 4.2.5 | View trash | 1. Click "Trash" toggle | Deleted links shown | [ ] | | 4.2.6 | Restore link | 1. View trash
2. Click restore on a link | Link restored, works again | [ ] | | 4.2.7 | Disable link | 1. Edit link
2. Set status to Disabled
3. Save | Link returns 404/disabled message | [ ] | - ### 4.3 Bulk Import | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 4.3.1 | Bulk import URLs | 1. Click "Bulk Import"
2. Paste multiple URLs (one per line)
3. Import | All links created | [ ] | | 4.3.2 | Bulk import with titles | 1. Paste URLs with titles: `https://example.com, My Title`
2. Import | Links created with titles | [ ] | | 4.3.3 | Bulk import with invalid URLs | 1. Include some invalid URLs in list
2. Import | Valid URLs imported, errors shown for invalid | [ ] | - ### 4.4 UTM Builder | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 4.4.1 | Add UTM parameters | 1. Create link
2. Expand UTM builder
3. Fill in source, medium, campaign | UTM params appended to destination | [ ] | | 4.4.2 | UTM presets | 1. Click "Google Ads" preset | Fields populated with Google preset values | [ ] | | 4.4.3 | UTM preview | 1. Fill in UTM fields | Preview shows full URL with params | [ ] | --- - ## 5. Redirect - ### 5.1 Public Redirect | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 5.1.1 | Basic redirect | 1. Create link to `https://example.com`
2. Visit short URL | Redirected to example.com | [ ] | | 5.1.2 | Non-existent slug | 1. Visit `/{random-slug}` | 404 error page | [ ] | | 5.1.3 | Disabled link | 1. Disable a link
2. Visit its short URL | Error message (link disabled) | [ ] | | 5.1.4 | Expired link | 1. Create link with past expiration
2. Visit short URL | Error message (link expired) | [ ] | | 5.1.5 | Password-protected link | 1. Create password-protected link
2. Visit short URL | Password prompt shown | [ ] | | 5.1.6 | Password entry | 1. Visit password-protected link
2. Enter correct password | Redirected to destination | [ ] | | 5.1.7 | Wrong password | 1. Visit password-protected link
2. Enter wrong password | Error, stays on password page | [ ] | | 5.1.8 | Redirect rate limiting | 1. Hit redirect endpoint 100+ times rapidly | Rate limited after threshold | [ ] | --- - ## 6. QR Codes - ### 6.1 QR Creation | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 6.1.1 | Create basic QR | 1. Go to `/qrcodes/new`
2. Select a link
3. Save | QR code created with default style | [ ] | | 6.1.2 | QR requires link | 1. Go to `/qrcodes/new`
2. Try to save without link | Error message | [ ] | | 6.1.3 | QR name | 1. Create QR with custom name | Name shown in list | [ ] | - ### 6.2 QR Designer | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 6.2.1 | Change foreground color | 1. Edit QR
2. Change foreground color
3. View preview | QR updates with new color | [ ] | | 6.2.2 | Change background color | 1. Change background color | QR updates with new background | [ ] | | 6.2.3 | Module shapes | 1. Try Square, Rounded, Dots shapes | Each shape renders correctly | [ ] | | 6.2.4 | Eye shapes | 1. Try Square, Rounded, Circle eye shapes | Each eye shape renders correctly | [ ] | | 6.2.5 | Error correction levels | 1. Try L, M, Q, H levels | QR generates with different densities | [ ] | | 6.2.6 | Quiet zone | 1. Adjust quiet zone size | Padding around QR changes | [ ] | | 6.2.7 | Style presets | 1. Click each of 6 presets | Styles applied correctly | [ ] | | 6.2.8 | Upload logo | 1. Click logo upload
2. Select image file | Logo appears in center of QR | [ ] | | 6.2.9 | Select existing logo | 1. Have uploaded assets
2. Select from asset gallery | Logo applied from gallery | [ ] | | 6.2.10 | Remove logo | 1. Have QR with logo
2. Remove logo | QR renders without logo | [ ] | - ### 6.3 QR Export | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 6.3.1 | Export PNG | 1. View QR
2. Click "Download PNG" | PNG file downloaded | [ ] | | 6.3.2 | Export SVG | 1. Click "Download SVG" | SVG file downloaded | [ ] | | 6.3.3 | PNG has correct size | 1. Export PNG
2. Check dimensions | Matches selected size (256/512/1024) | [ ] | | 6.3.4 | QR is scannable | 1. Export QR
2. Scan with phone | Opens correct short URL | [ ] | | 6.3.5 | QR with logo is scannable | 1. Add logo to QR
2. Export and scan | Still scans correctly | [ ] | - ### 6.4 QR List | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 6.4.1 | View QR list | 1. Go to `/qrcodes` | List shows QR previews | [ ] | | 6.4.2 | QR preview thumbnails | 1. View list | Each QR shows thumbnail preview | [ ] | | 6.4.3 | Delete QR | 1. Click delete on QR
2. Confirm | QR removed from list | [ ] | | 6.4.4 | Edit QR | 1. Click edit on QR | Opens designer with saved settings | [ ] | --- - ## 7. Analytics - ### 7.1 Event Tracking | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 7.1.1 | Click event recorded | 1. Create link
2. Visit short URL
3. Check analytics | Click count increased | [ ] | | 7.1.2 | Scan event recorded | 1. Create QR
2. Export and scan QR
3. Check QR analytics | Scan count increased | [ ] | | 7.1.3 | Unique visitor tracking | 1. Visit same link multiple times quickly
2. Check analytics | Only 1 unique visitor (dedupe) | [ ] | | 7.1.4 | Device detection | 1. Visit from mobile device
2. Check device breakdown | Mobile device recorded | [ ] | | 7.1.5 | Referrer tracking | 1. Visit link from a webpage
2. Check referrer breakdown | Referrer domain recorded | [ ] | | 7.1.6 | Country detection | 1. Visit link
2. Check geo breakdown | Country detected (if GeoIP configured) | [ ] | - ### 7.2 Dashboard Analytics | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 7.2.1 | View dashboard | 1. Go to `/dashboard` | Stats cards, chart, breakdowns shown | [ ] | | 7.2.2 | Total clicks | 1. Check total clicks card | Matches sum of all link clicks | [ ] | | 7.2.3 | Time series chart | 1. View activity chart | Shows clicks/scans over time | [ ] | | 7.2.4 | Top links | 1. View top links section | Most clicked links shown | [ ] | | 7.2.5 | Device breakdown | 1. View device breakdown | Desktop/Mobile/Tablet shown | [ ] | | 7.2.6 | Referrer breakdown | 1. View referrer breakdown | Top referrers listed | [ ] | | 7.2.7 | Country breakdown | 1. View geo breakdown | Countries with flags shown | [ ] | - ### 7.3 Period Filters | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 7.3.1 | 24h filter | 1. Click "24h" period | Data filtered to last 24 hours | [ ] | | 7.3.2 | 7d filter | 1. Click "7d" period | Data filtered to last 7 days | [ ] | | 7.3.3 | 30d filter | 1. Click "30d" period | Data filtered to last 30 days | [ ] | - ### 7.4 Per-Link Analytics | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 7.4.1 | View link detail | 1. Go to `/links/{id}` | Link analytics shown | [ ] | | 7.4.2 | Link-specific stats | 1. View link detail | Stats match only that link's data | [ ] | - ### 7.5 Per-QR Analytics | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 7.5.1 | View QR analytics | 1. Go to `/qrcodes/{id}/analytics` | QR scan stats shown | [ ] | | 7.5.2 | QR-specific stats | 1. View QR detail | Stats match only that QR's scans | [ ] | --- - ## 8. Domains - ### 8.1 Domain Management | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 8.1.1 | View domains page | 1. Go to `/domains` | Domains list or upgrade prompt (Free plan) | [ ] | | 8.1.2 | Add domain (Pro+) | 1. Click "Add Domain"
2. Enter hostname
3. Submit | Domain added with Pending status | [ ] | | 8.1.3 | Verification instructions | 1. Add new domain | TXT record and CNAME instructions shown | [ ] | | 8.1.4 | Copy verification token | 1. Click copy on verification token | Token copied to clipboard | [ ] | | 8.1.5 | Verify domain | 1. Add DNS records
2. Click "Verify" | Domain status changes to Verified | [ ] | | 8.1.6 | Delete domain | 1. Click delete on domain
2. Confirm | Domain removed | [ ] | | 8.1.7 | Free plan restriction | 1. Be on Free plan
2. Try to add domain | Upgrade prompt shown | [ ] | --- - ## 9. Settings - ### 9.1 Profile | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 9.1.1 | View profile | 1. Go to `/settings` | Current email shown | [ ] | | 9.1.2 | Update email | 1. Change email
2. Save | Email updated | [ ] | | 9.1.3 | Verification warning | 1. Have unverified email | Warning shown with resend link | [ ] | - ### 9.2 Password Change | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 9.2.1 | Change password | 1. Enter current password
2. Enter new password twice
3. Submit | Password changed | [ ] | | 9.2.2 | Wrong current password | 1. Enter wrong current password
2. Submit | Error message | [ ] | | 9.2.3 | Passwords don't match | 1. Enter different passwords
2. Submit | Error message | [ ] | - ### 9.3 API Keys | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 9.3.1 | View API keys | 1. Scroll to API Keys section | List of keys (or empty state) | [ ] | | 9.3.2 | Create API key | 1. Click "Create Key"
2. Enter name
3. Create | Key created, full key shown once | [ ] | | 9.3.3 | Copy API key | 1. Create key
2. Click copy | Key copied to clipboard | [ ] | | 9.3.4 | Key with expiry | 1. Create key with 30-day expiry | Expiry date shown | [ ] | | 9.3.5 | Delete API key | 1. Click delete on a key
2. Confirm | Key removed | [ ] | | 9.3.6 | Key prefix shown | 1. View existing keys | Only prefix visible (e.g., `tq_abc...`) | [ ] | - ### 9.4 Danger Zone | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 9.4.1 | Delete account prompt | 1. Click "Delete Account" | Confirmation modal with password | [ ] | | 9.4.2 | Delete account | 1. Enter password
2. Confirm delete | Account deleted, logged out | [ ] | | 9.4.3 | Wrong password on delete | 1. Enter wrong password | Error, account not deleted | [ ] | --- - ## 10. Billing - ### 10.1 Plan Display | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 10.1.1 | View billing page | 1. Go to `/billing` | Current plan and features shown | [ ] | | 10.1.2 | Plan comparison | 1. View billing page | Free/Pro/Business plans compared | [ ] | | 10.1.3 | Current plan highlighted | 1. View billing page | Current plan marked as "Current" | [ ] | - ### 10.2 Stripe Integration | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 10.2.1 | Upgrade to Pro | 1. Click "Upgrade" on Pro
2. Complete Stripe checkout | Plan upgraded | [ ] | | 10.2.2 | Manage subscription | 1. Click "Manage Subscription"
2. Stripe portal opens | Can update payment, cancel | [ ] | | 10.2.3 | Cancel subscription | 1. Cancel in Stripe portal | Plan downgrades at period end | [ ] | --- - ## 11. Plan Limits - ### 11.1 Free Plan Limits | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 11.1.1 | Workspace limit | 1. Be on Free plan
2. Try to create 2nd workspace | Error: limit reached | [ ] | | 11.1.2 | Link limit | 1. Create 50 links
2. Try to create 51st | Error: limit reached | [ ] | | 11.1.3 | QR code limit | 1. Create 25 QR codes
2. Try to create 26th | Error: limit reached | [ ] | | 11.1.4 | No custom domains | 1. Try to add domain on Free | Upgrade prompt | [ ] | - ### 11.2 Usage Display | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 11.2.1 | View usage | 1. Check billing or dashboard | Usage stats shown (links, QRs, events) | [ ] | --- - ## 12. UI/UX - ### 12.1 Navigation | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 12.1.1 | Sidebar navigation | 1. Click each nav item | Correct page loads | [ ] | | 12.1.2 | Active nav highlight | 1. Navigate to different pages | Current page highlighted in sidebar | [ ] | | 12.1.3 | Mobile responsive | 1. Resize to mobile width | Layout adapts correctly | [ ] | - ### 12.2 Loading States | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 12.2.1 | Loading indicators | 1. Navigate to data-heavy page | Loading indicator shown while fetching | [ ] | | 12.2.2 | Button loading states | 1. Submit a form | Button shows loading state | [ ] | - ### 12.3 Error Handling | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 12.3.1 | API errors shown | 1. Trigger an API error | User-friendly error message | [ ] | | 12.3.2 | Network error | 1. Disable network
2. Try an action | Appropriate error message | [ ] | | 12.3.3 | 401 redirect | 1. Clear token from localStorage
2. Try protected action | Redirected to login | [ ] | - ### 12.4 Empty States | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 12.4.1 | No links | 1. View links with none created | Empty state with CTA | [ ] | | 12.4.2 | No QR codes | 1. View QR codes with none created | Empty state with CTA | [ ] | | 12.4.3 | No projects | 1. View projects with none created | Empty state with CTA | [ ] | | 12.4.4 | No analytics data | 1. View analytics with no events | Empty state message | [ ] | --- - ## 13. Security - ### 13.1 Authentication | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 13.1.1 | Protected routes | 1. Log out
2. Try to access `/dashboard` | Redirected to login | [ ] | | 13.1.2 | JWT expiry | 1. Wait for token to expire
2. Try an action | Redirected to login | [ ] | | 13.1.3 | Cross-workspace access | 1. Try to access another user's workspace ID | 404 or 403 error | [ ] | - ### 13.2 Input Validation | # | Test Case | Steps | Expected Result | Status | |---|-----------|-------|-----------------|--------| | 13.2.1 | XSS in link title | 1. Create link with `