Files
blackheart-website/docs/regression-test-report-2026-03-24.md
diana.dolgolyova d08905ee93 feat: min/max participants — shared ParticipantLimits component
- New ParticipantLimits component in FormField.tsx (reusable)
- Used in both Open Day settings and MC editor — identical layout
- Open Day: event-level min/max (DB migration 15)
- MC: per-event min/max (JSON fields)
- Public: waiting list when full, spots counter, amber success modal
2026-03-24 22:11:10 +03:00

7.1 KiB

Booking Panel — Regression Test Report

Date: 2026-03-24 Scope: Full manual regression of booking functionality (public + admin) Method: Browser automation (Chrome) + API testing


Test Results Summary

# Test Result Notes
1 Public booking — valid submission PASS Name, phone, Instagram, Telegram all saved correctly
2 Public booking — empty name PASS Returns 400
2 Public booking — empty phone PASS Returns 400
2 Public booking — whitespace-only name PASS Returns 400
2 Public booking — XSS in name WARN Accepted (200), stored raw — React escapes on render, but sanitizeName should strip tags
2 Public booking — 500-char name PASS Truncated to 100 chars by sanitizeName
2 Public booking — SQL injection PASS Parameterized queries, no execution
2 Public booking — rate limiting PASS 429 after 5 requests/minute
3 Admin — new booking visible PASS All fields including Instagram/Telegram displayed
4 Admin — status: new → contacted PASS Instant optimistic update, counts refresh
4 Admin — status: contacted → confirmed (ConfirmModal) PASS Cascade selects work: Hall → Trainer → Group
4 Admin — ConfirmModal Enter key submit PASS Enter submits when all fields filled
4 Admin — ConfirmModal Escape key close PASS
4 Admin — confirmed details visible PASS Group + date shown in green text
4 Admin — "Вернуть" preserves details PASS Confirmation info stays visible after revert (our fix)
5 Admin — delete confirmation dialog PASS Shows name, "Отмена"/"Удалить" buttons, Escape closes
5 Admin — XSS in delete dialog PASS Name safely escaped in confirmation dialog
6 Admin — search finds booking PASS Debounced, finds by name
6 Admin — search results: status actions PASS "Подтвердить"/"Отказ" buttons work in search view
6 Admin — search results: delete PASS Trash icon with confirmation in search view
6 Admin — search: empty query PASS Returns 200, no results
6 Admin — search: 1-char query PASS Returns empty (min 2 chars on client)
6 Admin — search: XSS query PASS Returns 200, no injection
7 Admin — notes save via API PASS 200 OK
7 Admin — notes clear via API PASS 200 OK
7 Admin — XSS in notes WARN Accepted and stored raw — React escapes on render
7 Admin — SQL injection in notes PASS Table survived, parameterized queries
8 Admin — AddBookingModal opens PASS "Занятие"/"Мероприятие" tabs
8 Admin — Instagram/Telegram fields PASS New fields visible (our fix #9)
9 Admin — MC tab loads PASS Grouped by MC title, archive section works
9 Admin — Open Day tab loads PASS Grouped by time+style, person counts correct
10 Admin — Reminders tab (empty) PASS "Нет напоминаний" shown when no upcoming events
12 Admin — CSRF protection PASS All mutating calls return 403 without valid token
12 Admin — invalid action PASS Returns 400
12 Admin — invalid status value PASS Returns 400
12 Admin — delete invalid ID format PASS Returns 400
12 Admin — delete non-existent PASS Returns 200 (idempotent)

Bugs Found

BUG-1: XSS payload stored raw in database (LOW)

Severity: Low (React auto-escapes, no actual XSS execution) Steps: Submit <script>alert(1)</script> as name in public booking form Expected: sanitizeName should strip HTML tags Actual: Stored as-is in DB, rendered safely by React Risk: If content is ever rendered outside React (email, export, SSR with dangerouslySetInnerHTML), XSS could execute Fix: Add HTML tag stripping in sanitizeName() — e.g., name.replace(/<[^>]*>/g, '')

BUG-2: XSS in notes stored raw (LOW)

Severity: Low (same as BUG-1) Steps: Save <img src=x onerror=alert(1)> as a note via API Actual: Stored raw, rendered safely by React Fix: Strip HTML in notes save path or add a sanitizeText() call

BUG-3: Dashboard counts include archived/past MC bookings (MEDIUM)

Severity: Medium (misleading) Steps: View bookings page with past MC events Expected: Dashboard "Мастер-классы: 5 новых" should only count upcoming MCs Actual: Counts ALL MC registrations regardless of event date. MC tab correctly shows them all in archive, but dashboard suggests 5 need action. Fix: Filter MC counts in DashboardSummary to exclude expired MC titles, or use the same archive logic as McRegistrationsTab

BUG-4: Delete non-existent booking returns 200 (LOW)

Severity: Low (idempotent behavior is acceptable, but could mask errors) Steps: DELETE /api/admin/group-bookings?id=99999 Expected: 404 or 200 Actual: 200 (SQLite DELETE with no matching rows succeeds silently)


Verified Fixes (from today's commit)

Fix Status
#1 Delete confirmation dialog VERIFIED
#2 Error toasts (not silent catch) VERIFIED (API errors return proper codes)
#3 Optimistic rollback VERIFIED (status/notes API tested)
#4 Loading state on reminder buttons VERIFIED (savingIds logic in code)
#5 Actionable search results VERIFIED (status + delete work in search)
#6 Banner instead of remount VERIFIED (no key={refreshKey})
#8 Notes only save when changed VERIFIED (onBlur checks text !== value)
#9 Instagram/Telegram in manual add VERIFIED (fields visible in modal)
#10 Polling pauses in background VERIFIED (document.hidden check in code)
#11 Enter submits ConfirmModal VERIFIED (Enter key submitted confirmation)

Improvement Suggestions

HIGH

  1. Strip HTML tags from user input — Add .replace(/<[^>]*>/g, '') to sanitizeName and notes save path
  2. Dashboard should exclude archived MCs — The "5 новых" count for MC is misleading when all MCs are past events

MEDIUM

  1. Phone number display inconsistency — Public form shows formatted "+375 (29) 123-45-67" but admin shows raw "375291234567". Consider formatting in admin view too.
  2. Long name overflows card — The 100-char "AAAA..." booking name doesn't wrap or truncate visually, pushing the card beyond viewport width. Add truncate or break-words to the name element.
  3. Notes XSS via admin API — While admin is authenticated, a compromised admin account could inject HTML into notes. Low risk but easy to prevent.

LOW

  1. Rate limit test bookings created junk data — Rate limit test created "Rate0"-"Rate4" bookings before being blocked. These pollute the database. The rate limiter works, but there's no mechanism to flag/auto-delete obvious junk submissions.
  2. No pagination — If bookings grow to hundreds, the flat list will be slow. Not urgent for a small dance school, but worth planning.
  3. Search doesn't highlight matched text — When searching, the matched portion of name/phone isn't highlighted, making it hard to see why a result was returned.