198 lines
6.2 KiB
TypeScript
198 lines
6.2 KiB
TypeScript
// @vitest-environment jsdom
|
|
|
|
import { beforeEach, describe, expect, test } from "vitest";
|
|
|
|
import { promptForFavoriteFolderName } from "../src/content/market/favorite-folder-dialog";
|
|
|
|
function readDialog(): HTMLElement {
|
|
const dialog = document.querySelector(
|
|
'[data-sces-favorite-folder-dialog="root"]'
|
|
);
|
|
if (!(dialog instanceof HTMLElement)) {
|
|
throw new Error("Expected favorite folder dialog");
|
|
}
|
|
return dialog;
|
|
}
|
|
|
|
function readInput(): HTMLInputElement {
|
|
const input = document.querySelector(
|
|
'[data-sces-favorite-folder-dialog="input"]'
|
|
);
|
|
if (!(input instanceof HTMLInputElement)) {
|
|
throw new Error("Expected favorite folder name input");
|
|
}
|
|
return input;
|
|
}
|
|
|
|
function readButton(name: "confirm" | "cancel"): HTMLButtonElement {
|
|
const button = document.querySelector(
|
|
`[data-sces-favorite-folder-dialog="${name}"]`
|
|
);
|
|
if (!(button instanceof HTMLButtonElement)) {
|
|
throw new Error(`Expected ${name} button`);
|
|
}
|
|
return button;
|
|
}
|
|
|
|
describe("favorite-folder-dialog", () => {
|
|
beforeEach(() => {
|
|
document.body.innerHTML = "";
|
|
});
|
|
|
|
test("renders the requested title and initial name, then trims a confirmation", async () => {
|
|
const result = promptForFavoriteFolderName(document, {
|
|
initialValue: " 重点合作 ",
|
|
title: "重命名收藏夹"
|
|
});
|
|
|
|
expect(readDialog().getAttribute("role")).toBe("dialog");
|
|
expect(readDialog().textContent).toContain("重命名收藏夹");
|
|
expect(readInput().value).toBe(" 重点合作 ");
|
|
expect(document.activeElement).toBe(readInput());
|
|
|
|
readButton("confirm").click();
|
|
|
|
await expect(result).resolves.toBe("重点合作");
|
|
expect(
|
|
document.querySelector('[data-sces-favorite-folder-dialog="root"]')
|
|
).toBeNull();
|
|
});
|
|
|
|
test("keeps an inline error open for blank names and clears it after input", async () => {
|
|
const result = promptForFavoriteFolderName(document, {
|
|
title: "新建收藏夹"
|
|
});
|
|
|
|
readInput().value = " ";
|
|
readButton("confirm").click();
|
|
|
|
expect(
|
|
document.querySelector('[data-sces-favorite-folder-dialog="error"]')?.textContent
|
|
).toBe("请输入收藏夹名称");
|
|
expect(readInput().getAttribute("aria-invalid")).toBe("true");
|
|
expect(document.activeElement).toBe(readInput());
|
|
|
|
readInput().value = "重点";
|
|
readInput().dispatchEvent(new Event("input", { bubbles: true }));
|
|
expect(
|
|
document.querySelector('[data-sces-favorite-folder-dialog="error"]')?.textContent
|
|
).toBe("");
|
|
|
|
readButton("cancel").click();
|
|
await expect(result).resolves.toBeNull();
|
|
});
|
|
|
|
test("cancels from its cancel button, overlay click, and Escape", async () => {
|
|
const fromCancel = promptForFavoriteFolderName(document, {
|
|
title: "新建收藏夹"
|
|
});
|
|
readButton("cancel").click();
|
|
await expect(fromCancel).resolves.toBeNull();
|
|
|
|
const fromOverlay = promptForFavoriteFolderName(document, {
|
|
title: "新建收藏夹"
|
|
});
|
|
readDialog().dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
await expect(fromOverlay).resolves.toBeNull();
|
|
|
|
const fromEscape = promptForFavoriteFolderName(document, {
|
|
title: "新建收藏夹"
|
|
});
|
|
document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" }));
|
|
await expect(fromEscape).resolves.toBeNull();
|
|
});
|
|
|
|
test("confirms a valid name with Enter", async () => {
|
|
const result = promptForFavoriteFolderName(document, {
|
|
title: "新建收藏夹"
|
|
});
|
|
readInput().value = "待联系";
|
|
|
|
document.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter" }));
|
|
|
|
await expect(result).resolves.toBe("待联系");
|
|
});
|
|
|
|
test("reuses and focuses one active dialog per document, then allows a new one after close", async () => {
|
|
const first = promptForFavoriteFolderName(document, {
|
|
initialValue: "首个值",
|
|
title: "新建收藏夹"
|
|
});
|
|
const input = readInput();
|
|
input.blur();
|
|
|
|
const reused = promptForFavoriteFolderName(document, {
|
|
initialValue: "不会替换",
|
|
title: "重命名收藏夹"
|
|
});
|
|
|
|
expect(reused).toBe(first);
|
|
expect(
|
|
document.querySelectorAll('[data-sces-favorite-folder-dialog="root"]')
|
|
).toHaveLength(1);
|
|
expect(readInput().value).toBe("首个值");
|
|
expect(document.activeElement).toBe(input);
|
|
|
|
readButton("cancel").click();
|
|
await expect(first).resolves.toBeNull();
|
|
|
|
const second = promptForFavoriteFolderName(document, {
|
|
title: "新建收藏夹"
|
|
});
|
|
expect(
|
|
document.querySelectorAll('[data-sces-favorite-folder-dialog="root"]')
|
|
).toHaveLength(1);
|
|
readButton("cancel").click();
|
|
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";
|
|
opener.textContent = "打开收藏夹";
|
|
document.body.append(opener);
|
|
opener.focus();
|
|
|
|
const result = promptForFavoriteFolderName(document, {
|
|
title: "新建收藏夹"
|
|
});
|
|
|
|
readInput().focus();
|
|
document.dispatchEvent(new KeyboardEvent("keydown", { key: "Tab", shiftKey: true }));
|
|
expect(document.activeElement).toBe(readButton("confirm"));
|
|
document.dispatchEvent(new KeyboardEvent("keydown", { key: "Tab" }));
|
|
expect(document.activeElement).toBe(readInput());
|
|
|
|
readButton("cancel").click();
|
|
await expect(result).resolves.toBeNull();
|
|
expect(document.activeElement).toBe(opener);
|
|
});
|
|
});
|