|
|
@@ -0,0 +1,106 @@
|
|
|
+const { spawn } = require('child_process');
|
|
|
+const http = require('http');
|
|
|
+const WebSocket = require('ws');
|
|
|
+const fs = require('fs');
|
|
|
+const path = require('path');
|
|
|
+const assert = require('assert');
|
|
|
+
|
|
|
+const SERVER_PATH = path.join(__dirname, '../../lib/brainstorm-server/index.js');
|
|
|
+const TEST_PORT = 3334;
|
|
|
+const TEST_SCREEN = '/tmp/brainstorm-test/screen.html';
|
|
|
+
|
|
|
+// Clean up test directory
|
|
|
+function cleanup() {
|
|
|
+ if (fs.existsSync(path.dirname(TEST_SCREEN))) {
|
|
|
+ fs.rmSync(path.dirname(TEST_SCREEN), { recursive: true });
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function sleep(ms) {
|
|
|
+ return new Promise(resolve => setTimeout(resolve, ms));
|
|
|
+}
|
|
|
+
|
|
|
+async function fetch(url) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ http.get(url, (res) => {
|
|
|
+ let data = '';
|
|
|
+ res.on('data', chunk => data += chunk);
|
|
|
+ res.on('end', () => resolve({ status: res.statusCode, body: data }));
|
|
|
+ }).on('error', reject);
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+async function runTests() {
|
|
|
+ cleanup();
|
|
|
+
|
|
|
+ // Start server
|
|
|
+ const server = spawn('node', [SERVER_PATH], {
|
|
|
+ env: { ...process.env, BRAINSTORM_PORT: TEST_PORT, BRAINSTORM_SCREEN: TEST_SCREEN }
|
|
|
+ });
|
|
|
+
|
|
|
+ let stdout = '';
|
|
|
+ server.stdout.on('data', (data) => { stdout += data.toString(); });
|
|
|
+ server.stderr.on('data', (data) => { console.error('Server stderr:', data.toString()); });
|
|
|
+
|
|
|
+ await sleep(1000); // Wait for server to start
|
|
|
+
|
|
|
+ try {
|
|
|
+ // Test 1: Server starts and outputs JSON
|
|
|
+ console.log('Test 1: Server startup message');
|
|
|
+ assert(stdout.includes('server-started'), 'Should output server-started');
|
|
|
+ assert(stdout.includes(TEST_PORT.toString()), 'Should include port');
|
|
|
+ console.log(' PASS');
|
|
|
+
|
|
|
+ // Test 2: GET / returns HTML with helper injected
|
|
|
+ console.log('Test 2: Serves HTML with helper injected');
|
|
|
+ const res = await fetch(`http://localhost:${TEST_PORT}/`);
|
|
|
+ assert.strictEqual(res.status, 200);
|
|
|
+ assert(res.body.includes('brainstorm'), 'Should include brainstorm content');
|
|
|
+ assert(res.body.includes('WebSocket'), 'Should have helper.js injected');
|
|
|
+ console.log(' PASS');
|
|
|
+
|
|
|
+ // Test 3: WebSocket connection and event relay
|
|
|
+ console.log('Test 3: WebSocket relays events to stdout');
|
|
|
+ stdout = ''; // Reset stdout capture
|
|
|
+ const ws = new WebSocket(`ws://localhost:${TEST_PORT}`);
|
|
|
+ await new Promise(resolve => ws.on('open', resolve));
|
|
|
+
|
|
|
+ ws.send(JSON.stringify({ type: 'click', text: 'Test Button' }));
|
|
|
+ await sleep(300);
|
|
|
+
|
|
|
+ assert(stdout.includes('user-event'), 'Should relay user events');
|
|
|
+ assert(stdout.includes('Test Button'), 'Should include event data');
|
|
|
+ ws.close();
|
|
|
+ console.log(' PASS');
|
|
|
+
|
|
|
+ // Test 4: File change triggers reload notification
|
|
|
+ console.log('Test 4: File change notifies browsers');
|
|
|
+ const ws2 = new WebSocket(`ws://localhost:${TEST_PORT}`);
|
|
|
+ await new Promise(resolve => ws2.on('open', resolve));
|
|
|
+
|
|
|
+ let gotReload = false;
|
|
|
+ ws2.on('message', (data) => {
|
|
|
+ const msg = JSON.parse(data.toString());
|
|
|
+ if (msg.type === 'reload') gotReload = true;
|
|
|
+ });
|
|
|
+
|
|
|
+ // Modify the screen file
|
|
|
+ fs.writeFileSync(TEST_SCREEN, '<html><body>Updated</body></html>');
|
|
|
+ await sleep(500);
|
|
|
+
|
|
|
+ assert(gotReload, 'Should send reload message on file change');
|
|
|
+ ws2.close();
|
|
|
+ console.log(' PASS');
|
|
|
+
|
|
|
+ console.log('\nAll tests passed!');
|
|
|
+
|
|
|
+ } finally {
|
|
|
+ server.kill();
|
|
|
+ cleanup();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+runTests().catch(err => {
|
|
|
+ console.error('Test failed:', err);
|
|
|
+ process.exit(1);
|
|
|
+});
|