# Debug Logging in Action: How to Diagnose Test Failures This document explains how the new comprehensive debugging infrastructure helps diagnose the E2E test failures with concrete examples. ## What Changed: Before vs. After ### BEFORE: Generic Failure Output ``` βœ— [chromium] β€Ί tests/settings/account-settings.spec.ts β€Ί should validate certificate email format Timeout 30s exceeded, waiting for expect(locator).toBeDisabled() at account-settings.spec.ts:290 ``` **Problem**: No information about: - What page was displayed when it failed - What network requests were in flight - What the actual button state was - How long the test ran before timing out --- ### AFTER: Rich Debug Logging Output #### 1. **Test Step Logging** (From enhanced global-setup.ts) ``` βœ… Global setup complete πŸ” Health Checks: βœ… Caddy admin API health (port 2019) [45ms] βœ… Emergency tier-2 server health (port 2020) [123ms] βœ… Security modules status verified [89ms] πŸ”“ Security Reset: βœ… Emergency reset via tier-2 server [134ms] βœ… Modules disabled (ACL, WAF, rate-limit, CrowdSec) ⏳ Waiting for propagation... [510ms] ``` #### 2. **Network Activity Logging** (From network.ts interceptor) ``` πŸ“‘ Network Log (automatic) ──────────────────────────────────────────────────────────── Timestamp β”‚ Method β”‚ URL β”‚ Status β”‚ Duration ──────────────────────────────────────────────────────────── 03:48:12.456 β”‚ GET β”‚ /api/auth/profile β”‚ 200 β”‚ 234ms 03:48:12.690 β”‚ GET β”‚ /api/settings β”‚ 200 β”‚ 45ms 03:48:13.001 β”‚ POST β”‚ /api/certificates β”‚ 200 β”‚ 567ms 03:48:13.568 β”‚ GET β”‚ /api/acl/lists β”‚ 200 β”‚ 89ms 03:48:13.912 β”‚ POST β”‚ /api/account/email -PEND...β”‚ 422 β”‚ 234ms ⚠️ ERROR ``` **Key Insight**: The 422 error on email update shows the API is rejecting the input, which explains why the button didn't disableβ€”the form never validated successfully. #### 3. **Locator Matching Logs** (From debug-logger.ts) ``` 🎯 Locator Actions: ──────────────────────────────────────────────────────────── [03:48:14.123] βœ… getByRole('button', {name: /save certificate/i}) matched [8ms] -> Elements found: 1 -> Action: click() [03:48:14.234] ❌ getByRole('button', {name: /save certificate/i}) NOT matched [5000ms+] -> Elements found: 0 -> Reason: Test timeout while waiting for element -> DOM Analysis: - Dialog present? YES - Form visible? NO (display: none) - Button HTML: ``` --- ### Category 3: Locator Failures **Indicator**: `getByRole('button', {name: /save/i}): multiple elements found` **Debug Output**: ``` 🚨 Strict Mode Violation: Multiple elements matched Selector: getByRole('button', {name: /save/i}) Elements found: 2 [1] βœ“ └─ Located in: Modal dialog └─ Visible: YES └─ Class: btn-primary [2] βœ— └─ Located in: Table row └─ Visible: YES └─ Class: btn-ghost Problem: Selector matches both buttons - test can't decide which to click Solution: Scope selector to dialog context page.getByRole('dialog').getByRole('button', {name: /save certificate/i}) ``` **Diagnosis**: Locator is too broad and matches multiple elements. **Fix**: ```javascript // βœ… Good: Scoped to dialog await page.getByRole('dialog').getByRole('button', {name: /save certificate/i}).click(); // βœ… Also good: Use .first() if scoping isn't possible await page.getByRole('button', {name: /save certificate/i}).first().click(); // ❌ Bad: Too broad await page.getByRole('button', {name: /save/i}).click(); ``` --- ### Category 4: Network/API Failures **Indicator**: `API returned 422` or `POST /api/endpoint failed with 500` **Debug Output**: ``` ❌ Network Error Request: POST /api/account/email Status: 422 Unprocessable Entity Duration: 234ms Request Body: { "email": "invalid@", ← Invalid format "format": "personal" } Response Body: { "code": "INVALID_EMAIL", "message": "Email must contain domain", "field": "email", "errors": [ "Invalid email format: missing @domain" ] } What Went Wrong: 1. Form submitted with invalid data 2. Backend rejected it (expected behavior) 3. Frontend didn't show error message 4. Test expected button to disable but it didn't Root Cause: Error handling code in frontend isn't updating the form state ``` **Diagnosis**: The API is working correctly, but the frontend error handling isn't working. **Fix**: ```javascript // In frontend error handler: try { const response = await fetch('/api/account/email', {body}); if (!response.ok) { const error = await response.json(); setFormErrors(error.errors); // ← Update form state with error setFormErrorVisible(true); // ← Show error message } } catch (error) { setFormError(error.message); } ``` --- ## Real-World Example: The Certificate Email Test **Test Code** (simplified): ```javascript test('should validate certificate email format', async ({page}) => { await page.goto('/account'); // Fill with invalid email await page.getByLabel('Certificate Email').fill('invalid@'); // Trigger validation await page.getByLabel('Certificate Email').blur(); // Expect button to disable await expect( page.getByRole('button', {name: /save certificate/i}) ).toBeDisabled(); // ← THIS FAILED }); ``` **Debug Output Sequence**: ``` 1️⃣ Navigate to /account βœ… Page loaded [1234ms] 2️⃣ Fill certificate email field βœ… Input found and focused [45ms] βœ… Value set to "invalid@" [23ms] 3️⃣ Trigger validation (blur) βœ… Blur event fired [8ms] πŸ“‘ API request: POST /api/account/email [payload: {email: "invalid@"}] 4️⃣ Wait for API response βœ‹ Network activity: Waiting... βœ… Response received: 422 Unprocessable Entity [234ms] └─ Error: "Email must contain @ domain" 5️⃣ Check form error state βœ… Form has errors: email = "Email must contain @ domain" βœ… Error message DOM element exists ❌ But error message has display: none (CSS) 6️⃣ Wait for button to disable ⏰ [03:48:14.000] Start waiting for button[disabled] ⏰ [03:48:14.500] Still waiting... ⏰ [03:48:15.000] Still waiting... ⏰ [03:48:19.000] Still waiting... ❌ [03:48:24.000] TIMEOUT after 10000ms Button Found: - HTML: - Attribute disabled: MISSING (not disabled!) - Aria-disabled: false - Computed CSS: pointer-events: auto (not disabled) Form State: - Validation errors: YES (email invalid) - Button should disable: YES (by test logic) - Button actually disabled: NO (bug!) πŸ” ROOT CAUSE: The form disables the button in HTML, but the CSS is hiding the error message and not calling setState to disable the button. This suggests: 1. Form validation ran on backend (API returned 422) 2. Error wasn't set in React state 3. Button didn't re-render as disabled LIKELY CODE BUG: - Error response not processed in catch/error handler - setFormErrors() not called - Button disable logic checks form.state.errors but it's empty ``` **How to Fix**: 1. Check the `Account.tsx` form submission error handler 2. Ensure API errors update form state: `setFormErrors(response.errors)` 3. Ensure button disable logic: `disabled={Object.keys(formErrors).length > 0}` 4. Verify error message shows: `{formErrors.email &&

