csrf = new class () { public function generateToken(string $formId): CsrfToken { return new CsrfToken('test-csrf-token-' . $formId); } }; } public function getId(): string { return 'test-session'; } public function start(): void { } public function destroy(): void { } public function regenerateId(): void { } public function get(string $key, mixed $default = null): mixed { return $default; } public function set(string $key, mixed $value): void { } public function has(string $key): bool { return false; } public function remove(string $key): void { } public function clear(): void { } public function getFlashBag(): mixed { return null; } }; // Create a mock TemplateRenderer (we won't use it for this test) $templateRenderer = new class () implements TemplateRenderer { public function render(...$args): string { return ''; } public function renderPartial(...$args): string { return ''; } }; // Create LiveComponentRenderer $renderer = new LiveComponentRenderer($templateRenderer, $session); // Test 1: Render WITHOUT SSE channel echo "Test 1: Render without SSE channel\n"; echo "-----------------------------------\n"; $html1 = $renderer->renderWithWrapper( componentId: 'test-component:demo', componentHtml: '

Test Content

', state: ['count' => 0], sseChannel: null ); if (str_contains($html1, 'data-sse-channel=')) { echo "❌ FAIL: data-sse-channel should NOT be present when sseChannel is null\n"; } else { echo "✅ SUCCESS: data-sse-channel not present (as expected)\n"; } // Verify other attributes are present $requiredAttrs = ['data-live-component', 'data-state', 'data-csrf-token']; foreach ($requiredAttrs as $attr) { if (str_contains($html1, $attr . '=')) { echo "✅ {$attr} present\n"; } else { echo "❌ {$attr} MISSING\n"; } } echo "\n"; // Test 2: Render WITH SSE channel echo "Test 2: Render with SSE channel\n"; echo "-----------------------------------\n"; $html2 = $renderer->renderWithWrapper( componentId: 'notification-center:user-123', componentHtml: '
Notifications
', state: ['notifications' => []], sseChannel: 'user:user-123' ); if (str_contains($html2, 'data-sse-channel=')) { echo "✅ SUCCESS: data-sse-channel attribute found\n"; // Extract and verify value if (preg_match('/data-sse-channel="([^"]+)"/', $html2, $matches)) { $channel = $matches[1]; echo " Channel value: {$channel}\n"; if ($channel === 'user:user-123') { echo "✅ Channel value correct\n"; } else { echo "❌ Channel value incorrect. Expected: user:user-123, Got: {$channel}\n"; } } } else { echo "❌ FAIL: data-sse-channel should be present when sseChannel is provided\n"; echo "HTML: " . substr($html2, 0, 200) . "\n"; } // Verify all attributes are present foreach ($requiredAttrs as $attr) { if (str_contains($html2, $attr . '=')) { echo "✅ {$attr} present\n"; } else { echo "❌ {$attr} MISSING\n"; } } echo "\n"; // Test 3: Full HTML structure verification echo "Test 3: HTML Structure\n"; echo "-----------------------------------\n"; echo "Sample HTML (first 400 chars):\n"; echo substr($html2, 0, 400) . "\n\n"; // Test 4: Special characters in channel name echo "Test 4: Special characters handling\n"; echo "-----------------------------------\n"; $html3 = $renderer->renderWithWrapper( componentId: 'test:special', componentHtml: '

Test

', state: ['test' => true], sseChannel: 'presence:room-&"chars"' ); if (preg_match('/data-sse-channel="([^"]+)"/', $html3, $matches)) { $channel = $matches[1]; echo "Rendered channel: {$channel}\n"; // Check if HTML entities are escaped properly if (str_contains($channel, '<') || str_contains($channel, '"')) { echo "✅ Special characters properly escaped\n"; } else { echo "⚠️ Special characters may not be escaped: {$channel}\n"; } } echo "\n=== Test Complete ===\n";