# LiveComponents E2E Integration Tests

Comprehensive end-to-end integration tests for LiveComponents cross-cutting features: **Partial Rendering**, **Batch Operations**, and **Server-Sent Events (SSE)**.

## Overview

This test suite validates the seamless integration of multiple LiveComponents features working together in real-world scenarios. It ensures that partial rendering, batch operations, and real-time updates work harmoniously without conflicts or state inconsistencies.

## Test Categories

### 1. Partial Rendering Tests (6 tests)

**Purpose**: Validate fragment-based updates without full component re-rendering

**Tests**:

1. **Update only targeted fragment without full component re-render**
   - Triggers fragment-specific action
   - Verifies only fragment HTML updated
   - Ensures component timestamp unchanged (no full render)
   - Validates DOM patch efficiency

2. **Update multiple fragments in single request**
   - Updates 2+ fragments simultaneously
   - Verifies all fragments updated correctly
   - Ensures only 1 HTTP request sent
   - Validates network efficiency

3. **Preserve component state during partial render**
   - Sets component state before fragment update
   - Triggers fragment update
   - Verifies state preserved after update
   - Validates state consistency

4. **Handle nested fragment updates**
   - Updates parent fragment containing child fragment
   - Verifies parent and child both updated
   - Ensures sibling fragments NOT updated
   - Validates selective rendering

5. **Apply morphing algorithm for minimal DOM changes**
   - Counts DOM nodes before update
   - Applies small content change
   - Verifies node count unchanged (morphing, not replacement)
   - Validates morphing stats (added/removed/updated)

6. **Handle fragment-not-found gracefully**
   - Calls action with non-existent fragment ID
   - Verifies error message displayed
   - Ensures component remains functional
   - Validates error recovery

### 2. Batch Operations Tests (6 tests)

**Purpose**: Validate efficient execution of multiple actions in single request

**Tests**:

1. **Execute multiple actions in single batch request**
   - Batches 3 different actions
   - Verifies only 1 HTTP request sent
   - Ensures all actions executed successfully
   - Validates batch efficiency

2. **Maintain action execution order in batch**
   - Batches 3 actions in specific order
   - Logs execution sequence
   - Verifies actions executed in correct order
   - Validates deterministic execution

3. **Rollback batch on action failure**
   - Batches successful + failing + successful actions
   - Verifies entire batch rolled back on failure
   - Ensures no partial state changes
   - Validates transactional behavior

4. **Handle partial batch execution with continueOnError flag**
   - Batches actions with `continueOnError: true`
   - Includes failing action in middle
   - Verifies successful actions still execute
   - Validates partial execution mode

5. **Support batch with mixed action types**
   - Batches sync + async + fragment update actions
   - Verifies all action types execute correctly
   - Ensures type compatibility
   - Validates heterogeneous batching

6. **Batch state updates efficiently**
   - Batches 10 state update actions
   - Verifies final state correct
   - Ensures only 1 state update event fired (optimization)
   - Validates state batching

### 3. Server-Sent Events (SSE) Tests (8 tests)

**Purpose**: Validate real-time server-to-client communication

**Tests**:

1. **Establish SSE connection for real-time updates**
   - Enables SSE
   - Verifies connection state OPEN (readyState === 1)
   - Ensures connection indicator visible
   - Validates connection establishment

2. **Receive and apply server-pushed updates**
   - Establishes SSE connection
   - Simulates server push event
   - Verifies component updated automatically
   - Validates push handling

3. **Handle SSE reconnection on connection loss**
   - Establishes connection
   - Simulates connection close
   - Verifies reconnecting indicator shown
   - Ensures automatic reconnection after retry period

4. **Support multiple SSE event types**
   - Sends different event types (update, notification, sync)
   - Logs received events
   - Verifies all types processed correctly
   - Validates event type handling

5. **Batch SSE updates for performance**
   - Sends 20 rapid SSE updates
   - Verifies final value correct
   - Ensures renders < updates (batching optimization)
   - Validates performance optimization

6. **Close SSE connection when component unmounts**
   - Establishes connection
   - Unmounts component
   - Verifies connection closed (readyState === 2)
   - Validates cleanup

7. **Handle SSE authentication and authorization**
   - Tests SSE with valid auth token
   - Verifies authenticated connection
   - Tests SSE with invalid token
   - Ensures auth error displayed

8. **Support SSE with custom event filters**
   - Enables SSE with priority filter
   - Sends events with different priorities
   - Verifies only filtered events processed
   - Validates client-side filtering

