this repo has no description
at main 139 lines 3.7 kB view raw
1#!/usr/bin/env node 2/** 3 * Playwright test runner for x-ocaml browser tests. 4 * 5 * Usage: 6 * node run_tests.js [--headed] 7 * 8 * Serves x-ocaml build output + test HTML, runs tests in Chromium. 9 */ 10 11const { chromium } = require('playwright'); 12const http = require('http'); 13const fs = require('fs'); 14const path = require('path'); 15 16const PORT = 8766; 17const TIMEOUT = 60000; 18 19const testDir = path.dirname(fs.realpathSync(__filename)); 20const xocamlDir = path.resolve(testDir, '..'); 21 22const mimeTypes = { 23 '.html': 'text/html', 24 '.js': 'application/javascript', 25 '.css': 'text/css', 26}; 27 28function startServer() { 29 return new Promise((resolve, reject) => { 30 const server = http.createServer((req, res) => { 31 let filePath = req.url === '/' ? '/test_modes.html' : req.url; 32 33 // Serve from: test dir first, then x-ocaml root (for promoted JS), 34 // then jsoo-code-mirror includes (for bundle.js) 35 const searchPaths = [ 36 path.join(testDir, filePath), 37 path.join(xocamlDir, filePath), 38 path.join(xocamlDir, 'jsoo-code-mirror/includes', filePath), 39 ]; 40 41 let fullPath = searchPaths.find(p => fs.existsSync(p)); 42 43 if (!fullPath) { 44 res.writeHead(404); 45 res.end('Not found: ' + filePath); 46 return; 47 } 48 49 const ext = path.extname(fullPath); 50 const contentType = mimeTypes[ext] || 'application/octet-stream'; 51 52 fs.readFile(fullPath, (err, content) => { 53 if (err) { 54 res.writeHead(500); 55 res.end('Error reading file'); 56 return; 57 } 58 res.writeHead(200, { 'Content-Type': contentType }); 59 res.end(content); 60 }); 61 }); 62 63 server.listen(PORT, () => { 64 console.log(`Test server running at http://localhost:${PORT}/`); 65 resolve(server); 66 }); 67 68 server.on('error', reject); 69 }); 70} 71 72async function runTests(headed = false) { 73 let server; 74 let browser; 75 let exitCode = 0; 76 77 try { 78 server = await startServer(); 79 80 browser = await chromium.launch({ headless: !headed }); 81 const page = await browser.newPage(); 82 83 const logs = []; 84 page.on('console', msg => { 85 const text = msg.text(); 86 logs.push(text); 87 console.log(`[browser] ${text}`); 88 }); 89 90 page.on('pageerror', err => { 91 console.error(`[browser error] ${err.message}`); 92 }); 93 94 console.log('Loading test page...'); 95 await page.goto(`http://localhost:${PORT}/`); 96 97 console.log('Waiting for tests to complete...'); 98 await page.waitForFunction( 99 () => window.testResults && window.testResults.done, 100 { timeout: TIMEOUT } 101 ); 102 103 const testResults = await page.evaluate(() => ({ 104 total: window.testResults.total, 105 passed: window.testResults.passed, 106 failed: window.testResults.failed, 107 details: window.testResults.details || [], 108 })); 109 110 console.log('\n========================================'); 111 console.log(`Test Results: ${testResults.passed}/${testResults.total} passed`); 112 if (testResults.details.length > 0) { 113 for (const d of testResults.details) { 114 const icon = d.ok ? 'PASS' : 'FAIL'; 115 console.log(` [${icon}] ${d.name}`); 116 } 117 } 118 console.log('========================================\n'); 119 120 if (testResults.failed > 0) { 121 console.log('FAILED: Some tests did not pass'); 122 exitCode = 1; 123 } else { 124 console.log('SUCCESS: All tests passed'); 125 } 126 127 } catch (err) { 128 console.error('Error running tests:', err.message); 129 exitCode = 1; 130 } finally { 131 if (browser) await browser.close(); 132 if (server) server.close(); 133 } 134 135 process.exit(exitCode); 136} 137 138const headed = process.argv.includes('--headed'); 139runTests(headed);