{formErrors.email}

}` --- ## Interpreting the Report Summary After tests complete, you'll see: ``` ⏱️ Slow Tests (>5s): ──────────────────────────────────────────────────────────── 1. test name [16.25s] ← Takes 16+ seconds to run/timeout 2. test name [12.61s] ← Long test setup or many operations ... πŸ” Failure Analysis by Type: ──────────────────────────────────────────────────────────── timeout β”‚ β–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ 4/11 (36%) β”‚ Action: Add waits, increase timeouts β”‚ assertion β”‚ β–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ 3/11 (27%) β”‚ Action: Check component state logic β”‚ locator β”‚ β–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ 2/11 (18%) β”‚ Action: Make selectors more specific β”‚ other β”‚ β–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘ 2/11 (18%) β”‚ Action: Review trace for error details ``` **What this tells you**: - **36% Timeout**: Network is slow or test expectations unrealistic - **27% Assertion**: Component behavior wrong (disable logic, form state, etc.) - **18% Locator**: Selector strategy needs improvement - **18% Other**: Exceptions or edge cases (need to investigate individually) --- ## Next Steps When Tests Complete 1. **Run the tests**: Already in progress βœ… 2. **Open the report**: `npx playwright show-report` 3. **For each failure**: - Click test name - Read the assertion error - Check the console logs (our debug output) - Inspect the trace timeline - Watch the video 4. **Categorize the failure**: Timeout? Assertion? Locator? Network? 5. **Apply the appropriate fix** based on the category 6. **Re-run just that test**: `npx playwright test --grep "test name"` 7. **Validate**: Confirm test now passes The debugging infrastructure gives you everything you need to understand exactly why each test failed and what to fix.