feat: update language support and enhance API functionality
- Changed the default language in `index.html` from Russian to English, updating the title and button aria-labels for improved accessibility. - Refactored the `buildFetchOptions` function in `api.js` to include an optional external abort signal, enhancing request management. - Updated `fetchDuties` and `fetchCalendarEvents` to support request cancellation using the new abort signal, improving error handling. - Added unit tests for the API functions to ensure proper functionality, including handling of 403 errors and request cancellations. - Enhanced CSS styles for duty markers to improve visual consistency. - Removed unused code and improved the overall structure of the JavaScript files for better maintainability.
This commit is contained in:
87
webapp/js/i18n.test.js
Normal file
87
webapp/js/i18n.test.js
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Unit tests for i18n: getLang, t (fallback, params), monthName.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
|
||||
const mockGetInitData = vi.fn();
|
||||
vi.mock("./auth.js", () => ({ getInitData: () => mockGetInitData() }));
|
||||
|
||||
import { getLang, t, monthName, MESSAGES } from "./i18n.js";
|
||||
|
||||
describe("getLang", () => {
|
||||
const origNavigator = globalThis.navigator;
|
||||
|
||||
beforeEach(() => {
|
||||
mockGetInitData.mockReset();
|
||||
});
|
||||
|
||||
it("returns lang from initData user when present", () => {
|
||||
mockGetInitData.mockReturnValue(
|
||||
"user=" + encodeURIComponent(JSON.stringify({ language_code: "en" }))
|
||||
);
|
||||
expect(getLang()).toBe("en");
|
||||
});
|
||||
|
||||
it("normalizes ru from initData", () => {
|
||||
mockGetInitData.mockReturnValue(
|
||||
"user=" + encodeURIComponent(JSON.stringify({ language_code: "ru" }))
|
||||
);
|
||||
expect(getLang()).toBe("ru");
|
||||
});
|
||||
|
||||
it("falls back to navigator.language when initData empty", () => {
|
||||
mockGetInitData.mockReturnValue("");
|
||||
Object.defineProperty(globalThis, "navigator", {
|
||||
value: { ...origNavigator, language: "en-US", languages: ["en-US", "en"] },
|
||||
configurable: true,
|
||||
});
|
||||
expect(getLang()).toBe("en");
|
||||
});
|
||||
|
||||
it("normalizes to en for unknown language code", () => {
|
||||
mockGetInitData.mockReturnValue(
|
||||
"user=" + encodeURIComponent(JSON.stringify({ language_code: "uk" }))
|
||||
);
|
||||
expect(getLang()).toBe("en");
|
||||
});
|
||||
});
|
||||
|
||||
describe("t", () => {
|
||||
it("returns translation for existing key", () => {
|
||||
expect(t("en", "app.title")).toBe("Duty Calendar");
|
||||
expect(t("ru", "app.title")).toBe("Календарь дежурств");
|
||||
});
|
||||
|
||||
it("falls back to en when key missing in lang", () => {
|
||||
expect(t("ru", "app.title")).toBe("Календарь дежурств");
|
||||
expect(t("en", "loading")).toBe("Loading…");
|
||||
});
|
||||
|
||||
it("returns key when key missing in both", () => {
|
||||
expect(t("en", "missing.key")).toBe("missing.key");
|
||||
expect(t("ru", "unknown")).toBe("unknown");
|
||||
});
|
||||
|
||||
it("replaces params placeholder", () => {
|
||||
expect(t("en", "duty.until", { time: "14:00" })).toBe("until 14:00");
|
||||
expect(t("ru", "duty.until", { time: "09:30" })).toBe("до 09:30");
|
||||
});
|
||||
|
||||
it("handles empty params", () => {
|
||||
expect(t("en", "loading", {})).toBe("Loading…");
|
||||
});
|
||||
});
|
||||
|
||||
describe("monthName", () => {
|
||||
it("returns month name for 0-based index", () => {
|
||||
expect(monthName("en", 0)).toBe("January");
|
||||
expect(monthName("en", 11)).toBe("December");
|
||||
expect(monthName("ru", 0)).toBe("Январь");
|
||||
});
|
||||
|
||||
it("returns empty string for out-of-range", () => {
|
||||
expect(monthName("en", 12)).toBe("");
|
||||
expect(monthName("en", -1)).toBe("");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user