Compare commits

...

2 Commits

Author SHA1 Message Date
Gitea Actions
6e13570deb ci: Bump version to 0.12.5 [skip ci] 2026-01-22 01:36:01 +05:00
2eba66fb71 make e2e actually e2e - sigh
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 19m9s
2026-01-21 12:34:46 -08:00
16 changed files with 623 additions and 40 deletions

View File

@@ -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

View 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
View File

@@ -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",

View File

@@ -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\"",

View File

@@ -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)

View File

@@ -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;

View File

@@ -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();

View File

@@ -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);

View File

@@ -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`;

View File

@@ -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`;

View File

@@ -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`;

View File

@@ -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`;

View File

@@ -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`;

View File

@@ -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`;

View File

@@ -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 });

View File

@@ -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.