### 4. Integration Tests (4 tests)

**Purpose**: Validate combined usage of multiple features

**Tests**:

1. **Combine partial rendering with batch operations**
   - Batches multiple fragment updates + state changes
   - Verifies only 1 request sent
   - Ensures all fragments and state updated
   - Validates feature synergy

2. **Push partial updates via SSE**
   - Sends SSE event with fragment update
   - Verifies fragment updated via SSE
   - Ensures no full component render
   - Validates SSE + partial rendering

3. **Batch SSE-triggered actions efficiently**
   - Sends 5 rapid SSE events triggering actions
   - Verifies actions batched (< 5 executions)
   - Ensures final state correct
   - Validates SSE + batching

4. **Maintain consistency across all integration features**
   - Complex scenario: batch + partial + SSE simultaneously
   - Starts batch with fragment updates
   - Sends SSE update during batch execution
   - Verifies all updates applied correctly
   - Validates state consistency

## Quick Start

### Prerequisites

```bash
# Ensure Playwright is installed
npm install

# Install browsers
npx playwright install chromium

# Ensure development server is running
make up
```

### Running Integration Tests

```bash
# Run all integration tests
npm run test:integration

# Run with visible browser (debugging)
npm run test:integration:headed

# Run in debug mode (step through tests)
npm run test:integration:debug

# Run specific test category
npx playwright test integration.spec.js --grep "Partial Rendering"
npx playwright test integration.spec.js --grep "Batch Operations"
npx playwright test integration.spec.js --grep "Server-Sent Events"
npx playwright test integration.spec.js --grep "Integration:"
```

## Test Page Requirements

### HTML Structure

The test page `/livecomponents/test/integration` must include:

```html
<!DOCTYPE html>
<html>
<head>
    <title>LiveComponents Integration Tests</title>
    <script src="/assets/livecomponents.js"></script>
</head>
<body>
    <!-- Component Container -->
    <div data-component="integration:test" data-component-id="integration:test">
        <!-- Component Timestamp (für Full Render Detection) -->
        <div id="component-timestamp" data-timestamp="initial">initial</div>

        <!-- Fragment Rendering Tests -->
        <div id="target-fragment">Original Content</div>
        <div id="fragment-1">Fragment 1</div>
        <div id="fragment-2">Fragment 2</div>

        <!-- Nested Fragments -->
        <div id="parent-fragment">
            <div id="child-fragment">Child</div>
        </div>
        <div id="sibling-fragment" data-timestamp="original">Sibling</div>

        <!-- Morphing Test -->
        <div id="morph-fragment">
            <p>Paragraph 1</p>
            <p>Paragraph 2</p>
        </div>

        <!-- Batch Operation Tests -->
        <div id="counter-value">0</div>
        <div id="text-value">Original</div>
        <div id="flag-value">false</div>

        <!-- SSE Tests -->
        <div id="live-value">Initial</div>
        <div id="sse-counter">0</div>
        <div class="sse-connected" style="display:none">Connected</div>
        <div class="sse-reconnecting" style="display:none">Reconnecting...</div>
        <div class="sse-authenticated" style="display:none">Authenticated</div>
        <div class="sse-auth-error" style="display:none">Auth Error</div>

        <!-- State Management -->
        <input type="text" id="state-input" />

        <!-- Action Buttons -->
        <button id="update-fragment">Update Fragment</button>
        <button id="update-multiple-fragments">Update Multiple</button>
        <button id="save-state">Save State</button>
        <button id="update-nested-fragment">Update Nested</button>
        <button id="small-update">Small Update</button>
        <button id="enable-sse">Enable SSE</button>
        <button id="trigger-action">Trigger Action</button>

        <!-- Error Display -->
        <div class="fragment-error" style="display:none"></div>
        <div class="batch-error" style="display:none"></div>
        <div class="partial-success" style="display:none"></div>

        <!-- Results -->
        <div id="sync-result" style="display:none"></div>
        <div id="async-result" style="display:none"></div>
        <div id="fragment-result" style="display:none"></div>
        <div class="action-success" style="display:none"></div>
    </div>

    <script>
        // Global tracking variables
        window.__fragmentUpdateCount = 0;
        window.__requestCount = 0;
        window.__renderCount = 0;
        window.__stateUpdateCount = 0;
        window.__actionCount = 0;
        window.__originalSiblingTimestamp = document.getElementById('sibling-fragment').getAttribute('data-timestamp');
        window.__originalComponentTimestamp = document.getElementById('component-timestamp').textContent;

        // Morphing stats tracking
        window.__morphingStats = {
            nodesAdded: 0,
            nodesRemoved: 0,
            nodesUpdated: 0
        };

        // Initialize LiveComponents
        document.addEventListener('DOMContentLoaded', () => {
            window.LiveComponents.init();
        });
    </script>
</body>
</html>
```

