← Back to Punch John Eric

Test Report

Punch John Eric · Sat, 14 Mar 2026 22:30:30 GMT
Commit 3b25278
562
Total Tests
562
Passed
0
Failed
Passed 562
Unit Tests 127/127
Positive 118/118
F3.R2 creates counter file with count 0 when file does not exist 5ms
F3.R3 does not overwrite existing counter file 1ms
F15.R2 returns parsed counter data from file 1ms
F17.R2 recreates counter file when missing 1ms
F3.R1 writes count to file as JSON 0ms
F3.R1 overwrites existing count 1ms
F3.R4 handles count of 0 1ms
F27.R3 readCounter defaults shields to 0 for legacy files 217ms
F27.R3 writeState persists both count and shields 172ms
F28.R3 heal cannot go below 0 1ms
F28.R3 heal decrements by 1 1ms
F27.R5 punch with shields consumes a shield 0ms
F27.R5 punch without shields increments count 1ms
F27.R6 readCounter decays shields based on elapsed time 182ms
F27.R6 partial intervals do not over-decay shields 167ms
F27.R6 shields do not go negative from decay 169ms
F27.R7 shield cap prevents exceeding 10 1ms
F27.R7 shield cap allows increment below 10 1ms
F29.R3 trackUser records an IP in activeUsers map 1ms
F29.R3 getActiveUserCount returns number of recent users 1ms
F29.R3 getActiveUserCount excludes stale entries 1ms
F32.R1 isHealOnCooldown returns false for unknown IP 1ms
F32.R1 isHealOnCooldown returns true after heal recorded 1ms
F32.R1 isHealOnCooldown returns false after cooldown expires 1ms
F32.R2 getHealCooldownRemaining returns remaining ms 1ms
F32.R2 getHealCooldownRemaining returns 0 after expiry 0ms
F34.R1 writeState uses tmp + rename pattern 169ms
F34.R2 no .tmp file remains after write 168ms
F34.R3 data integrity preserved through atomic write 171ms
F33.R2 broadcast sends to all open clients 2ms
F33.R1 attachWebSocket creates a WebSocket server 0ms
F31.R1 rate limit constants are defined 1ms
F35.R2 manifest.json is valid JSON with required fields 1ms
F35.R5 HTML includes manifest link and theme-color 1ms
F39.R1 poll-feature-requests.js script exists and is valid JavaScript 35ms
F39.R1 poll script polls for approved feature requests 1ms
F39.R1 poll script launches Claude Code headlessly 1ms
F39.R2 script file exists and is valid JavaScript 30ms
F39.R2 script requires @anthropic-ai/sdk 1ms
F39.R2 script reads CLAUDE.md and features.json for context 1ms
F39.R2 script writes implementation plan to /tmp 1ms
F39.R3 mark-implemented workflow file exists 1ms
F39.R4 mark-implemented workflow triggers on workflow_dispatch 1ms
F39.R3 mark-implemented workflow calls webhook endpoint 1ms
F39.R3 old implement-feature workflow is disabled 1ms
F39.R5 poll script triggers mark-implemented workflow via gh CLI 1ms
F39.R6 script file exists and is valid JavaScript 31ms
F39.R6 script requires @anthropic-ai/sdk 1ms
F39.R7 implement-feature.js uses child_process for test execution 1ms
F39.R7 implement-feature.js has TDD phases (RED then GREEN) 1ms
F39.R7 implement-feature.js has fix loop with max attempts 0ms
F39.R7 implement-feature.js exits 0 only when all tests pass 0ms
F39.R7 poll script builds TDD-focused prompt for Claude Code 1ms
F40.R3 readCounter defaults bodyPartHits when missing from file 226ms
F40.R3 readCounter preserves existing bodyPartHits 182ms
F40.R3 writeState persists bodyPartHits to disk 177ms
F40.R3 bodyPartHits defaults to all zeros on counter init 197ms
F40.R3 contains exactly 6 entries 195ms
F40.R4 punch without shields increments specific body part 183ms
F40.R4 punch with shields does NOT increment bodyPartHits 193ms
F40.R3 all body part counters increment independently 188ms
F30.R1 test result files contain tests for all features in the registry 1ms
F30.R2 every requirement has at least one passing test in the result files 0ms
F30.R3 every feature in the registry has a matching entry in features.json 2ms
F29.R4 returns Unknown for localhost/private IPs 245ms
F29.R4 returns a location string for a known public IP 1ms
F29.R4 returns users grouped by location 2ms
F29.R4 sorts Unknown location last 1ms
F29.R4 sorts by count descending 1ms
F29.R4 excludes stale entries 1ms
F29.R4 returns empty array when no active users 1ms
F29.R4 trackUser stores location alongside lastSeen 2ms
F49.R2 returns 5 default reasons when file does not exist 3ms
F49.R2 all default reasons have isDefault=true 2ms
F49.R4 returns defaults plus custom reasons when file exists 2ms
F49.R2 default reasons appear before custom reasons 2ms
F49.R4 writes custom reasons to file atomically 1ms
F49.R4 no .tmp file remains after successful write 1ms
F50.R1 jeSessions starts empty 268ms
F50.R1 requireJe rejects requests without token 165ms
F50.R1 requireJe rejects expired sessions 220ms
F50.R1 requireJe allows valid sessions 266ms
F50.R6 image file and metadata persist to disk 7ms
F50.R6 image data survives read-write cycle 5ms
F38.R1 adminSessions starts empty 277ms
F38.R1 requireAdmin rejects requests without token 214ms
F38.R1 requireAdmin rejects expired sessions 223ms
F38.R1 requireAdmin allows valid sessions 232ms
F38.R3 readCounter defaults disabledActions when missing 223ms
F38.R3 writeState persists disabledActions 230ms
F36.R5 version.json has valid semver format 17ms
F36.R6 releases.json is valid JSON with required fields 2ms
F36.R6 all releases have valid semver versions 2ms
F36.R3 generate-releases.js creates releases page 19ms
F47.R1 generator has buildReleaseHTML function 2ms
F47.R2 generator reads releases.json 1ms
F47.R1 generator outputs release-view div 1ms
F47.R1 generator outputs By Release tab button 1ms
F47.R1 generator showView handles release view 1ms
F47.R2 generator extracts feature IDs from release strings 1ms
F47.R2 generator groups unassigned tests separately 1ms
F47.R4 generator places back-link after page-nav 1ms
F20.R7 generator adds feature-based IDs to requirement elements 1ms
F20.R7 generator adds feature-based IDs to feature elements 1ms
F20.R7 generator includes highlight CSS animation 1ms
F20.R7 generator includes hash navigation JavaScript 1ms
F25.R9 generator script exits with code 0 51ms
F25.R9 generator produces an HTML file 4ms
F25.R4 generated HTML contains all features from features.json 24ms
F25.R5 generated HTML contains all requirements 13ms
F25.R6 generated HTML contains the table of contents 4ms
F51.R3 returns empty object when file does not exist 3ms
F51.R3 returns counts from file when it exists 2ms
F51.R3 writes counts to file atomically 2ms
F51.R3 no .tmp file remains after successful write 1ms
F23.R1 deploy workflow configures HTTPS for production URL 2ms
F24.R1 deploy workflow has test, deploy, and verify jobs 1ms
F26.R6 CI pipeline includes npm audit step with high severity threshold 0ms
Negative 9/9
F17.R1 returns { count: 0 } and recreates file when file is missing 1ms
F17.R3 returns { count: 0 } when file contains invalid JSON 1ms
F17.R4 returns { count: 0 } when file is empty 1ms
F17.R5 returns raw parsed data even if shape is unexpected 1ms
F3.R4 handles negative numbers 1ms
F3.R4 handles very large numbers 0ms
F29.R4 returns Unknown for null/undefined input 1ms
F49.R4 returns only defaults when file has invalid JSON 1ms
F51.R3 returns empty object for invalid JSON 2ms
Integration Tests 149/149
Positive 102/102
F14.R1 returns 200 with HTML content 465ms
F15.R1 returns 200 with current count 198ms
F15.R4 reflects count after increments 213ms
F16.R3 increments counter by 1 and returns new count 188ms
F16.R3 increments correctly when called multiple times 180ms
F16.R4 persists increment to disk 174ms
F18.R1 API responses include CORS headers on GET 167ms
F21.R1 /test redirects to /test/ with 301 179ms
F20.R1 /test/ returns 200 with HTML content 165ms
F14.R2 HTML contains required elements and title 167ms
F18.R2 POST /api/increment includes CORS headers 180ms
F18.R3 OPTIONS /api/counter returns CORS headers 165ms
F25.R2 /features redirects to /features/ with 301 164ms
F25.R1 /features/ returns 200 with HTML content 168ms
F26.R1 GET / includes security headers 166ms
F26.R1 GET /api/counter includes security headers 170ms
F26.R1 POST /api/increment includes security headers 165ms
F27.R2 returns 200 with shields incremented 168ms
F27.R2 shields accumulate on multiple calls 170ms
F27.R5 consumes a shield instead of incrementing count 178ms
F27.R5 increments count when no shields remain 175ms
F28.R2 decrements counter by 1 185ms
F28.R3 counter stays at 0 when already 0 173ms
F27.R3 returns both count and shields 177ms
F27.R7 shields cannot exceed 10 174ms
F27.R6 GET /api/counter reflects decayed shields 172ms
F29.R2 GET /api/counter includes users field 186ms
F31.R2 returns 429 after exceeding rate limit 342ms
F31.R1 rate limiter applies to all /api/* routes 349ms
F32.R3 first heal succeeds 180ms
F32.R3 immediate second heal returns 429 177ms
F32.R2 429 response includes retryAfter 182ms
F34.R3 counter persists through rapid successive writes 190ms
F34.R2 no tmp file after increment 171ms
F35.R1 GET /manifest.json returns 200 167ms
F33.R3 client receives initial state on connect 172ms
F33.R2 broadcast sent after increment 284ms
F38.R1 POST /api/admin/login succeeds with correct credentials 238ms
F38.R1 POST /api/admin/logout clears session 186ms
F38.R1 GET /api/admin/status returns authenticated true when logged in 188ms
F38.R1 GET /api/admin/status returns authenticated false when not logged in 186ms
F38.R2 POST /api/admin/reset-counter resets count to 0 191ms
F38.R2 POST /api/admin/set-counter sets count to arbitrary value 193ms
F38.R3 POST /api/admin/toggle-action disables punch 192ms
F38.R3 disabled punch returns 403 from POST /api/increment 196ms
F38.R3 disabled shield returns 403 from POST /api/shield 198ms
F38.R3 disabled heal returns 403 from POST /api/heal 194ms
F38.R3 re-enabling action allows it again 193ms
F38.R4 POST /api/admin/approve-request sets status to approved 200ms
F38.R4 POST /api/admin/deny-request sets status to denied 206ms
F38.R3 GET /api/counter includes disabledActions 188ms
F50.R1 POST /api/je/login succeeds with correct credentials 250ms
F50.R1 POST /api/je/logout clears session 203ms
F50.R1 GET /api/je/status returns authenticated true when logged in 203ms
F50.R1 GET /api/je/status returns authenticated false when not logged in 196ms
F50.R2 POST /api/je/upload-image saves image when authenticated 210ms
F50.R3 GET /api/je/image returns uploaded image 206ms
F39.R5 marks feature request as implemented with version 248ms
F39.R5 skips manual dispatch IDs 198ms
F39.R5 is idempotent for already-implemented requests 203ms
F40.R2 POST with bodyPart=head increments head counter 305ms
F40.R2 POST with bodyPart=keister increments keister counter 213ms
F40.R2 POST without bodyPart defaults to torso 219ms
F40.R9 GET /api/counter includes bodyPartHits in response 215ms
F40.R9 bodyPartHits values accumulate across punches 222ms
F40.R4 POST with shields does not increment bodyPartHits 222ms
F40.R3 bodyPartHits persisted to disk after increment 220ms
F40.R8 GET after increment reflects bodyPartHits from broadcast 230ms
F49.R2 returns 200 with application/json 265ms
F49.R2 returns array with 5 default reasons when no custom reasons exist 218ms
F49.R4 returns defaults plus custom reasons 218ms
F49.R3 creates a new custom reason and returns 201 229ms
F49.R4 persists custom reason to reasons.json 223ms
F49.R4 new reason appears in subsequent GET /api/reasons 230ms
F49.R4 multiple custom reasons accumulate 235ms
F51.R2 returns 200 with application/json 271ms
F51.R2 returns empty object when no counts exist 232ms
F51.R2 returns counts after punches with reasons 244ms
F51.R1 tracks reason count when reason field is provided 245ms
F51.R1 increments existing reason count 252ms
F51.R1 does not track count when no reason provided 241ms
F51.R1 still increments punch counter normally 227ms
F51.R3 reason counts persist to reason-counts.json 231ms
F37.R3 GET /api/feature-requests returns empty array when no requests exist 275ms
F37.R3 POST /api/feature-requests creates a new request 188ms
F37.R3 GET /api/feature-requests returns previously submitted requests 189ms
F37.R3 requests persist to feature-requests.json file 242ms
F37.R4 implemented request has implementedVersion field 238ms
F45.R1 updates text of a pending request 296ms
F45.R1 trims whitespace from text 249ms
F29.R4 returns 200 with users and total 303ms
F29.R4 tracks requesting user (total >= 1 after request) 255ms
F29.R4 each user entry has location and count 251ms
F29.R4 returns JSON content-type 253ms
F29.R4 groups users by location correctly 253ms
F39.R1 approval sets status to approved 335ms
F39.R1 approved request is picked up by local polling 291ms
F48.R1 HTML contains character SVG with curved path elements in torso 333ms
F48.R2 HTML contains all 6 body-part zones with data-part attributes 297ms
F48.R1 character SVG uses organic shapes in arm body parts 351ms
F36.R5 GET /version.json returns 200 with valid version object 36ms
F36.R5 version.json version matches package.json 4ms
Negative 47/47
F22.R1 invalid JSON body returns 400 221ms
F22.R2 non-JSON content-type still increments 179ms
F19.R3 GET /api/nonexistent returns 404 168ms
F19.R3 GET /nonexistent returns 404 161ms
F19.R3 GET /nonexistent.css returns 404 163ms
F15.R1 POST /api/counter returns 404 187ms
F16.R1 GET /api/increment returns 404 189ms
F15.R1 PUT /api/counter returns 404 212ms
F16.R1 DELETE /api/increment returns 404 182ms
F20.R6 /test/ returns 404 when report has not been generated 166ms
F25.R8 /features/ returns 404 when page has not been generated 164ms
F38.R1 POST /api/admin/login rejects wrong password 191ms
F38.R1 POST /api/admin/login rejects wrong username 193ms
F38.R1 POST /api/admin/login rejects missing fields 186ms
F38.R2 POST /api/admin/set-counter rejects negative values 193ms
F38.R2 POST /api/admin/reset-counter requires auth 189ms
F38.R3 POST /api/admin/toggle-action rejects invalid action 197ms
F38.R3 POST /api/admin/toggle-action requires auth 187ms
F38.R4 POST /api/admin/approve-request returns 404 for unknown id 193ms
F38.R4 POST /api/admin/approve-request requires auth 183ms
F50.R1 POST /api/je/login rejects wrong password 203ms
F50.R1 POST /api/je/login rejects wrong username 213ms
F50.R1 POST /api/je/login rejects missing fields 206ms
F50.R2 POST /api/je/upload-image requires auth 195ms
F50.R2 POST /api/je/upload-image rejects invalid data URL 197ms
F50.R2 POST /api/je/upload-image rejects missing image field 206ms
F50.R3 GET /api/je/image returns 404 when no image uploaded 196ms
F39.R5 rejects missing Authorization header 204ms
F39.R5 rejects wrong webhook secret 201ms
F39.R5 returns 503 when WEBHOOK_SECRET not configured 214ms
F39.R5 rejects missing requestId 205ms
F39.R5 rejects missing version 209ms
F39.R5 returns 404 for unknown requestId 207ms
F40.R2 POST with invalid bodyPart defaults to torso 217ms
F49.R3 rejects empty text with 400 225ms
F49.R3 rejects missing text field with 400 225ms
F49.R3 rejects whitespace-only text with 400 220ms
F51.R1 ignores invalid reason ids 237ms
F37.R3 POST /api/feature-requests rejects empty text 235ms
F37.R3 POST /api/feature-requests rejects missing text 239ms
F45.R2 returns 403 for approved request 254ms
F45.R2 returns 403 for denied request 206ms
F45.R1 returns 404 for unknown request 192ms
F45.R1 returns 400 for empty text 255ms
F45.R1 returns 400 for text over 500 chars 261ms
F29.R4 response does not expose IP addresses 256ms
F29.R4 POST /api/users returns 404 259ms
Contract Tests 99/99
Positive 78/78
F15.R1 responds with application/json content-type 347ms
F15.R2 response body has count, shields, users, disabledActions, and bodyPartHits keys 272ms
F15.R2 count is a number 262ms
F15.R3 count is a non-negative integer 264ms
F16.R1 responds with application/json content-type 257ms
F16.R2 response body has count, shields, and bodyPartHits keys 261ms
F16.R2 count is a number 271ms
F16.R3 count is a positive integer after increment 257ms
F15.R1 accepts a plain GET with no body or special headers 280ms
F15.R1 accepts GET with Accept: application/json header 258ms
F16.R1 accepts POST with Content-Type: application/json and no body 260ms
F16.R1 accepts POST with no Content-Type header 269ms
F16.R5 accepts POST with an empty JSON body 284ms
F16.R5 ignores unexpected fields in the body 275ms
F25.R1 /features/ responds with text/html content-type 177ms
F25.R2 /features redirect includes Location header 173ms
F26.R2 Content-Security-Policy includes default-src self 175ms
F26.R3 X-Content-Type-Options is nosniff 172ms
F26.R4 X-Frame-Options prevents framing 166ms
F26.R5 Strict-Transport-Security has max-age 173ms
F26.R1 security headers present on API routes too 174ms
F27.R3 response body has count and shields keys 178ms
F27.R2 responds with application/json 173ms
F27.R2 response has count and shields keys 180ms
F28.R2 responds with application/json 181ms
F28.R2 response has count and shields keys 172ms
F28.R3 count is non-negative after heal 180ms
F29.R2 response body has users key as a number 180ms
F31.R2 429 response includes error message 346ms
F32.R2 429 heal response includes retryAfter as number 187ms
F35.R2 manifest has required PWA fields 168ms
F38.R1 login response has { success: true } 257ms
F38.R1 status response has { authenticated: boolean } 200ms
F38.R2 reset-counter response has { count, shields } 216ms
F38.R2 set-counter response has { count, shields } 208ms
F38.R3 toggle-action response has { disabledActions } 227ms
F38.R4 approve-request response has status and approvedAt 227ms
F38.R4 deny-request response has status and deniedAt 214ms
F38.R3 disabled action 403 response has { error } 210ms
F49.R2 returns an array 270ms
F49.R2 each reason has id, text, and isDefault fields 227ms
F49.R2 reason id is a string 227ms
F49.R2 reason text is a string 227ms
F49.R2 reason isDefault is a boolean 228ms
F49.R2 default reasons have isDefault=true 221ms
F49.R3 created reason has id, text, and isDefault fields 233ms
F49.R3 created reason has isDefault=false 234ms
F49.R3 created reason text matches submitted text 233ms
F49.R3 accepts JSON body with text field 224ms
F50.R1 login response has { success: true } 285ms
F50.R1 status response has { authenticated: boolean } 234ms
F50.R2 upload response has { success, ext, size, uploadedAt } 235ms
F50.R3 image endpoint returns correct content-type 238ms
F39.R5 success response has { updated, requestId, version } 306ms
F39.R5 skipped response has { skipped, reason } 265ms
F39.R5 already-implemented response has { updated, alreadyImplemented } 272ms
F29.R4 responds with application/json content-type 311ms
F29.R4 response body has users array and total number 266ms
F29.R4 each user entry has location (string) and count (positive number) 265ms
F29.R4 total equals sum of all user counts 258ms
F29.R4 response includes security headers 273ms
F29.R4 accepts plain GET with no body 263ms
F39.R1 approve response has correct shape 316ms
F39.R3 mark-implemented workflow YAML has required sections 240ms
F39.R3 mark-implemented workflow uses WEBHOOK_SECRET 214ms
F40.R9 response has bodyPartHits object with 6 keys 323ms
F40.R9 all bodyPartHits values are non-negative integers 281ms
F40.R9 bodyPartHits keys match VALID_BODY_PARTS 271ms
F40.R2 POST response includes bodyPartHits 271ms
F40.R2 POST accepts bodyPart field without error 288ms
F51.R2 returns an object 327ms
F51.R2 values are non-negative integers 279ms
F51.R2 keys are reason id strings 352ms
F51.R1 accepts JSON body with reason field alongside bodyPart 295ms
F45.R1 success returns full request object 342ms
F36.R5 /version.json response shape: { version: string } 15ms
F36.R6 releases.json entries have version, date, title, features 4ms
F36.R6 release features/improvements/fixes are arrays of strings 9ms
Negative 21/21
F22.R1 malformed JSON body returns 400 (not a 500 crash) 272ms
F22.R4 404 responses do not include a count property 269ms
F15.R1 POST /api/counter is not allowed 249ms
F16.R1 GET /api/increment is not allowed 169ms
F16.R1 PUT /api/increment is not allowed 178ms
F15.R1 DELETE /api/counter is not allowed 171ms
F22.R1 POST /api/increment with broken JSON returns 400 174ms
F22.R2 POST /api/increment with text/plain body still succeeds 180ms
F22.R3 POST /api/increment with extremely large JSON body still succeeds 178ms
F31.R2 429 response does not contain count property 347ms
F49.R3 error response has error field 237ms
F50.R3 image 404 response has { error } string 228ms
F50.R2 upload error response has { error } string 242ms
F50.R1 login error response has { error } string 236ms
F39.R5 401 response has { error } 263ms
F39.R5 400 response has { error } 257ms
F29.R4 response does not contain ip or address fields 269ms
F29.R4 POST /api/users is not allowed 270ms
F45.R2 403 returns { error } with status info 299ms
F45.R1 400 returns { error } 267ms
F45.R1 404 returns { error } 305ms
Functional Tests 187/187
Positive 175/175
F43.R1 abs lines are visible on the torso 802ms
F43.R1 abs lines have subtle opacity 715ms
F38.R1 admin page loads with login form 160ms
F38.R1 admin login with correct credentials shows admin view 231ms
F38.R2 admin panel shows counter management controls 297ms
F38.R3 admin panel shows action toggle switches 315ms
F38.R1 logout returns to login view 442ms
F38.R5 admin tab is hidden when not authenticated 716ms
F38.R5 admin tab is visible on admin page itself 133ms
F14.R2 page loads with correct title 237ms
F1.R1 counter element is visible on page load 237ms
F1.R2 counter display shows a number on load 238ms
F2.R1 clicking the character sends POST /api/increment 751ms
F2.R2 clicking the character increments the counter 283ms
F2.R3 clicking multiple times increments correctly 2.5s
F8.R1 punch animation class is applied and removed 685ms
F8.R2 .punching class removed after animation completes 778ms
F9.R1 counter bump animation fires on increment 262ms
F14.R1 no console errors on page load 1.2s
F19.R1 static assets load successfully 691ms
F5.R1 isUpdating lock prevents concurrent POST requests 1.6s
F1.R3 counter value matches API response on load 229ms
F4.R1 page polls GET /api/counter periodically 11.7s
F4.R2 polling updates display when server value changes externally 6.7s
F6.R1 number formatting shows thousands separators 737ms
F7.R1 character SVG is visible with nonzero dimensions 256ms
F12.R1 responsive layout adapts at mobile viewport 227ms
F12.R2 character figure is smaller on mobile than desktop 562ms
F7.R2 SVG has role="img" and aria-label for character 221ms
F13.R1 punch button has aria-label 210ms
F13.R2 SVG has role="img" and aria-label 217ms
F13.R3 toast container has aria-live="polite" 213ms
F13.R4 body-part zone has visible focus ring when focused via keyboard 185ms
F13.R5 punch button is keyboard-activatable 736ms
F19.R2 favicon link element exists 200ms
F21.R1 /test redirects to /test/ 143ms
F20.R2 report page loads with correct title 204ms
F20.R3 report page shows summary cards 233ms
F20.R4 report page shows test layers 214ms
F20.R5 report page has back link to main site 217ms
F20.R1 no console errors on report page 641ms
F20.R5 back link navigates to homepage 861ms
F20.R7 hash navigation highlights target for 5 seconds 6.8s
F47.R1 By Release tab is visible on test report page 211ms
F47.R2 clicking By Release tab shows release-grouped tests 293ms
F47.R3 releases shown in reverse chronological order 286ms
F47.R4 back link is at top of test report page 240ms
F47.R4 back link is at top of features page 231ms
F47.R4 back link is at top of releases page 173ms
F47.R4 back link is at top of feature requests page 176ms
F25.R2 /features redirects to /features/ 142ms
F25.R3 features page loads with correct title 201ms
F25.R4 features page lists all features 203ms
F25.R5 features page lists requirements for each feature 177ms
F25.R6 features page has a table of contents 206ms
F25.R7 features page has navigation links 199ms
F25.R7 home link navigates to homepage 804ms
F25.R10 hash navigation highlights target for 5 seconds 6.7s
F25.R11 each requirement shows test coverage indicator 1.3s
F27.R1 shield button is visible on page 244ms
F27.R2 clicking shield sends POST /api/shield 716ms
F27.R3 shield count is displayed 1.2s
F27.R4 shield animation class is applied and removed 632ms
F27.R5 punch with shields consumes shield instead of incrementing 1.2s
F28.R1 heal button is visible on page 213ms
F28.R2 clicking heal decrements the counter 1.8s
F28.R4 heal animation class is applied and removed 637ms
F28.R5 heal cooldown prevents rapid healing 2.9s
F28.R6 cooldown timer displayed after heal click 1.3s
F27.R7 shield count cannot exceed 10 1.2s
F26.R1 no CSP violations on main page 1.2s
F26.R2 no CSP violations on test report page 1.2s
F26.R2 no CSP violations on features page 1.2s
F29.R1 user count element is visible on page 705ms
F29.R1 Current Users label is displayed 712ms
F31.R3 frontend shows toast on 429 response 1.3s
F32.R4 frontend handles server-side cooldown rejection 1.3s
F33.R4 frontend connects WebSocket on page load 2.1s
F33.R5 polling interval adapts to WebSocket status 2.2s
F35.R1 manifest link present in HTML 222ms
F35.R3 service worker registers without errors 2.2s
F35.R5 theme-color meta tag present 220ms
F35.R4 service worker caches static assets 2.2s
F46.R1 features page has back link that navigates home 814ms
F46.R1 features page back link is accessible 707ms
F46.R2 test report page has back link that navigates home 804ms
F46.R2 test report page back link is accessible 723ms
F46.R3 back link has consistent styling on features page 777ms
F46.R3 back link has consistent styling on test report page 711ms
F40.R1 character figure visible with 6 body-part zones 231ms
F40.R1 all body-part zones have aria-labels 238ms
F40.R2 clicking a body part sends POST with correct bodyPart value 729ms
F40.R2 clicking different body parts sends correct bodyPart values 1.4s
F40.R5 hit animation class applied and removed on body part 1.3s
F40.R6 hover shows stats tooltip with hit count 755ms
F40.R7 character figure is responsive on mobile 222ms
F40.R7 body-part zones are tappable on mobile viewport 725ms
F40.R5 floating +1 indicator appears on punch 1.2s
F41.R1 counter displays with larger font size 708ms
F41.R1 counter font size is larger than subtitle text 711ms
F41.R1 counter remains readable after punching 1.2s
F41.R1 counter font size is consistent across page loads 1.3s
F41.R1 counter is visually prominent on the page 695ms
F45.R3 pending request shows Edit button 785ms
F45.R3 clicking Edit shows inline edit form 842ms
F45.R3 Cancel closes edit form without changes 901ms
F45.R3 Save updates the request text 986ms
F37.R1 Request a New Feature link is visible at top of features page 676ms
F37.R1 clicking Request a New Feature opens text box 701ms
F37.R1 text box allows user to enter feature request details 716ms
F37.R2 Feature Requests tab exists alongside Features, Tests, and Releases 689ms
F37.R2 Feature Requests tab is accessible at /feature-requests/ 631ms
F37.R2 Feature Requests page displays a form to submit new requests 637ms
F37.R2 Feature Requests page displays a list of previous requests 630ms
F37.R2 User can submit a feature request and see it in the list 786ms
F37.R2 Feature Requests tab is highlighted when on /feature-requests/ 630ms
F37.R3 submitted request is visible after page reload 1.3s
F37.R3 requests are loaded from server API 608ms
F37.R4 non-implemented request has no badge 784ms
F37.R4 implemented request has green styling and version link 664ms
F48.R1 character torso uses curved organic SVG shapes 684ms
F48.R1 arms use smooth curved paths instead of simple ellipses only 696ms
F48.R2 all 6 body part hit zones are present and visible 732ms
F48.R2 body part zones remain clickable after redesign 762ms
F48.R3 character has anatomically balanced proportions 723ms
F48.R3 character proportions are consistent on mobile viewport 712ms
F50.R4 main page shows SVG character when no image uploaded 247ms
F50.R4 main page has je-image element for uploaded photo 218ms
F50.R5 JE panel page loads with login form 134ms
F50.R5 JE login with correct credentials shows upload view 195ms
F50.R3 GET /api/je/image returns 404 when no image exists 313ms
F50.R1 GET /api/je/status returns authenticated false by default 5ms
F42.R1 legs SVG element exists with improved styling 682ms
F42.R1 legs have proper proportions relative to body 710ms
F42.R2 legs are clickable for punch feature 756ms
F42.R2 legs maintain hit area after styling improvements 712ms
F42.R3 legs render consistently in mobile viewport 711ms
F44.R1 party hat is visible on character head 696ms
F44.R1 party hat has colorful stripes and confetti 706ms
F49.R1 sidebar element is visible on main page 686ms
F49.R1 sidebar shows "Mad at JE" reason 663ms
F49.R1 sidebar shows "Annoyed at the world" reason 688ms
F49.R1 sidebar shows "Funny" reason 696ms
F49.R1 sidebar shows "Thinking of JE" reason 692ms
F49.R1 sidebar shows "Hungry" reason 676ms
F49.R2 page fetches reasons from GET /api/reasons on load 668ms
F49.R5 sidebar has add-reason input and submit button 686ms
F49.R5 submitting a custom reason adds it to the sidebar 873ms
F49.R5 input is cleared after submission 813ms
F49.R6 clicking a reason highlights it with selected class 713ms
F49.R6 clicking another reason moves selection away from first 753ms
F49.R4 custom reason persists after page reload 1.4s
F51.R4 toggle link is visible in the reasons sidebar 685ms
F51.R4 counts panel is hidden by default 674ms
F51.R4 clicking toggle reveals the counts panel 716ms
F51.R4 clicking toggle again hides the counts panel 728ms
F51.R1 punching with a selected reason increments that reason count 811ms
F51.R2 GET /api/reason-counts returns 200 459ms
F51.R5 reasons list has overflow-y auto styling 668ms
F51.R5 reasons list has a max-height set 705ms
F29.R5 clicking user count opens modal 704ms
F29.R5 modal displays location list from /api/users 846ms
F29.R5 modal shows total user count 848ms
F29.R5 modal shows location items with badges 835ms
F29.R5 close button closes the modal 1.1s
F36.R1 version number visible on main page 359ms
F36.R1 version number visible on /test page 229ms
F36.R1 version number visible on /features page 225ms
F36.R2 version number links to /releases/ 269ms
F36.R3 /releases/ page renders release notes 159ms
F36.R4 tabbed navigation present on /features 221ms
F36.R4 tabbed navigation present on /test 226ms
F36.R4 tabbed navigation present on /releases 171ms
F36.R4 main app page does NOT have tabs 199ms
F36.R4 active tab highlighted on each page 357ms
Negative 12/12
F38.R1 admin login with wrong credentials shows error 323ms
F19.R3 navigating to a nonexistent page returns 404 131ms
F5.R2 rapid clicking does not double-increment 1.5s
F10.R1 frontend handles API failure gracefully without crashing 1.3s
F10.R1 frontend handles network failure gracefully without crashing 1.4s
F10.R2 toast shows descriptive error message on failure 1.3s
F10.R3 toast auto-dismisses after 4 seconds 5.8s
F10.R4 toast has error styling class on failure 1.3s
F11.R1 counter shows "Error" text when initial load fails 6.1s
F11.R2 error recovery UI appears when initial load fails 6.0s
F50.R5 JE login with wrong credentials shows error 272ms
F29.R5 modal shows error on network failure 845ms
All tests passed