Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e13570deb | ||
| 2eba66fb71 |
@@ -50,7 +50,8 @@ services:
|
||||
- '80:80' # HTTP redirect to HTTPS (matches production)
|
||||
- '443:443' # Frontend HTTPS (nginx proxies Vite 5173 → 443)
|
||||
- '3001:3001' # Backend API
|
||||
- '8000:8000' # Bugsink error tracking (ADR-015)
|
||||
- '8000:8000' # Bugsink error tracking HTTP (ADR-015)
|
||||
- '8443:8443' # Bugsink error tracking HTTPS (ADR-015)
|
||||
environment:
|
||||
# Core settings
|
||||
- NODE_ENV=development
|
||||
@@ -81,9 +82,9 @@ services:
|
||||
- BUGSINK_ADMIN_EMAIL=admin@localhost
|
||||
- BUGSINK_ADMIN_PASSWORD=admin
|
||||
- BUGSINK_SECRET_KEY=dev-bugsink-secret-key-minimum-50-characters-for-security
|
||||
# Sentry SDK configuration (points to local Bugsink)
|
||||
- SENTRY_DSN=http://59a58583-e869-7697-f94a-cfa0337676a8@localhost:8000/1
|
||||
- VITE_SENTRY_DSN=http://d5fc5221-4266-ff2f-9af8-5689696072f3@localhost:8000/2
|
||||
# Sentry SDK configuration (points to local Bugsink HTTPS)
|
||||
- SENTRY_DSN=https://cea01396-c562-46ad-b587-8fa5ee6b1d22@localhost:8443/1
|
||||
- VITE_SENTRY_DSN=https://d92663cb-73cf-4145-b677-b84029e4b762@localhost:8443/2
|
||||
- SENTRY_ENVIRONMENT=development
|
||||
- VITE_SENTRY_ENVIRONMENT=development
|
||||
- SENTRY_ENABLED=true
|
||||
|
||||
534
docs/TESTING_SESSION_2026-01-21.md
Normal file
534
docs/TESTING_SESSION_2026-01-21.md
Normal file
@@ -0,0 +1,534 @@
|
||||
# Testing Session - UI/UX Improvements
|
||||
|
||||
**Date**: 2026-01-21
|
||||
**Tester**: [Your Name]
|
||||
**Session Start**: [Time]
|
||||
**Environment**: Dev Container
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Session Objective
|
||||
|
||||
Test all 4 critical UI/UX improvements:
|
||||
|
||||
1. Brand Colors (visual verification)
|
||||
2. Button Component (functional testing)
|
||||
3. Onboarding Tour (flow testing)
|
||||
4. Mobile Navigation (responsive testing)
|
||||
|
||||
---
|
||||
|
||||
## ✅ Pre-Test Setup Checklist
|
||||
|
||||
### 1. Dev Server Status
|
||||
|
||||
- [ ] Dev server running at `http://localhost:5173`
|
||||
- [ ] Browser open (Chrome/Edge recommended)
|
||||
- [ ] DevTools open (F12)
|
||||
|
||||
**Command to start**:
|
||||
|
||||
```bash
|
||||
podman exec -it flyer-crawler-dev npm run dev:container
|
||||
```
|
||||
|
||||
**Server Status**: [ ] Running [ ] Not Running
|
||||
|
||||
---
|
||||
|
||||
### 2. Browser Setup
|
||||
|
||||
- [ ] Clear cache (Ctrl+Shift+Delete)
|
||||
- [ ] Clear localStorage for localhost
|
||||
- [ ] Enable responsive design mode (Ctrl+Shift+M)
|
||||
|
||||
**Browser Version**: ********\_********
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Test Execution
|
||||
|
||||
### TEST 1: Onboarding Tour ⭐ CRITICAL
|
||||
|
||||
**Priority**: 🔴 Must Pass
|
||||
**Time**: 5 minutes
|
||||
|
||||
#### Steps:
|
||||
|
||||
1. Open DevTools → Application → Local Storage
|
||||
2. Delete key: `flyer_crawler_onboarding_completed`
|
||||
3. Refresh page (F5)
|
||||
4. Observe if tour appears
|
||||
|
||||
#### Expected:
|
||||
|
||||
- ✅ Tour modal appears within 2 seconds
|
||||
- ✅ Shows "Step 1 of 6"
|
||||
- ✅ Points to Flyer Uploader section
|
||||
- ✅ Skip button visible
|
||||
- ✅ Next button visible
|
||||
|
||||
#### Actual Result:
|
||||
|
||||
```
|
||||
[Record what you see here]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
**Status**: [ ] ✅ PASS [ ] ❌ FAIL [ ] ⚠️ PARTIAL
|
||||
|
||||
**Screenshots**: [Attach if needed]
|
||||
|
||||
---
|
||||
|
||||
### TEST 2: Tour Navigation
|
||||
|
||||
**Time**: 5 minutes
|
||||
|
||||
#### Steps:
|
||||
|
||||
Click "Next" button 6 times, observe each step
|
||||
|
||||
#### Verification Table:
|
||||
|
||||
| Step | Target | Visible? | Correct Text? | Notes |
|
||||
| ---- | -------------- | -------- | ------------- | ----- |
|
||||
| 1 | Flyer Uploader | [ ] | [ ] | |
|
||||
| 2 | Data Table | [ ] | [ ] | |
|
||||
| 3 | Watch Button | [ ] | [ ] | |
|
||||
| 4 | Watchlist | [ ] | [ ] | |
|
||||
| 5 | Price Chart | [ ] | [ ] | |
|
||||
| 6 | Shopping List | [ ] | [ ] | |
|
||||
|
||||
#### Additional Checks:
|
||||
|
||||
- [ ] Progress indicator updates (1/6 → 6/6)
|
||||
- [ ] Can click "Previous" button
|
||||
- [ ] Tour closes after step 6
|
||||
- [ ] localStorage key saved
|
||||
|
||||
**Status**: [ ] ✅ PASS [ ] ❌ FAIL
|
||||
|
||||
---
|
||||
|
||||
### TEST 3: Mobile Tab Bar ⭐ CRITICAL
|
||||
|
||||
**Priority**: 🔴 Must Pass
|
||||
**Time**: 8 minutes
|
||||
|
||||
#### Part A: Mobile View (375px)
|
||||
|
||||
**Setup**: Toggle device toolbar → iPhone SE
|
||||
|
||||
#### Checks:
|
||||
|
||||
- [ ] Bottom tab bar visible
|
||||
- [ ] 4 tabs present: Home, Deals, Lists, Profile
|
||||
- [ ] Left sidebar (flyer list) HIDDEN
|
||||
- [ ] Right sidebar (widgets) HIDDEN
|
||||
- [ ] Main content uses full width
|
||||
|
||||
**Visual Check**:
|
||||
|
||||
```
|
||||
Tab Bar Position: [ ] Bottom [ ] Other: _______
|
||||
Number of Tabs: _______
|
||||
Tab Bar Height: ~64px? [ ] Yes [ ] No
|
||||
```
|
||||
|
||||
#### Part B: Tab Navigation
|
||||
|
||||
Click each tab and verify:
|
||||
|
||||
| Tab | URL | Page Loads? | Highlights? | Content Correct? |
|
||||
| ------- | ---------- | ----------- | ----------- | ---------------- |
|
||||
| Home | `/` | [ ] | [ ] | [ ] |
|
||||
| Deals | `/deals` | [ ] | [ ] | [ ] |
|
||||
| Lists | `/lists` | [ ] | [ ] | [ ] |
|
||||
| Profile | `/profile` | [ ] | [ ] | [ ] |
|
||||
|
||||
#### Part C: Desktop View (1440px)
|
||||
|
||||
**Setup**: Exit device mode, maximize window
|
||||
|
||||
#### Checks:
|
||||
|
||||
- [ ] Tab bar HIDDEN (not visible)
|
||||
- [ ] Left sidebar VISIBLE
|
||||
- [ ] Right sidebar VISIBLE
|
||||
- [ ] 3-column layout intact
|
||||
- [ ] No layout regressions
|
||||
|
||||
**Status**: [ ] ✅ PASS [ ] ❌ FAIL
|
||||
|
||||
---
|
||||
|
||||
### TEST 4: Dark Mode ⭐ CRITICAL
|
||||
|
||||
**Priority**: 🔴 Must Pass
|
||||
**Time**: 5 minutes
|
||||
|
||||
#### Steps:
|
||||
|
||||
1. Click dark mode toggle in header
|
||||
2. Navigate: Home → Deals → Lists → Profile
|
||||
3. Observe colors and contrast
|
||||
|
||||
#### Visual Verification:
|
||||
|
||||
**Mobile Tab Bar**:
|
||||
|
||||
- [ ] Dark background (#111827 or similar)
|
||||
- [ ] Dark border color
|
||||
- [ ] Active tab: teal (#14b8a6)
|
||||
- [ ] Inactive tabs: gray
|
||||
|
||||
**New Pages**:
|
||||
|
||||
- [ ] DealsPage: dark background, light text
|
||||
- [ ] ShoppingListsPage: dark cards
|
||||
- [ ] FlyersPage: dark theme
|
||||
- [ ] No white boxes visible
|
||||
|
||||
**Button Component**:
|
||||
|
||||
- [ ] Primary buttons: teal background
|
||||
- [ ] Secondary buttons: gray background
|
||||
- [ ] Danger buttons: red background
|
||||
- [ ] All text readable
|
||||
|
||||
#### Toggle Back:
|
||||
|
||||
- [ ] Light mode restores correctly
|
||||
- [ ] No stuck dark elements
|
||||
|
||||
**Status**: [ ] ✅ PASS [ ] ❌ FAIL
|
||||
|
||||
---
|
||||
|
||||
### TEST 5: Brand Colors Visual Check
|
||||
|
||||
**Time**: 3 minutes
|
||||
|
||||
#### Verification:
|
||||
|
||||
Navigate through app and check teal color consistency:
|
||||
|
||||
- [ ] Active tab: teal
|
||||
- [ ] Primary buttons: teal
|
||||
- [ ] Links on hover: teal
|
||||
- [ ] Focus rings: teal
|
||||
- [ ] All teal shades match (#14b8a6)
|
||||
|
||||
**Color Picker Check** (optional):
|
||||
Use DevTools color picker on active tab:
|
||||
|
||||
- Expected: `#14b8a6` or `rgb(20, 184, 166)`
|
||||
- Actual: ********\_\_\_********
|
||||
|
||||
**Status**: [ ] ✅ PASS [ ] ❌ FAIL
|
||||
|
||||
---
|
||||
|
||||
### TEST 6: Button Component
|
||||
|
||||
**Time**: 5 minutes
|
||||
|
||||
#### Find and Test Buttons:
|
||||
|
||||
**FlyerUploader Page**:
|
||||
|
||||
- [ ] "Upload Another Flyer" button (primary, teal)
|
||||
- [ ] Button clickable
|
||||
- [ ] Hover effect works
|
||||
- [ ] Loading state (if applicable)
|
||||
|
||||
**ShoppingList Page** (navigate to /lists):
|
||||
|
||||
- [ ] "New List" button (secondary, gray)
|
||||
- [ ] "Delete List" button (danger, red)
|
||||
- [ ] Buttons functional
|
||||
- [ ] Hover states work
|
||||
|
||||
**In Dark Mode**:
|
||||
|
||||
- [ ] All button variants visible
|
||||
- [ ] Good contrast
|
||||
- [ ] No white backgrounds
|
||||
|
||||
**Status**: [ ] ✅ PASS [ ] ❌ FAIL
|
||||
|
||||
---
|
||||
|
||||
### TEST 7: Responsive Breakpoints
|
||||
|
||||
**Time**: 5 minutes
|
||||
|
||||
#### Test at each width:
|
||||
|
||||
**375px (Mobile)**:
|
||||
|
||||
```
|
||||
Tab bar: [ ] Visible [ ] Hidden
|
||||
Sidebars: [ ] Visible [ ] Hidden
|
||||
Layout: [ ] Single column [ ] Multi-column
|
||||
```
|
||||
|
||||
**768px (Tablet)**:
|
||||
|
||||
```
|
||||
Tab bar: [ ] Visible [ ] Hidden
|
||||
Sidebars: [ ] Visible [ ] Hidden
|
||||
Layout: [ ] Single column [ ] Multi-column
|
||||
```
|
||||
|
||||
**1024px (Desktop)**:
|
||||
|
||||
```
|
||||
Tab bar: [ ] Visible [ ] Hidden
|
||||
Sidebars: [ ] Visible [ ] Hidden
|
||||
Layout: [ ] Single column [ ] Multi-column
|
||||
```
|
||||
|
||||
**1440px (Large Desktop)**:
|
||||
|
||||
```
|
||||
Layout: [ ] Unchanged [ ] Broken
|
||||
All elements: [ ] Visible [ ] Hidden/Cut off
|
||||
```
|
||||
|
||||
**Status**: [ ] ✅ PASS [ ] ❌ FAIL
|
||||
|
||||
---
|
||||
|
||||
### TEST 8: Admin Routes (If Admin User)
|
||||
|
||||
**Time**: 3 minutes
|
||||
**Skip if**: [ ] Not admin user
|
||||
|
||||
#### Steps:
|
||||
|
||||
1. Log in as admin
|
||||
2. Navigate to `/admin`
|
||||
3. Check for tab bar
|
||||
|
||||
#### Checks:
|
||||
|
||||
- [ ] Admin dashboard loads
|
||||
- [ ] Tab bar NOT visible
|
||||
- [ ] Layout looks correct
|
||||
- [ ] Can navigate to subpages
|
||||
- [ ] Subpages work in mobile view
|
||||
|
||||
**Status**: [ ] ✅ PASS [ ] ❌ FAIL [ ] ⏭️ SKIPPED
|
||||
|
||||
---
|
||||
|
||||
### TEST 9: Console Errors
|
||||
|
||||
**Time**: 2 minutes
|
||||
|
||||
#### Steps:
|
||||
|
||||
1. Open Console tab in DevTools
|
||||
2. Clear console
|
||||
3. Navigate through app: Home → Deals → Lists → Profile → Home
|
||||
4. Check for red error messages
|
||||
|
||||
#### Results:
|
||||
|
||||
```
|
||||
Errors Found: [ ] None [ ] Some (list below)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
**React 19 warnings are OK** (peer dependencies)
|
||||
|
||||
**Status**: [ ] ✅ PASS (no errors) [ ] ❌ FAIL (errors present)
|
||||
|
||||
---
|
||||
|
||||
### TEST 10: Integration Flow
|
||||
|
||||
**Time**: 5 minutes
|
||||
|
||||
#### User Journey:
|
||||
|
||||
1. Start on Home page (mobile view)
|
||||
2. Navigate to Deals tab
|
||||
3. Navigate to Lists tab
|
||||
4. Navigate to Profile tab
|
||||
5. Navigate back to Home
|
||||
6. Toggle dark mode
|
||||
7. Navigate through tabs again
|
||||
|
||||
#### Checks:
|
||||
|
||||
- [ ] All navigation smooth
|
||||
- [ ] No data loss
|
||||
- [ ] Active tab always correct
|
||||
- [ ] Browser back button works
|
||||
- [ ] Dark mode persists across routes
|
||||
- [ ] No JavaScript errors
|
||||
- [ ] No layout shifting
|
||||
|
||||
**Status**: [ ] ✅ PASS [ ] ❌ FAIL
|
||||
|
||||
---
|
||||
|
||||
## 📊 Test Results Summary
|
||||
|
||||
### Critical Tests Status
|
||||
|
||||
| Test | Status | Priority | Notes |
|
||||
| ------------------- | ------ | ----------- | ----- |
|
||||
| 1. Onboarding Tour | [ ] | 🔴 Critical | |
|
||||
| 2. Tour Navigation | [ ] | 🟡 High | |
|
||||
| 3. Mobile Tab Bar | [ ] | 🔴 Critical | |
|
||||
| 4. Dark Mode | [ ] | 🔴 Critical | |
|
||||
| 5. Brand Colors | [ ] | 🟡 High | |
|
||||
| 6. Button Component | [ ] | 🟢 Medium | |
|
||||
| 7. Responsive | [ ] | 🔴 Critical | |
|
||||
| 8. Admin Routes | [ ] | 🟢 Medium | |
|
||||
| 9. Console Errors | [ ] | 🔴 Critical | |
|
||||
| 10. Integration | [ ] | 🟡 High | |
|
||||
|
||||
**Pass Rate**: **\_** / 10 tests passed
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Issues Found
|
||||
|
||||
### Critical Issues (Blockers)
|
||||
|
||||
1. ***
|
||||
2. ***
|
||||
3. ***
|
||||
|
||||
### High Priority Issues
|
||||
|
||||
1. ***
|
||||
2. ***
|
||||
3. ***
|
||||
|
||||
### Medium/Low Priority Issues
|
||||
|
||||
1. ***
|
||||
2. ***
|
||||
3. ***
|
||||
|
||||
---
|
||||
|
||||
## 📸 Screenshots
|
||||
|
||||
Attach screenshots for:
|
||||
|
||||
- [ ] Onboarding tour (step 1)
|
||||
- [ ] Mobile tab bar (375px)
|
||||
- [ ] Desktop layout (1440px)
|
||||
- [ ] Dark mode (tab bar)
|
||||
- [ ] Any bugs/issues found
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Final Decision
|
||||
|
||||
### Must-Pass Criteria
|
||||
|
||||
**Critical tests** (all must pass):
|
||||
|
||||
- [ ] Test 1: Onboarding Tour
|
||||
- [ ] Test 3: Mobile Tab Bar
|
||||
- [ ] Test 4: Dark Mode
|
||||
- [ ] Test 7: Responsive
|
||||
- [ ] Test 9: No Console Errors
|
||||
|
||||
**Result**: [ ] ALL CRITICAL PASS [ ] SOME FAIL
|
||||
|
||||
---
|
||||
|
||||
### Production Readiness
|
||||
|
||||
**Overall Assessment**:
|
||||
[ ] ✅ READY FOR PRODUCTION
|
||||
[ ] ⚠️ READY WITH MINOR ISSUES
|
||||
[ ] ❌ NOT READY (critical issues)
|
||||
|
||||
**Blocking Issues** (must fix before deploy):
|
||||
|
||||
1. ***
|
||||
2. ***
|
||||
3. ***
|
||||
|
||||
**Recommended Fixes** (can deploy, fix later):
|
||||
|
||||
1. ***
|
||||
2. ***
|
||||
3. ***
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Sign-Off
|
||||
|
||||
**Tester Name**: ****************\_\_\_****************
|
||||
|
||||
**Date/Time Completed**: ************\_\_\_************
|
||||
|
||||
**Total Testing Time**: **\_\_** minutes
|
||||
|
||||
**Recommended Action**:
|
||||
[ ] Deploy to production
|
||||
[ ] Deploy to staging first
|
||||
[ ] Fix issues, re-test
|
||||
[ ] Hold deployment
|
||||
|
||||
**Additional Notes**:
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 📋 Next Steps
|
||||
|
||||
**If PASS**:
|
||||
|
||||
1. [ ] Create commit with test results
|
||||
2. [ ] Update CHANGELOG.md
|
||||
3. [ ] Tag release (v0.12.4)
|
||||
4. [ ] Deploy to staging
|
||||
5. [ ] Monitor for 24 hours
|
||||
6. [ ] Deploy to production
|
||||
|
||||
**If FAIL**:
|
||||
|
||||
1. [ ] Log issues in GitHub/Gitea
|
||||
2. [ ] Assign to developer
|
||||
3. [ ] Schedule re-test
|
||||
4. [ ] Update test plan if needed
|
||||
|
||||
---
|
||||
|
||||
**Session End**: [Time]
|
||||
**Session Duration**: **\_\_** minutes
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "flyer-crawler",
|
||||
"version": "0.12.4",
|
||||
"version": "0.12.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "flyer-crawler",
|
||||
"version": "0.12.4",
|
||||
"version": "0.12.5",
|
||||
"dependencies": {
|
||||
"@bull-board/api": "^6.14.2",
|
||||
"@bull-board/express": "^6.14.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "flyer-crawler",
|
||||
"private": true,
|
||||
"version": "0.12.4",
|
||||
"version": "0.12.5",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "concurrently \"npm:start:dev\" \"vite\"",
|
||||
|
||||
@@ -17,9 +17,44 @@ set -e
|
||||
|
||||
echo "🚀 Starting Flyer Crawler Dev Container..."
|
||||
|
||||
# Configure Bugsink HTTPS (ADR-015)
|
||||
echo "🔒 Configuring Bugsink HTTPS..."
|
||||
mkdir -p /etc/bugsink/ssl
|
||||
if [ ! -f "/etc/bugsink/ssl/localhost+2.pem" ]; then
|
||||
cd /etc/bugsink/ssl && mkcert localhost 127.0.0.1 ::1 > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
# Create nginx config for Bugsink HTTPS
|
||||
cat > /etc/nginx/sites-available/bugsink <<'NGINX_EOF'
|
||||
server {
|
||||
listen 8443 ssl http2;
|
||||
listen [::]:8443 ssl http2;
|
||||
server_name localhost;
|
||||
|
||||
ssl_certificate /etc/bugsink/ssl/localhost+2.pem;
|
||||
ssl_certificate_key /etc/bugsink/ssl/localhost+2-key.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_redirect off;
|
||||
proxy_buffering off;
|
||||
client_max_body_size 20M;
|
||||
}
|
||||
}
|
||||
NGINX_EOF
|
||||
|
||||
ln -sf /etc/nginx/sites-available/bugsink /etc/nginx/sites-enabled/bugsink
|
||||
|
||||
# Start nginx in background (if installed)
|
||||
if command -v nginx &> /dev/null; then
|
||||
echo "🌐 Starting nginx (HTTPS proxy: Vite 5173 → port 443)..."
|
||||
echo "🌐 Starting nginx (HTTPS: Vite 5173 → 443, Bugsink 8000 → 8443)..."
|
||||
nginx &
|
||||
fi
|
||||
|
||||
@@ -41,8 +76,8 @@ cd /app
|
||||
echo "💻 Starting development server..."
|
||||
echo " - Frontend: https://localhost (nginx HTTPS → Vite on 5173)"
|
||||
echo " - Backend API: http://localhost:3001"
|
||||
echo " - Bugsink: http://localhost:8000"
|
||||
echo " - Note: Accept the self-signed certificate warning in your browser"
|
||||
echo " - Bugsink: https://localhost:8443 (nginx HTTPS → Bugsink on 8000)"
|
||||
echo " - Note: Accept the self-signed certificate warnings in your browser"
|
||||
echo ""
|
||||
|
||||
# Run npm dev server (this will block and keep container alive)
|
||||
|
||||
@@ -4,14 +4,14 @@ import supertest from 'supertest';
|
||||
import { cleanupDb } from '../utils/cleanup';
|
||||
import { createAndLoginUser } from '../utils/testHelpers';
|
||||
import type { UserProfile } from '../../types';
|
||||
import { app } from '../setup/e2e-global-setup';
|
||||
import { getServerUrl } from '../setup/e2e-global-setup';
|
||||
|
||||
/**
|
||||
* @vitest-environment node
|
||||
*/
|
||||
describe('Admin Route Authorization', () => {
|
||||
// Create a getter function that returns supertest instance with the app
|
||||
const getRequest = () => supertest(app);
|
||||
const getRequest = () => supertest(getServerUrl());
|
||||
|
||||
let regularUser: UserProfile;
|
||||
let regularUserAuthToken: string;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { describe, it, expect, afterAll } from 'vitest';
|
||||
import supertest from 'supertest';
|
||||
import { getPool } from '../../services/db/connection.db';
|
||||
import { cleanupDb } from '../utils/cleanup';
|
||||
import { app } from '../setup/e2e-global-setup';
|
||||
import { getServerUrl } from '../setup/e2e-global-setup';
|
||||
|
||||
/**
|
||||
* @vitest-environment node
|
||||
@@ -11,7 +11,7 @@ import { app } from '../setup/e2e-global-setup';
|
||||
|
||||
describe('E2E Admin Dashboard Flow', () => {
|
||||
// Create a getter function that returns supertest instance with the app
|
||||
const getRequest = () => supertest(app);
|
||||
const getRequest = () => supertest(getServerUrl());
|
||||
|
||||
// Use a unique email for every run to avoid collisions
|
||||
const uniqueId = Date.now();
|
||||
|
||||
@@ -4,7 +4,7 @@ import supertest from 'supertest';
|
||||
import { cleanupDb } from '../utils/cleanup';
|
||||
import { createAndLoginUser, TEST_PASSWORD } from '../utils/testHelpers';
|
||||
import type { UserProfile } from '../../types';
|
||||
import { app } from '../setup/e2e-global-setup';
|
||||
import { getServerUrl } from '../setup/e2e-global-setup';
|
||||
|
||||
/**
|
||||
* @vitest-environment node
|
||||
@@ -12,7 +12,7 @@ import { app } from '../setup/e2e-global-setup';
|
||||
|
||||
describe('Authentication E2E Flow', () => {
|
||||
// Create a getter function that returns supertest instance with the app
|
||||
const getRequest = () => supertest(app);
|
||||
const getRequest = () => supertest(getServerUrl());
|
||||
|
||||
let testUser: UserProfile;
|
||||
let testUserAuthToken: string;
|
||||
@@ -142,7 +142,7 @@ describe('Authentication E2E Flow', () => {
|
||||
|
||||
// Act: Use the token to access a protected route
|
||||
const response = await getRequest()
|
||||
.get('/api/users/me')
|
||||
.get('/api/users/profile')
|
||||
.set('Authorization', `Bearer ${token}`);
|
||||
|
||||
// Assert
|
||||
@@ -165,7 +165,7 @@ describe('Authentication E2E Flow', () => {
|
||||
|
||||
// Act: Call the update endpoint
|
||||
const updateResponse = await getRequest()
|
||||
.put('/api/users/me')
|
||||
.put('/api/users/profile')
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send(profileUpdates);
|
||||
|
||||
@@ -176,7 +176,7 @@ describe('Authentication E2E Flow', () => {
|
||||
|
||||
// Act 2: Fetch the profile again to verify persistence
|
||||
const verifyResponse = await getRequest()
|
||||
.get('/api/users/me')
|
||||
.get('/api/users/profile')
|
||||
.set('Authorization', `Bearer ${token}`);
|
||||
|
||||
// Assert 2: Check the fetched data
|
||||
@@ -295,7 +295,7 @@ describe('Authentication E2E Flow', () => {
|
||||
|
||||
// 5. Use the new access token to access a protected route.
|
||||
const profileResponse = await getRequest()
|
||||
.get('/api/users/me')
|
||||
.get('/api/users/profile')
|
||||
.set('Authorization', `Bearer ${newAccessToken}`);
|
||||
expect(profileResponse.status).toBe(200);
|
||||
expect(profileResponse.body.data.user.user_id).toBe(testUser.user.user_id);
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
cleanupStoreLocations,
|
||||
type CreatedStoreLocation,
|
||||
} from '../utils/storeHelpers';
|
||||
import { app } from '../setup/e2e-global-setup';
|
||||
import { getServerUrl } from '../setup/e2e-global-setup';
|
||||
|
||||
/**
|
||||
* @vitest-environment node
|
||||
@@ -21,7 +21,7 @@ import { app } from '../setup/e2e-global-setup';
|
||||
|
||||
describe('E2E Budget Management Journey', () => {
|
||||
// Create a getter function that returns supertest instance with the app
|
||||
const getRequest = () => supertest(app);
|
||||
const getRequest = () => supertest(getServerUrl());
|
||||
|
||||
const uniqueId = Date.now();
|
||||
const userEmail = `budget-e2e-${uniqueId}@example.com`;
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
cleanupStoreLocations,
|
||||
type CreatedStoreLocation,
|
||||
} from '../utils/storeHelpers';
|
||||
import { app } from '../setup/e2e-global-setup';
|
||||
import { getServerUrl } from '../setup/e2e-global-setup';
|
||||
|
||||
/**
|
||||
* @vitest-environment node
|
||||
@@ -21,7 +21,7 @@ import { app } from '../setup/e2e-global-setup';
|
||||
|
||||
describe('E2E Deals and Price Tracking Journey', () => {
|
||||
// Create a getter function that returns supertest instance with the app
|
||||
const getRequest = () => supertest(app);
|
||||
const getRequest = () => supertest(getServerUrl());
|
||||
|
||||
const uniqueId = Date.now();
|
||||
const userEmail = `deals-e2e-${uniqueId}@example.com`;
|
||||
|
||||
@@ -7,7 +7,7 @@ import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { cleanupDb } from '../utils/cleanup';
|
||||
import { poll } from '../utils/poll';
|
||||
import { app } from '../setup/e2e-global-setup';
|
||||
import { getServerUrl } from '../setup/e2e-global-setup';
|
||||
|
||||
/**
|
||||
* @vitest-environment node
|
||||
@@ -15,7 +15,7 @@ import { app } from '../setup/e2e-global-setup';
|
||||
|
||||
describe('E2E Flyer Upload and Processing Workflow', () => {
|
||||
// Create a getter function that returns supertest instance with the app
|
||||
const getRequest = () => supertest(app);
|
||||
const getRequest = () => supertest(getServerUrl());
|
||||
|
||||
const uniqueId = Date.now();
|
||||
const userEmail = `e2e-uploader-${uniqueId}@example.com`;
|
||||
|
||||
@@ -8,7 +8,7 @@ import supertest from 'supertest';
|
||||
import { cleanupDb } from '../utils/cleanup';
|
||||
import { poll } from '../utils/poll';
|
||||
import { getPool } from '../../services/db/connection.db';
|
||||
import { app } from '../setup/e2e-global-setup';
|
||||
import { getServerUrl } from '../setup/e2e-global-setup';
|
||||
|
||||
/**
|
||||
* @vitest-environment node
|
||||
@@ -16,7 +16,7 @@ import { app } from '../setup/e2e-global-setup';
|
||||
|
||||
describe('E2E Inventory/Expiry Management Journey', () => {
|
||||
// Create a getter function that returns supertest instance with the app
|
||||
const getRequest = () => supertest(app);
|
||||
const getRequest = () => supertest(getServerUrl());
|
||||
|
||||
const uniqueId = Date.now();
|
||||
const userEmail = `inventory-e2e-${uniqueId}@example.com`;
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
cleanupStoreLocations,
|
||||
type CreatedStoreLocation,
|
||||
} from '../utils/storeHelpers';
|
||||
import { app } from '../setup/e2e-global-setup';
|
||||
import { getServerUrl } from '../setup/e2e-global-setup';
|
||||
|
||||
/**
|
||||
* @vitest-environment node
|
||||
@@ -21,7 +21,7 @@ import { app } from '../setup/e2e-global-setup';
|
||||
|
||||
describe('E2E Receipt Processing Journey', () => {
|
||||
// Create a getter function that returns supertest instance with the app
|
||||
const getRequest = () => supertest(app);
|
||||
const getRequest = () => supertest(getServerUrl());
|
||||
|
||||
const uniqueId = Date.now();
|
||||
const userEmail = `receipt-e2e-${uniqueId}@example.com`;
|
||||
|
||||
@@ -8,7 +8,7 @@ import supertest from 'supertest';
|
||||
import { cleanupDb } from '../utils/cleanup';
|
||||
import { poll } from '../utils/poll';
|
||||
import { getPool } from '../../services/db/connection.db';
|
||||
import { app } from '../setup/e2e-global-setup';
|
||||
import { getServerUrl } from '../setup/e2e-global-setup';
|
||||
|
||||
/**
|
||||
* @vitest-environment node
|
||||
@@ -16,7 +16,7 @@ import { app } from '../setup/e2e-global-setup';
|
||||
|
||||
describe('E2E UPC Scanning Journey', () => {
|
||||
// Create a getter function that returns supertest instance with the app
|
||||
const getRequest = () => supertest(app);
|
||||
const getRequest = () => supertest(getServerUrl());
|
||||
|
||||
const uniqueId = Date.now();
|
||||
const userEmail = `upc-e2e-${uniqueId}@example.com`;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { describe, it, expect, afterAll } from 'vitest';
|
||||
import supertest from 'supertest';
|
||||
import { cleanupDb } from '../utils/cleanup';
|
||||
import { app } from '../setup/e2e-global-setup';
|
||||
import { getServerUrl } from '../setup/e2e-global-setup';
|
||||
|
||||
/**
|
||||
* @vitest-environment node
|
||||
@@ -10,7 +10,7 @@ import { app } from '../setup/e2e-global-setup';
|
||||
|
||||
describe('E2E User Journey', () => {
|
||||
// Create a getter function that returns supertest instance with the app
|
||||
const getRequest = () => supertest(app);
|
||||
const getRequest = () => supertest(getServerUrl());
|
||||
|
||||
// Use a unique email for every run to avoid collisions
|
||||
const uniqueId = Date.now();
|
||||
@@ -62,7 +62,7 @@ describe('E2E User Journey', () => {
|
||||
|
||||
// 3. Create a Shopping List
|
||||
const createListResponse = await getRequest()
|
||||
.post('/api/users/me/shopping-lists')
|
||||
.post('/api/users/shopping-lists')
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send({ name: 'E2E Party List' });
|
||||
|
||||
@@ -72,7 +72,7 @@ describe('E2E User Journey', () => {
|
||||
|
||||
// 4. Add an item to the list
|
||||
const addItemResponse = await getRequest()
|
||||
.post(`/api/users/me/shopping-lists/${shoppingListId}/items`)
|
||||
.post(`/api/users/shopping-lists/${shoppingListId}/items`)
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send({ customItemName: 'Chips' });
|
||||
|
||||
@@ -81,7 +81,7 @@ describe('E2E User Journey', () => {
|
||||
|
||||
// 5. Verify the list and item exist via GET
|
||||
const getListsResponse = await getRequest()
|
||||
.get('/api/users/me/shopping-lists')
|
||||
.get('/api/users/shopping-lists')
|
||||
.set('Authorization', `Bearer ${authToken}`);
|
||||
|
||||
expect(getListsResponse.status).toBe(200);
|
||||
@@ -94,7 +94,7 @@ describe('E2E User Journey', () => {
|
||||
|
||||
// 6. Delete the User Account (Self-Service)
|
||||
const deleteAccountResponse = await getRequest()
|
||||
.delete('/api/users/me')
|
||||
.delete('/api/users/account')
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send({ password: userPassword });
|
||||
|
||||
|
||||
@@ -21,8 +21,21 @@ let server: Server;
|
||||
let globalPool: ReturnType<typeof getPool> | null = null;
|
||||
// Temporary directory for test file storage (to avoid modifying committed fixtures)
|
||||
let tempStorageDir: string | null = null;
|
||||
// Export the Express app for use with supertest in e2e tests
|
||||
export let app: Express.Application;
|
||||
// Internal app variable - only used within the globalSetup process
|
||||
let app: Express.Application;
|
||||
|
||||
/**
|
||||
* Gets the base URL for the E2E test server.
|
||||
* Tests should make HTTP requests to this URL instead of accessing the app directly.
|
||||
*
|
||||
* NOTE: Due to Vitest's architecture, globalSetup runs in a separate Node.js process
|
||||
* from test files. This means the Express app instance cannot be shared directly.
|
||||
* Instead, tests should connect via HTTP to the server started by globalSetup.
|
||||
*/
|
||||
export function getServerUrl(): string {
|
||||
const port = process.env.TEST_PORT || 3098;
|
||||
return `http://localhost:${port}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans all BullMQ queues to ensure no stale jobs from previous test runs.
|
||||
|
||||
Reference in New Issue
Block a user