this repo has no description
at main 222 lines 7.9 kB view raw
1<!DOCTYPE html> 2<html> 3<head> 4 <title>x-ocaml Cell Modes Tests</title> 5 <style> 6 body { font-family: monospace; padding: 20px; max-width: 80ch; } 7 .test-section { border: 1px solid #ccc; padding: 10px; margin: 10px 0; } 8 .test-section h3 { margin-top: 0; } 9 #status { margin-bottom: 20px; font-size: 1.2em; } 10 #log { white-space: pre-wrap; } 11 .pass { color: green; } 12 .fail { color: red; } 13 </style> 14</head> 15<body> 16 <h1>x-ocaml Cell Modes Tests</h1> 17 <div id="status">Loading...</div> 18 <pre id="log"></pre> 19 20 <!-- T1: Hidden cell — not visible, definitions available --> 21 <div class="test-section" id="section-t1"> 22 <h3>T1: Hidden cell</h3> 23 <x-ocaml mode="hidden">let secret = 42</x-ocaml> 24 <x-ocaml mode="interactive">let t1_result = secret + 1</x-ocaml> 25 </div> 26 27 <!-- T2: Interactive cell — visible, read-only --> 28 <div class="test-section" id="section-t2"> 29 <h3>T2: Interactive cell (read-only)</h3> 30 <x-ocaml mode="interactive">let t2_x = 1 + 2</x-ocaml> 31 </div> 32 33 <!-- T3: Exercise cell — visible, editable --> 34 <div class="test-section" id="section-t3"> 35 <h3>T3: Exercise cell (editable)</h3> 36 <x-ocaml mode="exercise" data-id="t3ex">let double x = x * 2</x-ocaml> 37 </div> 38 39 <!-- T4: Test cell — visible, read-only --> 40 <div class="test-section" id="section-t4"> 41 <h3>T4: Test cell (read-only)</h3> 42 <x-ocaml mode="test" data-for="t3ex">assert (double 5 = 10)</x-ocaml> 43 </div> 44 45 <!-- T5: Default mode — no mode attribute defaults to interactive --> 46 <div class="test-section" id="section-t5"> 47 <h3>T5: Default mode (no attribute)</h3> 48 <x-ocaml>let t5_default = 99</x-ocaml> 49 </div> 50 51 <!-- T6: Per-cell merlin disable --> 52 <div class="test-section" id="section-t6"> 53 <h3>T6: Merlin disabled</h3> 54 <x-ocaml mode="exercise" data-merlin="false">let t6_no_merlin = 1</x-ocaml> 55 </div> 56 57 <!-- T7: Hidden → Exercise → Test chain (full assessment pattern) --> 58 <div class="test-section" id="section-t7"> 59 <h3>T7: Full chain (hidden → exercise → test)</h3> 60 <x-ocaml mode="hidden">let check_positive f = assert (f 1 > 0)</x-ocaml> 61 <x-ocaml mode="exercise" data-id="factorial"> 62let rec facr n = if n <= 1 then 1 else n * facr (n - 1) 63 </x-ocaml> 64 <x-ocaml mode="test" data-for="factorial"> 65assert (facr 10 = 3628800);; 66check_positive facr 67 </x-ocaml> 68 </div> 69 70 <!-- Load x-ocaml (includes CodeMirror bundle) with builtin backend --> 71 <script src="x-ocaml.js" 72 backend="builtin" 73 src-worker="x-ocaml.worker.js" 74 run-on="load"> 75 </script> 76 77 <!-- Test assertions run after cells have initialised and executed --> 78 <script> 79 window.testResults = { total: 0, passed: 0, failed: 0, done: false, details: [] }; 80 81 function assert(condition, name) { 82 window.testResults.total++; 83 if (condition) { 84 window.testResults.passed++; 85 window.testResults.details.push({ name, ok: true }); 86 console.log('PASS: ' + name); 87 } else { 88 window.testResults.failed++; 89 window.testResults.details.push({ name, ok: false }); 90 console.log('FAIL: ' + name); 91 } 92 } 93 94 function getCells() { 95 return Array.from(document.querySelectorAll('x-ocaml')); 96 } 97 98 function hasShadowContent(el) { 99 const shadow = el.shadowRoot; 100 if (!shadow) return false; 101 // Hidden cells have an empty shadow DOM (just the shadow root) 102 return shadow.children.length > 0; 103 } 104 105 function hasEditor(el) { 106 const shadow = el.shadowRoot; 107 if (!shadow) return false; 108 return shadow.querySelector('.cm-editor') !== null; 109 } 110 111 function isReadOnly(el) { 112 const shadow = el.shadowRoot; 113 if (!shadow) return false; 114 const editor = shadow.querySelector('.cm-editor'); 115 if (!editor) return false; 116 // CodeMirror 6 readOnly facet sets aria-readonly="true" on .cm-content 117 return editor.querySelector('.cm-content')?.getAttribute('aria-readonly') === 'true'; 118 } 119 120 function hasOutput(el) { 121 const shadow = el.shadowRoot; 122 if (!shadow) return false; 123 // Check for output widgets (rendered as .caml_stdout, .caml_stderr, .caml_meta) 124 return shadow.querySelector('.caml_stdout, .caml_stderr, .caml_meta') !== null; 125 } 126 127 // Wait for cells to initialise (connectedCallback uses setTimeout) 128 // and for auto-run to complete 129 function waitForResults(maxWait) { 130 return new Promise((resolve) => { 131 const start = Date.now(); 132 const check = () => { 133 const cells = getCells(); 134 const allCells = cells.length; 135 136 // Check if we have the expected number of cells 137 if (allCells < 10) { 138 // Not all cells registered yet 139 if (Date.now() - start < maxWait) { 140 setTimeout(check, 200); 141 } else { 142 resolve(false); 143 } 144 return; 145 } 146 147 // Check if visible cells that should auto-run have output 148 // Give cells time to evaluate 149 if (Date.now() - start < 3000) { 150 setTimeout(check, 200); 151 return; 152 } 153 154 resolve(true); 155 }; 156 setTimeout(check, 500); 157 }); 158 } 159 160 async function runAllTests() { 161 const ready = await waitForResults(30000); 162 if (!ready) { 163 console.log('WARNING: Timed out waiting for cells'); 164 } 165 166 const cells = getCells(); 167 console.log('Total x-ocaml elements: ' + cells.length); 168 169 // T1: Hidden cell — not visible 170 const t1_hidden = cells[0]; // first cell in section-t1 171 const t1_interactive = cells[1]; 172 assert(!hasShadowContent(t1_hidden), 'T1: hidden cell has no visible content'); 173 assert(hasEditor(t1_interactive), 'T1: interactive cell after hidden has editor'); 174 175 // T2: Interactive cell — visible, read-only 176 const t2 = cells[2]; 177 assert(hasEditor(t2), 'T2: interactive cell has editor'); 178 assert(isReadOnly(t2), 'T2: interactive cell is read-only'); 179 180 // T3: Exercise cell — visible, editable 181 const t3 = cells[3]; 182 assert(hasEditor(t3), 'T3: exercise cell has editor'); 183 assert(!isReadOnly(t3), 'T3: exercise cell is editable'); 184 185 // T4: Test cell — visible, read-only 186 const t4 = cells[4]; 187 assert(hasEditor(t4), 'T4: test cell has editor'); 188 assert(isReadOnly(t4), 'T4: test cell is read-only'); 189 190 // T5: Default mode — should be interactive (visible, read-only) 191 const t5 = cells[5]; 192 assert(hasEditor(t5), 'T5: default mode cell has editor'); 193 assert(isReadOnly(t5), 'T5: default mode cell is read-only'); 194 195 // T6: Exercise with merlin disabled — still editable 196 const t6 = cells[6]; 197 assert(hasEditor(t6), 'T6: merlin-disabled cell has editor'); 198 assert(!isReadOnly(t6), 'T6: merlin-disabled exercise cell is editable'); 199 200 // T7: Full chain — hidden, exercise, test 201 const t7_hidden = cells[7]; 202 const t7_exercise = cells[8]; 203 const t7_test = cells[9]; 204 assert(!hasShadowContent(t7_hidden), 'T7: chain hidden cell has no visible content'); 205 assert(hasEditor(t7_exercise), 'T7: chain exercise cell has editor'); 206 assert(!isReadOnly(t7_exercise), 'T7: chain exercise cell is editable'); 207 assert(hasEditor(t7_test), 'T7: chain test cell has editor'); 208 assert(isReadOnly(t7_test), 'T7: chain test cell is read-only'); 209 210 window.testResults.done = true; 211 const status = document.getElementById('status'); 212 if (window.testResults.failed === 0) { 213 status.innerHTML = '<span class="pass">All ' + window.testResults.passed + ' tests passed!</span>'; 214 } else { 215 status.innerHTML = '<span class="fail">' + window.testResults.failed + ' tests failed</span> (' + window.testResults.passed + ' passed)'; 216 } 217 } 218 219 runAllTests(); 220 </script> 221</body> 222</html>