Writing selectionOfferLockRef during render tripped react-hooks/refs after the two contract tests went green. The lock now updates like seededTurns, and nextSelectionCardLock reuses the same object when the key is unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
59 lines
2.5 KiB
TypeScript
59 lines
2.5 KiB
TypeScript
import assert from "node:assert/strict";
|
||
import { readFileSync } from "node:fs";
|
||
import test from "node:test";
|
||
|
||
const read = (relativePath: string) => readFileSync(new URL(relativePath, import.meta.url), "utf8");
|
||
|
||
const chat = read("../src/components/rectification-agentic-chat.tsx");
|
||
const delivery = read("../src/components/rectification-range-delivery.tsx");
|
||
const styles = read("../src/app/globals.css");
|
||
const showBlock = chat.slice(
|
||
chat.indexOf("const keepSelectionCardsWhileBusy"),
|
||
chat.indexOf("const showReadonlyRange"),
|
||
);
|
||
const messageLoop = chat.slice(
|
||
chat.indexOf("{messages.map((message) => {"),
|
||
chat.indexOf("{savedTime &&"),
|
||
);
|
||
|
||
test("clicking 更像这个 does not unmount the range card for busy", () => {
|
||
assert.match(showBlock, /acceptingCandidateId \|\| candidateResult\?\.selectedTime/);
|
||
assert.match(showBlock, /!busy \|\| keepSelectionCardsWhileBusy/);
|
||
assert.doesNotMatch(
|
||
showBlock,
|
||
/&& !busy\s*&& selectionCardMessageKey/,
|
||
);
|
||
});
|
||
|
||
test("after adopt the card stays on the locked offering message", () => {
|
||
assert.match(chat, /resolveSelectionCardMessageKey/);
|
||
assert.match(chat, /nextSelectionCardLock/);
|
||
// 原值:selectionOfferLockRef.current = nextSelectionCardLock(...)
|
||
// 新值:useState + 渲染期 setSelectionOfferLock(与 seededTurns 同一模式)
|
||
// 原因:react-hooks/refs 禁止渲染期读写 ref;门禁 lint 0 error(BUG-622)
|
||
assert.match(chat, /const \[selectionOfferLock, setSelectionOfferLock\] = useState/);
|
||
assert.match(chat, /if \(nextOfferLock !== selectionOfferLock\) \{\s*setSelectionOfferLock\(nextOfferLock\);/);
|
||
assert.doesNotMatch(chat, /selectionOfferLockRef/);
|
||
assert.match(messageLoop, /showSelectionCards && candidateResult && message\.renderKey === selectionCardMessageKey/);
|
||
});
|
||
|
||
test("the closing line sits under the card on the assistant column", () => {
|
||
assert.match(messageLoop, /verifiedIdleCopy && \(/);
|
||
assert.match(messageLoop, /className="rectification-pending-note"/);
|
||
assert.match(
|
||
chat,
|
||
/questionGap === "verified_idle" && verifiedIdleCopy && !showSelectionCards/,
|
||
);
|
||
assert.match(
|
||
styles,
|
||
/\.rectification-pending-note \{[\s\S]*margin-inline-start: var\(--assistant-content-inset\)/,
|
||
);
|
||
assert.doesNotMatch(chat, /rectification-adopt-status/);
|
||
});
|
||
|
||
test("the adopted column button is pressed and disabled; others stay 更像这个", () => {
|
||
assert.match(delivery, /disabled=\{busy \|\| adopted\}/);
|
||
assert.match(delivery, /rangeDeliveryAdopted/);
|
||
assert.match(delivery, /rangeDeliveryMoreLikeThis/);
|
||
});
|