329 lines
12 KiB
Markdown
329 lines
12 KiB
Markdown
# Payfrit Beacon iOS — QA Walkthrough Test Document
|
|
|
|
## Overview
|
|
|
|
**App:** Payfrit Beacon iOS (SwiftUI)
|
|
**Purpose:** BLE beacon management for business locations
|
|
**Auth:** Token-based with Keychain storage + biometric
|
|
**Environment:** Dev (`dev.payfrit.com`) / Prod (`biz.payfrit.com`) — hardcoded in `APIService.swift:50`
|
|
|
|
---
|
|
|
|
## Pre-Test Setup
|
|
|
|
- [ ] Device has iOS 14+
|
|
- [ ] Bluetooth enabled in Settings
|
|
- [ ] Location Services enabled in Settings
|
|
- [ ] Stable network connection (Wi-Fi or LTE)
|
|
- [ ] A physical BLE beacon available for scanner tests
|
|
- [ ] Test account with at least 1 business, 1 beacon, 1 service point
|
|
|
|
---
|
|
|
|
## 1. Authentication Flow
|
|
|
|
### 1.1 First-Time Login
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Launch app (fresh install) | LoginScreen shown |
|
|
| 2 | Leave fields empty, tap "Sign In" | Button disabled (name/password empty) |
|
|
| 3 | Enter email only, tap "Sign In" | Error: "Please enter username and password" |
|
|
| 4 | Enter valid email + wrong password | Error: "Invalid email/phone or password" |
|
|
| 5 | Enter valid credentials, tap "Sign In" | Loading spinner on button, navigates to BusinessSelectionScreen |
|
|
| 6 | Kill and relaunch app | Saved auth loaded, skips login |
|
|
|
|
### 1.2 Biometric Re-Auth (Device Only, Skipped on Simulator)
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Relaunch app with saved auth | Face ID / Touch ID prompt appears |
|
|
| 2 | Authenticate successfully | Navigates to BusinessSelectionScreen |
|
|
| 3 | Relaunch, cancel biometric | Still loads saved auth (fallback) |
|
|
|
|
### 1.3 Logout
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | From BusinessSelectionScreen, tap logout (top-right arrow icon) | Clears Keychain + UserDefaults, returns to LoginScreen |
|
|
| 2 | Kill and relaunch | LoginScreen shown (no saved auth) |
|
|
|
|
### 1.4 Session Expiry (401)
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Login, wait for token to expire (or invalidate server-side) | Next API call returns 401 |
|
|
| 2 | Try any action (list beacons, etc.) | Auto-logout, returns to LoginScreen |
|
|
|
|
### 1.5 Dev Mode Indicator
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Launch in dev environment | LoginScreen shows red "DEV MODE — password: 123456" |
|
|
| 2 | After login, check bottom-left of RootView | Orange "DEV" banner visible |
|
|
|
|
---
|
|
|
|
## 2. Business Selection
|
|
|
|
### 2.1 Load Businesses
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Login successfully | BusinessSelectionScreen loads with spinner |
|
|
| 2 | Wait for load | List of businesses with name + city |
|
|
| 3 | Tap a business | Pushes to BeaconDashboard, shows "Loading..." then TabView |
|
|
|
|
### 2.2 Edge Cases
|
|
|
|
| Scenario | Expected |
|
|
|----------|----------|
|
|
| User has no businesses | "No businesses found" message, can only logout |
|
|
| Network error during load | Error message + "Retry" button |
|
|
| Tap Retry | Re-fetches business list |
|
|
| 401 during load | Auto-logout to LoginScreen |
|
|
|
|
---
|
|
|
|
## 3. Beacon Management (Beacons Tab)
|
|
|
|
### 3.1 View Beacon List
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Tap "Beacons" tab | List loads with spinner |
|
|
| 2 | Verify beacon rows | Each shows: name, formatted UUID (XXXXXXXX-XXXX-...), active badge (green check / red X) |
|
|
| 3 | Pull down to refresh | Spinner appears, list refreshes |
|
|
| 4 | Empty list | "No beacons yet" + "Tap + to add" message |
|
|
|
|
### 3.2 Create Beacon
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Tap + button | BeaconEditSheet modal appears |
|
|
| 2 | Leave fields empty, tap Save | Button disabled |
|
|
| 3 | Enter name only | Button disabled (UUID required) |
|
|
| 4 | Enter name + valid 32-char hex UUID | Save enabled |
|
|
| 5 | Tap Save | Modal dismisses, list refreshes with new beacon |
|
|
| 6 | Network error during save | Error shown in sheet, sheet stays open |
|
|
|
|
### 3.3 Edit Beacon
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Tap a beacon row | Pushes to BeaconDetailScreen |
|
|
| 2 | Verify pre-filled fields | Name, UUID, Active toggle match beacon data |
|
|
| 3 | Verify read-only fields | ID, Business ID, Created, Updated dates shown |
|
|
| 4 | Change name, tap "Save Changes" | Returns to list, beacon updated |
|
|
| 5 | Toggle Active off, save | Badge changes from green check to red X |
|
|
| 6 | Clear name, try save | Button disabled |
|
|
|
|
### 3.4 Delete Beacon (Detail Screen)
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | From BeaconDetailScreen, tap "Delete Beacon" | Alert: "Delete Beacon?" with warning about service points |
|
|
| 2 | Tap "Cancel" | Nothing happens |
|
|
| 3 | Tap "Delete" | API call, returns to list, beacon removed |
|
|
| 4 | Network error during delete | Error shown, stays on detail screen |
|
|
|
|
### 3.5 Delete Beacon (Swipe)
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Swipe left on beacon row | Delete action appears |
|
|
| 2 | Tap delete | Beacon removed immediately (optimistic) |
|
|
| 3 | If API fails | Beacon re-added to list, error shown |
|
|
|
|
### 3.6 Rapid Delete Stress Test
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Swipe-delete 3 beacons quickly | All removed from UI |
|
|
| 2 | Wait for API responses | Failed deletes restored, successful ones stay removed |
|
|
|
|
---
|
|
|
|
## 4. Service Points (Service Points Tab)
|
|
|
|
### 4.1 View Service Points
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Tap "Service Points" tab | List loads (fetches both service points AND beacons) |
|
|
| 2 | Verify rows | Name, type, active indicator (green/red circle), beacon assignment |
|
|
| 3 | Pull down to refresh | Refreshes both lists |
|
|
| 4 | Empty list | "No service points" message |
|
|
|
|
### 4.2 Assign Beacon
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Find service point with "No beacon assigned" | "Assign" button visible |
|
|
| 2 | Tap "Assign" | BeaconPickerSheet opens with all beacons |
|
|
| 3 | Tap a beacon | Sheet dismisses, spinner on row, then beacon name in green |
|
|
|
|
### 4.3 Change Beacon Assignment
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Find service point with assigned beacon | "Change" button + X button visible |
|
|
| 2 | Tap "Change" | Picker opens, current beacon has checkmark |
|
|
| 3 | Select different beacon | Assignment updated |
|
|
|
|
### 4.4 Unassign Beacon
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Find service point with assigned beacon | X button visible |
|
|
| 2 | Tap X button | API called with nil beaconId, shows "No beacon assigned" |
|
|
|
|
### 4.5 Edge Cases
|
|
|
|
| Scenario | Expected |
|
|
|----------|----------|
|
|
| No beacons exist | Picker opens with empty list |
|
|
| Network error during assign | Error shown, assignment reverted |
|
|
| 401 during any operation | Auto-logout |
|
|
|
|
---
|
|
|
|
## 5. Scanner (Scanner Tab)
|
|
|
|
### 5.1 Basic Scanning Flow
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Tap "Scanner" tab | Beacon picker loads at top |
|
|
| 2 | No beacon selected | "Start Scanning" button disabled |
|
|
| 3 | Select a beacon from picker | Button becomes enabled |
|
|
| 4 | Tap "Start Scanning" | Status: "Scanning..." (blue antenna icon) |
|
|
| 5 | Move close to matching beacon | RSSI value appears, samples count up (X/5) |
|
|
| 6 | Stay close for 5+ samples with RSSI >= -75 | "Beacon Detected! (avg -XX dBm)" (green checkmark) |
|
|
| 7 | Tap "Stop Scanning" | Status resets to "Select a beacon to scan" |
|
|
|
|
### 5.2 Signal Strength Visualization
|
|
|
|
| RSSI Range | Bar Color | Signal Quality |
|
|
|------------|-----------|---------------|
|
|
| -30 to -51 | Green | Strong |
|
|
| -52 to -72 | Yellow | Medium |
|
|
| -73 to -100 | Red | Weak |
|
|
|
|
### 5.3 RSSI Threshold Behavior
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Scanning, RSSI >= -75 | Sample appended, count increments |
|
|
| 2 | RSSI drops below -75 | All samples cleared, count resets to 0 |
|
|
| 3 | RSSI returns above -75 | Samples start accumulating again from 0 |
|
|
| 4 | Beacon disappears entirely (RSSI = 0) | Samples cleared, "Searching for beacon signal..." |
|
|
|
|
### 5.4 Permission Handling
|
|
|
|
| Scenario | Expected |
|
|
|----------|----------|
|
|
| Location not determined | System prompt shown, scanning waits |
|
|
| Location granted after prompt | Scanning starts automatically |
|
|
| Location denied | "Location Permission Denied" (red), scanning stops |
|
|
| Location denied in Settings | Same as above on next scan attempt |
|
|
| Bluetooth off | "Bluetooth is OFF" (orange), scanning stops |
|
|
| Bluetooth turned off mid-scan | Detected within 5 seconds, scanning stops |
|
|
|
|
### 5.5 Beacon Change During Scan
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Start scanning for Beacon A | Scanning active |
|
|
| 2 | Change picker to Beacon B | Scan stops immediately |
|
|
| 3 | Tap "Start Scanning" | New scan starts for Beacon B |
|
|
|
|
### 5.6 Screen Lock Prevention
|
|
|
|
| Step | Action | Expected |
|
|
|------|--------|----------|
|
|
| 1 | Start scanning | Screen stays awake (idle timer disabled) |
|
|
| 2 | Stop scanning | Idle timer re-enabled, screen can lock normally |
|
|
|
|
### 5.7 Error Cases
|
|
|
|
| Scenario | Expected |
|
|
|----------|----------|
|
|
| Invalid UUID format on beacon | Error: "Invalid beacon UUID format" |
|
|
| Ranging failure (CLLocationManager error) | Error: "Beacon ranging failed: [description]" |
|
|
| Beacon list fails to load | Picker empty, scanner still functional if UUID known |
|
|
|
|
---
|
|
|
|
## 6. Cross-Cutting Concerns
|
|
|
|
### 6.1 Network Error Handling (All Screens)
|
|
|
|
| Scenario | Expected |
|
|
|----------|----------|
|
|
| Airplane mode during API call | Network error displayed |
|
|
| Server returns 500 | "HTTP 500" error shown |
|
|
| Server returns non-JSON | "Decoding error: Non-JSON response" |
|
|
| Server returns `{"OK": false, "ERROR": "..."}` | Error message from server shown |
|
|
| 401 on any authenticated endpoint | Auto-logout to LoginScreen |
|
|
|
|
### 6.2 Navigation
|
|
|
|
| Test | Expected |
|
|
|------|----------|
|
|
| Back button from BeaconDetailScreen | Returns to BeaconListScreen |
|
|
| Back button from BeaconDashboard | Returns to BusinessSelectionScreen |
|
|
| Tab switching in BeaconDashboard | Beacons / Service Points / Scanner tabs all functional |
|
|
| Deep link: Business > Beacon > Detail > Back > Back | Full nav stack unwinds cleanly |
|
|
|
|
### 6.3 Data Consistency
|
|
|
|
| Test | Expected |
|
|
|------|----------|
|
|
| Add beacon, switch to Service Points tab | New beacon available in picker |
|
|
| Delete beacon assigned to service point | Service point shows "No beacon assigned" on refresh |
|
|
| Edit beacon name | Updated name shows in list and service point rows |
|
|
|
|
---
|
|
|
|
## 7. API Endpoints Reference
|
|
|
|
| Action | Method | Endpoint |
|
|
|--------|--------|----------|
|
|
| Login | POST | `/auth/login.cfm` |
|
|
| List businesses | POST | `/workers/myBusinesses.cfm` |
|
|
| List beacons | POST | `/beacons/list.cfm` |
|
|
| Get beacon | POST | `/beacons/get.cfm` |
|
|
| Create beacon | POST | `/beacons/create.cfm` |
|
|
| Update beacon | POST | `/beacons/update.cfm` |
|
|
| Delete beacon | POST | `/beacons/delete.cfm` |
|
|
| List service points | POST | `/servicePoints/list.cfm` |
|
|
| List SP types | POST | `/servicePoints/types.cfm` |
|
|
| Assign beacon to SP | POST | `/servicePoints/assignBeacon.cfm` |
|
|
|
|
All requests include headers:
|
|
- `Content-Type: application/json`
|
|
- `X-User-Token: <token>` (after login)
|
|
- `X-Business-ID: <id>` (after business selection)
|
|
|
|
---
|
|
|
|
## 8. Permissions Checklist
|
|
|
|
| Permission | Info.plist Key | When Prompted |
|
|
|-----------|----------------|---------------|
|
|
| Location (When In Use) | `NSLocationWhenInUseUsageDescription` | First beacon scan |
|
|
| Location (Always) | `NSLocationAlwaysAndWhenInUseUsageDescription` | Background scanning |
|
|
| Bluetooth | `NSBluetoothAlwaysUsageDescription` | First beacon scan |
|
|
| Face ID | `NSFaceIDUsageDescription` | App relaunch with saved auth |
|
|
|
|
---
|
|
|
|
## 9. Known Limitations
|
|
|
|
1. **Environment switching** requires code change + recompile (`APIService.swift:50`)
|
|
2. **Service points are read-only** — can only assign/unassign beacons, not create/edit/delete SPs
|
|
3. **No token refresh** — expired tokens force full re-login
|
|
4. **Single scanner** — only one scan at a time, changing beacon stops previous
|
|
5. **Background scanning** depends on iOS version and background mode support
|
|
6. **Photo URLs** resolved but not cached across sessions
|