← 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