← Back to Punch John Eric

Test Report

Punch John Eric · Mon, 03 Aug 2026 21:46:04 GMT
Commit 42c8d3e
562
Total Tests
561
Passed
1
Failed
Passed 561
Failed 1
Unit Tests 126/127
Positive 117/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 0ms
✓ F17.R2 recreates counter file when missing 1ms
✓ F3.R1 writes count to file as JSON 1ms
✓ F3.R1 overwrites existing count 2ms
✓ F3.R4 handles count of 0 1ms
✓ F27.R3 readCounter defaults shields to 0 for legacy files 237ms
✓ F27.R3 writeState persists both count and shields 188ms
✓ F28.R3 heal cannot go below 0 1ms
✓ F28.R3 heal decrements by 1 0ms
✓ F27.R5 punch with shields consumes a shield 1ms
✓ F27.R5 punch without shields increments count 1ms
✓ F27.R6 readCounter decays shields based on elapsed time 199ms
✓ F27.R6 partial intervals do not over-decay shields 180ms
✓ F27.R6 shields do not go negative from decay 184ms
✓ F27.R7 shield cap prevents exceeding 10 1ms
✓ F27.R7 shield cap allows increment below 10 0ms
✓ 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 3ms
✓ F32.R2 getHealCooldownRemaining returns remaining ms 3ms
✓ F32.R2 getHealCooldownRemaining returns 0 after expiry 0ms
✓ F34.R1 writeState uses tmp + rename pattern 176ms
✓ F34.R2 no .tmp file remains after write 184ms
✓ F34.R3 data integrity preserved through atomic write 184ms
✓ F33.R2 broadcast sends to all open clients 1ms
✓ 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 34ms
✓ F39.R1 poll script polls for approved feature requests 1ms
✓ F39.R1 poll script launches Claude Code headlessly 0ms
✓ F39.R2 script file exists and is valid JavaScript 32ms
✓ 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 0ms
✓ F39.R3 mark-implemented workflow calls webhook endpoint 0ms
✓ F39.R3 old implement-feature workflow is disabled 1ms
✓ F39.R5 poll script triggers mark-implemented workflow via gh CLI 0ms
✓ F39.R6 script file exists and is valid JavaScript 32ms
✓ 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) 0ms
✓ F39.R7 implement-feature.js has fix loop with max attempts 1ms
✓ F39.R7 implement-feature.js exits 0 only when all tests pass 1ms
✓ F39.R7 poll script builds TDD-focused prompt for Claude Code 0ms
✓ F40.R3 readCounter defaults bodyPartHits when missing from file 244ms
✓ F40.R3 readCounter preserves existing bodyPartHits 188ms
✓ F40.R3 writeState persists bodyPartHits to disk 202ms
✓ F40.R3 bodyPartHits defaults to all zeros on counter init 194ms
✓ F40.R3 contains exactly 6 entries 203ms
✓ F40.R4 punch without shields increments specific body part 202ms
✓ F40.R4 punch with shields does NOT increment bodyPartHits 200ms
✓ F40.R3 all body part counters increment independently 193ms
✓ F30.R1 test result files contain tests for all features in the registry 2ms
✓ F30.R2 every requirement has at least one passing test in the result files 1ms
✓ F30.R3 every feature in the registry has a matching entry in features.json 4ms
✓ F29.R4 returns Unknown for localhost/private IPs 251ms
✓ F29.R4 returns a location string for a known public IP 1ms
✓ F29.R4 returns users grouped by location 3ms
✓ F29.R4 sorts Unknown location last 0ms
✓ F29.R4 sorts by count descending 0ms
✓ 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 1ms
✓ F49.R4 returns defaults plus custom reasons when file exists 1ms
✓ F49.R2 default reasons appear before custom reasons 1ms
✓ F49.R4 writes custom reasons to file atomically 2ms
✓ F49.R4 no .tmp file remains after successful write 1ms
✓ F50.R1 jeSessions starts empty 278ms
✓ F50.R1 requireJe rejects requests without token 228ms
✓ F50.R1 requireJe rejects expired sessions 180ms
✓ F50.R1 requireJe allows valid sessions 290ms
✓ F50.R6 image file and metadata persist to disk 4ms
✓ F50.R6 image data survives read-write cycle 7ms
✓ F38.R1 adminSessions starts empty 281ms
✓ F38.R1 requireAdmin rejects requests without token 186ms
✓ F38.R1 requireAdmin rejects expired sessions 290ms
✓ F38.R1 requireAdmin allows valid sessions 235ms
✓ F38.R3 readCounter defaults disabledActions when missing 245ms
✓ F38.R3 writeState persists disabledActions 207ms
✓ F36.R5 version.json has valid semver format 15ms
✓ 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 21ms
✓ 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 7ms
✓ 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 0ms
✓ F20.R7 generator adds feature-based IDs to feature elements 0ms
✓ F20.R7 generator includes highlight CSS animation 1ms
✓ F20.R7 generator includes hash navigation JavaScript 1ms
✓ F25.R9 generator script exits with code 0 49ms
✓ F25.R9 generator produces an HTML file 3ms
✓ F25.R4 generated HTML contains all features from features.json 16ms
✓ F25.R5 generated HTML contains all requirements 20ms
✓ F25.R6 generated HTML contains the table of contents 6ms
✓ F51.R3 returns empty object when file does not exist 3ms
✓ F51.R3 returns counts from file when it exists 1ms
✓ 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 3ms
Error: expect(received).toBeDefined()

