Embeddable Bridge Testing Guide
Documentation for Testing
Embeddable Bridge Testing Guide
For Claude Cowork Agent | Session 73 | Browser-Based Testing
This guide provides step-by-step instructions for testing the embeddable bridge features using Playwright browser tools.
Table of Contents
- Prerequisites & Setup
- Testing Phase 1: Core Bridge Functionality
- Testing Phase 2: AI API Integration
- Testing Phase 3: External Embedding
- Testing Phase 4: Advanced Features
- Automated Test Scenarios
- Troubleshooting
1. Prerequisites & Setup
1.1 Start Required Services
Before testing, ensure these services are running:
# Terminal 1: Start gograb-space (host app)
nx serve gograb-space --port=5200
# Terminal 2: Start gograb-ai (embedded app)
nx serve gograb-ai --port=5300
# Terminal 3: Start backend API (if testing real AI integration)
nx serve backend-ai-api --port=3001
1.2 Test URLs
| App | URL | Purpose |
|---|---|---|
| Host App | http://localhost:5200 |
Main platform |
| Embedded App | http://localhost:5300 |
AI panel standalone |
| Test Page | http://localhost:5200/system/ai-iframe-test |
Integration test harness |
| Panel Route | http://localhost:5300/panel |
Embedded panel view |
1.3 Browser Setup with Playwright
// Navigate to the test page
await mcp__playwright__browser_navigate({ url: 'http://localhost:5200/system/ai-iframe-test' });
// Take initial snapshot
await mcp__playwright__browser_snapshot({});
2. Testing Phase 1: Core Bridge Functionality
2.1 Test: Iframe Loading
Objective: Verify the embedded app loads correctly in the iframe.
Steps:
- Navigate to test page
- Wait for iframe to load
- Check for "AI_APP_READY" message
// Navigate to test page
await mcp__playwright__browser_navigate({ url: 'http://localhost:5200/system/ai-iframe-test' });
// Wait for iframe to be visible
await mcp__playwright__browser_wait_for({ text: 'Panel' });
// Take snapshot to verify UI
await mcp__playwright__browser_snapshot({});
Expected Results:
- Iframe should be visible
- "Connected" or "Ready" indicator should appear
- No console errors related to CORS or origin
2.2 Test: PostMessage Communication
Objective: Verify bidirectional message passing between host and embedded app.
Steps:
- Open browser console to monitor messages
- Click "Send Ping" button (if available)
- Verify ping/pong response
// Check console for messages
await mcp__playwright__browser_console_messages({ level: 'info' });
// Look for bridge-related logs
// Expected: "[Embeddable Host] Sent message: AI_PING"
// Expected: "[Embeddable Host] Received message: AI_PONG"
Expected Results:
- Messages should flow in both directions
- Message latency should be < 50ms
- No message loss
2.3 Test: Drag-Drop Functionality
Objective: Verify cross-iframe drag-drop works correctly.
Steps:
- Locate a draggable item in the embedded panel
- Initiate drag
- Move to drop zone in host
- Release and verify drop
// Take snapshot to find draggable elements
await mcp__playwright__browser_snapshot({});
// Find the drag source element (look for elements with drag-related attributes)
// The snapshot will show interactive elements with their refs
// Click on a draggable item to test click-to-add fallback
await mcp__playwright__browser_click({
ref: '[ref-to-add-button]',
element: 'Add to Canvas button'
});
Expected Results:
- Shadow element should appear during drag
- Drop zone should highlight on hover
- Item should be added on drop
- Success animation should play
2.4 Test: Auth Token Forwarding
Objective: Verify authentication tokens are passed to embedded app.
Steps:
- Log in to the host app (if required)
- Open the AI panel
- Check that API calls from embedded app include auth headers
// Check network requests for auth headers
await mcp__playwright__browser_network_requests({ includeStatic: false });
// Look for requests to /api/ endpoints
// Verify Authorization header is present
Expected Results:
AI_AUTH_INITmessage should be sent when panel opens- Embedded app should receive and store the token
- API requests should include
Authorization: Bearer <token>
3. Testing Phase 2: AI API Integration
3.1 Test: Chat Interface
Objective: Verify the chat interface sends and receives messages.
Steps:
- Navigate to panel
- Type a message
- Submit and wait for response
// Navigate to embedded panel directly for isolated testing
await mcp__playwright__browser_navigate({ url: 'http://localhost:5300/panel' });
// Wait for panel to load
await mcp__playwright__browser_snapshot({});
// Find chat input and type a message
await mcp__playwright__browser_type({
ref: '[ref-to-chat-input]',
element: 'Chat input field',
text: 'Hello, this is a test message',
submit: true
});
// Wait for response
await mcp__playwright__browser_wait_for({ text: 'assistant', time: 10 });
Expected Results:
- Message should appear in chat
- Loading indicator should show
- Response should appear (or mock response in dev)
3.2 Test: Streaming Response
Objective: Verify streaming responses display incrementally.
Steps:
- Send a prompt that triggers streaming
- Observe incremental text display
- Verify completion
// After sending a message, observe the response area
await mcp__playwright__browser_snapshot({});
// Check for streaming indicators
// Look for typing animation or incremental text
Expected Results:
- Text should appear character by character or in chunks
AI_STREAM_START,AI_STREAM_CHUNK,AI_STREAM_ENDmessages in console- Final message should be complete
3.3 Test: Context Awareness
Objective: Verify context is passed to AI and affects responses.
Steps:
- Navigate to a specific page with context
- Open AI panel
- Verify context is received
// Navigate to a page with specific context (e.g., a widget editor)
await mcp__playwright__browser_navigate({ url: 'http://localhost:5200/space/demo/page-builder' });
// Open AI panel and check context
await mcp__playwright__browser_console_messages({ level: 'info' });
// Look for AI_CONTEXT_UPDATE message with pageId, widgetId, etc.
Expected Results:
- Context payload should include current route
- Context should include selected items (if any)
- AI responses should be contextually relevant
4. Testing Phase 3: External Embedding
4.1 Test: Embed.js Loader
Objective: Verify the embed.js script works on external pages.
Steps:
- Create a test HTML file with embed script
- Load it in browser
- Verify panel appears
// First, create a simple test HTML file or navigate to a test page
// For testing, you can use data URL or a local server
// Navigate to a page that uses embed.js
await mcp__playwright__browser_navigate({ url: 'http://localhost:5200/embed-test.html' });
// Or evaluate JavaScript to simulate embed
await mcp__playwright__browser_evaluate({
function: `() => {
// Check if GograbAI is available
return typeof window.GograbAI !== 'undefined';
}`
});
Test HTML for Embed.js:
<!DOCTYPE html>
<html>
<head>
<title>Embed Test</title>
</head>
<body>
<h1>Embed.js Test Page</h1>
<div id="ai-panel"></div>
<script src="http://localhost:5300/embed.js"></script>
<script>
const ai = GograbAI.embed({
container: '#ai-panel',
theme: 'light',
position: 'right',
onReady: (api) => {
console.log('AI Panel Ready!', api);
}
});
</script>
</body>
</html>
Expected Results:
GograbAIglobal should be availableembed()should create an iframe- Panel should be visible and functional
4.2 Test: Chrome Extension (Manual)
Objective: Verify the Chrome extension injects panel correctly.
Note: Chrome extensions require manual installation. This test provides verification steps.
Steps:
- Load unpacked extension from
apps/gograb-ai-extension/ - Navigate to any website
- Click extension icon
- Verify panel appears
Manual Verification Checklist:
- Extension icon appears in toolbar
- Clicking icon opens popup
- Toggle button shows/hides panel
- Panel is positioned correctly (right/left/float)
- Theme matches system preference or setting
- Context menu "Ask GoGrab AI about" works on selected text
4.3 Test: Security - Origin Validation
Objective: Verify only allowed origins can communicate.
Steps:
- Try to send message from disallowed origin
- Verify message is rejected
// Evaluate code that simulates cross-origin message
await mcp__playwright__browser_evaluate({
function: `() => {
// This should be rejected if origin validation works
window.postMessage({
type: 'AI_MALICIOUS_REQUEST',
payload: { test: true },
timestamp: Date.now()
}, '*');
return 'Message sent';
}`
});
// Check console for rejection message
await mcp__playwright__browser_console_messages({ level: 'warning' });
Expected Results:
- Messages from unknown origins should be ignored
- Console should show warning about rejected origin
- No data should be leaked to unauthorized origins
4.4 Test: API Key Authentication
Objective: Verify API key auth works for external sites.
Steps:
- Configure embed with API key
- Verify requests include API key header
// Check network requests for API key
await mcp__playwright__browser_network_requests({ includeStatic: false });
// Look for X-API-Key header in requests
Expected Results:
- Requests should include
X-API-Keyheader - Invalid API keys should be rejected
- Rate limiting should apply per API key
5. Testing Phase 4: Advanced Features
5.1 Test: Multi-Instance Support
Objective: Verify multiple panels can exist on the same page.
Steps:
- Create page with two embedded panels
- Verify each has unique instanceId
- Send message to specific instance
- Verify only target instance receives it
// Navigate to a page with multiple instances
await mcp__playwright__browser_navigate({ url: 'http://localhost:5200/multi-instance-test' });
// Take snapshot to see both panels
await mcp__playwright__browser_snapshot({});
// Check console for instance IDs
await mcp__playwright__browser_console_messages({ level: 'info' });
// Look for:
// "[Embeddable Host] Host adapter initialized with instanceId: host_xxxxx"
Test HTML for Multi-Instance:
<div id="panel-1"></div>
<div id="panel-2"></div>
<script>
// Panel 1 - Editor context
const panel1 = GograbAI.embed({
container: '#panel-1',
instanceId: 'editor-panel',
context: { type: 'editor' }
});
// Panel 2 - Preview context
const panel2 = GograbAI.embed({
container: '#panel-2',
instanceId: 'preview-panel',
context: { type: 'preview' }
});
</script>
Expected Results:
- Each panel should have unique instanceId
- Messages should only go to intended instance
- Broadcast messages (auth) should go to all
- State should be isolated per instance
5.2 Test: Widget Embedding
Objective: Verify individual widgets can be embedded.
Steps:
- Navigate to page with embedded widget
- Verify widget loads correctly
- Test widget interactions
// Navigate to widget embed test
await mcp__playwright__browser_navigate({ url: 'http://localhost:5200/widget-embed-test' });
// Wait for widget to load
await mcp__playwright__browser_wait_for({ text: 'Widget loaded' });
// Take snapshot
await mcp__playwright__browser_snapshot({});
Expected Results:
- Widget should render in iframe
- Widget should respect readOnly mode
- Theme should apply correctly
- Interactions should emit events to host
5.3 Test: Offline Support
Objective: Verify app works offline with cached data.
Steps:
- Load app while online
- Simulate offline mode
- Verify cached content displays
- Go back online and verify sync
// First, load the page while online
await mcp__playwright__browser_navigate({ url: 'http://localhost:5300/panel' });
// Wait for service worker to register
await mcp__playwright__browser_wait_for({ time: 3 });
// Check service worker status
await mcp__playwright__browser_evaluate({
function: `async () => {
const registration = await navigator.serviceWorker.getRegistration();
return {
active: !!registration?.active,
waiting: !!registration?.waiting,
scope: registration?.scope
};
}`
});
// Simulate offline (Note: Playwright may need special handling for this)
// In manual testing, use DevTools Network tab to go offline
Expected Results:
- Service worker should be registered
- Cached resources should load when offline
- Offline indicator should appear
- Queued messages should sync when back online
5.4 Test: Offline Indicator Component
Objective: Verify offline indicator shows correct state.
Steps:
- Load page online - indicator should be hidden
- Go offline - indicator should appear
- Dismiss indicator
- Go online - indicator should hide
// Check if offline indicator exists (should be hidden when online)
await mcp__playwright__browser_snapshot({});
// The indicator has class "offline-indicator" and shows when offline
// Look for absence of this element when online
Expected Results:
- Indicator hidden when online
- Shows "You're offline" message when offline
- Dismiss button works
- Auto-reappears after 30 seconds if still offline
6. Automated Test Scenarios
6.1 Full Integration Test Flow
// Complete integration test
async function runIntegrationTest() {
// 1. Navigate to test page
await mcp__playwright__browser_navigate({
url: 'http://localhost:5200/system/ai-iframe-test'
});
// 2. Wait for panel to be ready
await mcp__playwright__browser_wait_for({ text: 'Ready', time: 10 });
// 3. Take snapshot
const snapshot = await mcp__playwright__browser_snapshot({});
// 4. Check console for errors
const messages = await mcp__playwright__browser_console_messages({
level: 'error'
});
// 5. Verify no errors
if (messages.includes('error')) {
throw new Error('Console errors detected');
}
// 6. Test click-to-add functionality
await mcp__playwright__browser_click({
ref: '[add-button-ref]',
element: 'Add to Canvas button'
});
// 7. Verify success
await mcp__playwright__browser_wait_for({ text: 'Added' });
console.log('Integration test passed!');
}
6.2 Performance Test
// Performance measurement test
async function runPerformanceTest() {
// Navigate and measure load time
const startTime = Date.now();
await mcp__playwright__browser_navigate({
url: 'http://localhost:5200/system/ai-iframe-test'
});
await mcp__playwright__browser_wait_for({ text: 'Ready' });
const loadTime = Date.now() - startTime;
console.log(`Panel load time: ${loadTime}ms`);
// Check against threshold
if (loadTime > 2000) {
console.warn('Panel load time exceeds 2s threshold');
}
// Check network requests
const requests = await mcp__playwright__browser_network_requests({
includeStatic: false
});
console.log(`Network requests: ${requests}`);
}
6.3 Security Test
// Security validation test
async function runSecurityTest() {
await mcp__playwright__browser_navigate({
url: 'http://localhost:5200/system/ai-iframe-test'
});
// Try to inject malicious message
await mcp__playwright__browser_evaluate({
function: `() => {
// Attempt XSS via postMessage
window.postMessage({
type: 'AI_CONTEXT_UPDATE',
payload: {
contextType: '<script>alert("XSS")</script>'
},
timestamp: Date.now()
}, '*');
}`
});
// Verify XSS was not executed
// Check for alert dialog
// If no dialog, XSS was prevented
console.log('Security test passed!');
}
7. Troubleshooting
7.1 Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Iframe not loading | CORS or CSP blocking | Check allowed origins in config |
| Messages not received | Origin mismatch | Verify allowedOrigins includes both hosts |
| Auth not working | Token not forwarded | Check autoSendOnReady is true |
| Drag-drop not working | Event forwarding issue | Check mouse event handlers |
| Panel not visible | CSS z-index issue | Verify z-index is high enough |
7.2 Debug Commands
// Check console messages
await mcp__playwright__browser_console_messages({ level: 'debug' });
// Check network requests
await mcp__playwright__browser_network_requests({ includeStatic: true });
// Take screenshot for visual verification
await mcp__playwright__browser_take_screenshot({
type: 'png',
filename: 'debug-screenshot.png'
});
// Get page state
await mcp__playwright__browser_evaluate({
function: `() => ({
url: window.location.href,
title: document.title,
iframes: document.querySelectorAll('iframe').length,
errors: window.__errors || []
})`
});
7.3 Logging Reference
Look for these log prefixes in console:
| Prefix | Source | Purpose |
|---|---|---|
[Embeddable Host] |
Host adapter | Host-side operations |
[Embeddable Embedded] |
Embedded adapter | Embedded app operations |
[SW] |
Service worker | Offline/caching operations |
[Offline] |
Offline service | Network status changes |
Quick Reference Card
Test Page URLs
Host Test: http://localhost:5200/system/ai-iframe-test
Panel Direct: http://localhost:5300/panel
Widget Embed: http://localhost:5200/widget-embed-test
Multi-Instance: http://localhost:5200/multi-instance-test
Key Playwright Commands
// Navigate
await mcp__playwright__browser_navigate({ url: '...' });
// Snapshot (accessibility tree)
await mcp__playwright__browser_snapshot({});
// Click element
await mcp__playwright__browser_click({ ref: '...', element: '...' });
// Type text
await mcp__playwright__browser_type({ ref: '...', text: '...' });
// Wait for text/element
await mcp__playwright__browser_wait_for({ text: '...' });
// Check console
await mcp__playwright__browser_console_messages({ level: 'info' });
// Check network
await mcp__playwright__browser_network_requests({ includeStatic: false });
// Screenshot
await mcp__playwright__browser_take_screenshot({ type: 'png' });
// Evaluate JS
await mcp__playwright__browser_evaluate({ function: '() => {...}' });
Expected Message Flow
1. Host loads iframe
2. Embedded sends AI_APP_READY
3. Host sends AI_AUTH_INIT (if autoSendOnReady)
4. Host sends AI_CONTEXT_UPDATE
5. Heartbeat: AI_PING / AI_PONG every 10s
Last Updated: Session 73 | Phase 4 Complete