import { expect, test } from '@playwright/test' const adminEmail = 'playwright-admin@example.com' const adminPassword = 'playwright-admin-password' /** * 관리자 화면에 로그인한다. * @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(?:\/)?$/) } /** * 관리자 API로 태그를 만든다. * @param {import('@playwright/test').APIRequestContext} request - API 요청 컨텍스트 * @param {{ name: string, slug: string }} tag - 태그 입력값 * @returns {Promise} */ const createTag = async (request, tag) => { const response = await request.post('/admin/api/tags', { data: { name: tag.name, slug: tag.slug, description: '', color: '#15171a', tagType: 'managed' } }) expect([200, 409]).toContain(response.status()) } /** * 관리자 API로 공개 게시물을 만든다. * @param {import('@playwright/test').APIRequestContext} request - API 요청 컨텍스트 * @param {Object} post - 게시물 입력값 * @returns {Promise} 생성된 게시물 */ const createPublishedPost = async (request, post) => { const response = await request.post('/admin/api/posts', { data: { title: post.title, slug: post.slug, content: post.content, excerpt: '', featuredImage: null, showFeaturedImage: false, status: 'published', publishedAt: new Date().toISOString(), tags: post.tags || [], tagNames: post.tagNames || {} } }) expect(response.status()).toBe(200) return await response.json() } test('태그 추천을 선택한 뒤에도 다음 후보를 바로 선택할 수 있다', async ({ page }) => { await loginAdmin(page) await createTag(page.request, { name: 'Regression Recent', slug: 'regression-recent' }) await createTag(page.request, { name: 'Regression Next', slug: 'regression-next' }) await createPublishedPost(page.request, { title: 'Regression Tag Seed', slug: 'regression-tag-seed', content: '태그 추천 회귀 테스트용 게시물', tags: ['regression-recent'], tagNames: { 'regression-recent': 'Regression Recent' } }) await page.goto('/admin/posts/new') await page.waitForLoadState('networkidle') await page.getByLabel('태그 추천 목록 열기').click() await page.getByRole('option', { name: /Regression Recent/ }).click() await expect(page.getByRole('option', { name: /Regression Next/ })).toBeVisible() await page.getByRole('option', { name: /Regression Next/ }).click() await expect(page.getByLabel('Regression Recent 태그 삭제')).toBeVisible() await expect(page.getByLabel('Regression Next 태그 삭제')).toBeVisible() }) test('미디어 파생 탭에는 역할 배지가 표시되지 않는다', async ({ page }) => { await loginAdmin(page) await page.goto('/admin/media') await page.waitForLoadState('networkidle') await page.getByRole('button', { name: '대표 이미지' }).click() await expect(page.locator('.admin-media__usage-badge')).toHaveCount(0) await page.getByRole('button', { name: '본문 이미지' }).click() await expect(page.locator('.admin-media__usage-badge')).toHaveCount(0) }) test('단일 이미지 size 옵션이 공개 화면과 관리자 미리보기에 반영된다', async ({ page }) => { await loginAdmin(page) const post = await createPublishedPost(page.request, { title: 'Regression Image Width', slug: 'regression-image-width', content: '![](https://example.com/playwright-width.png){size=180}' }) await page.goto('/post/regression-image-width') const publicImage = page.locator('.prose-image').first() await expect(publicImage).toHaveCSS('max-width', '180px') await page.goto(`/admin/posts/${post.id}`) await page.waitForLoadState('networkidle') await page.getByLabel('미리보기로 전환').click() const previewImage = page.locator('.admin-markdown-editor__preview .prose-image').first() await expect(previewImage).toHaveCSS('max-width', '180px') })