- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
721 lines
19 KiB
Markdown
721 lines
19 KiB
Markdown
# 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
|