mirror of
https://github.com/obra/superpowers.git
synced 2026-04-21 08:59:04 +08:00
refactor: server-side frame wrapping and helper.js consolidation
Move toggleSelect/send/selectedChoice from frame-template.html inline script to helper.js so they're auto-injected. Server now detects bare HTML fragments (no DOCTYPE/html tag) and wraps them in the frame template automatically. Full documents pass through as before. Fix dark mode in sendToClaude confirmation (was using hardcoded colors). Fix test env var bug (BRAINSTORM_SCREEN -> BRAINSTORM_DIR). Add tests for fragment wrapping, full doc passthrough, and helper.js.
This commit is contained in:
@@ -7,12 +7,11 @@ 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';
|
||||
const TEST_DIR = '/tmp/brainstorm-test';
|
||||
|
||||
// Clean up test directory
|
||||
function cleanup() {
|
||||
if (fs.existsSync(path.dirname(TEST_SCREEN))) {
|
||||
fs.rmSync(path.dirname(TEST_SCREEN), { recursive: true });
|
||||
if (fs.existsSync(TEST_DIR)) {
|
||||
fs.rmSync(TEST_DIR, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,19 +29,23 @@ async function fetch(url) {
|
||||
});
|
||||
}
|
||||
|
||||
function startServer() {
|
||||
return spawn('node', [SERVER_PATH], {
|
||||
env: { ...process.env, BRAINSTORM_PORT: TEST_PORT, BRAINSTORM_DIR: TEST_DIR }
|
||||
});
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
cleanup();
|
||||
fs.mkdirSync(TEST_DIR, { recursive: true });
|
||||
|
||||
// Start server
|
||||
const server = spawn('node', [SERVER_PATH], {
|
||||
env: { ...process.env, BRAINSTORM_PORT: TEST_PORT, BRAINSTORM_SCREEN: TEST_SCREEN }
|
||||
});
|
||||
const server = startServer();
|
||||
|
||||
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
|
||||
await sleep(1000);
|
||||
|
||||
try {
|
||||
// Test 1: Server starts and outputs JSON
|
||||
@@ -51,17 +54,17 @@ async function runTests() {
|
||||
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');
|
||||
// Test 2: GET / returns waiting page with helper injected when no screens exist
|
||||
console.log('Test 2: Serves waiting page 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('Waiting for Claude'), 'Should show waiting message');
|
||||
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
|
||||
stdout = '';
|
||||
const ws = new WebSocket(`ws://localhost:${TEST_PORT}`);
|
||||
await new Promise(resolve => ws.on('open', resolve));
|
||||
|
||||
@@ -84,14 +87,61 @@ async function runTests() {
|
||||
if (msg.type === 'reload') gotReload = true;
|
||||
});
|
||||
|
||||
// Modify the screen file
|
||||
fs.writeFileSync(TEST_SCREEN, '<html><body>Updated</body></html>');
|
||||
fs.writeFileSync(path.join(TEST_DIR, 'test-screen.html'), '<html><body>Full doc</body></html>');
|
||||
await sleep(500);
|
||||
|
||||
assert(gotReload, 'Should send reload message on file change');
|
||||
ws2.close();
|
||||
console.log(' PASS');
|
||||
|
||||
// Test 5: Full HTML document served as-is (not wrapped)
|
||||
console.log('Test 5: Full HTML document served without frame wrapping');
|
||||
const fullDoc = '<!DOCTYPE html>\n<html><head><title>Custom</title></head><body><h1>Custom Page</h1></body></html>';
|
||||
fs.writeFileSync(path.join(TEST_DIR, 'full-doc.html'), fullDoc);
|
||||
await sleep(300);
|
||||
|
||||
const fullRes = await fetch(`http://localhost:${TEST_PORT}/`);
|
||||
assert(fullRes.body.includes('<h1>Custom Page</h1>'), 'Should contain original content');
|
||||
assert(fullRes.body.includes('WebSocket'), 'Should still inject helper.js');
|
||||
// Should NOT have the frame template's feedback footer
|
||||
assert(!fullRes.body.includes('feedback-footer') || fullDoc.includes('feedback-footer'),
|
||||
'Should not wrap full documents in frame template');
|
||||
console.log(' PASS');
|
||||
|
||||
// Test 6: Bare HTML fragment gets wrapped in frame template
|
||||
console.log('Test 6: Content fragment wrapped in frame template');
|
||||
const fragment = '<h2>Pick a layout</h2>\n<p class="subtitle">Choose one</p>\n<div class="options"><div class="option" data-choice="a"><div class="letter">A</div><div class="content"><h3>Simple</h3></div></div></div>';
|
||||
fs.writeFileSync(path.join(TEST_DIR, 'fragment.html'), fragment);
|
||||
await sleep(300);
|
||||
|
||||
const fragRes = await fetch(`http://localhost:${TEST_PORT}/`);
|
||||
// Should have the frame template structure
|
||||
assert(fragRes.body.includes('feedback-footer'), 'Fragment should get feedback footer from frame');
|
||||
assert(fragRes.body.includes('Brainstorm Companion'), 'Fragment should get header from frame');
|
||||
assert(fragRes.body.includes('--bg-primary'), 'Fragment should get theme CSS from frame');
|
||||
// Should have the original content inside
|
||||
assert(fragRes.body.includes('Pick a layout'), 'Fragment content should be present');
|
||||
assert(fragRes.body.includes('data-choice="a"'), 'Fragment content should be intact');
|
||||
// Should have helper.js injected
|
||||
assert(fragRes.body.includes('WebSocket'), 'Fragment should have helper.js injected');
|
||||
console.log(' PASS');
|
||||
|
||||
// Test 7: Helper.js includes toggleSelect and send functions
|
||||
console.log('Test 7: Helper.js provides toggleSelect and send');
|
||||
const helperContent = fs.readFileSync(
|
||||
path.join(__dirname, '../../lib/brainstorm-server/helper.js'), 'utf-8'
|
||||
);
|
||||
assert(helperContent.includes('toggleSelect'), 'helper.js should define toggleSelect');
|
||||
assert(helperContent.includes('send'), 'helper.js should define send function');
|
||||
assert(helperContent.includes('selectedChoice'), 'helper.js should track selectedChoice');
|
||||
console.log(' PASS');
|
||||
|
||||
// Test 8: sendToClaude confirmation uses CSS variables (dark mode support)
|
||||
console.log('Test 8: sendToClaude confirmation respects theming');
|
||||
assert(!helperContent.includes('color: #666'), 'Should not use hardcoded light-mode colors');
|
||||
assert(!helperContent.includes('color: #333'), 'Should not use hardcoded light-mode colors');
|
||||
console.log(' PASS');
|
||||
|
||||
console.log('\nAll tests passed!');
|
||||
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user