# E2E Test Troubleshooting Guide

Common issues and solutions for E2E testing.

---

## Quick Reference

| Issue | Quick Fix |
|-------|-----------|
| Tests fail due to auth | `rm -rf tests/e2e/.auth/*.json && npm run e2e` |
| Database connection fails | `docker restart tidb-test` |
| TiDB not responding | `./tests/e2e/scripts/reset-db.sh` |
| Browser not found | `npx playwright install --with-deps` |
| Tests timeout | Increase timeout or check app performance |

---

## Authentication Issues

### Tests Fail Due to Authentication

**Symptoms:**
- Tests redirect to login unexpectedly
- `authenticatedPage` fixture not working
- Storage state errors

**Solutions:**

```bash
# 1. Delete auth state files
rm -rf tests/e2e/.auth/*.json

# 2. Re-run tests (global-setup will regenerate)
npm run e2e

# 3. Or manually run global setup
npx playwright test --list
```

### Login Form Not Found

**Symptoms:**
- `Error: locator.click: Timeout 15000ms exceeded`
- `Error: locator.fill: Timeout 15000ms exceeded`

**Solutions:**

1. Check if frontend is running:
   ```bash
   curl http://localhost:5173
   ```

2. Wait for app to fully load:
   ```typescript
   await page.goto('/login');
   await page.waitForLoadState('networkidle');
   ```

3. Check for loading overlay:
   ```typescript
   await page.waitForSelector('[data-testid="loading"]', { state: 'detached' });
   ```

### Session Expired During Test

**Symptoms:**
- Mid-test redirect to login
- 401 errors in API calls

**Solutions:**

1. Increase session timeout in test:
   ```typescript
   test.setTimeout(60000);
   ```

2. Re-authenticate in test:
   ```typescript
   test('long test', async ({ browser }) => {
     const context = await browser.newContext({
       storageState: 'tests/e2e/.auth/admin.json',
     });
     const page = await context.newPage();
     // ... continue test
   });
   ```

---

## Database Issues

### TiDB Connection Refused

**Symptoms:**
- `Error: connect ECONNREFUSED 127.0.0.1:4000`
- Database reset script fails

**Solutions:**

```bash
# 1. Check if TiDB is running
docker ps | grep tidb-test

# 2. Restart TiDB
docker restart tidb-test

# 3. Wait for TiDB to be ready
sleep 10

# 4. Test connection
mysql -h 127.0.0.1 -P 4000 -u root -e "SELECT 1;"

# 5. If still failing, recreate container
docker rm -f tidb-test
docker run -d --name tidb-test -p 4000:4000 -p 10080:10080 pingcap/tidb:latest
```

### Database Reset Fails

**Symptoms:**
- `reset-db.sh` exits with error
- Database schema not applied

**Solutions:**

```bash
# 1. Check script permissions
chmod +x tests/e2e/scripts/reset-db.sh

# 2. Run manually with verbose output
bash -x tests/e2e/scripts/reset-db.sh

# 3. Reset manually
mysql -h 127.0.0.1 -P 4000 -u root -e "DROP DATABASE IF EXISTS nexuscrm_test;"
mysql -h 127.0.0.1 -P 4000 -u root -e "CREATE DATABASE nexuscrm_test;"
```

### Seed Data Not Found

**Symptoms:**
- Tests fail expecting seed data
- Tables are empty

**Solutions:**

```bash
# 1. Run seed script
cd tests/e2e/scripts
go run seed-test-data.go

# 2. Verify data
mysql -h 127.0.0.1 -P 4000 -u root nexuscrm_test -e "SELECT COUNT(*) FROM accounts;"
```

---

## Test Timeout Issues

### Test Exceeds Timeout

**Symptoms:**
- `Test timeout of 30000ms exceeded`
- Tests pass locally but fail in CI

**Solutions:**

1. Increase test timeout:
   ```typescript
   test('slow test', async ({ page }) => {
     test.setTimeout(60000);
     // ... test logic
   });
   ```

