Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline (Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/* packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
104 lines
3.8 KiB
TypeScript
104 lines
3.8 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("expo", () => ({
|
|
reloadAppAsync: vi.fn().mockResolvedValue(undefined),
|
|
}));
|
|
|
|
vi.mock("react-native", () => ({
|
|
I18nManager: {
|
|
allowRTL: vi.fn(),
|
|
forceRTL: vi.fn(),
|
|
isRTL: false,
|
|
},
|
|
Platform: { OS: "ios" },
|
|
}));
|
|
|
|
describe("mobile ui direction", () => {
|
|
beforeEach(() => {
|
|
vi.resetModules();
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("resolves a locale tag from Intl", async () => {
|
|
const resolvedOptions = vi.fn().mockReturnValue({ locale: "he-IL" });
|
|
vi.stubGlobal("Intl", {
|
|
...Intl,
|
|
DateTimeFormat: vi.fn().mockImplementation(() => ({ resolvedOptions })),
|
|
});
|
|
const { resolveMobileUiLocale } = await import("./ui-direction");
|
|
|
|
expect(resolveMobileUiLocale()).toBe("he-IL");
|
|
});
|
|
|
|
it("forces rtl and reloads when the runtime is still ltr", async () => {
|
|
const { I18nManager } = await import("react-native");
|
|
const { reloadAppAsync } = await import("expo");
|
|
(I18nManager as { isRTL: boolean }).isRTL = false;
|
|
const { applyMobileUiDirection } = await import("./ui-direction");
|
|
|
|
expect(applyMobileUiDirection("he-IL")).toBe(true);
|
|
expect(I18nManager.allowRTL).toHaveBeenCalledWith(true);
|
|
expect(I18nManager.forceRTL).toHaveBeenCalledWith(true);
|
|
await Promise.resolve();
|
|
expect(reloadAppAsync).toHaveBeenCalledWith("ui-direction");
|
|
});
|
|
|
|
it("forces ltr and reloads when the runtime is still rtl", async () => {
|
|
const { I18nManager } = await import("react-native");
|
|
const { reloadAppAsync } = await import("expo");
|
|
(I18nManager as { isRTL: boolean }).isRTL = true;
|
|
const { applyMobileUiDirection } = await import("./ui-direction");
|
|
|
|
expect(applyMobileUiDirection("en-US")).toBe(false);
|
|
expect(I18nManager.allowRTL).toHaveBeenCalledWith(true);
|
|
expect(I18nManager.forceRTL).toHaveBeenCalledWith(false);
|
|
await Promise.resolve();
|
|
expect(reloadAppAsync).toHaveBeenCalledWith("ui-direction");
|
|
});
|
|
|
|
it("skips forceRTL and reload when layout already matches the locale", async () => {
|
|
const { I18nManager } = await import("react-native");
|
|
const { reloadAppAsync } = await import("expo");
|
|
(I18nManager as { isRTL: boolean }).isRTL = true;
|
|
const { applyMobileUiDirection } = await import("./ui-direction");
|
|
|
|
expect(applyMobileUiDirection("he-IL")).toBe(true);
|
|
expect(I18nManager.allowRTL).toHaveBeenCalledWith(true);
|
|
expect(I18nManager.forceRTL).not.toHaveBeenCalled();
|
|
await Promise.resolve();
|
|
expect(reloadAppAsync).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("defaults to LTR English so a no-arg call cannot fight zh/en/ru UI locale bootstrap", async () => {
|
|
const resolvedOptions = vi.fn().mockReturnValue({ locale: "ar-SA" });
|
|
vi.stubGlobal("Intl", {
|
|
...Intl,
|
|
DateTimeFormat: vi.fn().mockImplementation(() => ({ resolvedOptions })),
|
|
});
|
|
const { I18nManager } = await import("react-native");
|
|
const { reloadAppAsync } = await import("expo");
|
|
(I18nManager as { isRTL: boolean }).isRTL = true;
|
|
const { applyMobileUiDirection } = await import("./ui-direction");
|
|
|
|
expect(applyMobileUiDirection()).toBe(false);
|
|
expect(I18nManager.forceRTL).toHaveBeenCalledWith(false);
|
|
await Promise.resolve();
|
|
expect(reloadAppAsync).toHaveBeenCalledWith("ui-direction");
|
|
});
|
|
|
|
it("keeps root layout from applying device-locale direction before i18n bootstrap", async () => {
|
|
const { readFileSync } = await import("node:fs");
|
|
const { dirname, join } = await import("node:path");
|
|
const { fileURLToPath } = await import("node:url");
|
|
const layout = readFileSync(
|
|
join(dirname(fileURLToPath(import.meta.url)), "../app/_layout.tsx"),
|
|
"utf8",
|
|
);
|
|
expect(layout).not.toMatch(/applyMobileUiDirection\s*\(/);
|
|
});
|
|
});
|