Loading...
Loading...
Resilience review and testing: evaluate error handling, graceful degradation, API contract compliance, edge cases, and failure recovery with browser-based fault injection and validation.
npx skill4agent add shiplightai/agent-skills resilience-review/resilience-reviewPhase 1: EDUCATE → Why resilience matters and what we test
Phase 2: SCOPE → Map failure points, dependencies, critical flows
Phase 3: ANALYZE → Browser-based fault injection and edge case testing
Phase 4: REPORT → Findings with evidence and user impact assessment
Phase 5: REMEDIATE → Fix guidance + YAML regression testsWhy this matters: Users don't experience your app in ideal conditions. 53% of mobile visits are abandoned if a page takes >3 seconds. Error pages with no guidance increase support tickets 5x. A blank screen is the worst possible failure mode — it tells the user nothing and offers no recovery path. Resilient apps maintain trust even when backend systems fail.
new_sessionrecord_evidence: true| Check ID | Check | Standard | Method |
|---|---|---|---|
| ERR-01 | API errors show user-friendly message (not blank screen) | UX best practice | Mock API to return 500, check UI response |
| ERR-02 | Network timeout shows appropriate state | UX best practice | Mock network delay (30s), check UI |
| ERR-03 | 404 page exists and is helpful | UX best practice | Navigate to non-existent route |
| ERR-04 | JavaScript errors don't crash the page | Error boundaries | Inject JS error, check if page recovers |
| ERR-05 | Error messages are actionable | NN/g heuristics | Check error messages for: what happened, why, what to do |
| ERR-06 | Errors don't expose technical details | OWASP | Check error messages for stack traces, SQL, internal paths |
| ERR-07 | Form validation errors are clear and positioned | UX best practice | Submit invalid forms, check error placement and text |
| ERR-08 | Error states allow retry without page refresh | UX best practice | After error, check for retry button or recovery action |
| ERR-09 | Concurrent error handling (multiple simultaneous failures) | Resilience | Mock multiple API failures, check UI doesn't cascade |
| ERR-10 | Error logging doesn't expose PII | OWASP / Privacy | Check |
CODEpage.route()get_browser_console_logs// Example: Mock API 500 error
await page.route('**/api/**', route => {
route.fulfill({ status: 500, body: JSON.stringify({ error: 'Internal Server Error' }) });
});| Check ID | Check | Standard | Method |
|---|---|---|---|
| DEG-01 | Page works with JavaScript disabled (basic content) | Progressive enhancement | Disable JS, check if content is accessible |
| DEG-02 | Page works on slow connection (3G simulation) | Performance | Throttle to Slow 3G, check load behavior |
| DEG-03 | Non-critical features degrade without breaking critical ones | Graceful degradation | Disable third-party scripts, check core functionality |
| DEG-04 | Offline state is handled (if applicable) | PWA best practice | Go offline, check UI state and messaging |
| DEG-05 | Third-party service failure doesn't block page load | Resilience | Block third-party domains, check page loads |
| DEG-06 | Image loading failure shows fallback | UX best practice | Block image URLs, check for alt text/placeholder |
| DEG-07 | Font loading failure doesn't hide text | FOUT handling | Block font URLs, check text remains visible |
| DEG-08 | Feature detection over browser sniffing | Progressive enhancement | Check code for |
page.route()| Check ID | Check | Standard | Method |
|---|---|---|---|
| EDGE-01 | Empty data state shows helpful message | UX best practice | Navigate to pages with no data, check display |
| EDGE-02 | Pagination handles zero results | UX best practice | Search for nonexistent term, check pagination |
| EDGE-03 | Long text doesn't break layout | Defensive CSS | Enter very long strings (500+ chars), check overflow |
| EDGE-04 | Special characters in input don't break UI | Input handling | Enter |
| EDGE-05 | Large data sets don't freeze UI | Performance | Load pages with maximum data, check responsiveness |
| EDGE-06 | Rapid user actions don't cause duplicate submissions | State management | Double-click submit buttons, rapid nav |
| EDGE-07 | Back/forward navigation maintains state | History management | Fill form, navigate away, come back |
| EDGE-08 | Refresh preserves expected state | State persistence | Refresh during multi-step flow, check state |
| EDGE-09 | Concurrent tab/session behavior | Session management | Open same page in two tabs, perform actions |
| EDGE-10 | Maximum file upload size handled | Input validation | Upload oversized file, check error message |
act| Check ID | Check | Standard | Method |
|---|---|---|---|
| API-01 | UI handles all HTTP error codes gracefully | API contract | Mock 400, 401, 403, 404, 422, 429, 500, 503 |
| API-02 | UI handles null/undefined fields without crashing | Defensive coding | Mock API response with null fields |
| API-03 | UI handles empty arrays/objects | Defensive coding | Mock API response with empty collections |
| API-04 | UI handles unexpected data types | Defensive coding | Mock API response with wrong types |
| API-05 | Loading states shown during API calls | UX best practice | Add 2s delay to API, verify loading indicator |
| API-06 | Race conditions handled (stale responses) | State management | Trigger rapid sequential requests, verify latest wins |
| API-07 | Rate limiting (429) handled with user feedback | API contract | Mock 429 response, check UI feedback |
| API-08 | Authentication expiry handled mid-session | Session management | Mock 401 during session, check redirect to login |
page.route()| Check ID | Check | Standard | Method |
|---|---|---|---|
| REC-01 | Retry mechanisms exist for transient failures | Resilience | Mock intermittent failure, check auto-retry |
| REC-02 | User can manually retry after failure | UX best practice | After error, verify retry action available |
| REC-03 | Progress is not lost on errors | UX best practice | Fill long form, trigger error, check data persists |
| REC-04 | User is informed of degraded functionality | Communication | When features fail, check for degradation notice |
| REC-05 | Recovery actions are clear and accessible | NN/g heuristics | After each error type, evaluate recovery UX |
| REC-06 | Status indicators for background operations | UX best practice | Start async operation, verify progress feedback |
shiplight/reports/resilience-review-{date}.md# Resilience Review Report
**Date:** {date}
**URL:** {url}
**Critical flows tested:** {list}
**API dependencies tested:** {count}
**Failure scenarios simulated:** {count}
## Overall Score: {X}/10 | Confidence: {X}%
## Score Breakdown
| Category | Score | Findings |
|----------|-------|----------|
| Error Handling (ERR) | 5/10 | 2 critical, 1 high |
| Graceful Degradation (DEG) | 6/10 | 1 high, 2 medium |
| Empty & Edge States (EDGE) | 4/10 | 1 critical, 3 high |
| API Contract (API) | 7/10 | 1 high, 1 medium |
| Recovery (REC) | 3/10 | 2 high, 1 medium |
## Failure Matrix
| Failure Scenario | Expected Behavior | Actual Behavior | Status |
|-----------------|-------------------|-----------------|--------|
| API returns 500 | Error message + retry | Blank screen | FAIL |
| Network timeout | Loading → timeout message | Infinite spinner | FAIL |
| Empty data set | "No results" message | Blank page | FAIL |
| ... | | | |
## Findings
(structured findings with evidence, screenshots of failure states)#### ERR-01: API error shows blank screen instead of error message
**Impact:** Users see empty page, think app is broken, leave
**File:** src/pages/Dashboard.tsx:45
**Current:** `const data = await fetch('/api/data').then(r => r.json())`
**Problem:** No error handling — fetch throws on network error, .json() throws on non-JSON response
**Fix:**
- Wrap in try/catch
- Add error state: `const [error, setError] = useState(null)`
- Render error UI with retry button
- Add React Error Boundary as fallback- name: err-01-api-error-shows-message
description: Verify API failure shows user-friendly error message instead of blank screen
severity: critical
standard: UX-Error-Handling
steps:
- CODE: |
await page.route('**/api/data**', route => {
route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'Internal Server Error' })
});
});
- URL: /dashboard
- WAIT_UNTIL: Page has finished attempting to load data
timeout_seconds: 15
- VERIFY: An error message is visible explaining that data could not be loaded
- VERIFY: A retry button or recovery action is available to the user
- VERIFY: The page is NOT blank — navigation and header are still visibleshiplight/tests/resilience-review.test.yamlpage.route()get_browser_console_logsclose_sessiongenerate_html_report