- 版本更新
- 正在检查更新...
+
`;
}
@@ -38,50 +53,54 @@ export function renderUpdateStatus(
options: {
currentVersion: string;
manifest?: UpdateManifest;
+ message?: string | null;
status: "checking" | "error" | "latest" | "available";
}
): void {
- const container = root.querySelector('[data-popup-update="root"]');
+ const container = root.querySelector('[data-popup-update-root="root"]');
if (!container) {
return;
}
if (options.status === "checking") {
container.innerHTML = `
- 版本更新
- 当前版本:${options.currentVersion}
- 正在检查更新...
+
+
+
`;
return;
}
if (options.status === "error") {
container.innerHTML = `
- 版本更新
- 当前版本:${options.currentVersion}
- 暂时无法检查更新
- 如果需要新版,请联系维护同事获取更新包。
+
+
+
+ ${options.message ? `` : ""}
+
`;
return;
}
if (options.status === "latest" || !options.manifest) {
container.innerHTML = `
- 版本更新
- 当前版本:${options.currentVersion}
- 当前已是最新版本
+
+
+
`;
return;
}
container.innerHTML = `
- 版本更新
- 当前版本:${options.currentVersion}
- 发现新版本:${options.manifest.latestVersion}
+
+
+
${renderReleaseNotes(options.manifest.releaseNotes)}
-
-
- 下载后请解压新版 zip,并在 chrome://extensions 里重新加载插件。
+
+
`;
}
@@ -103,7 +122,7 @@ function renderReleaseNotes(releaseNotes: string[]): string {
}
return `
-
+
`;
@@ -122,15 +141,16 @@ export function renderDevPanel(
authState: AuthStateValue
): void {
const panel = root.ownerDocument.createElement("section");
+ panel.className = "popup-card popup-card--dev";
panel.dataset.popupDevPanel = "root";
panel.innerHTML = `
- dev auth panel
- resource: ${authState.resource ?? ""}
- scopes: ${(authState.scopes ?? []).join(", ")}
- token: ${authState.tokenAvailable ? "available" : "missing"}
- expires: ${authState.accessTokenExpiresAt ?? "unknown"}
- error: ${authState.lastError ?? ""}
-
+
+
+
+
+
+
+
`;
root.appendChild(panel);
diff --git a/tests/popup-entry.test.ts b/tests/popup-entry.test.ts
index 1bc2ef4..0965c1d 100644
--- a/tests/popup-entry.test.ts
+++ b/tests/popup-entry.test.ts
@@ -3,6 +3,10 @@ import { beforeEach, describe, expect, test, vi } from "vitest";
import { bootPopup } from "../src/popup/index";
+function flushTasks(): Promise {
+ return new Promise((resolve) => setTimeout(resolve, 0));
+}
+
describe("popup-entry", () => {
let dom: JSDOM;
@@ -22,6 +26,12 @@ describe("popup-entry", () => {
}))
});
+ expect(
+ dom.window.document.querySelector('[data-popup-shell="root"]')
+ ).not.toBeNull();
+ expect(
+ dom.window.document.querySelector('[data-popup-account="card"]')
+ ).not.toBeNull();
expect(dom.window.document.querySelector("button")?.textContent).toContain(
"登录"
);
@@ -47,6 +57,12 @@ describe("popup-entry", () => {
}))
});
+ expect(
+ dom.window.document.querySelector('[data-popup-header="root"]')
+ ).not.toBeNull();
+ expect(
+ dom.window.document.querySelector('[data-popup-account="card"]')
+ ).not.toBeNull();
expect(dom.window.document.body.textContent).toContain("resource");
expect(dom.window.document.body.textContent).toContain("token");
});
@@ -76,12 +92,16 @@ describe("popup-entry", () => {
}
}))
});
- await Promise.resolve();
+ await flushTasks();
+ await flushTasks();
expect(fetchUpdateManifest).toHaveBeenCalledTimes(1);
expect(dom.window.document.body.textContent).toContain("当前版本:0.2.0421.2");
expect(dom.window.document.body.textContent).toContain("发现新版本:0.2.0421.3");
expect(dom.window.document.body.textContent).toContain("支持检查更新");
+ expect(
+ dom.window.document.querySelector('[data-popup-update="card"]')
+ ).not.toBeNull();
expect(
dom.window.document.querySelector('[data-popup-download-update="button"]')
).not.toBeNull();
@@ -118,7 +138,8 @@ describe("popup-entry", () => {
})),
sendMessage
});
- await Promise.resolve();
+ await flushTasks();
+ await flushTasks();
(
dom.window.document.querySelector(
@@ -245,6 +266,63 @@ describe("popup-entry", () => {
expect(sendMessage).toHaveBeenCalledWith({ type: "auth:sign-out" });
});
+ test("shows the latest state without update actions", async () => {
+ dom.window.document.body.innerHTML = "";
+
+ await bootPopup({
+ currentVersion: "0.0525.5",
+ document: dom.window.document,
+ fetchUpdateManifest: vi.fn(async () => ({
+ guideUrl: "https://cos.example.com/guide.pdf",
+ latestVersion: "0.0525.5",
+ minSupportedVersion: "0.0525.5",
+ publishedAt: "2026-05-19",
+ releaseNotes: ["支持检查更新"],
+ zipUrl: "https://cos.example.com/plugin.zip"
+ })),
+ sendMessage: vi.fn(async () => ({
+ ok: true,
+ type: "auth:state",
+ value: {
+ isAuthenticated: true,
+ userInfo: { name: "Dev" }
+ }
+ }))
+ });
+ await flushTasks();
+ await flushTasks();
+
+ expect(dom.window.document.body.textContent).toContain("当前已是最新版本");
+ expect(
+ dom.window.document.querySelector('[data-popup-download-update="button"]')
+ ).toBeNull();
+ });
+
+ test("shows a readable error state when the manifest fetch fails", async () => {
+ dom.window.document.body.innerHTML = "";
+
+ await bootPopup({
+ currentVersion: "0.0525.5",
+ document: dom.window.document,
+ fetchUpdateManifest: vi.fn(async () => {
+ throw new Error("network down");
+ }),
+ sendMessage: vi.fn(async () => ({
+ ok: true,
+ type: "auth:state",
+ value: {
+ isAuthenticated: true,
+ userInfo: { name: "Dev" }
+ }
+ }))
+ });
+ await flushTasks();
+ await flushTasks();
+
+ expect(dom.window.document.body.textContent).toContain("暂时无法检查更新");
+ expect(dom.window.document.body.textContent).toContain("network down");
+ });
+
test("shows the auth error when sign-in fails", async () => {
const sendMessage = vi
.fn()