> ## Documentation Index
> Fetch the complete documentation index at: https://docs.charle.agency/llms.txt
> Use this file to discover all available pages before exploring further.

# E2E Tests

> End-to-end testing with Cypress in CharleOS

CharleOS uses **Cypress** for end-to-end testing of critical user flows. This page covers how to write and run E2E tests.

## Test Users

E2E tests use dedicated test accounts that are isolated from real user data:

| Email                       | Access Level | Work Type   | Use For                               |
| --------------------------- | ------------ | ----------- | ------------------------------------- |
| `test-admin@charle.co.uk`   | Admin        | CSM         | Admin features, settings, full access |
| `test-manager@charle.co.uk` | Manager      | PM          | Management features, scheduling       |
| `test-staff@charle.co.uk`   | Staff        | Development | IC features, task assignment          |

**Important:** Always run `npm run db:seed:e2e` before running E2E tests to ensure test users exist.

## Custom Commands

### Login Commands

```typescript theme={null}
// Login as specific user
cy.login("test-admin@charle.co.uk");

// Convenience commands for role-based login
cy.loginAsAdmin();   // test-admin@charle.co.uk
cy.loginAsManager(); // test-manager@charle.co.uk
cy.loginAsStaff();   // test-staff@charle.co.uk
```

## What to E2E Test

Focus on **critical user journeys** and **integration points**:

<CardGroup cols={2}>
  <Card title="Authentication" icon="lock">
    Sign in, sign out, session management
  </Card>

  <Card title="Core Workflows" icon="workflow">
    Create task, approve quote, assign subtask
  </Card>

  <Card title="Client Portal" icon="building">
    Client sign-in, view tasks, submit tickets
  </Card>

  <Card title="Permissions" icon="shield">
    Admin vs manager vs staff access
  </Card>
</CardGroup>

## Test Structure

### File Organization

```
cypress/
├── e2e/
│   ├── auth.cy.ts          # Authentication flows
│   ├── clients.cy.ts       # Client management
│   ├── tasks.cy.ts         # Task viewing/access
│   └── permissions.cy.ts   # Role-based access
├── support/
│   ├── e2e.ts              # Custom commands
│   └── commands.ts         # Additional commands
└── fixtures/               # Test data
```

### Basic Test Example

```typescript theme={null}
// cypress/e2e/auth.cy.ts
describe("Authentication", () => {
  it("redirects unauthenticated users to sign-in", () => {
    cy.visit("/clients");
    cy.url().should("include", "sign-in");
  });

  describe("Authenticated - Admin", () => {
    beforeEach(() => {
      cy.loginAsAdmin();
    });

    it("can access dashboard", () => {
      cy.visit("/");
      cy.url().should("not.include", "sign-in");
    });

    it("can access admin settings", () => {
      cy.visit("/admin/settings");
      cy.url().should("include", "/admin/settings");
    });
  });
});
```

## Cypress Features

### Navigation

```typescript theme={null}
// Navigate to page
cy.visit("/tasks");

// Wait for URL
cy.url().should("include", "/tasks");

// Go back/forward
cy.go("back");
cy.go("forward");
```

### Locators

```typescript theme={null}
// By text
cy.contains("Create Task");
cy.contains("button", "Submit");

// By selector
cy.get('[data-testid="task-card"]');
cy.get(".task-card");
cy.get("#task-123");

// By attribute
cy.get('input[name="title"]');
cy.get('button[type="submit"]');
```

### Interactions

```typescript theme={null}
// Click
cy.get("button").click();
cy.contains("Submit").click();

// Type
cy.get('input[name="title"]').type("New Task");
cy.get('input[name="title"]').clear().type("Updated Task");

// Select
cy.get('select[name="priority"]').select("high");

// Check/uncheck
cy.get('input[type="checkbox"]').check();
cy.get('input[type="checkbox"]').uncheck();
```

### Assertions

```typescript theme={null}
// Visibility
cy.get(".success-message").should("be.visible");
cy.get(".error-message").should("not.exist");

// Text content
cy.get("h1").should("have.text", "Dashboard");
cy.get(".status").should("contain", "Complete");

// URL
cy.url().should("include", "/tasks");
cy.url().should("match", /\/tasks\/task-/);

// Count
cy.get(".task-card").should("have.length", 3);

// Attribute
cy.get("button").should("be.disabled");
cy.get("input").should("have.value", "Initial value");
```

## Common Test Patterns

### Authentication Flow

```typescript theme={null}
// cypress/e2e/auth.cy.ts
describe("Authentication", () => {
  it("redirects unauthenticated users to sign-in", () => {
    cy.visit("/clients");
    cy.url().should("include", "sign-in");
  });

  describe("Admin Access", () => {
    beforeEach(() => {
      cy.loginAsAdmin();
    });

    it("can access all pages", () => {
      cy.visit("/admin/settings");
      cy.url().should("include", "/admin/settings");
    });
  });

  describe("Staff Access", () => {
    beforeEach(() => {
      cy.loginAsStaff();
    });

    it("cannot access admin pages", () => {
      cy.visit("/admin/settings");
      // Should redirect away from admin
      cy.url().should("not.include", "/admin");
    });
  });
});
```

### Task Viewing

