feat: implement 4 new features with bug fixes and refactoring plan
## Features Implemented ### 1. Event Rename Functionality - Add EventRenameDialog component with live slug preview - Create eventRenameService for safe event renaming - Add slug_redirects table for old URL redirects - Support optional email notification on rename - Fix date formatting in slug (YYYY-MM-DD format) ### 2. Optional Event Contact Fields - Add settings to make customer name/email/admin email optional - Create migration for field requirement settings - Update CreateEventPage forms to show "(optional)" labels - Fix boolean parsing in publicSettings.js ### 3. Photo Filtering & Export - Add PhotoFilterPanel with rating/likes/favorites/comments filters - Create PhotoExportMenu with ZIP/metadata/XMP export options - Add photoExportService with Lightroom XMP sidecar generation - Create photoFilterBuilder utility for query construction - Wire up photo selection to export button via onSelectionChange ### 4. Custom CSS Gallery Templates - Add CssTemplateEditor component with 3 template slots - Create cssSanitizer utility blocking XSS vectors - Add gallery CSS endpoint for template delivery - Integrate Custom CSS tab into Settings page - Include default "Elegant Dark" template ## Bug Fixes - Fix event rename date formatting (was showing full Date string) - Fix common.optional translation key missing in locales - Fix photo export button staying disabled when photos selected - Fix authService import missing in SettingsPage ## Documentation - Add comprehensive REFACTORING_PLAN.md for codebase improvement - Add test specification documents for all features - Add feature documentation for CSS templates ## Database Migrations - 049_add_slug_redirects.js - 050_add_optional_event_fields_settings.js - 051_add_photo_filter_indexes.js - 052_add_css_templates.js
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,338 @@
|
||||
# Test Specification: Custom CSS Gallery Templates
|
||||
|
||||
This document specifies the test cases for the Custom CSS Gallery Templates feature, which allows administrators to create and manage up to 3 custom CSS templates for gallery styling.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Local Docker environment running (`docker-compose up`)
|
||||
- Access to admin dashboard
|
||||
- Backend migrations applied (052_add_css_templates.js)
|
||||
|
||||
## Test Cases
|
||||
|
||||
### 1. Template Editor Access
|
||||
|
||||
#### TC-CCT-001: Access CSS Templates Tab
|
||||
**Steps:**
|
||||
1. Navigate to Settings page
|
||||
2. Look for "Custom CSS Templates" or "Styling" section
|
||||
|
||||
**Expected Result:**
|
||||
- CSS Templates editor is accessible
|
||||
- Three template slots are visible as tabs
|
||||
|
||||
#### TC-CCT-002: Default Template Content
|
||||
**Steps:**
|
||||
1. Navigate to CSS Templates editor
|
||||
2. Select Template 1 tab
|
||||
|
||||
**Expected Result:**
|
||||
- Template 1 named "Elegant Dark"
|
||||
- Contains pre-populated CSS content
|
||||
- Is marked as enabled
|
||||
- Is marked as default
|
||||
|
||||
### 2. Template Editing
|
||||
|
||||
#### TC-CCT-003: Edit Template Name
|
||||
**Steps:**
|
||||
1. Select Template 2
|
||||
2. Change name from "Untitled" to "My Custom Theme"
|
||||
3. Save template
|
||||
|
||||
**Expected Result:**
|
||||
- Name updates in tab
|
||||
- Save confirmation shown
|
||||
- Name persists after refresh
|
||||
|
||||
#### TC-CCT-004: Edit CSS Content
|
||||
**Steps:**
|
||||
1. Select Template 2
|
||||
2. Add CSS: `.gallery-page { background: #ff0000; }`
|
||||
3. Save template
|
||||
|
||||
**Expected Result:**
|
||||
- CSS saved successfully
|
||||
- No sanitization warnings for valid CSS
|
||||
- Character count updates
|
||||
|
||||
#### TC-CCT-005: Enable/Disable Template
|
||||
**Steps:**
|
||||
1. Select Template 2
|
||||
2. Toggle "Enable this template" checkbox
|
||||
3. Save template
|
||||
|
||||
**Expected Result:**
|
||||
- Template status changes
|
||||
- Tab shows check mark when enabled
|
||||
- Disabled templates not available in event form
|
||||
|
||||
### 3. CSS Sanitization
|
||||
|
||||
#### TC-CCT-006: Block JavaScript Expressions
|
||||
**Steps:**
|
||||
1. Enter CSS with `expression(alert('xss'))`
|
||||
2. Save template
|
||||
|
||||
**Expected Result:**
|
||||
- Pattern blocked (replaced with /* BLOCKED */)
|
||||
- Sanitization warning shown
|
||||
- Template saves with sanitized content
|
||||
|
||||
#### TC-CCT-007: Block @import Rules
|
||||
**Steps:**
|
||||
1. Enter CSS with `@import url('http://evil.com/styles.css');`
|
||||
2. Save template
|
||||
|
||||
**Expected Result:**
|
||||
- @import blocked
|
||||
- Warning shown
|
||||
- External resource not loaded
|
||||
|
||||
#### TC-CCT-008: Block External URLs
|
||||
**Steps:**
|
||||
1. Enter CSS with `background-image: url('http://external.com/image.jpg');`
|
||||
2. Save template
|
||||
|
||||
**Expected Result:**
|
||||
- External URL blocked
|
||||
- Only data: URIs allowed for images
|
||||
- Warning shown
|
||||
|
||||
#### TC-CCT-009: Allow Safe CSS Properties
|
||||
**Steps:**
|
||||
1. Enter CSS with standard properties:
|
||||
```css
|
||||
.gallery-page {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 20px;
|
||||
}
|
||||
```
|
||||
2. Save template
|
||||
|
||||
**Expected Result:**
|
||||
- All properties saved as-is
|
||||
- No sanitization warnings
|
||||
- CSS valid
|
||||
|
||||
### 4. Template Size Limits
|
||||
|
||||
#### TC-CCT-010: CSS Size Limit
|
||||
**Steps:**
|
||||
1. Try to save CSS content > 100KB
|
||||
2. Attempt to save
|
||||
|
||||
**Expected Result:**
|
||||
- Error message about size limit
|
||||
- Template not saved
|
||||
- User informed of 100KB limit
|
||||
|
||||
### 5. Reset to Default
|
||||
|
||||
#### TC-CCT-011: Reset Template 1
|
||||
**Steps:**
|
||||
1. Modify Template 1 CSS
|
||||
2. Save changes
|
||||
3. Click "Reset to Default"
|
||||
4. Confirm action
|
||||
|
||||
**Expected Result:**
|
||||
- Template reverts to default "Elegant Dark" content
|
||||
- Name reset to "Elegant Dark"
|
||||
- Enable status reset to true
|
||||
|
||||
#### TC-CCT-012: Reset Button Only on Template 1
|
||||
**Steps:**
|
||||
1. Select Template 2
|
||||
2. Look for Reset button
|
||||
|
||||
**Expected Result:**
|
||||
- Reset to Default button NOT shown for Template 2 or 3
|
||||
- Only Template 1 has reset option
|
||||
|
||||
### 6. Event Integration
|
||||
|
||||
#### TC-CCT-013: Template Dropdown in Event Form
|
||||
**Steps:**
|
||||
1. Enable at least one CSS template
|
||||
2. Navigate to Create Event page
|
||||
3. Look for CSS Template selector
|
||||
|
||||
**Expected Result:**
|
||||
- Dropdown shows "None (Use default theme)" option
|
||||
- Enabled templates appear in list
|
||||
- Disabled templates NOT shown
|
||||
|
||||
#### TC-CCT-014: Assign Template to Event
|
||||
**Steps:**
|
||||
1. Create new event
|
||||
2. Select an enabled CSS template
|
||||
3. Save event
|
||||
|
||||
**Expected Result:**
|
||||
- Event created with template assigned
|
||||
- Template ID stored in database
|
||||
- Event edit shows selected template
|
||||
|
||||
#### TC-CCT-015: Update Event Template
|
||||
**Steps:**
|
||||
1. Edit existing event
|
||||
2. Change CSS template selection
|
||||
3. Save event
|
||||
|
||||
**Expected Result:**
|
||||
- Template updated successfully
|
||||
- Gallery reflects new template
|
||||
|
||||
### 7. Gallery CSS Loading
|
||||
|
||||
#### TC-CCT-016: Gallery Loads Custom CSS
|
||||
**Steps:**
|
||||
1. Assign template to an event
|
||||
2. View gallery as guest
|
||||
3. Inspect page source/styles
|
||||
|
||||
**Expected Result:**
|
||||
- Custom CSS injected via `<style id="gallery-custom-css">`
|
||||
- Gallery styling matches template
|
||||
- CSS scoped to .gallery-page
|
||||
|
||||
#### TC-CCT-017: Gallery Without Template
|
||||
**Steps:**
|
||||
1. Create event without template (select "None")
|
||||
2. View gallery
|
||||
|
||||
**Expected Result:**
|
||||
- No custom CSS loaded
|
||||
- Default theme used
|
||||
- No errors
|
||||
|
||||
#### TC-CCT-018: Disabled Template Not Applied
|
||||
**Steps:**
|
||||
1. Assign template to event
|
||||
2. Disable the template in settings
|
||||
3. View gallery
|
||||
|
||||
**Expected Result:**
|
||||
- Custom CSS NOT loaded
|
||||
- Gallery uses default styling
|
||||
- No errors
|
||||
|
||||
### 8. API Tests
|
||||
|
||||
#### TC-CCT-019: Get All Templates
|
||||
**Steps:**
|
||||
1. Call API: `GET /api/admin/css-templates`
|
||||
|
||||
**Expected Result:**
|
||||
- Returns array of 3 templates
|
||||
- Each has: id, slot_number, name, css_content, is_enabled, is_default, updated_at
|
||||
|
||||
#### TC-CCT-020: Get Enabled Templates
|
||||
**Steps:**
|
||||
1. Call API: `GET /api/admin/css-templates/enabled`
|
||||
|
||||
**Expected Result:**
|
||||
- Returns only enabled templates
|
||||
- Each has: id, name, slot_number
|
||||
|
||||
#### TC-CCT-021: Update Template
|
||||
**Steps:**
|
||||
1. Call API: `PUT /api/admin/css-templates/2`
|
||||
Body: `{ "name": "Test", "css_content": "...", "is_enabled": true }`
|
||||
|
||||
**Expected Result:**
|
||||
- Returns 200 OK
|
||||
- Template updated
|
||||
- Sanitization warnings array included
|
||||
|
||||
#### TC-CCT-022: Gallery CSS Endpoint
|
||||
**Steps:**
|
||||
1. Assign template to event with slug "test-gallery"
|
||||
2. Call API: `GET /api/gallery/test-gallery/css-template`
|
||||
|
||||
**Expected Result:**
|
||||
- Returns 200 OK with Content-Type: text/css
|
||||
- Body contains sanitized CSS content
|
||||
|
||||
#### TC-CCT-023: Gallery CSS Not Found
|
||||
**Steps:**
|
||||
1. Create event without template
|
||||
2. Call API: `GET /api/gallery/no-template-event/css-template`
|
||||
|
||||
**Expected Result:**
|
||||
- Returns 204 No Content
|
||||
- No body
|
||||
|
||||
### 9. Edge Cases
|
||||
|
||||
#### TC-CCT-024: Empty CSS Content
|
||||
**Steps:**
|
||||
1. Save template with empty CSS content
|
||||
2. Assign to event
|
||||
3. View gallery
|
||||
|
||||
**Expected Result:**
|
||||
- Template saves successfully
|
||||
- Gallery loads without custom CSS
|
||||
- No errors
|
||||
|
||||
#### TC-CCT-025: Invalid CSS Syntax
|
||||
**Steps:**
|
||||
1. Enter CSS with mismatched braces: `{ color: red;`
|
||||
2. Try to save
|
||||
|
||||
**Expected Result:**
|
||||
- Validation error shown
|
||||
- Template not saved
|
||||
- Error message indicates syntax issue
|
||||
|
||||
### 10. Persistence Tests
|
||||
|
||||
#### TC-CCT-026: Template Persists After Restart
|
||||
**Steps:**
|
||||
1. Create and save custom template
|
||||
2. Restart backend container
|
||||
3. Reload template editor
|
||||
|
||||
**Expected Result:**
|
||||
- Template content preserved
|
||||
- All settings intact
|
||||
- No data loss
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
### Backend
|
||||
- `/backend/migrations/core/052_add_css_templates.js`
|
||||
- `/backend/src/utils/cssSanitizer.js`
|
||||
- `/backend/src/routes/adminCssTemplates.js`
|
||||
- `/backend/src/routes/gallery.js`
|
||||
- `/backend/server.js`
|
||||
|
||||
### Frontend
|
||||
- `/frontend/src/services/cssTemplates.service.ts`
|
||||
- `/frontend/src/components/admin/CssTemplateEditor.tsx`
|
||||
- `/frontend/src/components/admin/index.ts`
|
||||
- `/frontend/src/hooks/useGalleryCustomCss.ts`
|
||||
|
||||
## Integration Notes
|
||||
|
||||
The following additional integrations are recommended:
|
||||
1. Add CssTemplateEditor to Settings page styling tab
|
||||
2. Add CSS template dropdown to CreateEventPageEnhanced.tsx
|
||||
3. Add CSS template dropdown to CreateEventPage.tsx
|
||||
4. Update event edit forms to show/edit template selection
|
||||
5. Update GalleryPage.tsx to use useGalleryCustomCss hook
|
||||
6. Add `.gallery-page` class to gallery container components
|
||||
|
||||
## Automated Testing Notes
|
||||
|
||||
For Playwright tests:
|
||||
1. Login to admin dashboard
|
||||
2. Navigate to CSS Templates editor
|
||||
3. Manipulate template tabs, inputs, and checkboxes
|
||||
4. Verify save operations via API calls
|
||||
5. Navigate to Create Event, verify template dropdown
|
||||
6. View gallery, verify custom CSS is applied
|
||||
@@ -0,0 +1,342 @@
|
||||
# Test Specification: Event Rename Feature
|
||||
|
||||
## Overview
|
||||
This document specifies the test cases for the Event Rename feature, which allows administrators to rename events from the admin panel.
|
||||
|
||||
---
|
||||
|
||||
## Test Environment
|
||||
- **URL**: http://localhost:7100
|
||||
- **Admin Credentials**: admin / AdminTest@2026!
|
||||
- **Prerequisites**: At least one active (non-archived) event with photos
|
||||
|
||||
---
|
||||
|
||||
## Test Cases
|
||||
|
||||
### TC-RN-001: Access Rename Dialog
|
||||
**Description**: Verify the Rename button is visible and opens the rename dialog
|
||||
|
||||
**Steps**:
|
||||
1. Login to admin panel
|
||||
2. Navigate to an active event's detail page
|
||||
3. Verify "Rename" button is visible in the header action buttons
|
||||
4. Click the "Rename" button
|
||||
5. Verify rename dialog opens
|
||||
|
||||
**Expected Results**:
|
||||
- Rename button visible next to Edit button
|
||||
- Dialog opens with current event name pre-filled
|
||||
- Dialog shows input field for new name
|
||||
|
||||
---
|
||||
|
||||
### TC-RN-002: Validate New Name - Too Short
|
||||
**Description**: Verify validation error for names shorter than 3 characters
|
||||
|
||||
**Steps**:
|
||||
1. Open rename dialog for an event
|
||||
2. Enter a name with 2 characters (e.g., "AB")
|
||||
3. Observe validation state
|
||||
|
||||
**Expected Results**:
|
||||
- Rename button remains disabled
|
||||
- No API call made for validation
|
||||
|
||||
---
|
||||
|
||||
### TC-RN-003: Validate New Name - Same as Current
|
||||
**Description**: Verify error when new name generates same slug as current
|
||||
|
||||
**Steps**:
|
||||
1. Open rename dialog for an event
|
||||
2. Enter the same name as current (or minor variation that results in same slug)
|
||||
3. Wait for validation
|
||||
|
||||
**Expected Results**:
|
||||
- Error message: "New name generates the same URL as the current name"
|
||||
- Rename button disabled
|
||||
|
||||
---
|
||||
|
||||
### TC-RN-004: Validate New Name - Duplicate Slug
|
||||
**Description**: Verify error when new name would conflict with existing event
|
||||
|
||||
**Steps**:
|
||||
1. Create two events with different names on the same date
|
||||
2. Open rename dialog for event A
|
||||
3. Enter event B's name
|
||||
4. Wait for validation
|
||||
|
||||
**Expected Results**:
|
||||
- Error message: "An event with this name already exists for the same date"
|
||||
- Rename button disabled
|
||||
|
||||
---
|
||||
|
||||
### TC-RN-005: Validate New Name - Valid
|
||||
**Description**: Verify successful validation of a valid new name
|
||||
|
||||
**Steps**:
|
||||
1. Open rename dialog for an event
|
||||
2. Enter a valid, unique new name (at least 3 characters)
|
||||
3. Wait for validation
|
||||
|
||||
**Expected Results**:
|
||||
- New URL preview shown in green box
|
||||
- Rename button becomes enabled
|
||||
- No error messages
|
||||
|
||||
---
|
||||
|
||||
### TC-RN-006: Successful Rename
|
||||
**Description**: Verify complete rename operation
|
||||
|
||||
**Steps**:
|
||||
1. Open rename dialog for an event with photos
|
||||
2. Enter a valid new name
|
||||
3. Wait for validation to complete
|
||||
4. Click "Rename Event" button
|
||||
5. Wait for operation to complete
|
||||
|
||||
**Expected Results**:
|
||||
- Progress indicator shown during operation
|
||||
- Success message displayed
|
||||
- New share link shown
|
||||
- Files renamed count displayed
|
||||
- Event name updated in UI
|
||||
- Share link updated
|
||||
|
||||
---
|
||||
|
||||
### TC-RN-007: Rename with Email Notification
|
||||
**Description**: Verify rename with email notification option
|
||||
|
||||
**Steps**:
|
||||
1. Open rename dialog for event with customer email
|
||||
2. Enter valid new name
|
||||
3. Check "Resend invitation email with new gallery link" checkbox
|
||||
4. Click "Rename Event"
|
||||
5. Wait for completion
|
||||
|
||||
**Expected Results**:
|
||||
- Operation completes successfully
|
||||
- Email sent confirmation shown
|
||||
- (If SMTP configured) Email received with new link
|
||||
|
||||
---
|
||||
|
||||
### TC-RN-008: Old URL Redirects to New URL
|
||||
**Description**: Verify old gallery URLs redirect to new location
|
||||
|
||||
**Steps**:
|
||||
1. Note the current gallery share link before rename
|
||||
2. Rename the event
|
||||
3. Try to access the old share link
|
||||
4. Verify redirect to new URL
|
||||
|
||||
**Expected Results**:
|
||||
- Old URL returns 301 redirect response
|
||||
- Browser redirects to new gallery URL
|
||||
- Gallery is accessible at new URL
|
||||
|
||||
---
|
||||
|
||||
### TC-RN-009: Archived Events Cannot Be Renamed
|
||||
**Description**: Verify rename button is not available for archived events
|
||||
|
||||
**Steps**:
|
||||
1. Navigate to an archived event's detail page
|
||||
2. Check for Rename button presence
|
||||
|
||||
**Expected Results**:
|
||||
- Rename button is NOT visible
|
||||
- Only archive-related actions available
|
||||
|
||||
---
|
||||
|
||||
### TC-RN-010: Rename Dialog Cancel
|
||||
**Description**: Verify cancel functionality in rename dialog
|
||||
|
||||
**Steps**:
|
||||
1. Open rename dialog
|
||||
2. Enter a new name
|
||||
3. Click Cancel button
|
||||
4. Verify dialog closes
|
||||
5. Verify event name unchanged
|
||||
|
||||
**Expected Results**:
|
||||
- Dialog closes
|
||||
- No changes made to event
|
||||
- Event name remains original
|
||||
|
||||
---
|
||||
|
||||
### TC-RN-011: Rename Dialog Close (X Button)
|
||||
**Description**: Verify X button closes dialog without changes
|
||||
|
||||
**Steps**:
|
||||
1. Open rename dialog
|
||||
2. Enter a new name
|
||||
3. Click X button in top-right
|
||||
4. Verify dialog closes
|
||||
5. Verify event name unchanged
|
||||
|
||||
**Expected Results**:
|
||||
- Dialog closes
|
||||
- No changes made to event
|
||||
|
||||
---
|
||||
|
||||
### TC-RN-012: Photo File Paths Updated
|
||||
**Description**: Verify photo file paths are updated after rename
|
||||
|
||||
**Steps**:
|
||||
1. Create event with photos
|
||||
2. Note photo paths in database
|
||||
3. Rename the event
|
||||
4. Verify photos are still accessible
|
||||
5. Check photo paths in database
|
||||
|
||||
**Expected Results**:
|
||||
- All photos remain accessible
|
||||
- Photo paths updated to reflect new slug
|
||||
- Thumbnails still work
|
||||
|
||||
---
|
||||
|
||||
### TC-RN-013: Activity Log Entry Created
|
||||
**Description**: Verify rename operation is logged
|
||||
|
||||
**Steps**:
|
||||
1. Rename an event
|
||||
2. Check activity logs in database or admin panel
|
||||
|
||||
**Expected Results**:
|
||||
- Activity log entry created with type "event_renamed"
|
||||
- Metadata includes old name, new name, old slug, new slug
|
||||
- Actor information recorded
|
||||
|
||||
---
|
||||
|
||||
## API Test Cases
|
||||
|
||||
### TC-RN-API-001: POST /api/admin/events/:id/rename
|
||||
**Description**: Test rename API endpoint
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"newEventName": "New Event Name",
|
||||
"resendEmail": false
|
||||
}
|
||||
```
|
||||
|
||||
**Expected Response (200)**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Event renamed successfully",
|
||||
"data": {
|
||||
"eventId": 1,
|
||||
"oldName": "Old Event Name",
|
||||
"newName": "New Event Name",
|
||||
"oldSlug": "wedding-old-event-name-2026-01-01",
|
||||
"newSlug": "wedding-new-event-name-2026-01-01",
|
||||
"newShareLink": "/gallery/wedding-new-event-name-2026-01-01/abc123...",
|
||||
"emailSent": false,
|
||||
"filesRenamed": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### TC-RN-API-002: POST /api/admin/events/:id/validate-rename
|
||||
**Description**: Test rename validation endpoint
|
||||
|
||||
**Request**:
|
||||
```json
|
||||
{
|
||||
"newEventName": "New Event Name"
|
||||
}
|
||||
```
|
||||
|
||||
**Expected Response (200)**:
|
||||
```json
|
||||
{
|
||||
"valid": true,
|
||||
"newSlug": "wedding-new-event-name-2026-01-01"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### TC-RN-API-003: Rename Requires Authentication
|
||||
**Description**: Verify API requires admin authentication
|
||||
|
||||
**Steps**:
|
||||
1. Call rename endpoint without auth token
|
||||
2. Call rename endpoint with invalid token
|
||||
|
||||
**Expected Results**:
|
||||
- 401 Unauthorized response
|
||||
|
||||
---
|
||||
|
||||
## Database Verification
|
||||
|
||||
### TC-RN-DB-001: Events Table Updated
|
||||
After successful rename, verify:
|
||||
- `event_name` updated to new name
|
||||
- `slug` updated to new slug
|
||||
- `share_link` updated with new slug
|
||||
|
||||
### TC-RN-DB-002: Photos Table Updated
|
||||
After successful rename, verify:
|
||||
- `path` column updated for all event photos
|
||||
- `thumbnail_path` column updated for all event photos
|
||||
|
||||
### TC-RN-DB-003: Slug Redirects Table Populated
|
||||
After successful rename, verify:
|
||||
- Entry created in `slug_redirects` table
|
||||
- `old_slug` contains previous slug
|
||||
- `new_slug` contains new slug
|
||||
- `event_id` references correct event
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
### TC-RN-ERR-001: Database Error During Rename
|
||||
**Description**: Verify rollback on database error
|
||||
|
||||
**Expected Behavior**:
|
||||
- File system changes rolled back
|
||||
- Original event state preserved
|
||||
- Error message returned to user
|
||||
|
||||
### TC-RN-ERR-002: File System Error During Rename
|
||||
**Description**: Verify handling of file system errors
|
||||
|
||||
**Expected Behavior**:
|
||||
- Transaction rolled back
|
||||
- Error message returned to user
|
||||
- Event remains unchanged
|
||||
|
||||
---
|
||||
|
||||
## Performance
|
||||
|
||||
### TC-RN-PERF-001: Rename Large Event
|
||||
**Description**: Verify performance with events containing many photos
|
||||
|
||||
**Steps**:
|
||||
1. Create event with 100+ photos
|
||||
2. Rename the event
|
||||
3. Measure time taken
|
||||
|
||||
**Expected Results**:
|
||||
- Operation completes within reasonable time
|
||||
- Progress indicator keeps user informed
|
||||
- All photos remain accessible
|
||||
@@ -0,0 +1,245 @@
|
||||
# Test Specification: Optional Event Fields
|
||||
|
||||
This document specifies the test cases for the Optional Event Fields feature, which allows administrators to configure whether customer name, customer email, and admin email are required when creating events.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Local Docker environment running (`docker-compose up`)
|
||||
- Access to admin dashboard
|
||||
- Backend migrations applied
|
||||
|
||||
## Test Cases
|
||||
|
||||
### 1. Settings Page - Event Creation Tab
|
||||
|
||||
#### TC-OEF-001: Event Creation Tab Visibility
|
||||
**Steps:**
|
||||
1. Navigate to Settings page (`/admin/settings`)
|
||||
2. Verify "Event Creation" tab is visible
|
||||
|
||||
**Expected Result:**
|
||||
- Tab labeled "Event Creation" or similar should be present in the settings navigation
|
||||
|
||||
#### TC-OEF-002: Default Field Requirements
|
||||
**Steps:**
|
||||
1. Navigate to Settings > Event Creation tab
|
||||
2. Check initial state of all three toggles
|
||||
|
||||
**Expected Result:**
|
||||
- "Require Customer Name" toggle is ON (enabled)
|
||||
- "Require Customer Email" toggle is ON (enabled)
|
||||
- "Require Admin Email" toggle is ON (enabled)
|
||||
|
||||
#### TC-OEF-003: Toggle Customer Name Requirement
|
||||
**Steps:**
|
||||
1. Navigate to Settings > Event Creation
|
||||
2. Toggle OFF "Require Customer Name"
|
||||
3. Click Save
|
||||
|
||||
**Expected Result:**
|
||||
- Setting saves successfully
|
||||
- Toast notification confirms save
|
||||
- Toggle remains OFF after page refresh
|
||||
|
||||
#### TC-OEF-004: Toggle Customer Email Requirement
|
||||
**Steps:**
|
||||
1. Navigate to Settings > Event Creation
|
||||
2. Toggle OFF "Require Customer Email"
|
||||
3. Click Save
|
||||
|
||||
**Expected Result:**
|
||||
- Setting saves successfully
|
||||
- Warning message about email functionality is shown
|
||||
- Toggle remains OFF after page refresh
|
||||
|
||||
#### TC-OEF-005: Toggle Admin Email Requirement
|
||||
**Steps:**
|
||||
1. Navigate to Settings > Event Creation
|
||||
2. Toggle OFF "Require Admin Email"
|
||||
3. Click Save
|
||||
|
||||
**Expected Result:**
|
||||
- Setting saves successfully
|
||||
- Warning message about notifications is shown
|
||||
- Toggle remains OFF after page refresh
|
||||
|
||||
### 2. Create Event Form - Conditional Validation
|
||||
|
||||
#### TC-OEF-006: All Fields Required (Default)
|
||||
**Steps:**
|
||||
1. Ensure all three settings are ON in Settings > Event Creation
|
||||
2. Navigate to Create Event page
|
||||
3. Try to submit form without filling customer name, customer email, or admin email
|
||||
|
||||
**Expected Result:**
|
||||
- Validation errors shown for all three empty fields
|
||||
- Form does not submit
|
||||
|
||||
#### TC-OEF-007: Customer Name Optional
|
||||
**Steps:**
|
||||
1. Set "Require Customer Name" to OFF in Settings
|
||||
2. Navigate to Create Event page
|
||||
3. Verify Host Name field label shows "(optional)"
|
||||
4. Submit form without customer name (but with required fields filled)
|
||||
|
||||
**Expected Result:**
|
||||
- Host Name label shows "(optional)" suffix
|
||||
- Form submits successfully without customer name
|
||||
- Event is created
|
||||
|
||||
#### TC-OEF-008: Customer Email Optional
|
||||
**Steps:**
|
||||
1. Set "Require Customer Email" to OFF in Settings
|
||||
2. Navigate to Create Event page
|
||||
3. Verify Host Email field label shows "(optional)"
|
||||
4. Submit form without customer email (but with required fields filled)
|
||||
|
||||
**Expected Result:**
|
||||
- Host Email label shows "(optional)" suffix
|
||||
- Form submits successfully without customer email
|
||||
- Event is created
|
||||
|
||||
#### TC-OEF-009: Admin Email Optional
|
||||
**Steps:**
|
||||
1. Set "Require Admin Email" to OFF in Settings
|
||||
2. Navigate to Create Event page
|
||||
3. Verify Admin Email field label shows "(optional)"
|
||||
4. Submit form without admin email (but with required fields filled)
|
||||
|
||||
**Expected Result:**
|
||||
- Admin Email label shows "(optional)" suffix
|
||||
- Form submits successfully without admin email
|
||||
- Event is created
|
||||
|
||||
#### TC-OEF-010: All Fields Optional
|
||||
**Steps:**
|
||||
1. Set all three settings to OFF in Settings
|
||||
2. Navigate to Create Event page
|
||||
3. Submit form with only event name, date, and password
|
||||
|
||||
**Expected Result:**
|
||||
- All three optional fields show "(optional)" suffix
|
||||
- Form submits successfully
|
||||
- Event is created with null/empty contact fields
|
||||
|
||||
### 3. Backend Validation
|
||||
|
||||
#### TC-OEF-011: Backend Respects Settings
|
||||
**Steps:**
|
||||
1. Set "Require Customer Email" to OFF
|
||||
2. Make direct API call to create event without customer_email:
|
||||
```
|
||||
POST /api/admin/events
|
||||
{ event_name: "Test", event_date: "2025-01-15", ... }
|
||||
```
|
||||
|
||||
**Expected Result:**
|
||||
- API accepts the request
|
||||
- Returns 201 Created
|
||||
- Event is created without customer_email
|
||||
|
||||
#### TC-OEF-012: Backend Rejects When Required
|
||||
**Steps:**
|
||||
1. Set "Require Customer Email" to ON
|
||||
2. Make direct API call to create event without customer_email
|
||||
|
||||
**Expected Result:**
|
||||
- API rejects the request
|
||||
- Returns 400 Bad Request with validation error
|
||||
- Error message indicates customer_email is required
|
||||
|
||||
### 4. Format Validation for Optional Fields
|
||||
|
||||
#### TC-OEF-013: Invalid Email Format Still Rejected
|
||||
**Steps:**
|
||||
1. Set "Require Customer Email" to OFF
|
||||
2. Navigate to Create Event page
|
||||
3. Enter invalid email format (e.g., "notanemail")
|
||||
4. Submit form
|
||||
|
||||
**Expected Result:**
|
||||
- Validation error for invalid email format
|
||||
- Form does not submit
|
||||
- Error message: "Invalid email format"
|
||||
|
||||
### 5. Create Event Enhanced Page
|
||||
|
||||
#### TC-OEF-014: Enhanced Page Respects Settings
|
||||
**Steps:**
|
||||
1. Set all three settings to OFF
|
||||
2. Navigate to enhanced Create Event page (`/admin/events/create`)
|
||||
3. Verify all three fields show "(optional)"
|
||||
4. Submit form without contact fields
|
||||
|
||||
**Expected Result:**
|
||||
- All optional labels visible
|
||||
- Form submits successfully
|
||||
- Event is created
|
||||
|
||||
### 6. Settings Persistence
|
||||
|
||||
#### TC-OEF-015: Settings Persist Across Sessions
|
||||
**Steps:**
|
||||
1. Set specific combination (e.g., customer name OFF, emails ON)
|
||||
2. Log out
|
||||
3. Log back in
|
||||
4. Navigate to Settings > Event Creation
|
||||
|
||||
**Expected Result:**
|
||||
- Settings remain as configured
|
||||
- Toggle states match what was saved
|
||||
|
||||
#### TC-OEF-016: Settings Persist Across Backend Restart
|
||||
**Steps:**
|
||||
1. Set specific combination of settings
|
||||
2. Restart backend container
|
||||
3. Create new event
|
||||
|
||||
**Expected Result:**
|
||||
- Validation behavior matches saved settings
|
||||
- Database persisted settings correctly
|
||||
|
||||
## Edge Cases
|
||||
|
||||
### TC-OEF-017: Empty String vs Null
|
||||
**Steps:**
|
||||
1. Set field to optional
|
||||
2. Create event with empty string for that field
|
||||
3. View event details
|
||||
|
||||
**Expected Result:**
|
||||
- Field stores empty value appropriately
|
||||
- No errors in display
|
||||
|
||||
### TC-OEF-018: Rapid Toggle Changes
|
||||
**Steps:**
|
||||
1. Quickly toggle settings on/off multiple times
|
||||
2. Save after each change
|
||||
|
||||
**Expected Result:**
|
||||
- Each save completes without error
|
||||
- Final state matches last save action
|
||||
|
||||
## Automated Testing Notes
|
||||
|
||||
For Playwright tests:
|
||||
1. Login to admin dashboard
|
||||
2. Navigate to Settings
|
||||
3. Click Event Creation tab
|
||||
4. Manipulate toggles using checkbox selectors
|
||||
5. Navigate to Create Event
|
||||
6. Verify label text contains or doesn't contain "(optional)"
|
||||
7. Attempt form submission with various field combinations
|
||||
8. Assert on validation messages and success/failure states
|
||||
|
||||
## Files Modified
|
||||
|
||||
### Backend
|
||||
- `/backend/migrations/core/050_add_optional_event_fields_settings.js`
|
||||
- `/backend/src/routes/adminEvents.js`
|
||||
- `/backend/src/routes/publicSettings.js`
|
||||
|
||||
### Frontend
|
||||
- `/frontend/src/pages/admin/SettingsPage.tsx`
|
||||
- `/frontend/src/pages/admin/CreateEventPage.tsx`
|
||||
- `/frontend/src/pages/admin/CreateEventPageEnhanced.tsx`
|
||||
@@ -0,0 +1,327 @@
|
||||
# Test Specification: Photo Filtering & Export
|
||||
|
||||
This document specifies the test cases for the Photo Filtering & Export feature, which allows administrators to filter photos by guest feedback and export filtered selections in various formats.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Local Docker environment running (`docker-compose up`)
|
||||
- Access to admin dashboard
|
||||
- Event with photos that have feedback (ratings, likes, favorites, comments)
|
||||
- Backend migrations applied (051_add_photo_filter_indexes.js)
|
||||
|
||||
## Test Cases
|
||||
|
||||
### 1. Filter Panel UI
|
||||
|
||||
#### TC-PFE-001: Filter Panel Visibility
|
||||
**Steps:**
|
||||
1. Navigate to Event Details > Photos tab
|
||||
2. Verify "Feedback Filters" panel is visible
|
||||
|
||||
**Expected Result:**
|
||||
- Filter panel shows rating dropdown
|
||||
- Checkboxes for: Has likes, Has favorites, Has comments
|
||||
- Summary counts displayed
|
||||
|
||||
#### TC-PFE-002: Rating Filter Options
|
||||
**Steps:**
|
||||
1. Open the Rating dropdown in filter panel
|
||||
2. Verify all rating options are present
|
||||
|
||||
**Expected Result:**
|
||||
- Options: All photos, Any rating, 1+ stars, 2+ stars, 3+ stars, 4+ stars, 5 stars only
|
||||
- Dropdown is functional
|
||||
|
||||
#### TC-PFE-003: Filter by Rating
|
||||
**Steps:**
|
||||
1. Select "4+ stars" from rating dropdown
|
||||
2. Observe changes
|
||||
|
||||
**Expected Result:**
|
||||
- Filter is applied
|
||||
- Export menu becomes enabled (if photos match)
|
||||
|
||||
#### TC-PFE-004: Filter by Likes
|
||||
**Steps:**
|
||||
1. Check "Has likes" checkbox
|
||||
2. Observe changes
|
||||
|
||||
**Expected Result:**
|
||||
- Count shows number of photos with likes
|
||||
- Filter is applied
|
||||
|
||||
#### TC-PFE-005: Filter by Favorites
|
||||
**Steps:**
|
||||
1. Check "Has favorites" checkbox
|
||||
2. Observe changes
|
||||
|
||||
**Expected Result:**
|
||||
- Count shows number of favorited photos
|
||||
- Filter is applied
|
||||
|
||||
#### TC-PFE-006: Filter by Comments
|
||||
**Steps:**
|
||||
1. Check "Has comments" checkbox
|
||||
2. Observe changes
|
||||
|
||||
**Expected Result:**
|
||||
- Count shows number of commented photos
|
||||
- Filter is applied
|
||||
|
||||
#### TC-PFE-007: AND/OR Logic Toggle
|
||||
**Steps:**
|
||||
1. Check multiple feedback filters (e.g., Has likes AND Has favorites)
|
||||
2. Toggle between AND and OR
|
||||
3. Observe export button state
|
||||
|
||||
**Expected Result:**
|
||||
- AND: Photos must have both likes AND favorites
|
||||
- OR: Photos can have likes OR favorites
|
||||
- Toggle is visible when multiple filters selected
|
||||
|
||||
#### TC-PFE-008: Clear Filters
|
||||
**Steps:**
|
||||
1. Apply multiple filters
|
||||
2. Click "Clear" button
|
||||
|
||||
**Expected Result:**
|
||||
- All filters reset to default
|
||||
- Rating dropdown shows "All photos"
|
||||
- All checkboxes unchecked
|
||||
|
||||
### 2. Export Menu
|
||||
|
||||
#### TC-PFE-009: Export Menu Disabled State
|
||||
**Steps:**
|
||||
1. Clear all feedback filters
|
||||
2. Ensure no photos are selected
|
||||
3. Observe Export button
|
||||
|
||||
**Expected Result:**
|
||||
- Export button is disabled
|
||||
- Hint text: "Select photos or apply filters to export"
|
||||
|
||||
#### TC-PFE-010: Export Menu Enabled with Filters
|
||||
**Steps:**
|
||||
1. Apply a feedback filter (e.g., rating >= 3)
|
||||
2. Observe Export button
|
||||
|
||||
**Expected Result:**
|
||||
- Export button becomes enabled
|
||||
- Dropdown shows export format options
|
||||
|
||||
#### TC-PFE-011: Export Format Options
|
||||
**Steps:**
|
||||
1. Enable Export button with filters
|
||||
2. Click to open dropdown
|
||||
|
||||
**Expected Result:**
|
||||
- Four format options visible:
|
||||
- Filename List (TXT)
|
||||
- Filename List (CSV)
|
||||
- XMP Sidecar Files (ZIP)
|
||||
- Metadata (JSON)
|
||||
- Each has description text
|
||||
|
||||
### 3. Export Functionality
|
||||
|
||||
#### TC-PFE-012: Export TXT Format
|
||||
**Steps:**
|
||||
1. Apply filter (e.g., has favorites)
|
||||
2. Click Export > Filename List (TXT)
|
||||
3. Open downloaded file
|
||||
|
||||
**Expected Result:**
|
||||
- File downloads with .txt extension
|
||||
- Contains one filename per line
|
||||
- Uses original filenames (e.g., IMG_0001.jpg)
|
||||
|
||||
#### TC-PFE-013: Export CSV Format
|
||||
**Steps:**
|
||||
1. Apply filter
|
||||
2. Click Export > Filename List (CSV)
|
||||
3. Open in spreadsheet
|
||||
|
||||
**Expected Result:**
|
||||
- File downloads with .csv extension
|
||||
- Headers: filename, original_filename, rating, rating_count, likes, favorites, comments, category, etc.
|
||||
- Data rows for each filtered photo
|
||||
|
||||
#### TC-PFE-014: Export XMP Format
|
||||
**Steps:**
|
||||
1. Apply filter
|
||||
2. Click Export > XMP Sidecar Files (ZIP)
|
||||
3. Extract ZIP and inspect files
|
||||
|
||||
**Expected Result:**
|
||||
- ZIP file downloads
|
||||
- Contains .xmp files for each photo
|
||||
- XMP files contain:
|
||||
- xmp:Rating (1-5)
|
||||
- xmp:Label (color)
|
||||
- Description with feedback summary
|
||||
- Keywords including "picpeak-export"
|
||||
|
||||
#### TC-PFE-015: Export JSON Format
|
||||
**Steps:**
|
||||
1. Apply filter
|
||||
2. Click Export > Metadata (JSON)
|
||||
3. Open/parse JSON file
|
||||
|
||||
**Expected Result:**
|
||||
- JSON file downloads
|
||||
- Contains export_info (event name, date, exported_at, total_photos)
|
||||
- Contains photos array with full metadata
|
||||
|
||||
#### TC-PFE-016: XMP Rating Mapping
|
||||
**Steps:**
|
||||
1. Have photos with various ratings
|
||||
2. Export XMP
|
||||
3. Check xmp:Rating values
|
||||
|
||||
**Expected Result:**
|
||||
- 4.5-5.0 stars → xmp:Rating="5", Label="Red"
|
||||
- 3.5-4.4 stars → xmp:Rating="4", Label="Yellow"
|
||||
- 2.5-3.4 stars → xmp:Rating="3", Label="Green"
|
||||
- 1.5-2.4 stars → xmp:Rating="2", Label="Blue"
|
||||
- 0.5-1.4 stars → xmp:Rating="1", Label="Purple"
|
||||
|
||||
### 4. Backend API Tests
|
||||
|
||||
#### TC-PFE-017: Filtered Photos Endpoint
|
||||
**Steps:**
|
||||
1. Call API: `GET /api/admin/photo-export/:eventId/filtered?min_rating=4&has_likes=true`
|
||||
|
||||
**Expected Result:**
|
||||
- Returns 200 OK
|
||||
- Response includes photos, pagination, and summary
|
||||
- Only photos matching filter returned
|
||||
|
||||
#### TC-PFE-018: Filter Summary Endpoint
|
||||
**Steps:**
|
||||
1. Call API: `GET /api/admin/photo-export/:eventId/filter-summary`
|
||||
|
||||
**Expected Result:**
|
||||
- Returns 200 OK
|
||||
- Response includes: total, withRatings, withLikes, withFavorites, withComments
|
||||
|
||||
#### TC-PFE-019: Export Endpoint with Photo IDs
|
||||
**Steps:**
|
||||
1. Call API: `POST /api/admin/photo-export/:eventId/export`
|
||||
Body: `{ "photo_ids": [1, 2, 3], "format": "csv" }`
|
||||
|
||||
**Expected Result:**
|
||||
- Returns CSV file
|
||||
- Contains only specified photos
|
||||
|
||||
#### TC-PFE-020: Export Endpoint with Filters
|
||||
**Steps:**
|
||||
1. Call API: `POST /api/admin/photo-export/:eventId/export`
|
||||
Body: `{ "filter": { "minRating": 4 }, "format": "txt" }`
|
||||
|
||||
**Expected Result:**
|
||||
- Returns TXT file
|
||||
- Contains all photos matching filter
|
||||
|
||||
### 5. Edge Cases
|
||||
|
||||
#### TC-PFE-021: Empty Filter Results
|
||||
**Steps:**
|
||||
1. Apply filter that matches no photos (e.g., rating = 5 when no 5-star photos exist)
|
||||
2. Try to export
|
||||
|
||||
**Expected Result:**
|
||||
- Export button disabled or shows "0 photos"
|
||||
- Error message if attempting export
|
||||
|
||||
#### TC-PFE-022: Large Export
|
||||
**Steps:**
|
||||
1. Filter to include 100+ photos
|
||||
2. Export as XMP ZIP
|
||||
|
||||
**Expected Result:**
|
||||
- Export completes (may take time)
|
||||
- ZIP contains all matching .xmp files
|
||||
- Loading indicator shown during export
|
||||
|
||||
#### TC-PFE-023: Special Characters in Filenames
|
||||
**Steps:**
|
||||
1. Have photo with special characters in original filename
|
||||
2. Export CSV
|
||||
|
||||
**Expected Result:**
|
||||
- Filename properly escaped in CSV
|
||||
- No parsing errors
|
||||
|
||||
### 6. Integration Tests
|
||||
|
||||
#### TC-PFE-024: XMP Import to Lightroom
|
||||
**Steps:**
|
||||
1. Export XMP files
|
||||
2. Place XMP files next to original photos
|
||||
3. In Lightroom: Select photos > Metadata > Read Metadata from Files
|
||||
|
||||
**Expected Result:**
|
||||
- Lightroom reads XMP files
|
||||
- Ratings appear on photos
|
||||
- Color labels applied
|
||||
- Keywords visible in metadata panel
|
||||
|
||||
#### TC-PFE-025: TXT List in Lightroom Search
|
||||
**Steps:**
|
||||
1. Export TXT filename list
|
||||
2. In Lightroom: Library > Filter > Text > Filename > Contains
|
||||
3. Paste comma-separated list
|
||||
|
||||
**Expected Result:**
|
||||
- Lightroom filters to matching files
|
||||
- Can select and add to collection
|
||||
|
||||
### 7. Performance Tests
|
||||
|
||||
#### TC-PFE-026: Filter Performance
|
||||
**Steps:**
|
||||
1. Event with 500+ photos
|
||||
2. Apply feedback filter
|
||||
3. Measure response time
|
||||
|
||||
**Expected Result:**
|
||||
- Filter applied within 2 seconds
|
||||
- UI remains responsive
|
||||
|
||||
#### TC-PFE-027: Export Performance
|
||||
**Steps:**
|
||||
1. Export 200 photos as XMP ZIP
|
||||
2. Measure download time
|
||||
|
||||
**Expected Result:**
|
||||
- Export completes within reasonable time
|
||||
- Progress indicator shown for large exports
|
||||
|
||||
## Automated Testing Notes
|
||||
|
||||
For Playwright tests:
|
||||
1. Login to admin dashboard
|
||||
2. Navigate to an event with photos
|
||||
3. Go to Photos tab
|
||||
4. Manipulate filter panel controls
|
||||
5. Click export menu
|
||||
6. Verify file downloads
|
||||
7. For API tests, use direct fetch/axios calls
|
||||
|
||||
## Files Modified
|
||||
|
||||
### Backend
|
||||
- `/backend/migrations/core/051_add_photo_filter_indexes.js`
|
||||
- `/backend/src/utils/photoFilterBuilder.js`
|
||||
- `/backend/src/services/xmpGenerator.js`
|
||||
- `/backend/src/services/photoExportService.js`
|
||||
- `/backend/src/routes/adminPhotoExport.js`
|
||||
- `/backend/server.js`
|
||||
|
||||
### Frontend
|
||||
- `/frontend/src/services/photos.service.ts`
|
||||
- `/frontend/src/components/admin/PhotoFilterPanel.tsx`
|
||||
- `/frontend/src/components/admin/PhotoExportMenu.tsx`
|
||||
- `/frontend/src/components/admin/index.ts`
|
||||
- `/frontend/src/pages/admin/EventDetailsPage.tsx`
|
||||
Reference in New Issue
Block a user