Files
trade-message-center/apps/chrome-extension/test/onetalk-action-status-tooltip.test.js
T

225 lines
8.6 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import { installOneTalkActionStatusTooltip } from "../src/onetalk/main-page/dom/action-status-tooltip.ts";
import { ConnectionStatusTooltip } from "../src/onetalk/main-page/connection-status-tooltip.ts";
import { HistoryBootstrapProgressTooltip } from "../src/onetalk/main-page/current-conversation-history/bootstrap-progress-tooltip.ts";
class FakeElement {
constructor() {
this.attributes = new Map();
this.children = [];
this.dataset = {};
this.parent = null;
this.style = { cssText: "" };
this.textContent = "";
}
setAttribute(name, value) {
this.attributes.set(name, value);
}
getAttribute(name) {
return this.attributes.get(name) ?? null;
}
append(element) {
if (element.parent) {
element.parent.children = element.parent.children.filter((child) => child !== element);
}
element.parent = this;
this.children.push(element);
}
get parentElement() {
return this.parent;
}
remove() {
if (!this.parent) return;
this.parent.children = this.parent.children.filter((child) => child !== this);
this.parent = null;
}
}
const hasAttribute = (element, attribute) => element.attributes.has(attribute);
const findByAttribute = (element, attribute) => {
if (hasAttribute(element, attribute)) return element;
for (const child of element.children) {
const match = findByAttribute(child, attribute);
if (match) return match;
}
return null;
};
const createPageWindow = () => {
const body = new FakeElement();
const document = {
body,
createElement: () => new FakeElement(),
querySelector: (selector) => {
const attribute = selector.match(/^div\[([^\]]+)\]$/u)?.[1];
return attribute ? findByAttribute(body, attribute) : null;
},
};
return { document };
};
const tooltipFor = (pageWindow) => {
installOneTalkActionStatusTooltip(pageWindow);
return pageWindow.__tradeMessageCenterOneTalk.tooltip;
};
test("starts one neutral status row and does not duplicate its id", () => {
const pageWindow = createPageWindow();
const tooltip = tooltipFor(pageWindow);
tooltip.start("collect-history", "正在收集所有对话历史");
tooltip.start("collect-history", "不应覆盖已有动作", "error");
const container = pageWindow.document.body.children[0];
assert.equal(container.getAttribute("aria-live"), "polite");
assert.match(container.style.cssText, /position: fixed/u);
assert.match(container.style.cssText, /top: 0/u);
assert.match(container.style.cssText, /pointer-events: none/u);
assert.equal(container.children.length, 1);
assert.equal(container.children[0].textContent, "正在收集所有对话历史");
assert.equal(container.children[0].dataset.tmcActionStatusId, "collect-history");
assert.equal(container.children[0].getAttribute("data-tmc-action-status-color"), "neutral");
assert.match(container.children[0].style.cssText, /background: #f5f5f5/u);
});
test("updates only its existing id and preserves insertion order", () => {
const pageWindow = createPageWindow();
const tooltip = tooltipFor(pageWindow);
tooltip.start("collect-history", "正在收集所有对话历史");
tooltip.start("sync-profile", "正在同步联系人资料", "info");
assert.equal(tooltip.update("missing", "不会创建"), false);
assert.equal(tooltip.update("collect-history", "已收集一半", "warning"), true);
const rows = pageWindow.document.body.children[0].children;
assert.deepEqual(
rows.map((row) => row.dataset.tmcActionStatusId),
["collect-history", "sync-profile"],
);
assert.equal(rows[0].textContent, "已收集一半");
assert.equal(rows[0].getAttribute("data-tmc-action-status-color"), "warning");
assert.match(rows[0].style.cssText, /color: #d48806/u);
assert.equal(rows[1].getAttribute("data-tmc-action-status-color"), "info");
});
test("rejects raw CSS colors and closes only the requested status", () => {
const pageWindow = createPageWindow();
const tooltip = tooltipFor(pageWindow);
assert.throws(() => tooltip.start("collect-history", "正在收集所有对话历史", "#ff0000"));
tooltip.start("collect-history", "正在收集所有对话历史", "error");
tooltip.start("sync-profile", "正在同步联系人资料");
assert.throws(() => tooltip.update("collect-history", "不应应用", "#ff0000"));
assert.equal(
pageWindow.document.body.children[0].children[0].textContent,
"正在收集所有对话历史",
);
assert.equal(tooltip.close("missing"), false);
assert.equal(tooltip.close("collect-history"), true);
assert.equal(pageWindow.document.body.children[0].children.length, 1);
assert.equal(
pageWindow.document.body.children[0].children[0].dataset.tmcActionStatusId,
"sync-profile",
);
assert.equal(tooltip.close("sync-profile"), true);
assert.equal(pageWindow.document.body.children.length, 0);
});
test("reuses the installed facade when page features install again", () => {
const pageWindow = createPageWindow();
const first = tooltipFor(pageWindow);
first.start("collect-history", "正在收集所有对话历史");
const second = tooltipFor(pageWindow);
assert.equal(second, first);
assert.equal(pageWindow.document.body.children[0].children.length, 1);
});
test("remounts active statuses after the page removes the extension container", () => {
const pageWindow = createPageWindow();
const tooltip = tooltipFor(pageWindow);
tooltip.start("collect-history", "正在收集所有对话历史");
pageWindow.document.body.children[0].remove();
assert.equal(tooltip.update("collect-history", "正在收集所有对话历史(已恢复)"), true);
assert.equal(pageWindow.document.body.children.length, 1);
assert.equal(pageWindow.document.body.children[0].children.length, 1);
assert.equal(
pageWindow.document.body.children[0].children[0].textContent,
"正在收集所有对话历史(已恢复)",
);
});
test("reinstalls active statuses when the existing facade is mounted again", () => {
const pageWindow = createPageWindow();
const tooltip = tooltipFor(pageWindow);
tooltip.start("collect-history", "正在收集所有对话历史");
pageWindow.document.body.children[0].remove();
installOneTalkActionStatusTooltip(pageWindow);
assert.equal(pageWindow.document.body.children.length, 1);
assert.equal(pageWindow.document.body.children[0].children.length, 1);
assert.equal(
pageWindow.document.body.children[0].children[0].textContent,
"正在收集所有对话历史",
);
});
test("restores a detached active row when its container remains mounted", () => {
const pageWindow = createPageWindow();
const tooltip = tooltipFor(pageWindow);
tooltip.start("collect-history", "正在收集所有对话历史");
const container = pageWindow.document.body.children[0];
container.children[0].remove();
assert.equal(tooltip.update("collect-history", "正在收集所有对话历史(已恢复)"), true);
assert.equal(container.children.length, 1);
assert.equal(container.children[0].textContent, "正在收集所有对话历史(已恢复)");
});
test("keeps the disconnect warning independent from history progress through remount and close", () => {
const pageWindow = createPageWindow();
const tooltip = tooltipFor(pageWindow);
const history = new HistoryBootstrapProgressTooltip(tooltip);
const connection = new ConnectionStatusTooltip(tooltip);
history.beginDiscovery();
connection.update(true);
connection.update(true);
let rows = pageWindow.document.body.children[0].children;
assert.deepEqual(
rows.map((row) => row.dataset.tmcActionStatusId),
["onetalk-history-bootstrap", "onetalk-connection-status"],
);
assert.equal(rows[1].textContent, "连接已断开,正在尝试重新连接");
assert.equal(rows[1].getAttribute("data-tmc-action-status-color"), "warning");
pageWindow.document.body.children[0].remove();
connection.update(true);
rows = pageWindow.document.body.children[0].children;
assert.deepEqual(
rows.map((row) => row.dataset.tmcActionStatusId),
["onetalk-history-bootstrap", "onetalk-connection-status"],
);
connection.update(false);
rows = pageWindow.document.body.children[0].children;
assert.deepEqual(
rows.map((row) => row.dataset.tmcActionStatusId),
["onetalk-history-bootstrap"],
);
});