2. Check for missing waits:
   ```typescript
   // Before (may timeout)
   await page.click('button');
   await expect(page.getByText('Success')).toBeVisible();

   // After (with wait)
   await page.click('button');
   await waitForLoad(page);  // or specific wait
   await expect(page.getByText('Success')).toBeVisible();
   ```

3. Use Playwright's built-in auto-retrying assertions:
   ```typescript
   // Playwright auto-retries expect() until timeout
   await expect(page.getByText('Result')).toBeVisible({ timeout: 10000 });
   ```

### Action Timeout

**Symptoms:**
- `Action timeout of 15000ms exceeded`
- Click/fill operations timing out

**Solutions:**

1. Wait for element to be ready:
   ```typescript
   await page.getByTestId('button').waitFor({ state: 'visible' });
   await page.getByTestId('button').click();
   ```

2. Scroll element into view:
   ```typescript
   await page.getByTestId('button').scrollIntoViewIfNeeded();
   await page.getByTestId('button').click();
   ```

3. Use click with increased timeout:
   ```typescript
   await page.getByTestId('button').click({ timeout: 10000 });
   ```

---

## Element Not Found Issues

### Selector Not Working

**Symptoms:**
- `Error: locator.click: Element not found`
- Tests pass in manual but fail automated

**Solutions:**

1. Verify selector with codegen:
   ```bash
   npx playwright codegen http://localhost:5173
   ```

2. Try alternative selectors:
   ```typescript
   // Instead of CSS
   await page.locator('.btn-primary').click();

   // Use testId
   await page.getByTestId('submit-button').click();

   // Or ARIA role
   await page.getByRole('button', { name: 'Submit' }).click();
   ```

3. Check for iframe:
   ```typescript
   const frame = page.frameLocator('iframe');
   await frame.getByTestId('element').click();
   ```

### Element Behind Overlay

**Symptoms:**
- `Error: Element is not visible`
- Click intercepted

**Solutions:**

1. Wait for overlay to disappear:
   ```typescript
   await page.waitForSelector('[data-testid="loading"]', { state: 'detached' });
   await page.waitForSelector('[data-testid="modal-overlay"]', { state: 'hidden' });
   ```

2. Force click (use carefully):
   ```typescript
   await page.getByTestId('button').click({ force: true });
   ```

3. Close interfering modals:
   ```typescript
   const modal = page.getByTestId('modal');
   if (await modal.isVisible()) {
     await page.keyboard.press('Escape');
   }
   ```

---

## CI/CD Issues

### Tests Pass Locally, Fail in CI

**Common Causes:**

1. **Different timing** - CI is slower
   ```typescript
   // Add explicit waits
   await waitForLoad(page);
   ```

2. **Different screen size** - Responsive issues
   ```typescript
   // Don't rely on pixel positions
   // Use responsive selectors
   ```

3. **Missing environment variables**
   ```yaml
   # In workflow file
   env:
     PLAYWRIGHT_BASE_URL: http://localhost:5173
   ```

4. **Race conditions**
   ```typescript
   // Wait for API to complete
   await page.waitForResponse(resp => resp.url().includes('/api/save'));
   ```

### Shard Failures

**Symptoms:**
- Some shards fail, others pass
- Inconsistent results

**Solutions:**

1. Increase retries:
   ```typescript
   // playwright.config.ts
   retries: process.env.CI ? 3 : 0,
   ```

2. Ensure test isolation:
   ```typescript
   test.beforeEach(async ({ apiRequest, dbCleanup }) => {
     // Create fresh data for each test
   });
   ```

3. Check for shared state:
   ```typescript
   // Avoid global state
   // Use test-specific data
   ```

---

## Browser Issues

### Browser Not Installed

**Symptoms:**
- `Error: Browser is not installed`
- `Error: Executable doesn't exist`

**Solutions:**

```bash
# Install all browsers
npx playwright install

# Install with system dependencies
npx playwright install --with-deps

# Install specific browser
npx playwright install chromium
```

### Headless Mode Issues

**Symptoms:**
- Tests pass headed, fail headless
- Different behavior in headless

**Solutions:**

