import { fireEvent, screen, waitFor } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import SettingPage from '@/pages/users/SettingPage' import { buildUser } from '@/test/factories' import { renderWithProviders } from '@/test/render' const api = vi.hoisted (() => ({ apiGet: vi.fn (), apiPut: vi.fn (), isApiError: vi.fn (), })) const toastApi = vi.hoisted (() => ({ toast: vi.fn (), })) vi.mock ('@/lib/api', () => api) vi.mock ('@/components/ui/use-toast', () => toastApi) vi.mock ('@/components/users/UserCodeDialogue', () => ({ default: () => null, })) vi.mock ('@/components/users/InheritDialogue', () => ({ default: () => null, })) describe ('SettingPage', () => { beforeEach (() => { vi.clearAllMocks () api.isApiError.mockReturnValue (false) api.apiGet.mockImplementation ((path: string) => { if (path === '/users/settings') return Promise.resolve ({ theme: 'system', auto_fetch_title: 'manual', auto_fetch_thumbnail: 'manual', wiki_editor_mode: 'split' }) if (path === '/users/theme_slots') return Promise.resolve ([]) return Promise.resolve ({}) }) }) it ('shows loading when user is absent', () => { renderWithProviders () expect (screen.getAllByText ('Loading...').length).toBeGreaterThan (0) }) it ('updates the current user name', async () => { const user = buildUser ({ id: 11, name: 'old' }) const setUser = vi.fn () api.apiPut.mockResolvedValueOnce ({ ...user, name: 'new' }) renderWithProviders () const nameInput = (await screen.findAllByRole ('textbox'))[0] fireEvent.change (nameInput, { target: { value: 'new' } }) fireEvent.click (screen.getAllByRole ('button', { name: '表示名を更新' })[0]) await waitFor (() => { expect (api.apiPut).toHaveBeenCalledWith ( '/users/11', expect.any (FormData), { headers: { 'Content-Type': 'multipart/form-data' } }, ) }) const formData = api.apiPut.mock.calls[0]?.[1] as FormData expect (formData.get ('name')).toBe ('new') expect (setUser).toHaveBeenCalled () expect (toastApi.toast).toHaveBeenCalledWith ({ title: '表示名を更新しました.' }) }) it ('shows validation errors returned for the name field', async () => { const user = buildUser ({ id: 11, name: 'old' }) api.isApiError.mockReturnValue (true) api.apiPut.mockRejectedValueOnce ({ response: { status: 422, data: { type: 'validation_error', message: '入力内容を確認してください.', errors: { name: ['名前は必須です.'] }, base_errors: [], }, }, }) renderWithProviders () const nameInput = (await screen.findAllByRole ('textbox'))[0] fireEvent.change (nameInput, { target: { value: '' } }) fireEvent.click (screen.getAllByRole ('button', { name: '表示名を更新' })[0]) expect ((await screen.findAllByText ('名前は必須です.')).length).toBeGreaterThan (0) expect (nameInput).toHaveAttribute ('aria-invalid', 'true') }) })