tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

site.test.mjs (6811B)


      1 import test from 'node:test';
      2 import assert from 'node:assert/strict';
      3 import {readFile,access} from 'node:fs/promises';
      4 const here=new URL('../',import.meta.url);
      5 const read=file=>readFile(new URL(file,here),'utf8');
      6 test('single flowing editor starts before ecosystem, without tabs or graph JSON UI',async()=>{
      7  const html=await read('index.html');
      8  assert.doesNotMatch(html,/role="tab|data-open-pane|id="graph-result"|id="export-definition"|class="run-tools"/);
      9  assert.ok(html.indexOf('class="hero wrap"')<html.indexOf('id="editor"'));
     10  assert.ok(html.indexOf('id="editor"')<html.indexOf('id="ecosystem"'));
     11  const ids=[...html.matchAll(/\bid="([^"]+)"/g)].map(m=>m[1]);assert.equal(ids.length,new Set(ids).size);
     12  const choices=html.match(/<select id="output-type">([\s\S]*?)<\/select>/)[1];
     13  for(const value of ['tricu','triangle','dag','string','number','record'])assert.ok(choices.includes(`value="${value}"`));
     14  assert.doesNotMatch(choices,/value="tree"|value="list"/);
     15  assert.match(choices,/<option value="triangle">â–³ source<\/option>/);
     16  assert.doesNotMatch(choices,/Lambada/i);
     17  assert.match(html,/id="output-target"/);assert.match(html,/Stored definition/);
     18  const app=await read('src/app.ts');assert.match(app,/void initializePackages\(\)/);assert.doesNotMatch(app,/pane-change|activePane/);
     19  await assert.rejects(access(new URL('dist/tabs.js',here)));await assert.rejects(access(new URL('dist/export-app.js',here)));
     20 });
     21 test('editor preserves authoring, IO, package and immutable download controls',async()=>{
     22  const html=await read('index.html');
     23  for(const id of ['source-editor','expression-editor','compile','stop','restore-source','output-type','mode','step-mode','input-form','line-input','next','arguments','raw-arguments','use-guided-inputs','download-bundle','package-version','program-hash','share'])assert.ok(html.includes(`id="${id}"`),id);
     24  assert.match(html,/Edits compile after a pause/);assert.match(html,/Stored definitions use the original bundle records/);
     25  assert.doesNotMatch(html,/id="edit-source"|compile-consent|slow-compilation|uncapped|execution-limits/);
     26  const app=await read('src/app.ts');assert.match(app,/sourceEditor.classList.toggle\('modified', sourceModified\)/);
     27  assert.match(app,/sourceEditor.value !== compiledSource/);
     28  assert.match(app,/data.hash !== selection\?\.hash/);
     29  assert.match(app,/r\.name === name && r\.version === versionPicker\.value/);
     30 });
     31 test('inspector supports mobile modality, escape, focus return and focus containment',async()=>{
     32  const html=await read('index.html'),controller=await read('src/inspector.ts');
     33  assert.match(html,/aria-controls="settings-panel" aria-expanded="false"/);
     34  for(const token of ['aria-modal','aria-expanded','element.inert','Escape','Tab','toggle.focus()','getClientRects'])assert.ok(controller.includes(token),token);
     35  const css=await read('style.css');assert.match(css,/\.workspace\{width:100%;max-width:none;margin:0;border-left:0;border-right:0;border-radius:0/);
     36  assert.match(css,/#source-editor\{font-size:16px;line-height:27px;padding:16px;width:100%/);
     37  assert.match(css,/prefers-reduced-motion/);
     38 });
     39 test('ecosystem remains with copyright and footer feedback',async()=>{
     40  const html=await read('index.html');assert.equal((html.match(/<article>/g)||[]).length,6);
     41  assert.match(html,/Copyright 2026 <a href="https:\/\/eversole.co">James Eversole<\/a>/);
     42  assert.doesNotMatch(html,/>Demo documentation<\/a>/);
     43  assert.doesNotMatch(html,/Arboricx and tricu are research experiments|was discovered by|Johannes Bader/);
     44  for(const match of html.matchAll(/href="https:\/\/git\.eversole\.co\/James\/tricu\/file\/([^"#]+)\.html(?:#[^"]*)?"/g))await access(new URL('../../../'+match[1],import.meta.url));
     45 });
     46 test('conformance workers retain unbounded execution and shared transport limits',async()=>{
     47  const {BROWSER_BUDGET,PACKAGE_LOAD_MS,EXPORT_ACTIVE_MS}=await import('../dist/execution.js');
     48  assert.deepEqual(BROWSER_BUDGET,{fuel:Infinity,nodes:Infinity,stack:Infinity});assert.equal(PACKAGE_LOAD_MS,60000);assert.equal(EXPORT_ACTIVE_MS,120000);
     49  for(const file of ['src/conformance/worker.ts','src/conformance/compiler-worker.ts']){
     50   const source=await read(file);assert.match(source,/const budget = BROWSER_BUDGET/);assert.doesNotMatch(source,/executionSettings|executionBudget|\['limits'\]|\['uncapped'\]/);
     51  }
     52  for(const file of ['src/protocol.ts','src/compiler-protocol.ts','src/app.ts'])assert.doesNotMatch(await read(file),/ExecutionLimits|readSettings|limits-change|activeMilliseconds/);
     53  await assert.rejects(access(new URL('dist/limits-ui.js',here)));
     54 });
     55 
     56 test('evaluation actions follow their expression and precede output, outside the sticky toolbar',async()=>{
     57  const html=await read('index.html');
     58  const toolbar=html.slice(html.indexOf('class="editor-toolbar"'),html.indexOf('id="editor-body"'));
     59  assert.doesNotMatch(toolbar,/id="run"/);assert.match(toolbar,/id="stop" disabled hidden/);
     60  assert.ok(html.indexOf('id="expression-editor"')<html.indexOf('id="run"'));
     61  assert.ok(html.indexOf('id="run"')<html.indexOf('class="output-panel"'));
     62  assert.ok(html.indexOf('class="output-panel"')<html.indexOf('id="settings-panel"'));
     63  assert.match(html,/id="stop-run"/);
     64 });
     65 test('hero invitation, direct footer feedback and self-hosted preview metadata',async()=>{
     66  const html=await read('index.html');
     67  assert.match(html,/<title>Arboricx: tricu and portable tree programs<\/title>/);
     68  assert.match(html,/Write familiar-looking code and explore how it becomes a tree you can run, inspect, and share\./);
     69  assert.doesNotMatch(html,/Start Here walks through/);
     70  assert.match(html,/<footer[^>]*>.*href="mailto:james@eversole.co">Email feedback<\/a>.*<\/footer>/);
     71  assert.match(html,/<link rel="canonical" href="https:\/\/arbori.cx\/">/);
     72  assert.match(html,/<meta property="og:image" content="https:\/\/arbori.cx\/images\/preview.png">/);
     73  for(const path of ['images/favicon.png','images/icon.svg','images/apple-touch-icon.png','images/preview.png'])await access(new URL(path,here));
     74  assert.doesNotMatch(html,/twitter:|facebook\.com|linkedin\.com|contact-form|Privacy policy/);
     75 });
     76 
     77 test('import picker and static agent entrypoint are discoverable and accessible',async()=>{
     78  const html=await read('index.html');
     79  assert.match(html,/<button id="add-import"[^>]*>Add import…<\/button>/);
     80  assert.match(html,/<dialog id="import-dialog" aria-labelledby="import-dialog-title">/);
     81  for(const id of ['import-search','import-alias','import-version','import-export-search'])assert.ok(html.includes(`for="${id}"`));
     82  assert.match(html,/<link rel="help" type="text\/plain" href=".\/llms.txt"/);
     83  assert.match(html,/<footer[^>]*>.*For AI assistants.*<\/footer>/);
     84  const source=await read('src/import-picker.ts');assert.match(source,/textContent/);assert.doesNotMatch(source,/innerHTML/);
     85 });