this repo has no description
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>