Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25ff7cd3f4 |
No files matched your search
@@ -0,0 +1,63 @@
|
||||
# Row 173 (#1671): a Seen click that waited out a Board read keeps the meaning it was clicked with
|
||||
|
||||
Brief: docs/plans/2026-10-19_seen-click-intent.md (blob 225ac823b). Branch dewey/row173 off aca16404c.
|
||||
Queue reviewer: Darkwing. Gate (queue row): Darkwing approves on #1671 naming the candidate manifest; webui and
|
||||
every scripts/test-*.sh green on Sage's gate rerun.
|
||||
|
||||
## Design
|
||||
|
||||
- Intent. `seenButton` draws `data-intent="seen"` on a Seen button and `data-intent="unsee"` on an Unsee
|
||||
button, from the same `r.seen` that picks the label. The click handler reads it into `mark` before
|
||||
`writing = true` and before the wait, so the fresh row can't change it. I chose a `data-` value over reading the
|
||||
label text so a later relabel can't change what a click posts.
|
||||
- The fresh row already holds the intent (`r.seen === mark`). This happens when another viewer marked or unmarked
|
||||
the row while the read was in flight. Nothing is posted, the button is re-enabled, and the page announces
|
||||
`<agent> already seen` or `<agent> already waiting`.
|
||||
- Why post nothing. The route takes a repeat without harm: `markSeen` with `seen:true` rewrites the same mark,
|
||||
and `seen:false` deletes a key that is already gone (control-board/src/scan.mjs:255-256). So posting the same
|
||||
value would also be correct. It would cost a write and a rescan, though, and the read that just finished has
|
||||
already drawn the row. The brief allows either.
|
||||
- An unseen row can still hold an old mark for an earlier lastActivity. Posting nothing on Unsee leaves that
|
||||
mark, which is harmless because `scanAgent` only counts a mark equal to the row's lastActivity (scan.mjs:340).
|
||||
- Otherwise the click posts `seen: mark` and announces from `mark`, not from the fresh row.
|
||||
- Unchanged: the row 172 wait, the single-click guard (`writing`), the no-row branch, and the `/api/seen` route
|
||||
(out of scope).
|
||||
- `seen.disabled = false` in the new branch matches the no-row branch. After the read, `accept` redraws the
|
||||
inspector and the cards, so `seen` is usually a detached button and the line has no visible effect. No test
|
||||
can tell it apart; see M5.
|
||||
|
||||
## Test
|
||||
|
||||
packages/webui/tests/board-held.test.mjs gains one test: "a Seen or Unsee click that waited out a Board read
|
||||
keeps its meaning: another viewer's mark in that read posts nothing". `setup` returns `boardURL: f.boardURL` so
|
||||
the test can post to the board as the other viewer.
|
||||
- Steps, for each of Seen and Unsee:
|
||||
- it holds `/api/board` with `window.__hold` and clicks Refresh;
|
||||
- while the read is held, it posts the other viewer's mark straight to the board;
|
||||
- it clicks the inspector's button and releases the read.
|
||||
- Checks: the announcement (`agent1 already seen`, then `agent1 already waiting`), `#seen-count` (1, then 0), and
|
||||
that the page sent no `/api/seen` at all.
|
||||
- The held fetch hasn't reached the board when the other viewer posts (the stub awaits the hold before calling
|
||||
the real fetch), so the released answer carries the other viewer's mark.
|
||||
|
||||
Hash pins and readers checked (git grep, with a positive control for 64-hex): nothing pins app.js. shell.test
|
||||
counts existing app.js strings only. browser.test, sso-browser.test and the other board-held tests click
|
||||
`[data-seen]` buttons the page draws, so they get `data-intent` with them.
|
||||
|
||||
## Mutants (run on CLEAR only; each one edit to app.js, then board-held.test.mjs whole)
|
||||
|
||||
| id | edit | expected kill |
|
||||
|----|------|---------------|
|
||||
| M1 | post `seen: !r.seen` and drop the `r.seen === mark` branch (the old toggle) | new test (a post, and "returned to waiting") |
|
||||
| M2 | drop the `r.seen === mark` branch only (post the repeat) | new test (a post, and "marked seen") |
|
||||
| M3 | `seenButton` swaps the intent (`r.seen ? 'seen' : 'unsee'`) | row 172 test (`#seen-count` never 1) and the new test |
|
||||
| M4 | the new branch returns without `announce` | new test (announcement wait times out) |
|
||||
| M5 | drop `seen.disabled = false` in the new branch | equivalent: the button is redrawn by `accept`; named, not run |
|
||||
|
||||
Each run: `timeout -k 10 180 node --test --test-force-exit packages/webui/tests/board-held.test.mjs`; a kill
|
||||
counts only as an assertion or a named wait failing in the named test.
|
||||
|
||||
## Runs
|
||||
|
||||
None yet. Gander holds the gate slot (row 253 run 5) from 16:32Z. The claim, the push and every run wait for
|
||||
Sage's CLEAR.
|
||||
@@ -1,70 +0,0 @@
|
||||
# Row214 C2-11a work record
|
||||
|
||||
Assignment sage-2794 at2026-10-22T16:33:20Z, Q220/mos-1301, issue#1715.
|
||||
New isolated branch herb/row214 on aca16404c. The earlier197/215 worktrees
|
||||
and their dirty/untracked historical packets are untouched. node_modules is
|
||||
the required plain symlink to /mnt/storage/src/mosaic-stack/node_modules.
|
||||
|
||||
Read C2-11a in both pinned briefs. The section is unchanged at51cc0445c;
|
||||
Q222 changes C2-06's reply-limit settings, not wikilinks. The editor model,
|
||||
entry, view, existing model/browser/provenance tests, build/toolchain and
|
||||
vendored markdown parser/serializer were read. No third_party source changes.
|
||||
|
||||
## Static finding and authored work
|
||||
|
||||
model.mjs stores a canonical sig and the raw original block, and serialise
|
||||
returns raw when the sig matches. The vendored text writer escapes brackets
|
||||
(to_markdown.ts:433-437). It is therefore possible for no-edit round-trips
|
||||
to pass while dirty-block output escapes a wikilink. This is a source-based
|
||||
hypothesis, not a measured result. Both outcomes are in DESIGN.md.
|
||||
|
||||
Authored, unrun: the pinned-bundle measurement; ten fixture round-trips;
|
||||
three browser regressions; inert wikilink schema/tokenizer/serializer. The
|
||||
schema includes headings, and front matter remains raw. The serializer
|
||||
validates its spelling before bypassing escaping. HTML/entity-like targets
|
||||
stay text under the existing escaping rules. No clicking or resolution.
|
||||
|
||||
Local static git diff --check is clean. No Node, npm, compiler, browser,
|
||||
measurement, test, Docker, HTTPS Git or push has run. The queue claim waits
|
||||
for a gap because scripts/mosaic invokes Node. No candidate or pass claim.
|
||||
Only design/path documents may be committed before the granted red/green
|
||||
checks; product, tests and measurement remain uncommitted until validated.
|
||||
|
||||
## Next granted steps
|
||||
|
||||
1. Claim from canonical during a CLEAR/gap.
|
||||
2. Run measure.mjs against aca16404c's pinned Git blobs; save JSON unchanged.
|
||||
3. Run the structural/edit regression alone on the baseline bundle (red).
|
||||
4. Install the already-pinned esbuild0.28.2 in this worktree's packages/webui
|
||||
with npm ci --ignore-scripts --no-audit --no-fund; no root install or lock edit.
|
||||
5. Build with the existing editor/build.mjs, then the whole new test file and
|
||||
existing editor, editor-browser and editor-provenance files, each singly.
|
||||
6. Kill the serializer-removal mutant, restore, rebuild, focused green.
|
||||
7. Commit only after the applicable checks, push only when Sage clears HTTPS
|
||||
Git, send Filbert and Mos the candidate SHA/hash manifest directly, cc Sage.
|
||||
8. After253 lands, new branch on its tip preserving all landed changes.
|
||||
9. Filbert and Sage's whole webui and scripts gate finish the row.
|
||||
|
||||
All runs wait for explicit Sage CLEAR. Gander's row253 run5 owns the slot.
|
||||
The whole suite gate and any unexpected failure use mos-0823's stop rule.
|
||||
|
||||
Proposed SESSIONS text for Sage's post-253 doc sweep (do not append here):
|
||||
2026-10-22 | Herb (gpt-6.1-sol, f6cf4287) | row214 C2-11a editor wikilinks authoring | On herb/row214 at aca16404c, authored baseline round-trip measurement, designs for equal/unequal results, inert inline node and regression tests under sage-2794/Q220. Claim, runs and pushes held while253 owns the slot; measurement and test results unavailable.
|
||||
|
||||
## Filbert static design/path review accepted, R1 and R2 required
|
||||
|
||||
Review at2026-10-22T16:43:35Z read dee9f3d8d's packet only, no product or
|
||||
runs. R1 is now in the plan: set MOSAIC_REQUIRE_EDITOR_BUILD=1 on focused
|
||||
provenance and whole webui runs, grep ^# skip plus TAP/spec skip directives,
|
||||
and name every skip. Missing/wrong pinned esbuild must fail, not skip.
|
||||
R2: baseline no-edit roundTrips are expected to pass. They test retention
|
||||
of raw bytes and are not red evidence for the node. Only the baseline
|
||||
same-block structural/edit regression is expected red.
|
||||
|
||||
Two added fixtures cover a reference-definition collision and an atom
|
||||
inside ordinary link text, taking the planned file to10 no-edit tests and
|
||||
3 browser tests. The explicit & exclusion preserves the existing escaped
|
||||
HTML/entity writer for dirty blocks; Q&A stays text and is a known limit.
|
||||
Measurement VM setup matches loadEditor. Browser TMPDIR must use the short
|
||||
non-/tmp scratch root. Sweep Node/Chromium after mutant/timeout and at clear.
|
||||
All remain authored and unrun while253 owns the slot.
|
||||
@@ -1,128 +0,0 @@
|
||||
# C2-11a: an inert editor wikilink node
|
||||
|
||||
Base: aca16404c. Owner: Herb. Reviewer: Filbert. Issue: #1715.
|
||||
Authority: sage-2794, Q220/mos-1301. The assignment permits authoring the
|
||||
node and tests before the measurement slot. Runs, claim and pushes wait
|
||||
for Sage's CLEAR. After row253 lands, a new branch will carry the change
|
||||
onto its landing with every landed hunk retained.
|
||||
|
||||
## Measurement before choosing the explanation
|
||||
|
||||
Run agents/herb/work/queue-214/measure.mjs only on a granted slot. It loads
|
||||
aca16404c's editor.js and markdown-preview.js directly from local Git
|
||||
objects. It verifies the editor bundle hash against that base's BUILD.json.
|
||||
It cannot accidentally measure a candidate rebuild. Its JSON records the
|
||||
full baseline SHA, bundle hash, timestamp, input, output and equality for
|
||||
each fixture. The first five are the brief's plain, labelled, heading,
|
||||
list and front-matter cases; extra cases cover a heading block, combined
|
||||
heading/label, Unicode/spacing, adjacency and an inert embed. No result is
|
||||
available yet. Static reasoning is not a measurement.
|
||||
|
||||
Outcome A, every baseline round-trip is equal: record that wikilinks do
|
||||
not trigger Source fallback for the measured inputs. Do not claim that
|
||||
bug exists. The source's raw/sig cache explains why an unchanged block can
|
||||
keep its bytes even when its canonical serializer escapes brackets. Add
|
||||
the node for structured wikilinks and dirty-block preservation, as the
|
||||
brief requests. A baseline structural/edit regression must fail even if
|
||||
all unchanged-file checks pass.
|
||||
|
||||
Outcome B, one or more differ: record the exact differing bytes and affected
|
||||
contexts. Keep the existing Source fallback fail-closed. The node restores
|
||||
the required spellings where the block is structured. Retain the same
|
||||
no-edit and dirty-block regressions. If the mismatch comes from a separate
|
||||
block, line-ending or front-matter defect, report it before widening scope.
|
||||
|
||||
## Representation and conversion
|
||||
|
||||
The schema gains one inline atom, wikilink, with a single source attribute.
|
||||
Its source is the complete spelling, not reconstructed target/label parts.
|
||||
It has no href, navigation, fetch or click behavior. A span with data-source
|
||||
and a text child displays the spelling. Heading content includes the new
|
||||
atom; paragraphs, quotes and lists already accept inline content.
|
||||
|
||||
One markdown-it inline rule runs before ordinary links, after escapes and
|
||||
backticks. A sticky match reads a complete single-line [[...]] or ![[...]]
|
||||
without copying every suffix or crossing state.posMax. The node writer
|
||||
writes its validated spelling without bracket escaping, so editing adjacent
|
||||
text cannot rewrite a wikilink. Front matter remains a raw block. No new
|
||||
file-read, save, routing, documentPath or target-resolution behavior is added.
|
||||
|
||||
The accepted atom grammar excludes brackets within the body, line breaks,
|
||||
angle brackets and ampersands. This is an editor representation boundary,
|
||||
not a library resolver or a refusal to open such a file. Malformed,
|
||||
HTML/entity-like and code syntax stays under the existing text/raw rules.
|
||||
This preserves the existing escaping of HTML/entities when a block changes.
|
||||
DOM paste accepts only that same grammar. Invalid attributes constructed
|
||||
programmatically are flattened to one line and escaped by the text writer.
|
||||
No input string is parsed as HTML and embeds never load an image.
|
||||
|
||||
## Acceptance and review
|
||||
|
||||
The measurement JSON goes in BUILD.md and the final manifest packet.
|
||||
Ten fixture round-trips run with and without a final newline. A browser
|
||||
test inspects real ProseMirror atom types and attributes, edits the same
|
||||
block and reparses it, for each fixture. Two more browser tests cover
|
||||
code/escape/malformed/hostile syntax and DOM/programmatic-attribute safety.
|
||||
Tests use the existing fixture and Chromium harness and the shipped bundle.
|
||||
They do not export new production APIs just for testing.
|
||||
|
||||
Before rebuilding, run only the structural regression against the baseline
|
||||
bundle and observe it fail. Rebuild the candidate with the exact pinned
|
||||
esbuild0.28.2 binary, run the whole new test file, existing editor tests,
|
||||
editor browser tests and provenance test. Remove only the wikilink serializer
|
||||
handler for one mutant; the dirty-block assertion must fail. Restore, rebuild
|
||||
and rerun the focused regression. All execution needs Sage's CLEAR, including
|
||||
installation, checks, builds, browser tests and the measurement.
|
||||
|
||||
The checked-in bundle and editor/BUILD.json must be regenerated by the
|
||||
existing build script. No hand edits of generated JavaScript or provenance.
|
||||
The current shared esbuild is0.28.1 and the local webui install is absent.
|
||||
A granted npm ci --ignore-scripts --no-audit --no-fund in packages/webui is
|
||||
needed before building; the root node_modules remains its plain symlink.
|
||||
No dependency or lockfile change is planned.
|
||||
|
||||
Final gate: Filbert approves the manifest, then the whole webui suite and
|
||||
every scripts/test-*.sh on Sage's slot. Two failures stop a gate. A failure
|
||||
outside this row is reported to Sage before any rerun. Source, generated
|
||||
output, mode/hash manifest and raw granted logs identify the candidate.
|
||||
|
||||
## Static review follow-through (Filbert, 2026-10-22T16:43:35Z)
|
||||
|
||||
R1: the focused editor-provenance run and Sage's whole webui gate must set
|
||||
MOSAIC_REQUIRE_EDITOR_BUILD=1. A missing, wrong-version or unpinned binary
|
||||
then fails. Check every log for ^# skip, as well as TAP # SKIP directives
|
||||
and the spec reporter's skipped-test lines; record each skipped name and
|
||||
reason. A skipped rebuild is not proof of bundle reproducibility. The CSS
|
||||
output must stay byte-identical and match BUILD.json.
|
||||
|
||||
R2: no-edit roundTrip checks are expected to pass on the baseline because
|
||||
parse sets sig=canonical and an unchanged serialise returns lead+raw. Their
|
||||
green result is compatibility evidence, never evidence that the atom was
|
||||
needed or installed. The same-block edit/structural regression is the red
|
||||
that proves the new node. The required measurement still runs and is kept.
|
||||
|
||||
N1: bus.js checks visual mode at949 on this base and also requires
|
||||
MosaicMarkdown. The measurement uses the existing helper's bare-window VM
|
||||
shape, markdown-preview.js first, then editor.js, both from pinned Git blobs.
|
||||
|
||||
N2: the exclusion of & and angle brackets is deliberate: the existing
|
||||
text serializer escapes entities/HTML when it rewrites a block for another
|
||||
renderer. An atom's verbatim writer must not bypass that rule, including
|
||||
for pasted/programmatic values. This keeps those targets as ordinary text;
|
||||
[[Q&A]] will still be escaped if its own block changes. It is a known
|
||||
representation limit beyond the five brief cases, not a resolver change.
|
||||
It is included among the text-only cases. Unedited bytes remain intact.
|
||||
|
||||
N3: the reference-definition fixture [[a]] with [a]: u deliberately becomes
|
||||
one inert wikilink rather than a bracket around a reference link. Literal
|
||||
spelling and the separate definition block remain intact.
|
||||
|
||||
N4: [x [[a]]](u) is an explicit fixture. The atom inherits the enclosing
|
||||
ordinary link mark, whose existing target gate still applies. The atom
|
||||
itself supplies no href or new click behavior; the fixture checks its
|
||||
structure and spelling before/after a same-block edit.
|
||||
|
||||
N6: every granted browser/test process gets TMPDIR from
|
||||
mktemp -d /mnt/storage/scratch/tmp/gXXX. Never /tmp. Sweep owned Chromium
|
||||
and Node processes by cwd and the temp-root path after the mutant, after
|
||||
any timeout, and before clearing the slot. Retain/report uncertain owners.
|
||||
@@ -1,23 +0,0 @@
|
||||
# Row214 implementation manifest, draft before measurement
|
||||
|
||||
Pinned base: aca16404c. Brief: docs/plans/2026-10-20_case2-rows.md,
|
||||
C2-11a. Also read at gosalyn/case2-06-idea-caps51cc0445c; that section is
|
||||
unchanged. Q222's reply-limit settings belong to C2-06, not this row.
|
||||
|
||||
| Path | Change |
|
||||
| --- | --- |
|
||||
| packages/webui/editor/model.mjs | inert inline atom, heading content, tokenizer rule and serializer |
|
||||
| packages/webui/tests/editor-wikilink.test.mjs | ten round-trips and three browser regressions |
|
||||
| packages/webui/tests/helpers/wikilink-cases.mjs | exact fixtures shared with measurement |
|
||||
| packages/webui/src/public/editor.js | generated by the pinned editor build, pending CLEAR |
|
||||
| packages/webui/editor/BUILD.json | generated provenance, pending CLEAR |
|
||||
| agents/herb/work/queue-214/measure.mjs | baseline Git-object measurement, authored and unrun |
|
||||
| agents/herb/work/queue-214/DESIGN.md | both measurement outcomes, implementation and acceptance |
|
||||
| agents/herb/work/queue-214/BUILD.md | rulings and receipts, pending runs |
|
||||
| agents/herb/work/queue-214/MANIFEST.md | this reviewed path list |
|
||||
|
||||
editor.css is an unchanged build output; no source CSS is touched. No
|
||||
entry/view, library, route, save, click, documentPath or dependency changes.
|
||||
Generated changes do not exist yet. This is a design/path manifest, not a
|
||||
frozen tested candidate. Candidate hashes and measurement results follow
|
||||
only after the granted runs. Filbert reviews the plan before the build.
|
||||
@@ -66,7 +66,7 @@
|
||||
$('mode').onchange = e => { mode = e.target.value; theme(); };
|
||||
function seenButton(r) {
|
||||
if (!['waiting', 'error'].includes(r.state) || !r.lastActivity) return '';
|
||||
return `<button type="button" class="btn" data-seen="${esc(key(r))}" aria-label="${r.seen ? 'Unsee' : 'Mark seen'} ${esc(r.agent)}">${r.seen ? 'Unsee' : 'Seen'}</button>`;
|
||||
return `<button type="button" class="btn" data-seen="${esc(key(r))}" data-intent="${r.seen ? 'unsee' : 'seen'}" aria-label="${r.seen ? 'Unsee' : 'Mark seen'} ${esc(r.agent)}">${r.seen ? 'Unsee' : 'Seen'}</button>`;
|
||||
}
|
||||
const historyButton = r => `<button type="button" class="btn conv-open" data-history="${esc(key(r))}" aria-label="History for ${esc(r.agent)}">History</button>`;
|
||||
const openButton = r => `<button type="button" class="session-open" data-open="${esc(key(r))}" aria-controls="inspector" aria-expanded="${selected === key(r)}">${esc(r.agent)}</button>`;
|
||||
@@ -415,12 +415,16 @@
|
||||
if (!seen || writing) return;
|
||||
// A Board read in flight (reading) holds busy. The click waits for that read and posts on the row it
|
||||
// drew; dropping it left the click doing nothing (row 172). A second click while one waits or posts is dropped.
|
||||
// The click means what the button said when clicked (Seen marks, Unsee returns the row to waiting), read before
|
||||
// the wait. If the fresh row already holds that, another viewer got there first: nothing is posted (row 173).
|
||||
const mark = seen.dataset.intent === 'seen';
|
||||
writing = true; seen.disabled = true;
|
||||
try {
|
||||
while (reading) await reading;
|
||||
const r = row(seen.dataset.seen); if (!r) { seen.disabled = false; return; }
|
||||
if (r.seen === mark) { seen.disabled = false; announce(`${r.agent} already ${mark ? 'seen' : 'waiting'}`); return; }
|
||||
busy = true;
|
||||
try { accept(await api('/api/seen', { project: r.project, agent: r.agent, lastActivity: r.lastActivity, seen: !r.seen })); announce(`${r.agent} ${r.seen ? 'returned to waiting' : 'marked seen'}`); }
|
||||
try { accept(await api('/api/seen', { project: r.project, agent: r.agent, lastActivity: r.lastActivity, seen: mark })); announce(`${r.agent} ${mark ? 'marked seen' : 'returned to waiting'}`); }
|
||||
catch (err) { error(err); seen.disabled = false; }
|
||||
finally { busy = false; schedule(); }
|
||||
} finally { writing = false; }
|
||||
|
||||
@@ -82,7 +82,7 @@ async function setup(machine, fast = false) {
|
||||
const polls = () => b.evaluate('window.__polls');
|
||||
const settle = () => new Promise(r => setTimeout(r, 600));
|
||||
const done = async () => { await b.close(); await close(web); await f.close(); store.close(); rmSync(root, { recursive: true, force: true }); };
|
||||
return { b, base, wait, text, shown, reqs, polls, settle, done };
|
||||
return { b, base, boardURL: f.boardURL, wait, text, shown, reqs, polls, settle, done };
|
||||
}
|
||||
|
||||
const HELD = 'document.querySelectorAll("#held table tbody tr").length === 2';
|
||||
@@ -191,6 +191,37 @@ test('a Seen click while a Board read is in flight waits for that read, then pos
|
||||
} finally { await done(); }
|
||||
});
|
||||
|
||||
// Row 173 (#1671): the click took its meaning from the fresh row, so another viewer's mark landing in the read
|
||||
// it waited for turned "Seen" into an unsee. The click means what its button said; a fresh row that already
|
||||
// holds that posts nothing and says so. The other viewer posts straight to the board while the read is held.
|
||||
test('a Seen or Unsee click that waited out a Board read keeps its meaning: another viewer\'s mark in that read posts nothing', { timeout: 120000 }, async () => {
|
||||
const { b, base, boardURL, wait, text, reqs, done } = await setup(false);
|
||||
const posts = async () => (await reqs()).filter(r => r.path === '/api/seen').length;
|
||||
const other = async seen => {
|
||||
const r = (await (await fetch(`${boardURL}/api/board`)).json()).sessions.find(s => s.project === 'proj' && s.agent === 'agent1');
|
||||
const res = await fetch(`${boardURL}/api/seen`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ project: 'proj', agent: 'agent1', lastActivity: r.lastActivity, seen }) });
|
||||
assert.equal(res.status, 200);
|
||||
};
|
||||
try {
|
||||
await b.navigate(base);
|
||||
await wait(`${SESSIONS} && ${HELD}`);
|
||||
await b.evaluate(`document.querySelector('table.sessions [data-open="proj/agent1"]').click(), true`);
|
||||
await wait('document.querySelector("#inspection [data-seen]")');
|
||||
for (const [label, seen, count, said] of [['Seen', true, '1', 'agent1 already seen'], ['Unsee', false, '0', 'agent1 already waiting']]) {
|
||||
assert.equal(await text('#inspection [data-seen]'), label);
|
||||
await b.evaluate(`window.__hold = '/api/board', document.querySelector('#refresh').click(), true`);
|
||||
await wait('window.__held.length === 1');
|
||||
// The held fetch has not reached the board yet, so its answer carries the other viewer's mark.
|
||||
await other(seen);
|
||||
await b.evaluate(`document.querySelector('#inspection [data-seen]').click(), true`);
|
||||
await b.evaluate('window.__held.splice(0).forEach(r => r()), true');
|
||||
await wait(`document.querySelector("#announce").textContent === ${JSON.stringify(said)}`);
|
||||
assert.equal(await text('#seen-count'), count);
|
||||
assert.equal(await posts(), 0);
|
||||
}
|
||||
} finally { await done(); }
|
||||
});
|
||||
|
||||
test('a redirect answer on a Board read, a bus read, a conversation read, a board POST, a module read and a send signs the page out: one line, nothing stale, no further reads', { timeout: 120000 }, async () => {
|
||||
// The poll timers fire after 150 ms here, so the 600 ms settle below spans several ticks.
|
||||
const { b, base, wait, text, shown, reqs, polls, settle, done } = await setup(true, true);
|
||||
|
||||
Reference in new issue
Block a user