import { expect, test } from '@playwright/test' const adminEmail = 'playwright-admin@example.com' const adminPassword = 'playwright-admin-password' /** * 관리자 화면에서 실제 로그인 API를 거쳐 로그인한다. * @param {import('@playwright/test').Page} page - 브라우저 페이지 * @returns {Promise} */ const loginAdmin = async (page) => { await page.goto('/admin/login') await page.getByLabel('이메일').fill(adminEmail) await page.locator('input[type="password"]').fill(adminPassword) await Promise.all([ page.waitForResponse((response) => response.url().endsWith('/admin/api/auth/login') && response.status() === 200 ), page.getByRole('button', { name: '로그인' }).click() ]) await page.waitForURL(/\/admin(?:\/)?$/) } test('설정 카드 하나만 저장하면 다른 카드의 미저장 입력을 보존한다', async ({ page }) => { const savedTitle = 'Playwright 독립 저장 사이트' const unsavedCopyright = 'Playwright 미저장 저작권' await loginAdmin(page) await page.goto('/admin/settings') await page.waitForLoadState('networkidle') const titleCard = page.getByTestId('title-and-description') const siteInfoCard = page.locator('#admin-settings-section-misc') await titleCard.getByRole('button', { name: '편집' }).click() await titleCard.getByLabel('사이트 이름').fill(savedTitle) await siteInfoCard.getByRole('button', { name: '편집' }).click() const copyrightInput = siteInfoCard.getByLabel('저작권 문구') const originalCopyright = await copyrightInput.inputValue() await copyrightInput.fill(unsavedCopyright) const saveResponsePromise = page.waitForResponse((response) => response.url().endsWith('/admin/api/settings') && response.request().method() === 'PATCH' && response.status() === 200 ) await titleCard.getByRole('button', { name: '저장', exact: true }).click() const saveResponse = await saveResponsePromise expect(saveResponse.request().postDataJSON()).toEqual({ title: savedTitle, description: expect.any(String) }) await expect(copyrightInput).toHaveValue(unsavedCopyright) await page.reload() await page.waitForLoadState('networkidle') await expect(titleCard).toContainText(savedTitle) await siteInfoCard.getByRole('button', { name: '편집' }).click() await expect(siteInfoCard.getByLabel('저작권 문구')).toHaveValue(originalCopyright) }) test('발행 글의 미저장 변경에서 이탈을 취소하면 입력과 경로를 유지한다', async ({ page }) => { const originalTitle = 'Playwright Unsaved Guard' const changedTitle = 'Playwright Unsaved Guard Changed' await loginAdmin(page) const createResponse = await page.request.post('/admin/api/posts', { data: { title: originalTitle, slug: 'playwright-unsaved-guard', content: '미저장 이탈 보호 E2E 본문', excerpt: '', status: 'published', publishedAt: new Date().toISOString(), tags: [], tagNames: {} } }) expect(createResponse.status()).toBe(200) const post = await createResponse.json() await page.goto(`/admin/posts/${post.id}`) await page.waitForLoadState('networkidle') const titleInput = page.getByPlaceholder('제목') await titleInput.fill(changedTitle) await expect(titleInput).toHaveValue(changedTitle) await page.getByRole('link', { name: 'Posts', exact: true }).click() const dialog = page.getByRole('dialog', { name: '이 페이지를 떠날까요?' }) await expect(dialog).toBeVisible() await dialog.getByRole('button', { name: '머무르기' }).click() await expect(dialog).toBeHidden() await expect(page).toHaveURL(new RegExp(`/admin/posts/${post.id}$`)) await expect(titleInput).toHaveValue(changedTitle) })