diff --git a/src/components/layout/HeaderActions/HeaderActions.test.tsx b/src/components/layout/HeaderActions/HeaderActions.test.tsx
index 53e4b7a..963a7ff 100644
--- a/src/components/layout/HeaderActions/HeaderActions.test.tsx
+++ b/src/components/layout/HeaderActions/HeaderActions.test.tsx
@@ -5,7 +5,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { HeaderActions } from './HeaderActions'
import { getSafeNotificationRoute } from './notificationPresentation'
-const USER = { name: '김민지', workplace: '한빛정밀', role: 'HR' }
+const USER = { name: '김민지', email: 'kim@example.com', workplace: '한빛정밀', role: 'HR' }
const NOTIFICATIONS = {
items: [
{
@@ -94,23 +94,31 @@ describe('HeaderActions', () => {
await user.click(screen.getByRole('menuitem', { name: /체류연장 요청문 승인이 필요합니다/ }))
await waitFor(() => expect(router.state.location.pathname).toBe('/tasks/task-1'))
- expect(vi.mocked(fetch).mock.calls.some(([url, init]) =>
- String(url).includes('/notifications/n1/read') && init?.method === 'POST',
- )).toBe(true)
+ expect(
+ vi
+ .mocked(fetch)
+ .mock.calls.some(
+ ([url, init]) =>
+ String(url).includes('/notifications/n1/read') && init?.method === 'POST',
+ ),
+ ).toBe(true)
})
it('shows an error and keeps the panel open when read processing fails', async () => {
vi.mocked(fetch).mockImplementation(async (_input, init) => {
if (init?.method === 'POST') {
- return jsonResponse({
- timestamp: '2026-08-10T01:00:00Z',
- status: 500,
- code: 'INTERNAL_SERVER_ERROR',
- message: 'failed',
- path: '/api/v1/notifications/n1/read',
- request_id: 'request-1',
- field_errors: [],
- }, 500)
+ return jsonResponse(
+ {
+ timestamp: '2026-08-10T01:00:00Z',
+ status: 500,
+ code: 'INTERNAL_SERVER_ERROR',
+ message: 'failed',
+ path: '/api/v1/notifications/n1/read',
+ request_id: 'request-1',
+ field_errors: [],
+ },
+ 500,
+ )
}
return jsonResponse(NOTIFICATIONS)
})
@@ -120,7 +128,9 @@ describe('HeaderActions', () => {
await user.click(await screen.findByLabelText('알림 1건 안 읽음'))
await user.click(screen.getByRole('menuitem', { name: /체류연장 요청문 승인이 필요합니다/ }))
- expect(await screen.findByText('일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.')).toBeInTheDocument()
+ expect(
+ await screen.findByText('일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.'),
+ ).toBeInTheDocument()
expect(router.state.location.pathname).toBe('/dashboard')
expect(screen.getByRole('menu', { name: '알림 목록' })).toBeInTheDocument()
})
@@ -156,7 +166,9 @@ describe('HeaderActions', () => {
describe('getSafeNotificationRoute', () => {
it('keeps expected internal routes and rejects external or unexpected routes', () => {
- expect(getSafeNotificationRoute('/documents/document-1?tab=ocr')).toBe('/documents/document-1?tab=ocr')
+ expect(getSafeNotificationRoute('/documents/document-1?tab=ocr')).toBe(
+ '/documents/document-1?tab=ocr',
+ )
expect(getSafeNotificationRoute('https://example.com')).toBe('/dashboard')
expect(getSafeNotificationRoute('//example.com/tasks/1')).toBe('/dashboard')
expect(getSafeNotificationRoute('/profile')).toBe('/dashboard')
diff --git a/src/pages/CaseDetailPage/CaseDetailPage.test.tsx b/src/pages/CaseDetailPage/CaseDetailPage.test.tsx
index bf8dd8d..0fee01b 100644
--- a/src/pages/CaseDetailPage/CaseDetailPage.test.tsx
+++ b/src/pages/CaseDetailPage/CaseDetailPage.test.tsx
@@ -706,7 +706,7 @@ describe('CaseDetailPage', () => {
it('does not offer the response review action to a viewer', async () => {
const user = userEvent.setup()
useAuthStore.setState({
- user: { name: 'viewer', workplace: 'FOWOCO', role: 'VIEWER' },
+ user: { name: 'viewer', email: 'viewer@example.com', workplace: 'FOWOCO', role: 'VIEWER' },
status: 'ready',
})
mockTaskAndActivities(
@@ -739,7 +739,7 @@ describe('CaseDetailPage', () => {
it('adopts a submitted file as an official worker document', async () => {
const user = userEvent.setup()
useAuthStore.setState({
- user: { name: 'hr', workplace: 'FOWOCO', role: 'HR' },
+ user: { name: 'hr', email: 'hr@example.com', workplace: 'FOWOCO', role: 'HR' },
status: 'ready',
})
mockTaskAndActivities(
diff --git a/src/pages/ProfilePage/CompanySettingsPanel.test.tsx b/src/pages/ProfilePage/CompanySettingsPanel.test.tsx
index e7cbb80..4edf2c2 100644
--- a/src/pages/ProfilePage/CompanySettingsPanel.test.tsx
+++ b/src/pages/ProfilePage/CompanySettingsPanel.test.tsx
@@ -53,7 +53,7 @@ function renderPanel() {
beforeEach(() => {
useAuthStore.setState({
- user: { name: 'admin', workplace: 'FOWOCO', role: 'ADMIN' },
+ user: { name: 'admin', email: 'admin@example.com', workplace: 'FOWOCO', role: 'ADMIN' },
status: 'ready',
})
useToastStore.setState({ toasts: [] })
@@ -108,7 +108,9 @@ describe('CompanySettingsPanel', () => {
})
it('renders HR and VIEWER settings as read-only', async () => {
- useAuthStore.setState({ user: { name: 'hr', workplace: 'FOWOCO', role: 'HR' } })
+ useAuthStore.setState({
+ user: { name: 'hr', email: 'hr@example.com', workplace: 'FOWOCO', role: 'HR' },
+ })
renderPanel()
expect(await screen.findByText('HR 조회 전용')).toBeInTheDocument()
@@ -122,21 +124,22 @@ describe('CompanySettingsPanel', () => {
const url = String(input)
if (url.includes('/company-members')) return jsonResponse(MEMBERS)
if (init?.method === 'PATCH') {
- return jsonResponse({
- timestamp: '2026-08-10T01:00:00Z',
- status: 409,
- code: 'CONCURRENT_MODIFICATION',
- message: 'conflict',
- path: '/api/v1/settings',
- request_id: 'request-1',
- field_errors: [],
- }, 409)
+ return jsonResponse(
+ {
+ timestamp: '2026-08-10T01:00:00Z',
+ status: 409,
+ code: 'CONCURRENT_MODIFICATION',
+ message: 'conflict',
+ path: '/api/v1/settings',
+ request_id: 'request-1',
+ field_errors: [],
+ },
+ 409,
+ )
}
settingsGetCount += 1
return jsonResponse(
- settingsGetCount === 1
- ? SETTINGS
- : { ...SETTINGS, link_expiry_hours: 24, version: 4 },
+ settingsGetCount === 1 ? SETTINGS : { ...SETTINGS, link_expiry_hours: 24, version: 4 },
)
})
const user = userEvent.setup()
@@ -153,20 +156,25 @@ describe('CompanySettingsPanel', () => {
it('shows a recoverable error when settings cannot be loaded', async () => {
vi.mocked(fetch).mockImplementation(async (input) => {
if (String(input).includes('/company-members')) return jsonResponse(MEMBERS)
- return jsonResponse({
- timestamp: '2026-08-10T01:00:00Z',
- status: 500,
- code: 'INTERNAL_SERVER_ERROR',
- message: 'failed',
- path: '/api/v1/settings',
- request_id: 'request-1',
- field_errors: [],
- }, 500)
+ return jsonResponse(
+ {
+ timestamp: '2026-08-10T01:00:00Z',
+ status: 500,
+ code: 'INTERNAL_SERVER_ERROR',
+ message: 'failed',
+ path: '/api/v1/settings',
+ request_id: 'request-1',
+ field_errors: [],
+ },
+ 500,
+ )
})
renderPanel()
- expect(await screen.findByText('일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.')).toBeInTheDocument()
+ expect(
+ await screen.findByText('일시적인 오류가 발생했습니다. 잠시 후 다시 시도해 주세요.'),
+ ).toBeInTheDocument()
expect(screen.getByRole('button', { name: '다시 시도' })).toBeInTheDocument()
})
})
diff --git a/src/pages/ProfilePage/ProfilePage.test.tsx b/src/pages/ProfilePage/ProfilePage.test.tsx
index 8156761..dbf3b28 100644
--- a/src/pages/ProfilePage/ProfilePage.test.tsx
+++ b/src/pages/ProfilePage/ProfilePage.test.tsx
@@ -1,7 +1,8 @@
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { createMemoryRouter, RouterProvider } from 'react-router-dom'
-import { beforeEach, describe, expect, it } from 'vitest'
+import { afterEach, beforeEach, describe, expect, it } from 'vitest'
+import { useAuthStore } from '../../store/authStore'
import { ToastViewport } from '../../components/ui/ToastViewport/ToastViewport'
import { useToastStore } from '../../store/toastStore'
import { ProfilePage } from './ProfilePage'
@@ -31,6 +32,10 @@ beforeEach(() => {
useToastStore.setState({ toasts: [] })
})
+afterEach(() => {
+ useAuthStore.setState({ user: null })
+})
+
describe('ProfilePage', () => {
it('renders the profile summary and read-only fields', () => {
renderPage()
@@ -42,6 +47,24 @@ describe('ProfilePage', () => {
expect(screen.getByText('체류·문서 운영')).toBeInTheDocument()
})
+ it("shows the real logged-in user's identity instead of the fixture persona", () => {
+ useAuthStore.setState({
+ user: {
+ name: 'demo.admin',
+ email: 'demo.admin@example.com',
+ workplace: 'FOWOCO Demo Company',
+ role: 'ADMIN',
+ },
+ status: 'ready',
+ })
+ renderPage()
+
+ expect(screen.getAllByText('demo.admin').length).toBeGreaterThan(0)
+ expect(screen.getAllByText(/demo\.admin@example\.com/).length).toBeGreaterThan(0)
+ expect(screen.getByText('FOWOCO Demo Company')).toBeInTheDocument()
+ expect(screen.queryByText('hr.demo@fowoco.example')).not.toBeInTheDocument()
+ })
+
it('edits and saves the editable fields', async () => {
const user = userEvent.setup()
renderPage()
@@ -156,7 +179,9 @@ describe('ProfilePage', () => {
await user.type(screen.getByLabelText('연락처'), '9')
await user.click(screen.getByRole('button', { name: '비밀번호 변경' }))
- expect(screen.getByRole('dialog', { name: '저장하지 않은 변경사항이 있습니다.' })).toBeInTheDocument()
+ expect(
+ screen.getByRole('dialog', { name: '저장하지 않은 변경사항이 있습니다.' }),
+ ).toBeInTheDocument()
expect(screen.getByText(/변경사항 1개/)).toBeInTheDocument()
await user.click(screen.getByRole('button', { name: '계속 수정' }))
diff --git a/src/pages/ProfilePage/ProfilePage.tsx b/src/pages/ProfilePage/ProfilePage.tsx
index aade3f8..c15a2fb 100644
--- a/src/pages/ProfilePage/ProfilePage.tsx
+++ b/src/pages/ProfilePage/ProfilePage.tsx
@@ -4,6 +4,7 @@ import { Button } from '../../components/ui/Button/Button'
import { DetailRow } from '../../components/ui/DetailRow/DetailRow'
import { Modal } from '../../components/ui/Modal/Modal'
import { StatusLabel } from '../../components/ui/StatusLabel/StatusLabel'
+import { useAuthStore } from '../../store/authStore'
import { useToastStore } from '../../store/toastStore'
import { CompanySettingsPanel } from './CompanySettingsPanel'
import {
@@ -49,20 +50,28 @@ function validateFields(input: EditableProfileFields): FieldErrors {
export function ProfilePage() {
const navigate = useNavigate()
const showToast = useToastStore((state) => state.showToast)
+ const user = useAuthStore((state) => state.user)
- const [fields, setFields] = useState {fields.name}
- {PROFILE_SUMMARY.role} · {PROFILE_SUMMARY.email}
+ {user?.role ?? PROFILE_SUMMARY.role} · {user?.email ?? PROFILE_SUMMARY.email}
{fieldErrors[key]} {PROFILE_SUMMARY.email} 계정 보호 상태와 최근 로그인 정보를 확인합니다.
-