diff --git a/app/[locale]/community/news/[id]/NewsViewer.tsx b/app/[locale]/community/news/[id]/NewsViewer.tsx index 393c0570..036b6cab 100644 --- a/app/[locale]/community/news/[id]/NewsViewer.tsx +++ b/app/[locale]/community/news/[id]/NewsViewer.tsx @@ -37,7 +37,9 @@ export default async function NewsViewer({ news }: NewsPostPageProps) { wrapperClassName="mb-10" /> - +
+ +
+
{t('태그')}
{ diff --git a/e2e/fixtures/pages.fixture.ts b/e2e/fixtures/pages.fixture.ts index 7cdf3c19..aedf5f2c 100644 --- a/e2e/fixtures/pages.fixture.ts +++ b/e2e/fixtures/pages.fixture.ts @@ -3,6 +3,8 @@ import { Page } from '@playwright/test'; +import { NewsCreatePage } from '../pages/community/news/create.page'; +import { NewsPage } from '../pages/community/news/index.page'; import { NoticeCreatePage } from '../pages/community/notice/create.page'; import { NoticePage } from '../pages/community/notice/index.page'; import { test as base } from './role.fixture'; @@ -10,6 +12,8 @@ import { test as base } from './role.fixture'; type PagesFixtures = { noticePage: NoticePage; noticeCreatePage: NoticeCreatePage; + newsPage: NewsPage; + newsCreatePage: NewsCreatePage; }; export const test = base.extend({ @@ -22,4 +26,13 @@ export const test = base.extend({ ) => { await use(new NoticeCreatePage(page)); }, + newsPage: async ({ page }: { page: Page }, use: (page: NewsPage) => Promise) => { + await use(new NewsPage(page)); + }, + newsCreatePage: async ( + { page }: { page: Page }, + use: (page: NewsCreatePage) => Promise, + ) => { + await use(new NewsCreatePage(page)); + }, }); diff --git a/e2e/pages/community/news/create.page.ts b/e2e/pages/community/news/create.page.ts new file mode 100644 index 00000000..8ce24c4f --- /dev/null +++ b/e2e/pages/community/news/create.page.ts @@ -0,0 +1,31 @@ +import type { Locator, Page } from '@playwright/test'; + +export class NewsCreatePage { + readonly page: Page; + readonly titleInput: Locator; + readonly contentEditor: Locator; + readonly submitButton: Locator; + + constructor(page: Page) { + this.page = page; + this.titleInput = page.getByPlaceholder('제목을 입력하세요.'); + this.contentEditor = page.locator('div.sun-editor-editable'); + this.submitButton = page.getByRole('button', { name: /게시/ }); + } + + async goto() { + await this.page.goto('/community/news/create'); + } + + async fillTitle(title: string) { + await this.titleInput.fill(title); + } + + async fillContent(content: string) { + await this.contentEditor.fill(content); + } + + async clickSubmitButton() { + await this.submitButton.click(); + } +} diff --git a/e2e/pages/community/news/index.page.ts b/e2e/pages/community/news/index.page.ts new file mode 100644 index 00000000..f89b605c --- /dev/null +++ b/e2e/pages/community/news/index.page.ts @@ -0,0 +1,49 @@ +import type { Locator, Page } from '@playwright/test'; + +export const TAGS = [ + '행사', + '연구', + '수상', + '채용', + '칼럼', + '강연', + '교육', + '인터뷰', + '진로', + '과거 미분류', +]; + +export class NewsPage { + readonly page: Page; + readonly newPostButton: Locator; + readonly tagFilterBox: Locator; + readonly selectedTags: Locator; + + constructor(page: Page) { + this.page = page; + this.newPostButton = page.getByRole('button', { name: '새 게시글' }); + this.tagFilterBox = page.locator('#tag-filter'); + this.selectedTags = page.locator('#selected-tags'); + } + + async goto() { + await this.page.goto('/community/news'); + } + + async clickNewPostButton() { + await this.newPostButton.click(); + } + + async clickTag(tagName: string) { + await this.tagFilterBox.locator(`label[for="${tagName}"]`).click(); + } + + // To click the close button of the selected tag. (It shows as 'X' in the UI) + async removeTag(tagName: string) { + await this.selectedTags.locator(`span:has-text("${tagName}") button`).click(); + } + + async resetTags() { + await this.selectedTags.locator('button:has-text("태그 초기화")').click(); + } +} diff --git a/e2e/tests/community/news.test.ts b/e2e/tests/community/news.test.ts new file mode 100644 index 00000000..dc1e4fb0 --- /dev/null +++ b/e2e/tests/community/news.test.ts @@ -0,0 +1,67 @@ +import { expect } from '@playwright/test'; + +import { test } from '../../fixtures'; +import { TAGS } from '../../pages/community/news/index.page'; + +test.describe('News', () => { + test('should allow STAFF to create a new news post with tags', async ({ + page, + loginAs, + newsPage, + newsCreatePage, + }) => { + // 1. Arrange + await newsPage.goto(); + const newsTitle = `[Test] New News - ${Date.now()}`; + const newsContent = 'This is a test news content.'; + const selectedTagsBox = page.locator('.flex.flex-wrap.items-center.gap-2\\.5.mt-3.ml-6'); + await loginAs('STAFF'); + + // 2. Act + await newsPage.clickNewPostButton(); + await expect(page).toHaveURL('/community/news/create'); + await newsCreatePage.fillTitle(newsTitle); + await newsCreatePage.fillContent(newsContent); + + // 태그 선택 + await newsCreatePage.page.click(`label[for="${TAGS[0]}"]`); + await newsCreatePage.page.click(`label[for="${TAGS[1]}"]`); + + await page.waitForTimeout(1000); + await newsCreatePage.clickSubmitButton(); + + // 3. Assert + await expect(page.getByText(newsTitle).first()).toBeVisible(); + await expect(selectedTagsBox.getByText(TAGS[0])).toBeVisible(); + await expect(selectedTagsBox.getByText(TAGS[1])).toBeVisible(); + + // To check the post is visible in the news list with selected tags + await newsPage.goto(); + await newsPage.clickTag(TAGS[0]); + await newsPage.clickTag(TAGS[1]); + + await expect(page.getByText(newsTitle)).toBeVisible(); + }); + + test('should allow basic user to remove selected tags and reset all tags', async ({ + page, + newsPage, + }) => { + await newsPage.goto(); + + const selectedTagsBox = page.locator('#selected-tags'); + + for (const tag of TAGS.slice(0, 3)) { + await newsPage.clickTag(tag); + await expect(selectedTagsBox.getByText(tag)).toBeVisible(); + } + + await newsPage.removeTag(TAGS[0]); + await expect(selectedTagsBox.getByText(TAGS[0])).not.toBeVisible(); + + await newsPage.resetTags(); + for (const tag of TAGS.slice(1, 3)) { + await expect(selectedTagsBox.getByText(tag)).not.toBeVisible(); + } + }); +});