### Component Actions

The `IntegrationTestComponent` must implement:

```php
use App\Framework\LiveComponents\LiveComponent;
use App\Framework\LiveComponents\Attributes\Action;
use App\Framework\LiveComponents\Attributes\Fragment;

final readonly class IntegrationTestComponent extends LiveComponent
{
    #[Action]
    #[Fragment('target-fragment')]
    public function updateFragment(array $params = []): array
    {
        $fragmentId = $params['fragmentId'] ?? 'target-fragment';

        if (!$this->hasFragment($fragmentId)) {
            return ['error' => 'Fragment not found'];
        }

        return [
            'fragments' => [
                $fragmentId => '<div id="' . $fragmentId . '">Updated</div>'
            ]
        ];
    }

    #[Action]
    public function updateMultipleFragments(): array
    {
        return [
            'fragments' => [
                'fragment-1' => '<div id="fragment-1">Fragment 1 Updated</div>',
                'fragment-2' => '<div id="fragment-2">Fragment 2 Updated</div>'
            ]
        ];
    }

    #[Action]
    public function saveState(string $value): void
    {
        $this->state->set('savedValue', $value);
    }

    #[Action]
    #[Fragment('parent-fragment')]
    public function updateNestedFragment(): array
    {
        return [
            'fragments' => [
                'parent-fragment' => '
                    <div id="parent-fragment">Parent Updated
                        <div id="child-fragment">Child Updated</div>
                    </div>
                '
            ]
        ];
    }

    #[Action]
    #[Fragment('morph-fragment')]
    public function smallUpdate(): array
    {
        return [
            'fragments' => [
                'morph-fragment' => '
                    <div id="morph-fragment">
                        <p>Paragraph 1 Updated</p>
                        <p>Paragraph 2</p>
                    </div>
                '
            ]
        ];
    }

    #[Action]
    public function incrementCounter(): void
    {
        $current = $this->state->get('counter', 0);
        $this->state->set('counter', $current + 1);
    }

    #[Action]
    public function updateText(string $text): void
    {
        $this->state->set('text', $text);
    }

    #[Action]
    public function toggleFlag(): void
    {
        $current = $this->state->get('flag', false);
        $this->state->set('flag', !$current);
    }

    #[Action]
    public function action1(): void
    {
        // Log execution
        $this->logExecution('action1');
    }

    #[Action]
    public function action2(): void
    {
        $this->logExecution('action2');
    }

    #[Action]
    public function action3(): void
    {
        $this->logExecution('action3');
    }

    #[Action]
    public function failingAction(): void
    {
        throw new \RuntimeException('Intentional failure for testing');
    }

    #[Action]
    public function syncAction(): array
    {
        return ['success' => true];
    }

    #[Action]
    public function asyncAction(): array
    {
        // Simulate async operation
        usleep(100000); // 100ms
        return ['success' => true];
    }

    #[Action]
    #[Fragment('fragment-result')]
    public function fragmentAction(): array
    {
        return [
            'fragments' => [
                'fragment-result' => '<div id="fragment-result">Fragment Action Complete</div>'
            ]
        ];
    }

    public function enableSSE(array $options = []): void
    {
        // SSE setup logic
        $this->sseEnabled = true;
    }

    public function validateStateConsistency(): bool
    {
        // State validation logic
        return true;
    }
}
```

### SSE Endpoint

The SSE endpoint `/live-component/sse/{componentId}` must support:

```php
use App\Framework\LiveComponents\SSE\SseStream;

final readonly class LiveComponentSseController
{
    #[Route('/live-component/sse/{componentId}', method: Method::GET)]
    public function stream(string $componentId, Request $request): Response
    {
        $authToken = $request->headers->get('Authorization');

        if (!$this->validateAuthToken($authToken)) {
            return new Response(status: Status::UNAUTHORIZED);
        }

        $stream = new SseStream();

        // Set headers
        $response = new Response(
            status: Status::OK,
            headers: [
                'Content-Type' => 'text/event-stream',
                'Cache-Control' => 'no-cache',
                'Connection' => 'keep-alive'
            ]
        );

        // Event loop
        while ($connection->isAlive()) {
            $event = $this->eventQueue->poll($componentId);

            if ($event) {
                $stream->send($event->type, $event->data);
            }

            usleep(100000); // 100ms polling interval
        }

        return $response;
    }
}
```

