/** * Integration tests for the ribbit editor using Selenium + Firefox. * * Run: npm run test:e2e */ const { Builder, By, Key, until } = require('selenium-webdriver'); const firefox = require('selenium-webdriver/firefox'); const { createServer } = require('./server'); let server; let driver; async function setup() { server = createServer(9999); await server.start(); const options = new firefox.Options().addArguments('--headless'); driver = await new Builder() .forBrowser('firefox') .setFirefoxOptions(options) .build(); await driver.get(server.url); // Wait for ribbit to initialize await driver.wait(async () => { return driver.executeScript('return window.__ribbitReady === true'); }, 10000).catch(async () => { const logs = await driver.manage().logs().get('browser').catch(() => []); console.log('Browser logs:', logs.map(l => l.message)); const ready = await driver.executeScript('return { ready: window.__ribbitReady, ribbit: typeof window.ribbit, editor: typeof window.__ribbitEditor }'); console.log('State:', ready); throw new Error('Editor did not become ready'); }); } async function teardown() { if (driver) await driver.quit(); if (server) await server.stop(); } // Test helpers async function getEditorHTML() { return driver.executeScript('return document.getElementById("ribbit").innerHTML'); } async function getEditorText() { return driver.executeScript('return document.getElementById("ribbit").textContent'); } async function getState() { return driver.executeScript('return window.__ribbitEditor.getState()'); } async function clickButton(label) { const buttons = await driver.findElements(By.css('.ribbit-toolbar button')); for (const btn of buttons) { const text = await btn.getText(); if (text === label) { await btn.click(); return; } } throw new Error(`Button "${label}" not found`); } async function clickEditor() { const editor = await driver.findElement(By.id('ribbit')); await editor.click(); } // Test runner let passed = 0; let failed = 0; const errors = []; async function test(name, fn) { try { await fn(); passed++; console.log(` ✓ ${name}`); } catch (e) { failed++; errors.push(name); console.log(` ✗ ${name}`); console.log(` ${e.message}`); } } function assert(condition, message) { if (!condition) throw new Error(message || 'Assertion failed'); } // Tests async function runTests() { console.log('\nRibbit Integration Tests\n'); await test('page loads', async () => { const title = await driver.getTitle(); assert(title === 'Ribbit Integration Test Page', `Title: ${title}`); }); await test('editor renders in view mode', async () => { const state = await getState(); assert(state === 'view', `State: ${state}`); }); await test('editor renders markdown as HTML', async () => { const html = await getEditorHTML(); assert(html.includes('bold'), 'Missing bold'); assert(html.includes('italic'), 'Missing italic'); assert(html.includes('code'), 'Missing code'); }); await test('editor renders headings', async () => { const html = await getEditorHTML(); assert(html.includes(' { const html = await getEditorHTML(); assert(html.includes('