mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 12:56:01 -06:00
3d03c415d1
* test(ui): remove two load-dependent flakes in the Control UI suite Both failed only under a saturated worker pool, roughly one full run in four. The board refresh tests asserted a 1ms boundary (nothing at 4999ms, fired at 5000ms) on a timer armed during a render cycle, so any drift in when it was armed failed them. Assert the band that carries the meaning instead: silent at 2s rules out the 1s floor, fired by 8s rules out the 15s a full-TTL calculation would give. Call count is no longer pinned because the unreplaced ticket keeps retrying, which has its own test. The router outlet settled by pumping a fixed five cycles, which returns while a chain is still resolving into another render. Drain to Lit's documented settled state instead, via a shared helper with its own coverage. * test(ui): keep a drain floor so settling never regresses the fixed pump A settled update only says that render did not reschedule itself, so a promise chain that requests an update at its end can still be in flight. Keep the five turns unconditionally and let the settled check extend past them. * docs(test): state the settle helper's bound instead of implying quiescence
106 lines
3.2 KiB
TypeScript
106 lines
3.2 KiB
TypeScript
/* @vitest-environment jsdom */
|
|
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { settleLitElement } from "./lit-settle.ts";
|
|
|
|
// A stand-in for Lit's contract: `updateComplete` resolves false when that update
|
|
// scheduled another one, and a pending promise chain only advances on a microtask turn.
|
|
function fakeElement(params: { cyclesBeforeSettled: number; resolveAfterCycle?: number }) {
|
|
let cycle = 0;
|
|
let chainResolved = false;
|
|
return {
|
|
get updateComplete(): Promise<boolean> {
|
|
cycle += 1;
|
|
const settled = cycle > params.cyclesBeforeSettled && chainResolved;
|
|
return Promise.resolve(settled);
|
|
},
|
|
startChain() {
|
|
// Resolves a few microtask turns later, the way a route loader would.
|
|
void Promise.resolve()
|
|
.then(() => undefined)
|
|
.then(() => {
|
|
chainResolved = true;
|
|
});
|
|
},
|
|
get cycles() {
|
|
return cycle;
|
|
},
|
|
};
|
|
}
|
|
|
|
describe("settleLitElement", () => {
|
|
it("keeps draining while updates schedule further updates", async () => {
|
|
const element = fakeElement({ cyclesBeforeSettled: 7 });
|
|
element.startChain();
|
|
|
|
await settleLitElement(element);
|
|
|
|
// A fixed five-cycle pump would have returned before cycle 7 with work outstanding.
|
|
expect(element.cycles).toBeGreaterThan(7);
|
|
});
|
|
|
|
it("gives pending promise chains a microtask turn before declaring settled", async () => {
|
|
let resolved = false;
|
|
const element = {
|
|
updateComplete: Promise.resolve(true),
|
|
};
|
|
void Promise.resolve().then(() => {
|
|
resolved = true;
|
|
});
|
|
|
|
await settleLitElement(element);
|
|
|
|
expect(resolved).toBe(true);
|
|
});
|
|
|
|
it("throws instead of hanging when an element never settles", async () => {
|
|
const element = { updateComplete: Promise.resolve(false) };
|
|
|
|
await expect(settleLitElement(element)).rejects.toThrow("render loop");
|
|
});
|
|
|
|
it("drains a deep promise chain that only schedules a render at its end", async () => {
|
|
// The shape the settled check alone cannot see: four inert microtask turns, then work
|
|
// that marks the element dirty. A pump that stopped at the first settled rounds would
|
|
// return before it. Chains deeper than the cycle cap are out of reach by design; see
|
|
// the note in lit-settle.ts.
|
|
let pendingRender = false;
|
|
let rendered = false;
|
|
const element = {
|
|
get updateComplete(): Promise<boolean> {
|
|
if (pendingRender) {
|
|
pendingRender = false;
|
|
rendered = true;
|
|
return Promise.resolve(false);
|
|
}
|
|
return Promise.resolve(true);
|
|
},
|
|
};
|
|
let chain = Promise.resolve();
|
|
for (let turn = 0; turn < 4; turn += 1) {
|
|
chain = chain.then(() => undefined);
|
|
}
|
|
void chain.then(() => {
|
|
pendingRender = true;
|
|
});
|
|
|
|
await settleLitElement(element);
|
|
|
|
expect(rendered).toBe(true);
|
|
});
|
|
|
|
it("keeps an unconditional floor of drain rounds for in-flight chains", async () => {
|
|
const updateComplete = vi.fn(() => Promise.resolve(true));
|
|
const element = {
|
|
get updateComplete() {
|
|
return updateComplete();
|
|
},
|
|
};
|
|
|
|
await settleLitElement(element);
|
|
|
|
// Never drains less than the fixed pump this helper replaced.
|
|
expect(updateComplete).toHaveBeenCalledTimes(5);
|
|
});
|
|
});
|