```typescript theme={null}
// cypress/e2e/tasks.cy.ts
describe("Tasks", () => {
  beforeEach(() => {
    cy.loginAsAdmin();
  });

  it("can access tasks page", () => {
    cy.visit("/tasks");
    cy.url().should("include", "/tasks");
    cy.url().should("not.include", "sign-in");
  });

  it("displays task list", () => {
    cy.visit("/tasks");
    // Verify page content loaded, not just that body exists
    cy.contains("Tasks", { timeout: 15000 }).should("be.visible");
  });
});
```

### Permission Tests

```typescript theme={null}
// cypress/e2e/permissions.cy.ts
describe("Role-Based Access", () => {
  describe("Admin user", () => {
    beforeEach(() => {
      cy.loginAsAdmin();
    });

    it("can access admin settings", () => {
      cy.visit("/admin/settings");
      cy.url().should("include", "/admin/settings");
    });

    it("can access clients page", () => {
      cy.visit("/clients");
      cy.url().should("include", "/clients");
    });
  });

  describe("Staff user", () => {
    beforeEach(() => {
      cy.loginAsStaff();
    });

    it("cannot access admin settings", () => {
      cy.visit("/admin/settings");
      cy.url().should("not.include", "/admin/settings");
    });

    it("can access tasks page", () => {
      cy.visit("/tasks");
      cy.url().should("include", "/tasks");
    });
  });
});
```

## Running E2E Tests

### Basic Commands

```bash theme={null}
# Seed test data first
npm run db:seed:e2e

# Run all E2E tests (headless)
npm run test:e2e

# Run with Cypress UI (interactive)
npm run test:e2e:open

# Run specific file
npx cypress run --spec "cypress/e2e/auth.cy.ts"

# Run with specific browser
npx cypress run --browser chrome
```

### Watch Mode

```bash theme={null}
# Open Cypress UI for interactive development
npm run test:e2e:open
```

## Debugging

### Screenshots and Videos

Cypress automatically captures screenshots on failure and videos of test runs:

```
cypress/
├── screenshots/    # Failure screenshots
└── videos/         # Test run videos
```

### Debug Commands

```typescript theme={null}
// Pause test execution
cy.pause();

// Log to console
cy.log("Debug message");

// Debug with devtools
cy.debug();
```

### Run in Headed Mode

```bash theme={null}
# See the browser while tests run
npx cypress run --headed
```

## Best Practices

<AccordionGroup>
  <Accordion title="Use Dedicated Test Users" icon="user">
    Always use the dedicated test accounts:

    ```typescript theme={null}
    // Good: Use test users
    cy.loginAsAdmin();

    // Bad: Use real user accounts
    cy.login("luke@charle.co.uk");
    ```
  </Accordion>

  <Accordion title="Test Real User Flows" icon="route">
    Focus on what users actually do:

    ```typescript theme={null}
    // Good: Complete user flow
    it("admin can access settings", () => {
      cy.loginAsAdmin();
      cy.visit("/admin/settings");
      cy.url().should("include", "/admin/settings");
    });

    // Bad: Testing implementation details
    it("API returns 200", () => {
      // Too low-level
    });
    ```
  </Accordion>

  <Accordion title="Use Reliable Selectors" icon="crosshairs">
    Prefer data-testid attributes over CSS classes:

    ```typescript theme={null}
    // Good: Resilient selectors
    cy.get('[data-testid="task-card"]');
    cy.contains("button", "Submit");

    // Bad: Fragile selectors
    cy.get('.btn-primary.create-btn');
    ```
  </Accordion>

  <Accordion title="Keep Tests Independent" icon="puzzle-piece">
    Each test should work in isolation:

    ```typescript theme={null}
    // Good: Test sets up its own state
    beforeEach(() => {
      cy.loginAsAdmin();
    });

    // Bad: Tests depend on each other
    it("creates task"); // Must run first
    it("edits task");   // Depends on previous test
    ```
  </Accordion>

  <Accordion title="Use Content Assertions" icon="check">
    Verify actual page content loaded, not just URL or body existence:

    ```typescript theme={null}
    // Good: Checks actual page content
    cy.visit("/tasks");
    cy.contains("Tasks", { timeout: 15000 }).should("be.visible");

    // Bad: Would pass with loading spinner or error page
    cy.visit("/tasks");
    cy.get("body").should("be.visible");
    ```

    Content assertions ensure the page has fully loaded and rendered the expected content,
    catching issues that URL-only or body-visibility checks would miss.
  </Accordion>
</AccordionGroup>

## CI Integration

E2E tests run automatically on pull requests to main:

```yaml theme={null}
# .github/workflows/e2e.yml
on:
  pull_request:
    branches: [main]
    paths:
      - 'app/**'
      - 'components/**'
      - 'lib/**'
      - 'cypress/**'

jobs:
  e2e-tests:
    steps:
      - name: Seed E2E data
        run: npm run db:seed:e2e

      - name: Run Cypress
        uses: cypress-io/github-action@v6
        with:
          build: npm run build
          start: npm start
```

## Related Documentation

<CardGroup cols={2}>
  <Card title="Testing Overview" icon="flask" href="/developer/testing">
    Testing strategy
  </Card>

  <Card title="Unit Tests" icon="vial" href="/developer/testing/unit-tests">
    Vitest unit tests
  </Card>

  <Card title="Cypress Docs" icon="book" href="https://docs.cypress.io/">
    Official Cypress docs
  </Card>
</CardGroup>
