mirror of
https://github.com/Buriburizaem0n/admin-frontend-domain.git
synced 2026-09-19 17:50:13 +00:00
feat(api-tokens): add PAT management UI, CSRF handling, and auth-loading fixes
Add an API tokens management route to create, list, and revoke PATs, showing the plaintext token once on creation with scope and server-id selection. Mirror the nz-csrf cookie into the X-CSRF-Token header on unsafe fetcher methods (POST/PUT/PATCH/DELETE) for the server-side double-submit check, and self-heal expired sessions via refresh-token without a recursive fetch loop. Gate protected routes behind resolved auth state to avoid pre-auth SWR fetches, and fix the login loading/race so stale probes cannot clobber the session. Add i18n keys for the new screens across all locales. Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
import { act, fireEvent, render, screen, waitFor } from "@testing-library/react"
|
||||
import { MemoryRouter } from "react-router-dom"
|
||||
import { afterEach, beforeEach, expect, test, vi } from "vitest"
|
||||
|
||||
const toastCalls: string[] = []
|
||||
vi.mock("sonner", () => ({
|
||||
toast: (msg: string) => {
|
||||
toastCalls.push(msg)
|
||||
},
|
||||
}))
|
||||
|
||||
const changeLanguageCalls: string[] = []
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (k: string) => k,
|
||||
i18n: {
|
||||
language: "en",
|
||||
changeLanguage: (lng: string) => {
|
||||
changeLanguageCalls.push(lng)
|
||||
},
|
||||
},
|
||||
}),
|
||||
initReactI18next: { type: "3rdParty", init: () => undefined },
|
||||
Trans: ({ children }: { children?: React.ReactNode }) => children ?? null,
|
||||
}))
|
||||
|
||||
vi.mock("@/hooks/useAuth", () => ({
|
||||
useAuth: () => ({ profile: { id: 1, role: 0 }, loading: false }),
|
||||
}))
|
||||
|
||||
const validConfig = {
|
||||
config: {
|
||||
site_name: "Nezha",
|
||||
language: "zh-CN",
|
||||
user_template: "user-dist",
|
||||
cover: 1,
|
||||
ip_change_notification_group_id: 0,
|
||||
},
|
||||
frontend_templates: [],
|
||||
}
|
||||
vi.mock("@/hooks/useSetting", () => ({
|
||||
default: () => ({ data: validConfig, mutate: vi.fn() }),
|
||||
}))
|
||||
|
||||
vi.mock("@/hooks/useNotfication", () => ({
|
||||
useNotification: () => ({ notifierGroup: [] }),
|
||||
}))
|
||||
|
||||
const updateSettings = vi.fn()
|
||||
vi.mock("@/api/settings", () => ({ updateSettings: (...args: unknown[]) => updateSettings(...args) }))
|
||||
|
||||
beforeEach(() => {
|
||||
toastCalls.length = 0
|
||||
changeLanguageCalls.length = 0
|
||||
updateSettings.mockReset()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
document.body.innerHTML = ""
|
||||
})
|
||||
|
||||
test("SettingsPage does not toast Success when updateSettings rejects", async () => {
|
||||
updateSettings.mockRejectedValue(new Error("boom"))
|
||||
const { default: SettingsPage } = await import("@/routes/settings")
|
||||
|
||||
await act(async () => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<SettingsPage />
|
||||
</MemoryRouter>,
|
||||
)
|
||||
})
|
||||
|
||||
const submit = screen.getByRole("button", { name: /Confirm|Submit|Save/i })
|
||||
await act(async () => {
|
||||
fireEvent.click(submit)
|
||||
})
|
||||
|
||||
await waitFor(() => expect(updateSettings).toHaveBeenCalled())
|
||||
|
||||
expect(toastCalls).toContain("Error")
|
||||
expect(toastCalls).not.toContain("Success")
|
||||
expect(changeLanguageCalls).toEqual([])
|
||||
})
|
||||
|
||||
test("SettingsPage toasts Success when updateSettings resolves", async () => {
|
||||
updateSettings.mockResolvedValue(undefined)
|
||||
const { default: SettingsPage } = await import("@/routes/settings")
|
||||
|
||||
await act(async () => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<SettingsPage />
|
||||
</MemoryRouter>,
|
||||
)
|
||||
})
|
||||
|
||||
const submit = screen.getByRole("button", { name: /Confirm|Submit|Save/i })
|
||||
await act(async () => {
|
||||
fireEvent.click(submit)
|
||||
})
|
||||
|
||||
await waitFor(() => expect(updateSettings).toHaveBeenCalled())
|
||||
|
||||
expect(toastCalls).toContain("Success")
|
||||
expect(toastCalls).not.toContain("Error")
|
||||
})
|
||||
Reference in New Issue
Block a user