mirror of
https://github.com/soconnor0919/hristudio.git
synced 2025-12-11 06:34:44 -05:00
Pre-conf work 2025
This commit is contained in:
86
public/simple-ws-test.html
Normal file
86
public/simple-ws-test.html
Normal file
@@ -0,0 +1,86 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Simple WebSocket Test</title>
|
||||
<style>
|
||||
body { font-family: Arial; padding: 20px; }
|
||||
.status { padding: 10px; margin: 10px 0; border-radius: 5px; }
|
||||
.connected { background: #d4edda; color: #155724; }
|
||||
.disconnected { background: #f8d7da; color: #721c24; }
|
||||
.connecting { background: #d1ecf1; color: #0c5460; }
|
||||
.log { background: #f8f9fa; padding: 10px; height: 300px; overflow-y: auto; border: 1px solid #ddd; font-family: monospace; white-space: pre-wrap; }
|
||||
button { padding: 8px 16px; margin: 5px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>WebSocket Test</h1>
|
||||
<div id="status" class="status disconnected">Disconnected</div>
|
||||
<button onclick="connect()">Connect</button>
|
||||
<button onclick="disconnect()">Disconnect</button>
|
||||
<button onclick="sendTest()">Send Test</button>
|
||||
<div id="log" class="log"></div>
|
||||
|
||||
<script>
|
||||
let ws = null;
|
||||
const log = document.getElementById('log');
|
||||
const status = document.getElementById('status');
|
||||
|
||||
function updateStatus(text, className) {
|
||||
status.textContent = text;
|
||||
status.className = 'status ' + className;
|
||||
}
|
||||
|
||||
function addLog(msg) {
|
||||
log.textContent += new Date().toLocaleTimeString() + ': ' + msg + '\n';
|
||||
log.scrollTop = log.scrollHeight;
|
||||
}
|
||||
|
||||
function connect() {
|
||||
const trialId = '931c626d-fe3f-4db3-a36c-50d6898e1b17';
|
||||
const token = btoa(JSON.stringify({userId: '08594f2b-64fe-4952-947f-3edc5f144f52', timestamp: Math.floor(Date.now()/1000)}));
|
||||
const url = `ws://localhost:3000/api/websocket?trialId=${trialId}&token=${token}`;
|
||||
|
||||
addLog('Connecting to: ' + url);
|
||||
updateStatus('Connecting...', 'connecting');
|
||||
|
||||
ws = new WebSocket(url);
|
||||
|
||||
ws.onopen = function() {
|
||||
addLog('✅ Connected!');
|
||||
updateStatus('Connected', 'connected');
|
||||
};
|
||||
|
||||
ws.onmessage = function(event) {
|
||||
addLog('📨 Received: ' + event.data);
|
||||
};
|
||||
|
||||
ws.onclose = function(event) {
|
||||
addLog('🔌 Closed: ' + event.code + ' ' + event.reason);
|
||||
updateStatus('Disconnected', 'disconnected');
|
||||
};
|
||||
|
||||
ws.onerror = function(error) {
|
||||
addLog('❌ Error: ' + error);
|
||||
updateStatus('Error', 'disconnected');
|
||||
};
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
if (ws) {
|
||||
ws.close();
|
||||
ws = null;
|
||||
}
|
||||
}
|
||||
|
||||
function sendTest() {
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
const msg = JSON.stringify({type: 'heartbeat', data: {}});
|
||||
ws.send(msg);
|
||||
addLog('📤 Sent: ' + msg);
|
||||
} else {
|
||||
addLog('❌ Not connected');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
297
public/test-websocket.html
Normal file
297
public/test-websocket.html
Normal file
@@ -0,0 +1,297 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>HRIStudio WebSocket Test</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.container {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
.status {
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
.connected { background-color: #d4edda; color: #155724; }
|
||||
.connecting { background-color: #d1ecf1; color: #0c5460; }
|
||||
.disconnected { background-color: #f8d7da; color: #721c24; }
|
||||
.error { background-color: #f5c6cb; color: #721c24; }
|
||||
.log {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
height: 300px;
|
||||
overflow-y: auto;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
button {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
button:hover { background-color: #0056b3; }
|
||||
button:disabled { background-color: #6c757d; cursor: not-allowed; }
|
||||
input, select {
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
margin: 5px;
|
||||
}
|
||||
.input-group {
|
||||
margin: 10px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.input-group label {
|
||||
min-width: 100px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🔌 HRIStudio WebSocket Test</h1>
|
||||
|
||||
<div class="input-group">
|
||||
<label>Trial ID:</label>
|
||||
<input type="text" id="trialId" value="931c626d-fe3f-4db3-a36c-50d6898e1b17" style="width: 300px;">
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label>User ID:</label>
|
||||
<input type="text" id="userId" value="08594f2b-64fe-4952-947f-3edc5f144f52" style="width: 300px;">
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label>Server:</label>
|
||||
<input type="text" id="serverUrl" value="ws://localhost:3000" style="width: 200px;">
|
||||
</div>
|
||||
|
||||
<div id="status" class="status disconnected">Disconnected</div>
|
||||
|
||||
<div>
|
||||
<button id="connectBtn" onclick="connect()">Connect</button>
|
||||
<button id="disconnectBtn" onclick="disconnect()" disabled>Disconnect</button>
|
||||
<button onclick="sendHeartbeat()" disabled id="heartbeatBtn">Send Heartbeat</button>
|
||||
<button onclick="requestStatus()" disabled id="statusBtn">Request Status</button>
|
||||
<button onclick="sendTestAction()" disabled id="actionBtn">Send Test Action</button>
|
||||
<button onclick="clearLog()">Clear Log</button>
|
||||
</div>
|
||||
|
||||
<h3>📨 Message Log</h3>
|
||||
<div id="log" class="log"></div>
|
||||
|
||||
<h3>🎮 Send Custom Message</h3>
|
||||
<div class="input-group">
|
||||
<label>Type:</label>
|
||||
<select id="messageType">
|
||||
<option value="heartbeat">heartbeat</option>
|
||||
<option value="request_trial_status">request_trial_status</option>
|
||||
<option value="trial_action">trial_action</option>
|
||||
<option value="wizard_intervention">wizard_intervention</option>
|
||||
<option value="step_transition">step_transition</option>
|
||||
</select>
|
||||
<button onclick="sendCustomMessage()" disabled id="customBtn">Send</button>
|
||||
</div>
|
||||
<textarea id="messageData" placeholder='{"key": "value"}' rows="3" style="width: 100%; margin: 5px 0;"></textarea>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let ws = null;
|
||||
let connectionAttempts = 0;
|
||||
const maxRetries = 3;
|
||||
|
||||
const statusEl = document.getElementById('status');
|
||||
const logEl = document.getElementById('log');
|
||||
const connectBtn = document.getElementById('connectBtn');
|
||||
const disconnectBtn = document.getElementById('disconnectBtn');
|
||||
const heartbeatBtn = document.getElementById('heartbeatBtn');
|
||||
const statusBtn = document.getElementById('statusBtn');
|
||||
const actionBtn = document.getElementById('actionBtn');
|
||||
const customBtn = document.getElementById('customBtn');
|
||||
|
||||
function log(message, type = 'info') {
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
const prefix = type === 'sent' ? '📤' : type === 'received' ? '📨' : type === 'error' ? '❌' : 'ℹ️';
|
||||
logEl.textContent += `[${timestamp}] ${prefix} ${message}\n`;
|
||||
logEl.scrollTop = logEl.scrollHeight;
|
||||
}
|
||||
|
||||
function updateStatus(status, className) {
|
||||
statusEl.textContent = status;
|
||||
statusEl.className = `status ${className}`;
|
||||
}
|
||||
|
||||
function updateButtons(connected) {
|
||||
connectBtn.disabled = connected;
|
||||
disconnectBtn.disabled = !connected;
|
||||
heartbeatBtn.disabled = !connected;
|
||||
statusBtn.disabled = !connected;
|
||||
actionBtn.disabled = !connected;
|
||||
customBtn.disabled = !connected;
|
||||
}
|
||||
|
||||
function generateToken() {
|
||||
const userId = document.getElementById('userId').value;
|
||||
const tokenData = {
|
||||
userId: userId,
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
};
|
||||
return btoa(JSON.stringify(tokenData));
|
||||
}
|
||||
|
||||
function connect() {
|
||||
if (ws && (ws.readyState === WebSocket.CONNECTING || ws.readyState === WebSocket.OPEN)) {
|
||||
log('Already connected or connecting', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const trialId = document.getElementById('trialId').value;
|
||||
const serverUrl = document.getElementById('serverUrl').value;
|
||||
const token = generateToken();
|
||||
|
||||
if (!trialId) {
|
||||
log('Please enter a trial ID', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const wsUrl = `${serverUrl}/api/websocket?trialId=${trialId}&token=${token}`;
|
||||
log(`Connecting to: ${wsUrl}`);
|
||||
|
||||
updateStatus('Connecting...', 'connecting');
|
||||
|
||||
try {
|
||||
ws = new WebSocket(wsUrl);
|
||||
|
||||
ws.onopen = function() {
|
||||
connectionAttempts = 0;
|
||||
updateStatus('Connected', 'connected');
|
||||
updateButtons(true);
|
||||
log('WebSocket connection established!');
|
||||
};
|
||||
|
||||
ws.onmessage = function(event) {
|
||||
try {
|
||||
const message = JSON.parse(event.data);
|
||||
log(`${message.type}: ${JSON.stringify(message.data, null, 2)}`, 'received');
|
||||
} catch (e) {
|
||||
log(`Raw message: ${event.data}`, 'received');
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = function(event) {
|
||||
updateStatus(`Disconnected (${event.code})`, 'disconnected');
|
||||
updateButtons(false);
|
||||
log(`Connection closed: ${event.code} ${event.reason}`);
|
||||
|
||||
// Auto-reconnect logic
|
||||
if (event.code !== 1000 && connectionAttempts < maxRetries) {
|
||||
connectionAttempts++;
|
||||
log(`Attempting reconnection ${connectionAttempts}/${maxRetries}...`);
|
||||
setTimeout(() => connect(), 2000 * connectionAttempts);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onerror = function(event) {
|
||||
updateStatus('Error', 'error');
|
||||
updateButtons(false);
|
||||
log('WebSocket error occurred', 'error');
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
log(`Failed to create WebSocket: ${error.message}`, 'error');
|
||||
updateStatus('Error', 'error');
|
||||
updateButtons(false);
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
if (ws) {
|
||||
ws.close(1000, 'Manual disconnect');
|
||||
ws = null;
|
||||
}
|
||||
connectionAttempts = maxRetries; // Prevent auto-reconnect
|
||||
}
|
||||
|
||||
function sendMessage(type, data = {}) {
|
||||
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
||||
log('WebSocket not connected', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const message = { type, data };
|
||||
ws.send(JSON.stringify(message));
|
||||
log(`${type}: ${JSON.stringify(data, null, 2)}`, 'sent');
|
||||
}
|
||||
|
||||
function sendHeartbeat() {
|
||||
sendMessage('heartbeat');
|
||||
}
|
||||
|
||||
function requestStatus() {
|
||||
sendMessage('request_trial_status');
|
||||
}
|
||||
|
||||
function sendTestAction() {
|
||||
sendMessage('trial_action', {
|
||||
actionType: 'test_action',
|
||||
message: 'Hello from WebSocket test!',
|
||||
timestamp: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
function sendCustomMessage() {
|
||||
const type = document.getElementById('messageType').value;
|
||||
let data = {};
|
||||
|
||||
try {
|
||||
const dataText = document.getElementById('messageData').value.trim();
|
||||
if (dataText) {
|
||||
data = JSON.parse(dataText);
|
||||
}
|
||||
} catch (e) {
|
||||
log('Invalid JSON in message data', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
sendMessage(type, data);
|
||||
}
|
||||
|
||||
function clearLog() {
|
||||
logEl.textContent = '';
|
||||
}
|
||||
|
||||
// Auto-connect on page load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
log('WebSocket test page loaded');
|
||||
log('Click "Connect" to start testing the WebSocket connection');
|
||||
});
|
||||
|
||||
// Handle page unload
|
||||
window.addEventListener('beforeunload', function() {
|
||||
if (ws) {
|
||||
ws.close(1000, 'Page unload');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
477
public/ws-check.html
Normal file
477
public/ws-check.html
Normal file
@@ -0,0 +1,477 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WebSocket Connection Test | HRIStudio</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
background: #f8fafc;
|
||||
color: #334155;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 2rem auto;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: #1e293b;
|
||||
color: white;
|
||||
padding: 1rem;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 20px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.status-connecting {
|
||||
background: #dbeafe;
|
||||
color: #1e40af;
|
||||
}
|
||||
|
||||
.status-connected {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.status-failed {
|
||||
background: #fef2f2;
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.status-fallback {
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
.dot.pulse {
|
||||
animation: pulse 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
.log {
|
||||
background: #f1f5f9;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
height: 300px;
|
||||
overflow-y: auto;
|
||||
font-family: "Courier New", monospace;
|
||||
font-size: 0.875rem;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
button:hover:not(:disabled) {
|
||||
background: #2563eb;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background: #94a3b8;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.input-group label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 6px;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
background: #f8fafc;
|
||||
padding: 0.75rem;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 0.75rem;
|
||||
color: #64748b;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-weight: 500;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
margin: 1rem 0;
|
||||
border-left: 4px solid;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background: #eff6ff;
|
||||
border-color: #3b82f6;
|
||||
color: #1e40af;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background: #fefce8;
|
||||
border-color: #eab308;
|
||||
color: #a16207;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
🔌 WebSocket Connection Test
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="alert alert-info">
|
||||
<strong>Development Mode:</strong> WebSocket connections are expected to fail in Next.js development server.
|
||||
The app automatically falls back to polling for real-time updates.
|
||||
</div>
|
||||
|
||||
<div id="status" class="status-badge status-failed">
|
||||
<div class="dot"></div>
|
||||
<span>Disconnected</span>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="trialId">Trial ID:</label>
|
||||
<input type="text" id="trialId" value="931c626d-fe3f-4db3-a36c-50d6898e1b17">
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="userId">User ID:</label>
|
||||
<input type="text" id="userId" value="08594f2b-64fe-4952-947f-3edc5f144f52">
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button id="connectBtn" onclick="testConnection()">Test WebSocket Connection</button>
|
||||
<button id="disconnectBtn" onclick="disconnect()" disabled>Disconnect</button>
|
||||
<button onclick="clearLog()">Clear Log</button>
|
||||
<button onclick="testPolling()">Test Polling Fallback</button>
|
||||
</div>
|
||||
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-label">Connection Attempts</div>
|
||||
<div class="info-value" id="attempts">0</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Messages Received</div>
|
||||
<div class="info-value" id="messages">0</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Connection Time</div>
|
||||
<div class="info-value" id="connectionTime">N/A</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">Last Error</div>
|
||||
<div class="info-value" id="lastError">None</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
📋 Connection Log
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div id="log" class="log"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
ℹ️ How This Works
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<h3 style="margin-bottom: 0.5rem;">Expected Behavior:</h3>
|
||||
<ul style="margin-left: 2rem; margin-bottom: 1rem;">
|
||||
<li><strong>Development:</strong> WebSocket fails, app uses polling fallback (2-second intervals)</li>
|
||||
<li><strong>Production:</strong> WebSocket connects successfully, minimal polling backup</li>
|
||||
</ul>
|
||||
|
||||
<h3 style="margin-bottom: 0.5rem;">Testing Steps:</h3>
|
||||
<ol style="margin-left: 2rem;">
|
||||
<li>Click "Test WebSocket Connection" - should fail with connection error</li>
|
||||
<li>Click "Test Polling Fallback" - should work and show API responses</li>
|
||||
<li>Check browser Network tab for ongoing tRPC polling requests</li>
|
||||
<li>Open actual wizard interface to see full functionality</li>
|
||||
</ol>
|
||||
|
||||
<div class="alert alert-warning" style="margin-top: 1rem;">
|
||||
<strong>Note:</strong> This test confirms the WebSocket failure is expected in development.
|
||||
Your trial runner works perfectly using the polling fallback system.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let ws = null;
|
||||
let attempts = 0;
|
||||
let messages = 0;
|
||||
let startTime = null;
|
||||
|
||||
const elements = {
|
||||
status: document.getElementById('status'),
|
||||
log: document.getElementById('log'),
|
||||
connectBtn: document.getElementById('connectBtn'),
|
||||
disconnectBtn: document.getElementById('disconnectBtn'),
|
||||
attempts: document.getElementById('attempts'),
|
||||
messages: document.getElementById('messages'),
|
||||
connectionTime: document.getElementById('connectionTime'),
|
||||
lastError: document.getElementById('lastError')
|
||||
};
|
||||
|
||||
function updateStatus(text, className, pulse = false) {
|
||||
elements.status.innerHTML = `
|
||||
<div class="dot ${pulse ? 'pulse' : ''}"></div>
|
||||
<span>${text}</span>
|
||||
`;
|
||||
elements.status.className = `status-badge ${className}`;
|
||||
}
|
||||
|
||||
function log(message, type = 'info') {
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
const prefix = {
|
||||
info: 'ℹ️',
|
||||
success: '✅',
|
||||
error: '❌',
|
||||
warning: '⚠️',
|
||||
websocket: '🔌',
|
||||
polling: '🔄'
|
||||
}[type] || 'ℹ️';
|
||||
|
||||
elements.log.textContent += `[${timestamp}] ${prefix} ${message}\n`;
|
||||
elements.log.scrollTop = elements.log.scrollHeight;
|
||||
}
|
||||
|
||||
function updateButtons(connecting = false, connected = false) {
|
||||
elements.connectBtn.disabled = connecting || connected;
|
||||
elements.disconnectBtn.disabled = !connected;
|
||||
}
|
||||
|
||||
function generateToken() {
|
||||
const userId = document.getElementById('userId').value;
|
||||
return btoa(JSON.stringify({
|
||||
userId: userId,
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
}));
|
||||
}
|
||||
|
||||
function testConnection() {
|
||||
const trialId = document.getElementById('trialId').value;
|
||||
const token = generateToken();
|
||||
|
||||
if (!trialId) {
|
||||
log('Please enter a trial ID', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
attempts++;
|
||||
elements.attempts.textContent = attempts;
|
||||
startTime = Date.now();
|
||||
|
||||
updateStatus('Connecting...', 'status-connecting', true);
|
||||
updateButtons(true, false);
|
||||
|
||||
const wsUrl = `ws://localhost:3000/api/websocket?trialId=${trialId}&token=${token}`;
|
||||
log(`Attempting WebSocket connection to: ${wsUrl}`, 'websocket');
|
||||
log('This is expected to fail in development mode...', 'warning');
|
||||
|
||||
try {
|
||||
ws = new WebSocket(wsUrl);
|
||||
|
||||
ws.onopen = function() {
|
||||
const duration = Date.now() - startTime;
|
||||
elements.connectionTime.textContent = `${duration}ms`;
|
||||
updateStatus('Connected', 'status-connected');
|
||||
updateButtons(false, true);
|
||||
log('🎉 WebSocket connected successfully!', 'success');
|
||||
log('This is unexpected in development mode - you may be in production', 'info');
|
||||
};
|
||||
|
||||
ws.onmessage = function(event) {
|
||||
messages++;
|
||||
elements.messages.textContent = messages;
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
log(`📨 Received: ${data.type} - ${JSON.stringify(data.data)}`, 'success');
|
||||
} catch (e) {
|
||||
log(`📨 Received (raw): ${event.data}`, 'success');
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = function(event) {
|
||||
updateStatus('Connection Failed (Expected)', 'status-failed');
|
||||
updateButtons(false, false);
|
||||
|
||||
if (event.code === 1006) {
|
||||
log('✅ Connection failed as expected in development mode', 'success');
|
||||
log('This confirms WebSocket failure behavior is working correctly', 'info');
|
||||
elements.lastError.textContent = 'Expected dev failure';
|
||||
} else {
|
||||
log(`Connection closed: ${event.code} - ${event.reason}`, 'error');
|
||||
elements.lastError.textContent = `${event.code}: ${event.reason}`;
|
||||
}
|
||||
|
||||
updateStatus('Fallback to Polling (Normal)', 'status-fallback');
|
||||
log('🔄 App will automatically use polling fallback', 'polling');
|
||||
};
|
||||
|
||||
ws.onerror = function(error) {
|
||||
log('✅ WebSocket error occurred (expected in dev mode)', 'success');
|
||||
log('Error details: Connection establishment failed', 'info');
|
||||
elements.lastError.textContent = 'Connection refused (expected)';
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
log(`Failed to create WebSocket: ${error.message}`, 'error');
|
||||
updateStatus('Connection Failed', 'status-failed');
|
||||
updateButtons(false, false);
|
||||
elements.lastError.textContent = error.message;
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
if (ws) {
|
||||
ws.close(1000, 'Manual disconnect');
|
||||
ws = null;
|
||||
}
|
||||
updateStatus('Disconnected', 'status-failed');
|
||||
updateButtons(false, false);
|
||||
log('Disconnected by user', 'info');
|
||||
}
|
||||
|
||||
function clearLog() {
|
||||
elements.log.textContent = '';
|
||||
messages = 0;
|
||||
elements.messages.textContent = messages;
|
||||
log('Log cleared', 'info');
|
||||
}
|
||||
|
||||
async function testPolling() {
|
||||
log('🔄 Testing polling fallback (tRPC API)...', 'polling');
|
||||
|
||||
try {
|
||||
const trialId = document.getElementById('trialId').value;
|
||||
const response = await fetch(`/api/trpc/trials.get?batch=1&input=${encodeURIComponent(JSON.stringify({0:{json:{id:trialId}}}))}`);
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
log('✅ Polling fallback working! API response received', 'success');
|
||||
log(`Response status: ${response.status}`, 'info');
|
||||
log('This is how the app gets real-time updates in development', 'polling');
|
||||
|
||||
if (data[0]?.result?.data) {
|
||||
log(`Trial status: ${data[0].result.data.json.status}`, 'info');
|
||||
}
|
||||
} else {
|
||||
log(`❌ Polling failed: ${response.status} ${response.statusText}`, 'error');
|
||||
if (response.status === 401) {
|
||||
log('You may need to sign in first', 'warning');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
log(`❌ Polling error: ${error.message}`, 'error');
|
||||
log('Make sure the dev server is running', 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
log('WebSocket test page loaded', 'info');
|
||||
log('Click "Test WebSocket Connection" to verify expected failure', 'info');
|
||||
log('Click "Test Polling Fallback" to verify API connectivity', 'info');
|
||||
|
||||
// Auto-test on load
|
||||
setTimeout(() => {
|
||||
log('Running automatic connection test...', 'websocket');
|
||||
testConnection();
|
||||
}, 1000);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user