test: cover favorites drawer lifecycle

This commit is contained in:
wxs
2026-07-17 13:03:27 +08:00
parent 2f2a590df4
commit 6570eb32b4
2 changed files with 92 additions and 3 deletions
+27
View File
@@ -146,6 +146,33 @@ describe("favorite-folder-dialog", () => {
await expect(second).resolves.toBeNull();
});
test("removes a closed dialog keydown listener before a fresh dialog opens", async () => {
let firstSettlementCount = 0;
const first = promptForFavoriteFolderName(document, {
title: "新建收藏夹"
});
void first.then(() => {
firstSettlementCount += 1;
});
readButton("cancel").click();
await expect(first).resolves.toBeNull();
await Promise.resolve();
expect(firstSettlementCount).toBe(1);
const current = promptForFavoriteFolderName(document, {
title: "重命名收藏夹"
});
document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" }));
await expect(current).resolves.toBeNull();
await Promise.resolve();
expect(firstSettlementCount).toBe(1);
expect(
document.querySelector('[data-sces-favorite-folder-dialog="root"]')
).toBeNull();
});
test("traps Tab within the dialog and restores the opener focus on close", async () => {
const opener = document.createElement("button");
opener.type = "button";
+65 -3
View File
@@ -120,12 +120,21 @@ describe("favorites-drawer", () => {
expect(readTab().textContent).toContain("收藏夹");
expect(readTab().style.position).toBe("fixed");
expect(readTab().style.right).toBe("56px");
expect(readTab().style.writingMode).toBe("vertical-rl");
expect(readTab().getAttribute("aria-controls")).toBe(readDrawer().id);
expect(readTab().getAttribute("aria-expanded")).toBe("false");
expect(readTab().querySelector("[data-sces-favorites-unread]")).toBeNull();
expect(readTab().querySelector("[data-sces-favorites-badge]")).toBeNull();
expect(readTab().dataset.scesFavoritesUnread).toBeUndefined();
expect(readTab().dataset.scesFavoritesCount).toBeUndefined();
expect(readTab().textContent).not.toMatch(/\d/);
expect(readTab().style.color).not.toMatch(/b42318|dc2626|ef4444|red/i);
expect(readDrawer().hidden).toBe(true);
expect(readDrawer().style.position).toBe("fixed");
expect(readDrawer().style.right).toBe("56px");
expect(readDrawer().style.width).toBe("380px");
expect(readDrawer().getAttribute("role")).toBe("dialog");
expect(readDrawer().getAttribute("aria-label")).toBe("收藏夹");
});
test("opens, closes, and restores focus through the trigger, close control, and Escape", () => {
@@ -263,6 +272,40 @@ describe("favorites-drawer", () => {
expect(onRemoveCreatorFromFolder).toHaveBeenCalledWith("author-1", firstFolder.id);
});
test("prunes selected creators that are outside the active folder before importing", async () => {
const onImportSelected = vi.fn(async (_authorIds: string[]) => {});
const { controller } = createHarness({ handlers: { onImportSelected } });
controller.render(
createState({
creators: [createCreator("111", "Alpha"), createCreator("222", "Beta")],
memberships: [
createMembership("111", firstFolder.id),
createMembership("222", secondFolder.id)
]
})
);
readTab().click();
const firstSelection = document.querySelector(
'[data-sces-favorites-select-author-id="111"]'
) as HTMLInputElement;
firstSelection.checked = true;
firstSelection.dispatchEvent(new Event("change", { bubbles: true }));
const secondSelection = document.querySelector(
'[data-sces-favorites-select-author-id="222"]'
) as HTMLInputElement;
secondSelection.checked = true;
secondSelection.dispatchEvent(new Event("change", { bubbles: true }));
expect(readDrawer().textContent).toContain("导入已选 2 位达人");
click(`[data-sces-favorites-folder-id="${firstFolder.id}"]`);
expect(readDrawer().textContent).toContain("导入已选 1 位达人");
click('[data-sces-favorites-drawer="import-selected"]');
await flush();
expect(onImportSelected).toHaveBeenCalledWith(["111"]);
});
test("calls create, rename, and delete handlers with the matching folder id", async () => {
const onCreateFolder = vi.fn(async () => {});
const onRenameFolder = vi.fn(async (_folderId: string) => {});
@@ -351,8 +394,9 @@ describe("favorites-drawer", () => {
expect(readDrawer().hidden).toBe(true);
});
test("renders errors and removes its DOM and document behavior on disposal", () => {
const { controller } = createHarness();
test("renders errors and removes DOM and event behavior on disposal", async () => {
const onCreateFolder = vi.fn(async () => {});
const { controller } = createHarness({ handlers: { onCreateFolder } });
controller.render(createState());
readTab().click();
controller.setError("保存失败");
@@ -360,10 +404,28 @@ describe("favorites-drawer", () => {
document.querySelector('[data-sces-favorites-drawer="error"]')?.textContent
).toBe("保存失败");
const staleCreateButton = document.querySelector(
'[data-sces-favorites-drawer="create-folder"]'
) as HTMLButtonElement;
const removeEventListener = vi.spyOn(document, "removeEventListener");
controller.dispose();
controller.dispose();
expect(document.querySelector('[data-sces-favorites-tab="button"]')).toBeNull();
expect(document.querySelector('[data-sces-favorites-drawer="root"]')).toBeNull();
document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" }));
expect(removeEventListener).toHaveBeenCalledWith("keydown", expect.any(Function));
const unrelatedButton = document.createElement("button");
unrelatedButton.type = "button";
document.body.append(unrelatedButton);
unrelatedButton.focus();
const escape = new KeyboardEvent("keydown", { cancelable: true, key: "Escape" });
document.dispatchEvent(escape);
staleCreateButton.dispatchEvent(new MouseEvent("click", { bubbles: true }));
await flush();
expect(escape.defaultPrevented).toBe(false);
expect(document.activeElement).toBe(unrelatedButton);
expect(onCreateFolder).not.toHaveBeenCalled();
removeEventListener.mockRestore();
});
});