Browse Source
The app now starts I18nProvider in zh-CN when no saved language exists, while preserving explicit saved choices. The login-required Ant Design modal also uses the v6 mask.closable shape so the console no longer warns about maskClosable. Constraint: First-run locale should not override an existing saved language preference Constraint: Ant Design 6 deprecates maskClosable on Modal.confirm Rejected: Keep browser-language detection | first entry needs deterministic Simplified Chinese Confidence: high Scope-risk: narrow Directive: Keep provider-less useI18n fallback stable for isolated tests unless all callers are wrapped in I18nProvider Tested: npm run test:run -- src/i18n/__tests__/index.test.tsx src/components/__tests__/TemplateExplorerView.test.tsx src/components/__tests__/Header.test.tsx src/utils/__tests__/loginRequiredModal.test.ts src/utils/__tests__/authFetch.test.ts src/store/__tests__/userStore.test.ts Tested: npm run build Not-tested: npm run lint | existing Next 16 next lint script resolves to nonexistent /lint directoryfeature/gateway
4 changed files with 80 additions and 12 deletions
@ -0,0 +1,50 @@ |
|||||
|
import { render, screen, waitFor } from "@testing-library/react"; |
||||
|
import { beforeEach, describe, expect, it } from "vitest"; |
||||
|
import { I18nProvider, useI18n } from "@/i18n"; |
||||
|
|
||||
|
function LanguageProbe() { |
||||
|
const { language, t } = useI18n(); |
||||
|
return ( |
||||
|
<div> |
||||
|
<span data-testid="language">{language}</span> |
||||
|
<span data-testid="label">{t("language.label")}</span> |
||||
|
</div> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
describe("I18nProvider", () => { |
||||
|
beforeEach(() => { |
||||
|
window.localStorage.clear(); |
||||
|
document.documentElement.lang = ""; |
||||
|
}); |
||||
|
|
||||
|
it("defaults first-time visitors to Simplified Chinese", async () => { |
||||
|
render( |
||||
|
<I18nProvider> |
||||
|
<LanguageProbe /> |
||||
|
</I18nProvider> |
||||
|
); |
||||
|
|
||||
|
expect(screen.getByTestId("language")).toHaveTextContent("zh-CN"); |
||||
|
expect(screen.getByTestId("label")).toHaveTextContent("语言"); |
||||
|
await waitFor(() => { |
||||
|
expect(window.localStorage.getItem("popiart-node-language")).toBe("zh-CN"); |
||||
|
expect(document.documentElement.lang).toBe("zh-CN"); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
it("keeps an existing saved language preference", async () => { |
||||
|
window.localStorage.setItem("popiart-node-language", "ja"); |
||||
|
|
||||
|
render( |
||||
|
<I18nProvider> |
||||
|
<LanguageProbe /> |
||||
|
</I18nProvider> |
||||
|
); |
||||
|
|
||||
|
await waitFor(() => { |
||||
|
expect(screen.getByTestId("language")).toHaveTextContent("ja"); |
||||
|
expect(screen.getByTestId("label")).toHaveTextContent("言語"); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,26 @@ |
|||||
|
import { beforeEach, describe, expect, it, vi } from "vitest"; |
||||
|
|
||||
|
describe("openLoginRequiredModal", () => { |
||||
|
beforeEach(() => { |
||||
|
vi.resetModules(); |
||||
|
}); |
||||
|
|
||||
|
it("uses the Ant Design 6 mask.closable option instead of deprecated maskClosable", async () => { |
||||
|
const confirm = vi.fn(() => ({ |
||||
|
destroy: vi.fn(), |
||||
|
update: vi.fn(), |
||||
|
})); |
||||
|
|
||||
|
vi.doMock("antd", () => ({ |
||||
|
Modal: { confirm }, |
||||
|
})); |
||||
|
|
||||
|
const { openLoginRequiredModal } = await import("@/utils/loginRequiredModal"); |
||||
|
openLoginRequiredModal(); |
||||
|
|
||||
|
expect(confirm).toHaveBeenCalledWith(expect.objectContaining({ |
||||
|
mask: { closable: false }, |
||||
|
})); |
||||
|
expect(confirm.mock.calls[0][0]).not.toHaveProperty("maskClosable"); |
||||
|
}); |
||||
|
}); |
||||
Loading…
Reference in new issue