Files
Randify.pro/tests/translation-client.test.ts
T

147 lines
4.3 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from "vitest";
const mockPostMessage = vi.fn();
const mockAddEventListener = vi.fn();
function MockBroadcastChannel(this: unknown, _name: string) {
return {
postMessage: mockPostMessage,
addEventListener: mockAddEventListener,
close: vi.fn(),
};
}
vi.stubGlobal("BroadcastChannel", MockBroadcastChannel as unknown as typeof BroadcastChannel);
describe("translation service", () => {
beforeEach(() => {
vi.resetModules();
mockPostMessage.mockClear();
mockAddEventListener.mockClear();
});
it("stores and retrieves cached translations", async () => {
const { getCachedTranslation, setCachedTranslation, clearTranslationCache } =
await import("../src/lib/client/translation");
clearTranslationCache();
expect(getCachedTranslation("goblin", "creature")).toBeNull();
setCachedTranslation("goblin", "creature", { name: "Гоблин" });
expect(getCachedTranslation("goblin", "creature")).toEqual({ name: "Гоблин" });
expect(getCachedTranslation("dragon", "creature")).toBeNull();
});
it("broadcasts cache updates via BroadcastChannel", async () => {
const { setCachedTranslation, clearTranslationCache } = await import(
"../src/lib/client/translation"
);
clearTranslationCache();
setCachedTranslation("goblin", "creature", { name: "Гоблин" });
expect(mockPostMessage).toHaveBeenCalledWith(
expect.objectContaining({
slug: "goblin",
type: "creature",
data: { name: "Гоблин" },
}),
);
});
it("fetches translation from API when not cached", async () => {
const mockFetch = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ translated: { name: "Гоблин" } }),
});
global.fetch = mockFetch as unknown as typeof fetch;
const { fetchTranslation, clearTranslationCache } = await import(
"../src/lib/client/translation"
);
clearTranslationCache();
const result = await fetchTranslation("goblin", "creature");
expect(mockFetch).toHaveBeenCalledWith(
"/api/dm/translate?slug=goblin&type=creature",
);
expect(result).toEqual({ name: "Гоблин" });
});
it("returns cached translation without fetching", async () => {
const mockFetch = vi.fn();
global.fetch = mockFetch as unknown as typeof fetch;
const {
getCachedTranslation,
setCachedTranslation,
fetchTranslation,
clearTranslationCache,
} = await import("../src/lib/client/translation");
clearTranslationCache();
setCachedTranslation("goblin", "creature", { name: "Гоблин" });
const result = await fetchTranslation("goblin", "creature");
expect(mockFetch).not.toHaveBeenCalled();
expect(result).toEqual({ name: "Гоблин" });
});
it("handles fetch errors", async () => {
const mockFetch = vi.fn().mockResolvedValue({
ok: false,
json: async () => ({ error: "Not found" }),
});
global.fetch = mockFetch as unknown as typeof fetch;
const { fetchTranslation, clearTranslationCache } = await import(
"../src/lib/client/translation"
);
clearTranslationCache();
await expect(fetchTranslation("unknown", "creature")).rejects.toThrow(
"Not found",
);
});
it("does not use localStorage", async () => {
const {
getCachedTranslation,
setCachedTranslation,
clearTranslationCache,
} = await import("../src/lib/client/translation");
clearTranslationCache();
const setItemSpy = vi.spyOn(Storage.prototype, "setItem");
const getItemSpy = vi.spyOn(Storage.prototype, "getItem");
setCachedTranslation("goblin", "creature", { name: "Гоблин" });
getCachedTranslation("goblin", "creature");
expect(setItemSpy).not.toHaveBeenCalled();
expect(getItemSpy).not.toHaveBeenCalled();
setItemSpy.mockRestore();
getItemSpy.mockRestore();
});
it("tracks cache size", async () => {
const {
setCachedTranslation,
getTranslationCacheSize,
clearTranslationCache,
} = await import("../src/lib/client/translation");
clearTranslationCache();
expect(getTranslationCacheSize()).toBe(0);
setCachedTranslation("a", "creature", { name: "A" });
setCachedTranslation("b", "spell", { name: "B" });
expect(getTranslationCacheSize()).toBe(2);
});
});