mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-08 08:52:12 +00:00
The font pickers (editor, fallback, UI, terminal) become editable comboboxes: type to search installed families or enter any family name, and Refresh re-reads the system fonts. ide-fonts' cached family list is now refreshable (DirectWrite re-queries the system collection with checkForUpdates; CoreText and fontconfig are read anew), exposed through system_font_families(refresh). Before the first native result the pickers fall back to Canvas detection of common families; saved names and the last catalog are kept when a refresh fails. Fonts are enumerated on the local desktop in remote-development windows too. Co-authored-by: bghtyu <[email protected]>
93 lines
4.1 KiB
TypeScript
93 lines
4.1 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
const { native, discover, detect } = vi.hoisted(() => ({
|
|
native: vi.fn(() => true),
|
|
discover: vi.fn(),
|
|
detect: vi.fn(),
|
|
}));
|
|
vi.mock("./candidateFonts", () => ({ detectCandidateFonts: detect }));
|
|
vi.mock("@tauri-apps/api/core", () => ({ isTauri: native }));
|
|
vi.mock("@/bindings", () => ({ commands: { systemFontFamilies: discover } }));
|
|
import { bundledFonts, loadSystemFonts, useSystemFontStore } from "./systemFonts";
|
|
|
|
beforeEach(() => {
|
|
native.mockReturnValue(true);
|
|
discover.mockReset();
|
|
detect.mockReset().mockReturnValue([{ name: "Menlo", monospaced: true }]);
|
|
useSystemFontStore.setState({ fonts: bundledFonts, status: "idle", source: "bundled" });
|
|
});
|
|
describe("local font discovery", () => {
|
|
it("shares an in-flight request, caches families outside the old list and explicitly refreshes", async () => {
|
|
let resolve!: (fonts: unknown) => void;
|
|
discover.mockReturnValue(
|
|
new Promise((done) => {
|
|
resolve = done;
|
|
}),
|
|
);
|
|
const first = loadSystemFonts();
|
|
expect(useSystemFontStore.getState().status).toBe("loading");
|
|
expect(loadSystemFonts()).toBe(first);
|
|
await Promise.resolve();
|
|
resolve({
|
|
status: "ok",
|
|
data: [
|
|
{ name: "Maple Mono NF", monospaced: true },
|
|
{ name: "maple mono nf", monospaced: true },
|
|
],
|
|
});
|
|
await first;
|
|
expect(useSystemFontStore.getState().fonts.map((f) => f.name)).toEqual([
|
|
"Inter",
|
|
"JetBrains Mono",
|
|
"maple mono nf",
|
|
]);
|
|
await loadSystemFonts();
|
|
expect(discover).toHaveBeenCalledTimes(1);
|
|
discover.mockResolvedValue({ status: "ok", data: [] });
|
|
await loadSystemFonts(true);
|
|
expect(discover).toHaveBeenLastCalledWith(true);
|
|
expect(useSystemFontStore.getState()).toMatchObject({ status: "empty", source: "native" });
|
|
expect(detect).not.toHaveBeenCalled();
|
|
expect(useSystemFontStore.getState().fonts).toEqual([bundledFonts[1], bundledFonts[0]]);
|
|
});
|
|
it("reports errors without losing suggestions, then retries", async () => {
|
|
discover.mockResolvedValueOnce({ status: "error", error: { kind: "unavailable", message: "Unavailable" } });
|
|
await loadSystemFonts();
|
|
expect(useSystemFontStore.getState()).toMatchObject({ status: "error", source: "limited" });
|
|
expect(useSystemFontStore.getState().fonts.some((f) => f.name === "Menlo")).toBe(true);
|
|
discover.mockResolvedValue({ status: "ok", data: [{ name: "思源宋体", monospaced: false }] });
|
|
await loadSystemFonts(true);
|
|
expect(useSystemFontStore.getState()).toMatchObject({ status: "ready", source: "native" });
|
|
expect(useSystemFontStore.getState().fonts.some((f) => f.name === "Menlo")).toBe(false);
|
|
expect(useSystemFontStore.getState().fonts.some((f) => f.name === "思源宋体")).toBe(true);
|
|
});
|
|
it("does not invoke desktop APIs in browser development", async () => {
|
|
native.mockReturnValue(false);
|
|
await loadSystemFonts();
|
|
expect(discover).not.toHaveBeenCalled();
|
|
expect(detect).toHaveBeenCalledOnce();
|
|
expect(useSystemFontStore.getState().status).toBe("browser");
|
|
});
|
|
});
|
|
|
|
it.each([{ fonts: [] }, { fonts: [{ name: "Outside Legacy List", monospaced: true }] }])(
|
|
"retains successful native catalogs, including empty ones, after refresh failure: %j",
|
|
async ({ fonts }) => {
|
|
discover.mockResolvedValueOnce({ status: "ok", data: fonts });
|
|
await loadSystemFonts();
|
|
const previous = useSystemFontStore.getState().fonts;
|
|
discover.mockRejectedValueOnce(new Error("native unavailable"));
|
|
await loadSystemFonts(true);
|
|
expect(useSystemFontStore.getState()).toMatchObject({ status: "error", source: "native", fonts: previous });
|
|
expect(detect).not.toHaveBeenCalled();
|
|
},
|
|
);
|
|
it("keeps bundled/manual recovery usable when candidate probing also fails", async () => {
|
|
discover.mockRejectedValue(new Error("native unavailable"));
|
|
detect.mockImplementation(() => {
|
|
throw new Error("canvas unavailable");
|
|
});
|
|
await loadSystemFonts();
|
|
expect(useSystemFontStore.getState()).toMatchObject({ status: "error", source: "limited" });
|
|
expect(useSystemFontStore.getState().fonts.map((font) => font.name)).toEqual(["Inter", "JetBrains Mono"]);
|
|
});
|