a1215774
test: verify harness catches up with the wall header; runtime reads hashed
a73x 2026-08-18 19:14
Commit message
build.zig
| Old | New | ||
|---|---|---|---|
| @@ -671,6 +671,12 @@ pub fn build(b: *std.Build) void { | |||
| 671 | // doctoring one, exactly as the fix round asked.) | 671 | // doctoring one, exactly as the fix round asked.) |
| 672 | verify.addFileArg(b.path("web/verify.js")); | 672 | verify.addFileArg(b.path("web/verify.js")); |
| 673 | verify.addFileArg(wasm_exe.getEmittedBin()); | 673 | verify.addFileArg(wasm_exe.getEmittedBin()); |
| 674 | // Hashed as INPUTS though they are not argv: verify.js reads both at | ||
| 675 | // runtime (the shell's call list and the page), so without these a | ||
| 676 | // mux.js change replays a stale cached pass — which is exactly how | ||
| 677 | // the dataset.tileId harness gap shipped green. | ||
| 678 | verify.addFileInput(b.path("web/mux.js")); | ||
| 679 | verify.addFileInput(b.path("web/index.html")); | ||
| 674 | verify.setName("verify wasm ABI (web/verify.js)"); | 680 | verify.setName("verify wasm ABI (web/verify.js)"); |
| 675 | // stdio is the assertion: a failing check exits non-zero. | 681 | // stdio is the assertion: a failing check exits non-zero. |
| 676 | verify.expectExitCode(0); | 682 | verify.expectExitCode(0); |
web/verify.js
| Old | New | ||
|---|---|---|---|
| @@ -338,6 +338,9 @@ function browserShell(source) { | |||
| 338 | this.type = ''; | 338 | this.type = ''; |
| 339 | this.rect = { left: 0, top: 0, width: 640, height: 480 }; | 339 | this.rect = { left: 0, top: 0, width: 640, height: 480 }; |
| 340 | this.capturedPointers = new Set(); | 340 | this.capturedPointers = new Set(); |
| 341 | // Real DOM nodes carry one; Tile stamps its id here so reorder can | ||
| 342 | // read the wall's order back out of the document. | ||
| 343 | this.dataset = {}; | ||
| 341 | } | 344 | } |
| 342 | set innerHTML(value) { | 345 | set innerHTML(value) { |
| 343 | this._innerHTML = value; | 346 | this._innerHTML = value; |
| @@ -346,15 +349,20 @@ function browserShell(source) { | |||
| 346 | const header = new FakeElement('header'); | 349 | const header = new FakeElement('header'); |
| 347 | const label = new FakeElement('span'); | 350 | const label = new FakeElement('span'); |
| 348 | label.className = 'label'; | 351 | label.className = 'label'; |
| 349 | const button = new FakeElement('button'); | ||
| 350 | button.className = 'copy-request'; | ||
| 351 | button.type = /<button[^>]*\btype="([^"]+)"/.exec(value)?.[1] ?? ''; | ||
| 352 | button.textContent = /<button[^>]*>([^<]*)<\/button>/.exec(value)?.[1] ?? ''; | ||
| 353 | const badge = new FakeElement('span'); | 352 | const badge = new FakeElement('span'); |
| 354 | badge.className = 'badge connecting'; | 353 | badge.className = 'badge connecting'; |
| 355 | badge.textContent = 'connecting'; | 354 | badge.textContent = 'connecting'; |
| 356 | header.appendChild(label); | 355 | header.appendChild(label); |
| 357 | if (value.includes('<button')) header.appendChild(button); | 356 | // Every button in the markup, classes read from the markup: a |
| 357 | // hardcoded single .copy-request is how the .close/.spawn buttons | ||
| 358 | // arrived unconstructable and Tile broke only under this harness. | ||
| 359 | for (const m of value.matchAll(/<button([^>]*)>([^<]*)<\/button>/g)) { | ||
| 360 | const button = new FakeElement('button'); | ||
| 361 | button.className = /\bclass="([^"]+)"/.exec(m[1])?.[1] ?? ''; | ||
| 362 | button.type = /\btype="([^"]+)"/.exec(m[1])?.[1] ?? ''; | ||
| 363 | button.textContent = m[2]; | ||
| 364 | header.appendChild(button); | ||
| 365 | } | ||
| 358 | header.appendChild(badge); | 366 | header.appendChild(badge); |
| 359 | this.appendChild(header); | 367 | this.appendChild(header); |
| 360 | } | 368 | } |
| @@ -737,7 +745,7 @@ async function verifyClipboardShell(shell, html) { | |||
| 737 | check('hidden fallback preserves terminal Tab encoding', terminalKeys.join('|'), '2,0,0'); | 745 | check('hidden fallback preserves terminal Tab encoding', terminalKeys.join('|'), '2,0,0'); |
| 738 | 746 | ||
| 739 | const header = click.tile.el.querySelector('header'); | 747 | const header = click.tile.el.querySelector('header'); |
| 740 | check('tile header places copy button between label and badge', header.children.map((el) => el.className).join('|'), 'label|copy-request on|badge connecting'); | 748 | check('tile header places copy button between label and badge', header.children.map((el) => el.className).join('|'), 'label|copy-request on|spawn|close|badge connecting'); |
| 741 | check('tile copy control is a real button', click.tile.copyButton.tagName, 'BUTTON'); | 749 | check('tile copy control is a real button', click.tile.copyButton.tagName, 'BUTTON'); |
| 742 | check('tile copy control has button type', click.tile.copyButton.type, 'button'); | 750 | check('tile copy control has button type', click.tile.copyButton.type, 'button'); |
| 743 | check('tile header does not interpolate label into innerHTML', click.tile.el.innerHTML.includes('<unsafe-label>'), false); | 751 | check('tile header does not interpolate label into innerHTML', click.tile.el.innerHTML.includes('<unsafe-label>'), false); |