Received: undefined
    at Object.toBeDefined (/home/runner/work/punchjohneric/punchjohneric/tests/unit/infrastructure.test.js:44:23)
    at Promise.finally.completed (/home/runner/work/punchjohneric/punchjohneric/node_modules/jest-circus/build/jestAdapterInit.js:1557:28)
    at new Promise (<anonymous>)
    at callAsyncCircusFn (/home/runner/work/punchjohneric/punchjohneric/node_modules/jest-circus/build/jestAdapterInit.js:1497:10)
    at _callCircusTest (
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 0ms
✓ 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 1ms
✓ F29.R4 returns Unknown for null/undefined input 1ms
✓ F49.R4 returns only defaults when file has invalid JSON 2ms
✓ F51.R3 returns empty object for invalid JSON 1ms
Integration Tests 149/149
Positive 102/102
✓ F14.R1 returns 200 with HTML content 470ms
✓ F15.R1 returns 200 with current count 259ms
✓ F15.R4 reflects count after increments 227ms
✓ F16.R3 increments counter by 1 and returns new count 194ms
✓ F16.R3 increments correctly when called multiple times 192ms
✓ F16.R4 persists increment to disk 179ms
✓ F18.R1 API responses include CORS headers on GET 187ms
✓ F21.R1 /test redirects to /test/ with 301 176ms
✓ F20.R1 /test/ returns 200 with HTML content 172ms
✓ F14.R2 HTML contains required elements and title 173ms
✓ F18.R2 POST /api/increment includes CORS headers 182ms
✓ F18.R3 OPTIONS /api/counter returns CORS headers 167ms
✓ F25.R2 /features redirects to /features/ with 301 189ms
✓ F25.R1 /features/ returns 200 with HTML content 207ms
✓ F26.R1 GET / includes security headers 185ms
✓ F26.R1 GET /api/counter includes security headers 209ms
✓ F26.R1 POST /api/increment includes security headers 222ms
✓ F27.R2 returns 200 with shields incremented 181ms
✓ F27.R2 shields accumulate on multiple calls 191ms
✓ F27.R5 consumes a shield instead of incrementing count 184ms
✓ F27.R5 increments count when no shields remain 184ms
✓ F28.R2 decrements counter by 1 184ms
✓ F28.R3 counter stays at 0 when already 0 180ms
✓ F27.R3 returns both count and shields 187ms
✓ F27.R7 shields cannot exceed 10 182ms
✓ F27.R6 GET /api/counter reflects decayed shields 175ms
✓ F29.R2 GET /api/counter includes users field 181ms
✓ F31.R2 returns 429 after exceeding rate limit 358ms
✓ F31.R1 rate limiter applies to all /api/* routes 358ms
✓ F32.R3 first heal succeeds 185ms
✓ F32.R3 immediate second heal returns 429 182ms
✓ F32.R2 429 response includes retryAfter 186ms
✓ F34.R3 counter persists through rapid successive writes 204ms
✓ F34.R2 no tmp file after increment 174ms
✓ F35.R1 GET /manifest.json returns 200 177ms
✓ F33.R3 client receives initial state on connect 181ms
✓ F33.R2 broadcast sent after increment 282ms
✓ F38.R1 POST /api/admin/login succeeds with correct credentials 248ms
✓ F38.R1 POST /api/admin/logout clears session 211ms
✓ F38.R1 GET /api/admin/status returns authenticated true when logged in 211ms
✓ F38.R1 GET /api/admin/status returns authenticated false when not logged in 189ms
✓ F38.R2 POST /api/admin/reset-counter resets count to 0 203ms
✓ F38.R2 POST /api/admin/set-counter sets count to arbitrary value 213ms
✓ F38.R3 POST /api/admin/toggle-action disables punch 209ms
✓ F38.R3 disabled punch returns 403 from POST /api/increment 213ms
✓ F38.R3 disabled shield returns 403 from POST /api/shield 220ms
✓ F38.R3 disabled heal returns 403 from POST /api/heal 221ms
✓ F38.R3 re-enabling action allows it again 213ms
✓ F38.R4 POST /api/admin/approve-request sets status to approved 215ms
✓ F38.R4 POST /api/admin/deny-request sets status to denied 207ms
✓ F38.R3 GET /api/counter includes disabledActions 202ms
✓ F50.R1 POST /api/je/login succeeds with correct credentials 254ms
✓ F50.R1 POST /api/je/logout clears session 217ms
✓ F50.R1 GET /api/je/status returns authenticated true when logged in 220ms
✓ F50.R1 GET /api/je/status returns authenticated false when not logged in 210ms
✓ F50.R2 POST /api/je/upload-image saves image when authenticated 219ms
✓ F50.R3 GET /api/je/image returns uploaded image 213ms
✓ F39.R5 marks feature request as implemented with version 262ms
✓ F39.R5 skips manual dispatch IDs 232ms
✓ F39.R5 is idempotent for already-implemented requests 213ms
✓ F40.R2 POST with bodyPart=head increments head counter 269ms
✓ F40.R2 POST with bodyPart=keister increments keister counter 225ms
✓ F40.R2 POST without bodyPart defaults to torso 226ms
✓ F40.R9 GET /api/counter includes bodyPartHits in response 236ms
✓ F40.R9 bodyPartHits values accumulate across punches 244ms
✓ F40.R4 POST with shields does not increment bodyPartHits 249ms
✓ F40.R3 bodyPartHits persisted to disk after increment 223ms
✓ F40.R8 GET after increment reflects bodyPartHits from broadcast 230ms
✓ F49.R2 returns 200 with application/json 277ms
✓ F49.R2 returns array with 5 default reasons when no custom reasons exist 224ms
✓ F49.R4 returns defaults plus custom reasons 200ms
✓ F49.R3 creates a new custom reason and returns 201 249ms
✓ F49.R4 persists custom reason to reasons.json 234ms
✓ F49.R4 new reason appears in subsequent GET /api/reasons 233ms
✓ F49.R4 multiple custom reasons accumulate 249ms
✓ F51.R2 returns 200 with application/json 285ms
✓ F51.R2 returns empty object when no counts exist 244ms
✓ F51.R2 returns counts after punches with reasons 262ms
✓ F51.R1 tracks reason count when reason field is provided 243ms
✓ F51.R1 increments existing reason count 249ms
✓ F51.R1 does not track count when no reason provided 248ms
✓ F51.R1 still increments punch counter normally 254ms
✓ F51.R3 reason counts persist to reason-counts.json 245ms
✓ F37.R3 GET /api/feature-requests returns empty array when no requests exist 296ms
✓ F37.R3 POST /api/feature-requests creates a new request 258ms
✓ F37.R3 GET /api/feature-requests returns previously submitted requests 244ms
✓ F37.R3 requests persist to feature-requests.json file 246ms
✓ F37.R4 implemented request has implementedVersion field 253ms
✓ F45.R1 updates text of a pending request 309ms
✓ F45.R1 trims whitespace from text 262ms
✓ F29.R4 returns 200 with users and total 308ms
✓ F29.R4 tracks requesting user (total >= 1 after request) 261ms
✓ F29.R4 each user entry has location and count 263ms
✓ F29.R4 returns JSON content-type 269ms
✓ F29.R4 groups users by location correctly 318ms
✓ F39.R1 approval sets status to approved 344ms
✓ F39.R1 approved request is picked up by local polling 216ms
✓ F48.R1 HTML contains character SVG with curved path elements in torso 338ms
✓ F48.R2 HTML contains all 6 body-part zones with data-part attributes 335ms
✓ F48.R1 character SVG uses organic shapes in arm body parts 342ms
✓ F36.R5 GET /version.json returns 200 with valid version object 31ms
✓ F36.R5 version.json version matches package.json 5ms
Negative 47/47
✓ F22.R1 invalid JSON body returns 400 178ms
✓ F22.R2 non-JSON content-type still increments 196ms
✓ F19.R3 GET /api/nonexistent returns 404 178ms
✓ F19.R3 GET /nonexistent returns 404 174ms
✓ F19.R3 GET /nonexistent.css returns 404 171ms
✓ F15.R1 POST /api/counter returns 404 173ms
✓ F16.R1 GET /api/increment returns 404 171ms
✓ F15.R1 PUT /api/counter returns 404 178ms
✓ F16.R1 DELETE /api/increment returns 404 173ms
✓ F20.R6 /test/ returns 404 when report has not been generated 174ms
✓ F25.R8 /features/ returns 404 when page has not been generated 237ms
✓ F38.R1 POST /api/admin/login rejects wrong password 214ms
✓ F38.R1 POST /api/admin/login rejects wrong username 201ms
✓ F38.R1 POST /api/admin/login rejects missing fields 206ms
✓ F38.R2 POST /api/admin/set-counter rejects negative values 206ms
✓ F38.R2 POST /api/admin/reset-counter requires auth 192ms
✓ F38.R3 POST /api/admin/toggle-action rejects invalid action 214ms
✓ F38.R3 POST /api/admin/toggle-action requires auth 208ms
✓ F38.R4 POST /api/admin/approve-request returns 404 for unknown id 205ms
✓ F38.R4 POST /api/admin/approve-request requires auth 201ms
✓ F50.R1 POST /api/je/login rejects wrong password 211ms
✓ F50.R1 POST /api/je/login rejects wrong username 228ms
✓ F50.R1 POST /api/je/login rejects missing fields 209ms
✓ F50.R2 POST /api/je/upload-image requires auth 214ms
✓ F50.R2 POST /api/je/upload-image rejects invalid data URL 215ms
✓ F50.R2 POST /api/je/upload-image rejects missing image field 211ms
✓ F50.R3 GET /api/je/image returns 404 when no image uploaded 206ms
✓ F39.R5 rejects missing Authorization header 210ms
✓ F39.R5 rejects wrong webhook secret 214ms
✓ F39.R5 returns 503 when WEBHOOK_SECRET not configured 217ms
✓ F39.R5 rejects missing requestId 223ms
✓ F39.R5 rejects missing version 218ms
✓ F39.R5 returns 404 for unknown requestId 217ms
✓ F40.R2 POST with invalid bodyPart defaults to torso 232ms
✓ F49.R3 rejects empty text with 400 240ms
✓ F49.R3 rejects missing text field with 400 241ms
✓ F49.R3 rejects whitespace-only text with 400 235ms
✓ F51.R1 ignores invalid reason ids 251ms
✓ F37.R3 POST /api/feature-requests rejects empty text 263ms
✓ F37.R3 POST /api/feature-requests rejects missing text 253ms
✓ F45.R2 returns 403 for approved request 267ms
✓ F45.R2 returns 403 for denied request 267ms
✓ F45.R1 returns 404 for unknown request 264ms
✓ F45.R1 returns 400 for empty text 219ms
✓ F45.R1 returns 400 for text over 500 chars 311ms
✓ F29.R4 response does not expose IP addresses 262ms
✓ F29.R4 POST /api/users returns 404 265ms
Contract Tests 99/99
Positive 78/78
✓ F15.R1 responds with application/json content-type 245ms
✓ F15.R2 response body has count, shields, users, disabledActions, and bodyPartHits keys 187ms
✓ F15.R2 count is a number 183ms
✓ F15.R3 count is a non-negative integer 200ms
✓ F16.R1 responds with application/json content-type 187ms
✓ F16.R2 response body has count, shields, and bodyPartHits keys 185ms
✓ F16.R2 count is a number 190ms
✓ F16.R3 count is a positive integer after increment 190ms
✓ F15.R1 accepts a plain GET with no body or special headers 183ms
✓ F15.R1 accepts GET with Accept: application/json header 202ms
✓ F16.R1 accepts POST with Content-Type: application/json and no body 194ms
✓ F16.R1 accepts POST with no Content-Type header 191ms
✓ F16.R5 accepts POST with an empty JSON body 189ms
✓ F16.R5 ignores unexpected fields in the body 195ms
✓ F25.R1 /features/ responds with text/html content-type 187ms
✓ F25.R2 /features redirect includes Location header 184ms
✓ F26.R2 Content-Security-Policy includes default-src self 200ms
✓ F26.R3 X-Content-Type-Options is nosniff 182ms
✓ F26.R4 X-Frame-Options prevents framing 180ms
✓ F26.R5 Strict-Transport-Security has max-age 185ms
✓ F26.R1 security headers present on API routes too 184ms
✓ F27.R3 response body has count and shields keys 190ms
✓ F27.R2 responds with application/json 187ms
✓ F27.R2 response has count and shields keys 192ms
✓ F28.R2 responds with application/json 186ms
✓ F28.R2 response has count and shields keys 186ms
✓ F28.R3 count is non-negative after heal 193ms
✓ F29.R2 response body has users key as a number 192ms
✓ F31.R2 429 response includes error message 371ms
✓ F32.R2 429 heal response includes retryAfter as number 197ms
✓ F35.R2 manifest has required PWA fields 181ms
✓ F38.R1 login response has { success: true } 268ms
✓ F38.R1 status response has { authenticated: boolean } 213ms
✓ F38.R2 reset-counter response has { count, shields } 223ms
✓ F38.R2 set-counter response has { count, shields } 227ms
✓ F38.R3 toggle-action response has { disabledActions } 228ms
✓ F38.R4 approve-request response has status and approvedAt 226ms
✓ F38.R4 deny-request response has status and deniedAt 239ms
✓ F38.R3 disabled action 403 response has { error } 234ms
✓ F49.R2 returns an array 281ms
✓ F49.R2 each reason has id, text, and isDefault fields 238ms
✓ F49.R2 reason id is a string 237ms
✓ F49.R2 reason text is a string 237ms
✓ F49.R2 reason isDefault is a boolean 240ms
✓ F49.R2 default reasons have isDefault=true 194ms
✓ F49.R3 created reason has id, text, and isDefault fields 272ms
✓ F49.R3 created reason has isDefault=false 239ms
✓ F49.R3 created reason text matches submitted text 242ms
✓ F49.R3 accepts JSON body with text field 244ms
✓ F50.R1 login response has { success: true } 303ms
✓ F50.R1 status response has { authenticated: boolean } 241ms
✓ F50.R2 upload response has { success, ext, size, uploadedAt } 258ms
✓ F50.R3 image endpoint returns correct content-type 257ms
✓ F39.R5 success response has { updated, requestId, version } 321ms
✓ F39.R5 skipped response has { skipped, reason } 274ms
✓ F39.R5 already-implemented response has { updated, alreadyImplemented } 272ms
✓ F29.R4 responds with application/json content-type 319ms
✓ F29.R4 response body has users array and total number 272ms
✓ F29.R4 each user entry has location (string) and count (positive number) 270ms
✓ F29.R4 total equals sum of all user counts 275ms
✓ F29.R4 response includes security headers 215ms
✓ F29.R4 accepts plain GET with no body 198ms
✓ F39.R1 approve response has correct shape 321ms
✓ F39.R3 mark-implemented workflow YAML has required sections 255ms
✓ F39.R3 mark-implemented workflow uses WEBHOOK_SECRET 264ms
✓ F40.R9 response has bodyPartHits object with 6 keys 335ms
✓ F40.R9 all bodyPartHits values are non-negative integers 282ms
✓ F40.R9 bodyPartHits keys match VALID_BODY_PARTS 255ms
✓ F40.R2 POST response includes bodyPartHits 329ms
✓ F40.R2 POST accepts bodyPart field without error 308ms
✓ F51.R2 returns an object 336ms
✓ F51.R2 values are non-negative integers 302ms
✓ F51.R2 keys are reason id strings 302ms
✓ F51.R1 accepts JSON body with reason field alongside bodyPart 235ms
✓ F45.R1 success returns full request object 344ms
✓ F36.R5 /version.json response shape: { version: string } 17ms
✓ F36.R6 releases.json entries have version, date, title, features 3ms
✓ 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) 190ms
✓ F22.R4 404 responses do not include a count property 187ms
✓ F15.R1 POST /api/counter is not allowed 184ms
✓ F16.R1 GET /api/increment is not allowed 187ms
✓ F16.R1 PUT /api/increment is not allowed 195ms
✓ F15.R1 DELETE /api/counter is not allowed 186ms
✓ F22.R1 POST /api/increment with broken JSON returns 400 211ms
✓ F22.R2 POST /api/increment with text/plain body still succeeds 191ms
✓ F22.R3 POST /api/increment with extremely large JSON body still succeeds 191ms
✓ F31.R2 429 response does not contain count property 377ms
✓ F49.R3 error response has error field 239ms
✓ F50.R3 image 404 response has { error } string 249ms
✓ F50.R2 upload error response has { error } string 254ms
✓ F50.R1 login error response has { error } string 246ms
✓ F39.R5 401 response has { error } 280ms
✓ F39.R5 400 response has { error } 269ms
✓ F29.R4 response does not contain ip or address fields 272ms
✓ F29.R4 POST /api/users is not allowed 261ms
✓ F45.R2 403 returns { error } with status info 222ms
✓ F45.R1 400 returns { error } 387ms
✓ F45.R1 404 returns { error } 310ms
Functional Tests 187/187
Positive 175/175
✓ F43.R1 abs lines are visible on the torso 896ms
✓ F43.R1 abs lines have subtle opacity 784ms
✓ F38.R1 admin page loads with login form 205ms
✓ F38.R1 admin login with correct credentials shows admin view 334ms
✓ F38.R2 admin panel shows counter management controls 298ms
✓ F38.R3 admin panel shows action toggle switches 381ms
✓ F38.R1 logout returns to login view 424ms
✓ F38.R5 admin tab is hidden when not authenticated 772ms
✓ F38.R5 admin tab is visible on admin page itself 202ms
✓ F14.R2 page loads with correct title 329ms
✓ F1.R1 counter element is visible on page load 282ms
✓ F1.R2 counter display shows a number on load 303ms
✓ F2.R1 clicking the character sends POST /api/increment 793ms
✓ F2.R2 clicking the character increments the counter 359ms
✓ F2.R3 clicking multiple times increments correctly 2.5s
✓ F8.R1 punch animation class is applied and removed 733ms
✓ F8.R2 .punching class removed after animation completes 892ms
✓ F9.R1 counter bump animation fires on increment 367ms
✓ F14.R1 no console errors on page load 1.3s
✓ F19.R1 static assets load successfully 769ms
✓ F5.R1 isUpdating lock prevents concurrent POST requests 1.7s
✓ F1.R3 counter value matches API response on load 337ms
✓ F4.R1 page polls GET /api/counter periodically 11.7s
✓ F4.R2 polling updates display when server value changes externally 6.6s
✓ F6.R1 number formatting shows thousands separators 834ms
✓ F7.R1 character SVG is visible with nonzero dimensions 349ms
✓ F12.R1 responsive layout adapts at mobile viewport 310ms
✓ F12.R2 character figure is smaller on mobile than desktop 667ms
✓ F7.R2 SVG has role="img" and aria-label for character 309ms
✓ F13.R1 punch button has aria-label 318ms
✓ F13.R2 SVG has role="img" and aria-label 321ms
✓ F13.R3 toast container has aria-live="polite" 297ms
✓ F13.R4 body-part zone has visible focus ring when focused via keyboard 299ms
✓ F13.R5 punch button is keyboard-activatable 816ms
✓ F19.R2 favicon link element exists 324ms
✓ F21.R1 /test redirects to /test/ 203ms
✓ F20.R2 report page loads with correct title 258ms
✓ F20.R3 report page shows summary cards 296ms
✓ F20.R4 report page shows test layers 355ms
✓ F20.R5 report page has back link to main site 271ms
✓ F20.R1 no console errors on report page 727ms
✓ F20.R5 back link navigates to homepage 1.0s
✓ F20.R7 hash navigation highlights target for 5 seconds 6.8s
✓ F47.R1 By Release tab is visible on test report page 327ms
✓ F47.R2 clicking By Release tab shows release-grouped tests 375ms
✓ F47.R3 releases shown in reverse chronological order 413ms
✓ F47.R4 back link is at top of test report page 363ms
✓ F47.R4 back link is at top of features page 340ms
✓ F47.R4 back link is at top of releases page 283ms
✓ F47.R4 back link is at top of feature requests page 228ms
✓ F25.R2 /features redirects to /features/ 198ms
✓ F25.R3 features page loads with correct title 299ms
✓ F25.R4 features page lists all features 308ms
✓ F25.R5 features page lists requirements for each feature 304ms
✓ F25.R6 features page has a table of contents 312ms
✓ F25.R7 features page has navigation links 306ms
✓ F25.R7 home link navigates to homepage 921ms
✓ F25.R10 hash navigation highlights target for 5 seconds 6.8s
✓ F25.R11 each requirement shows test coverage indicator 1.4s
✓ F27.R1 shield button is visible on page 302ms
✓ F27.R2 clicking shield sends POST /api/shield 745ms
✓ F27.R3 shield count is displayed 1.3s
✓ F27.R4 shield animation class is applied and removed 754ms
✓ F27.R5 punch with shields consumes shield instead of incrementing 1.3s
✓ F28.R1 heal button is visible on page 358ms
✓ F28.R2 clicking heal decrements the counter 1.9s
✓ F28.R4 heal animation class is applied and removed 752ms
✓ F28.R5 heal cooldown prevents rapid healing 3.0s
✓ F28.R6 cooldown timer displayed after heal click 1.4s
✓ F27.R7 shield count cannot exceed 10 1.3s
✓ F26.R1 no CSP violations on main page 1.3s
✓ F26.R2 no CSP violations on test report page 1.3s
✓ F26.R2 no CSP violations on features page 1.2s
✓ F29.R1 user count element is visible on page 726ms
✓ F29.R1 Current Users label is displayed 752ms
✓ F31.R3 frontend shows toast on 429 response 1.4s
✓ F32.R4 frontend handles server-side cooldown rejection 1.4s
✓ F33.R4 frontend connects WebSocket on page load 2.2s
✓ F33.R5 polling interval adapts to WebSocket status 2.2s
✓ F35.R1 manifest link present in HTML 316ms
✓ F35.R3 service worker registers without errors 2.2s
✓ F35.R5 theme-color meta tag present 281ms
✓ F35.R4 service worker caches static assets 2.2s
✓ F46.R1 features page has back link that navigates home 867ms
✓ F46.R1 features page back link is accessible 793ms
✓ F46.R2 test report page has back link that navigates home 903ms
✓ F46.R2 test report page back link is accessible 766ms
✓ F46.R3 back link has consistent styling on features page 789ms
✓ F46.R3 back link has consistent styling on test report page 811ms
✓ F40.R1 character figure visible with 6 body-part zones 316ms
✓ F40.R1 all body-part zones have aria-labels 313ms
✓ F40.R2 clicking a body part sends POST with correct bodyPart value 742ms
✓ 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 823ms
✓ F40.R7 character figure is responsive on mobile 310ms
✓ F40.R7 body-part zones are tappable on mobile viewport 756ms
✓ F40.R5 floating +1 indicator appears on punch 1.3s
✓ F41.R1 counter displays with larger font size 780ms
✓ F41.R1 counter font size is larger than subtitle text 807ms
✓ F41.R1 counter remains readable after punching 1.3s
✓ F41.R1 counter font size is consistent across page loads 1.3s
✓ F41.R1 counter is visually prominent on the page 762ms
✓ F45.R3 pending request shows Edit button 852ms
✓ F45.R3 clicking Edit shows inline edit form 907ms
✓ F45.R3 Cancel closes edit form without changes 931ms
✓ F45.R3 Save updates the request text 1.0s
✓ F37.R1 Request a New Feature link is visible at top of features page 784ms
✓ F37.R1 clicking Request a New Feature opens text box 821ms
✓ F37.R1 text box allows user to enter feature request details 836ms
✓ F37.R2 Feature Requests tab exists alongside Features, Tests, and Releases 830ms
✓ F37.R2 Feature Requests tab is accessible at /feature-requests/ 682ms
✓ F37.R2 Feature Requests page displays a form to submit new requests 684ms
✓ F37.R2 Feature Requests page displays a list of previous requests 684ms
✓ F37.R2 User can submit a feature request and see it in the list 839ms
✓ F37.R2 Feature Requests tab is highlighted when on /feature-requests/ 691ms
✓ F37.R3 submitted request is visible after page reload 1.4s
✓ F37.R3 requests are loaded from server API 645ms
✓ F37.R4 non-implemented request has no badge 855ms
✓ F37.R4 implemented request has green styling and version link 705ms
✓ F48.R1 character torso uses curved organic SVG shapes 758ms
✓ F48.R1 arms use smooth curved paths instead of simple ellipses only 752ms
✓ F48.R2 all 6 body part hit zones are present and visible 785ms
✓ F48.R2 body part zones remain clickable after redesign 892ms
✓ F48.R3 character has anatomically balanced proportions 828ms
✓ F48.R3 character proportions are consistent on mobile viewport 844ms
✓ F50.R4 main page shows SVG character when no image uploaded 304ms
✓ F50.R4 main page has je-image element for uploaded photo 334ms
✓ F50.R5 JE panel page loads with login form 206ms
✓ F50.R5 JE login with correct credentials shows upload view 288ms
✓ F50.R3 GET /api/je/image returns 404 when no image exists 316ms
✓ F50.R1 GET /api/je/status returns authenticated false by default 6ms
✓ F42.R1 legs SVG element exists with improved styling 787ms
✓ F42.R1 legs have proper proportions relative to body 777ms
✓ F42.R2 legs are clickable for punch feature 750ms
✓ F42.R2 legs maintain hit area after styling improvements 787ms
✓ F42.R3 legs render consistently in mobile viewport 736ms
✓ F44.R1 party hat is visible on character head 764ms
✓ F44.R1 party hat has colorful stripes and confetti 762ms
✓ F49.R1 sidebar element is visible on main page 738ms
✓ F49.R1 sidebar shows "Mad at JE" reason 745ms
✓ F49.R1 sidebar shows "Annoyed at the world" reason 761ms
✓ F49.R1 sidebar shows "Funny" reason 749ms
✓ F49.R1 sidebar shows "Thinking of JE" reason 770ms
✓ F49.R1 sidebar shows "Hungry" reason 715ms
✓ F49.R2 page fetches reasons from GET /api/reasons on load 718ms
✓ F49.R5 sidebar has add-reason input and submit button 776ms
✓ F49.R5 submitting a custom reason adds it to the sidebar 855ms
✓ F49.R5 input is cleared after submission 841ms
✓ F49.R6 clicking a reason highlights it with selected class 783ms
✓ F49.R6 clicking another reason moves selection away from first 881ms
✓ F49.R4 custom reason persists after page reload 1.5s
✓ F51.R4 toggle link is visible in the reasons sidebar 741ms
✓ F51.R4 counts panel is hidden by default 749ms
✓ F51.R4 clicking toggle reveals the counts panel 807ms
✓ F51.R4 clicking toggle again hides the counts panel 813ms
✓ F51.R1 punching with a selected reason increments that reason count 886ms
✓ F51.R2 GET /api/reason-counts returns 200 521ms
✓ F51.R5 reasons list has overflow-y auto styling 789ms
✓ F51.R5 reasons list has a max-height set 783ms
✓ F29.R5 clicking user count opens modal 787ms
✓ F29.R5 modal displays location list from /api/users 922ms
✓ F29.R5 modal shows total user count 979ms
✓ F29.R5 modal shows location items with badges 907ms
✓ F29.R5 close button closes the modal 1.2s
✓ F36.R1 version number visible on main page 391ms
✓ F36.R1 version number visible on /test page 306ms
✓ F36.R1 version number visible on /features page 318ms
✓ F36.R2 version number links to /releases/ 385ms
✓ F36.R3 /releases/ page renders release notes 240ms
✓ F36.R4 tabbed navigation present on /features 273ms
✓ F36.R4 tabbed navigation present on /test 359ms
✓ F36.R4 tabbed navigation present on /releases 250ms
✓ F36.R4 main app page does NOT have tabs 286ms
✓ F36.R4 active tab highlighted on each page 485ms
Negative 12/12
✓ F38.R1 admin login with wrong credentials shows error 386ms
✓ F19.R3 navigating to a nonexistent page returns 404 139ms
✓ F5.R2 rapid clicking does not double-increment 1.5s
✓ F10.R1 frontend handles API failure gracefully without crashing 1.4s
✓ F10.R1 frontend handles network failure gracefully without crashing 1.4s
✓ F10.R2 toast shows descriptive error message on failure 1.4s
✓ F10.R3 toast auto-dismisses after 4 seconds 5.9s
✓ F10.R4 toast has error styling class on failure 1.5s
✓ F11.R1 counter shows "Error" text when initial load fails 1.3s
✓ F11.R2 error recovery UI appears when initial load fails 6.2s
✓ F50.R5 JE login with wrong credentials shows error 262ms
✓ F29.R5 modal shows error on network failure 781ms
1 test(s) failed