## Performance Expectations

### Partial Rendering
- **Fragment Update Latency**: <50ms for small fragments (<5KB)
- **DOM Morphing Efficiency**: 0 node additions/removals for content-only changes
- **Memory Impact**: <1MB per fragment update

### Batch Operations
- **Network Savings**: 1 request for N actions (vs. N requests)
- **Execution Time**: Linear O(N) for N batched actions
- **Rollback Overhead**: <10ms for transaction rollback

### Server-Sent Events
- **Connection Establishment**: <500ms
- **Event Latency**: <100ms from server send to client receive
- **Reconnection Time**: <2s after connection loss
- **Batching Window**: 50-100ms for update batching

## Troubleshooting

### Fragment Updates Not Working

**Symptoms**:
- Fragment content doesn't update
- Full component re-renders instead

**Solutions**:

1. **Verify Fragment Attribute**:
```php
#[Fragment('target-fragment')]
public function updateFragment(): array
```

2. **Check Fragment ID in Response**:
```php
return [
    'fragments' => [
        'target-fragment' => '<div id="target-fragment">Updated</div>'
    ]
];
```

3. **Ensure Fragment Exists in DOM**:
```html
<div id="target-fragment">Original</div>
```

### Batch Operations Failing

**Symptoms**:
- Individual actions execute separately
- Multiple HTTP requests sent

**Solutions**:

1. **Use Batch API Correctly**:
```javascript
component.batch()
    .call('action1')
    .call('action2')
    .execute();  // Don't forget .execute()
```

2. **Check Batch Support**:
```javascript
if (component.supportsBatch) {
    // Batching available
}
```

### SSE Connection Issues

**Symptoms**:
- SSE connection not established
- Events not received

**Solutions**:

1. **Check SSE Endpoint**:
```bash
curl -N https://localhost/live-component/sse/integration:test \
  -H "Authorization: Bearer token"
```

2. **Verify Headers**:
```
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
```

3. **Test Auth Token**:
```javascript
component.enableSSE({ authToken: 'valid-token-123' });
```

4. **Check Browser Console**:
```javascript
component.sse.addEventListener('error', (e) => {
    console.error('SSE Error:', e);
});
```

## CI/CD Integration

### GitHub Actions

```yaml
name: Integration Tests

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  integration-tests:
    runs-on: ubuntu-latest
    timeout-minutes: 30

    steps:
      - uses: actions/checkout@v3

      - uses: actions/setup-node@v3
        with:
          node-version: '18'

      - name: Install dependencies
        run: npm ci

      - name: Install Playwright
        run: npx playwright install --with-deps chromium

      - name: Start dev server
        run: |
          make up
          sleep 10

      - name: Run Integration Tests
        run: npm run test:integration

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: integration-test-results
          path: test-results/

      - name: Upload test report
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: playwright-report
          path: playwright-report/
```

## Best Practices

### 1. Test Organization
- Group related tests with `test.describe()`
- Use descriptive test names
- Follow AAA pattern (Arrange, Act, Assert)

### 2. State Management
- Reset component state between tests
- Use `beforeEach` for consistent setup
- Avoid test interdependencies

### 3. Async Handling
- Use `page.waitForTimeout()` conservatively
- Prefer `page.waitForSelector()` or `page.waitForFunction()`
- Set appropriate timeouts for network operations

### 4. Error Handling
- Test both success and failure paths
- Verify error messages and recovery
- Check graceful degradation

### 5. Performance Testing
- Track request counts
- Measure render times
- Monitor memory usage
- Validate batching efficiency

## Resources

- [Playwright Documentation](https://playwright.dev/)
- [LiveComponents Framework Guide](../../../docs/livecomponents/README.md)
- [Fragment Rendering Guide](../../../docs/livecomponents/FRAGMENTS.md)
- [Batch Operations Guide](../../../docs/livecomponents/BATCH.md)
- [SSE Integration Guide](../../../docs/livecomponents/SSE.md)

## Support

For issues or questions:
1. Check troubleshooting section above
2. Review LiveComponents documentation
3. Inspect browser console and network tab
4. Run tests in headed mode for visual debugging
5. Create GitHub issue with test output and environment details