1. Run headed in CI:
   ```typescript
   // playwright.config.ts
   use: {
     headless: false,
   },
   ```

2. Add headless-specific waits:
   ```typescript
   // Headless may be faster, add waits
   await page.waitForTimeout(100);
   ```

3. Check for viewport issues:
   ```typescript
   // playwright.config.ts
   use: {
     viewport: { width: 1280, height: 720 },
   },
   ```

---

## Network Issues

### API Calls Failing

**Symptoms:**
- `Error: 500 Internal Server Error`
- `Error: 401 Unauthorized`

**Solutions:**

1. Check backend is running:
   ```bash
   curl http://localhost:3001/api/health
   ```

2. Verify auth headers:
   ```typescript
   const response = await apiRequest('/api/data', {
     method: 'GET',
     headers: {
       'Authorization': `Bearer ${token}`,
     },
   });
   ```

3. Wait for network:
   ```typescript
   await page.waitForLoadState('networkidle');
   ```

### WebSocket Issues

**Symptoms:**
- Real-time updates not appearing
- Connection errors

**Solutions:**

```typescript
// Wait for WebSocket message
await page.waitForResponse(async (response) => {
  return response.url().includes('/ws') && response.status() === 101;
});

// Or wait for update to appear
await expect(page.getByTestId('live-update')).toBeVisible();
```

---

## Debugging Tips

### Using Debug Mode

```bash
# Run specific test in debug mode
npx playwright test auth.spec.ts --debug

# Debug with breakpoint
npx playwright test --debug --headed
```

### Using Traces

```bash
# Enable trace in config (already default)
// trace: 'retain-on-failure'

# View trace
npx playwright show-trace test-results/trace.zip
```

### Using Console Logs

```typescript
test('debug test', async ({ page }) => {
  page.on('console', msg => console.log('PAGE LOG:', msg.text()));
  page.on('pageerror', err => console.log('PAGE ERROR:', err.message));

  // ... test logic
});
```

### Taking Screenshots

```typescript
test('debug test', async ({ page }) => {
  await page.screenshot({ path: 'debug-before.png' });

  // ... action

  await page.screenshot({ path: 'debug-after.png' });
});
```

---

## Performance Issues

### Slow Tests

**Symptoms:**
- Tests taking >60s
- CI timeout

**Solutions:**

1. Remove unnecessary waits:
   ```typescript
   // Bad
   await page.waitForTimeout(5000);

   // Good
   await page.waitForLoadState('networkidle');
   ```

2. Parallelize independent tests:
   ```typescript
   // Don't chain dependent tests
   // Each test should be independent
   ```

3. Optimize selectors:
   ```typescript
   // Fast
   await page.getByTestId('element').click();

   // Slow
   await page.locator('div > div > div > span').click();
   ```

### Memory Issues

**Symptoms:**
- Tests slow down over time
- Out of memory errors

**Solutions:**

1. Close contexts:
   ```typescript
   test.afterEach(async ({ context }) => {
     await context.close();
   });
   ```

2. Clear data:
   ```typescript
   await page.evaluate(() => window.localStorage.clear());
   ```

3. Restart browser periodically:
   ```typescript
   // In worker fixture
   // Browser restarts between workers
   ```

---

## Common Error Messages

### "ElementHandle.screenshot: Target page, context or browser has been closed"

**Cause:** Browser context closed prematurely

**Solution:** Ensure context cleanup happens after all operations

### "Navigation failed because of target frame detached"

**Cause:** Page navigated during operation

**Solution:** Wait for navigation to complete before next action

### "Protocol error (Runtime.callFunctionOn): Target closed"

**Cause:** Browser crashed

**Solution:** Check for memory issues, restart browser

### "ExecutionContext was destroyed"

**Cause:** Page reloaded during execution

**Solution:** Add proper wait conditions before page.evaluate()

---

## Getting Help

1. Check [Playwright Documentation](https://playwright.dev)
2. Review [INFRASTRUCTURE.md](./INFRASTRUCTURE.md)
3. Search existing issues
4. Contact QA team

---

## Last Updated

2026-03-24
