tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

site.test.mjs (8648B)


      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  assert.doesNotMatch(html,/id="expression-editor"|id="output-target"|class="output-panel"|class="expression-dock"/);
     13  const inspector=await read('src/definition-inspector.ts');
     14  for(const token of ["'tricu'", "'triangle'", "'dag'", "'string'", "'number'", "'values'"])assert.ok(inspector.includes(token));
     15  const app=await read('src/app.ts');assert.match(app,/void initializePackages\(\)/);assert.doesNotMatch(app,/pane-change|activePane/);
     16  await assert.rejects(access(new URL('dist/tabs.js',here)));await assert.rejects(access(new URL('dist/export-app.js',here)));
     17 });
     18 test('editor preserves authoring, IO, package and immutable download controls',async()=>{
     19  const html=await read('index.html');
     20  for(const id of ['source-editor','compile','stop','restore-source','download-bundle','package-version','program-hash','share','package-definition-search'])assert.ok(html.includes(`id="${id}"`),id);
     21  assert.match(html,/Edits compile after a pause/);assert.match(html,/Stored definitions can be inspected without source/);
     22  assert.doesNotMatch(html,/id="edit-source"|compile-consent|slow-compilation|uncapped|execution-limits/);
     23  const app=await read('src/app.ts');assert.match(app,/sourceEditor.dom.classList.toggle\('modified', modified\)/);
     24  assert.match(app,/sourceEditor.getSource\(\) !== compiledSource/);
     25  assert.match(app,/data.hash !== hash/);
     26  assert.match(app,/r\.name === name && r\.version === versionPicker\.value/);
     27 });
     28 test('inspector supports mobile modality, escape, focus return and focus containment',async()=>{
     29  const html=await read('index.html'),controller=await read('src/inspector.ts');
     30  assert.match(html,/aria-controls="settings-panel" aria-expanded="false"/);
     31  for(const token of ['aria-modal','aria-expanded','element.inert','Escape','Tab','toggle.focus()','getClientRects'])assert.ok(controller.includes(token),token);
     32  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/);
     33  assert.match(css,/#source-editor\{font-size:16px;line-height:27px;padding:16px;width:100%/);
     34  assert.match(css,/prefers-reduced-motion/);
     35  const rules=css.replace(/\/\*[\s\S]*?\*\//g,'');assert.equal((rules.match(/\{/g)||[]).length,(rules.match(/\}/g)||[]).length,'Balanced style blocks after removing comments');
     36  assert.equal((css.match(/\/\*/g)||[]).length,(css.match(/\*\//g)||[]).length,'Complete style comments');
     37 });
     38 test('ecosystem remains with copyright and footer feedback',async()=>{
     39  const html=await read('index.html');assert.equal((html.match(/<article>/g)||[]).length,6);
     40  assert.match(html,/Copyright 2026 <a href="https:\/\/eversole.co">James Eversole<\/a>/);
     41  assert.doesNotMatch(html,/>Demo documentation<\/a>/);
     42  assert.doesNotMatch(html,/Arboricx and tricu are research experiments|was discovered by|Johannes Bader/);
     43  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));
     44 });
     45 test('conformance workers retain unbounded execution and shared transport limits',async()=>{
     46  const {BROWSER_BUDGET,PACKAGE_LOAD_MS,EXPORT_ACTIVE_MS}=await import('../dist/execution.js');
     47  assert.deepEqual(BROWSER_BUDGET,{fuel:Infinity,nodes:Infinity,stack:Infinity});assert.equal(PACKAGE_LOAD_MS,60000);assert.equal(EXPORT_ACTIVE_MS,120000);
     48  for(const file of ['src/conformance/worker.ts','src/conformance/compiler-worker.ts']){
     49   const source=await read(file);assert.match(source,/const budget = BROWSER_BUDGET/);assert.doesNotMatch(source,/executionSettings|executionBudget|\['limits'\]|\['uncapped'\]/);
     50  }
     51  for(const file of ['src/protocol.ts','src/compiler-protocol.ts','src/app.ts'])assert.doesNotMatch(await read(file),/ExecutionLimits|readSettings|limits-change|activeMilliseconds/);
     52  await assert.rejects(access(new URL('dist/limits-ui.js',here)));
     53 });
     54 
     55 test('inline IO has explicit authority, revision checks and full-result actions',async()=>{
     56  const html=await read('index.html'),app=await read('src/app.ts'),inspector=await read('src/definition-inspector.ts'),worker=await read('src/wasm-worker.ts');
     57  assert.doesNotMatch(html,/id="run"|id="stop-run"|id="mode"|id="arguments"|id="output-type"/);
     58  for(const token of ['run-io','stop-io','terminal-io','step-mode','next','Returned value','Copy','Download'])assert.ok(inspector.includes(token),token);
     59  assert.doesNotMatch(inspector,/input-form|line-input|tricuIO v1/);
     60  assert.match(inspector,/io\.run\(selected/);assert.match(inspector,/ioDetected/);assert.match(inspector,/valueText\(value\)/);assert.match(inspector,/saved\.text/);
     61  assert.match(app,/editRevision !== compiledRevision/);assert.match(worker,/session\.probeIO\(name\)/);assert.match(worker,/session\.runDefinitionIO/);
     62 });
     63 test('hero invitation, direct footer feedback and self-hosted preview metadata',async()=>{
     64  const html=await read('index.html');
     65  assert.match(html,/<title>Arboricx: tricu and portable tree programs<\/title>/);
     66  assert.match(html,/Write familiar-looking code and explore how it becomes a tree you can run, inspect, and share\./);
     67  assert.doesNotMatch(html,/Start Here walks through/);
     68  assert.match(html,/<footer[^>]*>.*href="mailto:james@eversole.co">Email feedback<\/a>.*<\/footer>/);
     69  assert.match(html,/<link rel="canonical" href="https:\/\/arbori.cx\/">/);
     70  assert.match(html,/<meta property="og:image" content="https:\/\/arbori.cx\/images\/preview.png">/);
     71  for(const path of ['images/favicon.png','images/icon.svg','images/apple-touch-icon.png','images/preview.png'])await access(new URL(path,here));
     72  assert.doesNotMatch(html,/twitter:|facebook\.com|linkedin\.com|contact-form|Privacy policy/);
     73 });
     74 
     75 test('import picker and static agent entrypoint are discoverable and accessible',async()=>{
     76  const html=await read('index.html');
     77  assert.match(html,/<button id="add-import"[^>]*>Explore libraries…<\/button>/);
     78  assert.match(html,/<dialog id="import-dialog" aria-labelledby="import-dialog-title">/);
     79  for(const id of ['import-search','import-alias','import-version','import-export-search'])assert.ok(html.includes(`for="${id}"`));
     80  assert.match(html,/<link rel="help" type="text\/plain" href=".\/llms.txt"/);
     81  assert.match(html,/<footer[^>]*>.*For AI assistants.*<\/footer>/);
     82  const source=await read('src/import-picker.ts');assert.match(source,/textContent/);assert.doesNotMatch(source,/innerHTML/);
     83 });
     84 
     85 test('full Playground recovery stays in the toolbar and warns before losing local work',async()=>{
     86  const html=await read('index.html'),app=await read('src/app.ts');
     87  const button=html.match(/<button id="reload-playground"[^>]*>Reload Playground<\/button>/)?.[0];
     88  assert.ok(button);assert.doesNotMatch(button,/disabled|hidden/);
     89  assert.ok(html.indexOf('id="reload-playground"')<html.indexOf('id="editor-body"'));
     90  assert.match(app,/draft \|\| localModules\.entries\(\)\.length > 0/);
     91  assert.match(app,/window\.confirm\(/);assert.match(app,/location\.reload\(\)/);
     92  assert.doesNotMatch(app,/reload-playground'\)\.disabled/);
     93 });
     94 test('Ecosystem describes the live content store serving this Playground',async()=>{
     95  const html=await read('index.html');
     96  assert.match(html,/This Playground gets its demo, library and compiler bundles from a live Arboricx content store server written in tricu/);
     97  assert.match(html,/verifies their hashes before using them/);
     98  assert.doesNotMatch(html,/same language used for the examples can serve its own packages/);
     99 });
    100 
    101 // Durable user-facing contract; runtime and gesture semantics are browser-tested.
    102 test('inspection instructions use declaration names rather than an arrow gutter',async()=>{
    103  for(const file of ['index.html','README.md','reference-input/getting-started.md'])assert.doesNotMatch(await read(file),/â–¶/,file);
    104  const css=await read('style.css');assert.doesNotMatch(css,/definition-gutter|padding-left:48px/);
    105  assert.match(await read('src/definition-inspector.ts'),/Edit name/);
    106 });