diff --git a/e2e/fixtures/index.ts b/e2e/fixtures/index.fixture.ts similarity index 100% rename from e2e/fixtures/index.ts rename to e2e/fixtures/index.fixture.ts diff --git a/e2e/fixtures/pages.fixture.ts b/e2e/fixtures/pages.fixture.ts index 7cdf3c19..73e8a9e9 100644 --- a/e2e/fixtures/pages.fixture.ts +++ b/e2e/fixtures/pages.fixture.ts @@ -2,19 +2,26 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { Page } from '@playwright/test'; +import { test as base } from '@playwright/test'; import { NoticeCreatePage } from '../pages/community/notice/create.page'; -import { NoticePage } from '../pages/community/notice/index.page'; -import { test as base } from './role.fixture'; +import { NoticeDetailPage } from '../pages/community/notice/detail.page'; +import { NoticeEditPage } from '../pages/community/notice/edit.page'; +import { NoticeListPage } from '../pages/community/notice/index.page'; type PagesFixtures = { - noticePage: NoticePage; + noticeListPage: NoticeListPage; noticeCreatePage: NoticeCreatePage; + noticeEditPage: NoticeEditPage; + noticeDetailPage: NoticeDetailPage; }; export const test = base.extend({ - noticePage: async ({ page }: { page: Page }, use: (page: NoticePage) => Promise) => { - await use(new NoticePage(page)); + noticeListPage: async ( + { page }: { page: Page }, + use: (page: NoticeListPage) => Promise, + ) => { + await use(new NoticeListPage(page)); }, noticeCreatePage: async ( { page }: { page: Page }, @@ -22,4 +29,16 @@ export const test = base.extend({ ) => { await use(new NoticeCreatePage(page)); }, + noticeEditPage: async ( + { page }: { page: Page }, + use: (page: NoticeEditPage) => Promise, + ) => { + await use(new NoticeEditPage(page)); + }, + noticeDetailPage: async ( + { page }: { page: Page }, + use: (page: NoticeDetailPage) => Promise, + ) => { + await use(new NoticeDetailPage(page)); + }, }); diff --git a/e2e/fixtures/role.fixture.ts b/e2e/fixtures/role.fixture.ts index 35304f2e..2ce2381c 100644 --- a/e2e/fixtures/role.fixture.ts +++ b/e2e/fixtures/role.fixture.ts @@ -12,8 +12,8 @@ type RoleFixtures = { export const test = base.extend({ loginAs: async ({ page }, use) => { const login = async (role: Role) => { - await page.waitForLoadState('networkidle'); await page.getByRole('button', { name: role, exact: true }).click(); + await page.waitForLoadState('networkidle'); }; await use(login); }, diff --git a/e2e/pages/community/notice/create.page.ts b/e2e/pages/community/notice/create.page.ts index 58c04bca..b13380a2 100644 --- a/e2e/pages/community/notice/create.page.ts +++ b/e2e/pages/community/notice/create.page.ts @@ -4,12 +4,24 @@ export class NoticeCreatePage { readonly page: Page; readonly titleInput: Locator; readonly contentEditor: Locator; + readonly tagInput: Locator; + readonly attachmentInput: Locator; + readonly importantToggle: Locator; + readonly privateToggle: Locator; + readonly pinnedToggle: Locator; + readonly mainDisplayToggle: Locator; readonly submitButton: Locator; constructor(page: Page) { this.page = page; this.titleInput = page.getByPlaceholder('제목을 입력하세요.'); this.contentEditor = page.locator('div.sun-editor-editable'); + this.tagInput = page.locator('TODO_TAG_INPUT_SELECTOR'); // TODO: 실제 태그 입력 필드 selector 확인 필요 + this.attachmentInput = page.locator('input[type="file"]'); + this.importantToggle = page.locator('TODO_IMPORTANT_TOGGLE_SELECTOR'); // TODO: 중요 안내 토글 selector 확인 필요 + this.privateToggle = page.locator('TODO_PRIVATE_TOGGLE_SELECTOR'); // TODO: 비공개 토글 selector 확인 필요 + this.pinnedToggle = page.locator('TODO_PINNED_TOGGLE_SELECTOR'); // TODO: 상단고정 토글 selector 확인 필요 + this.mainDisplayToggle = page.locator('TODO_MAIN_DISPLAY_TOGGLE_SELECTOR'); // TODO: 메인표시 토글 selector 확인 필요 this.submitButton = page.getByRole('button', { name: /게시/ }); } @@ -22,7 +34,34 @@ export class NoticeCreatePage { } async fillContent(content: string) { + await this.page.waitForTimeout(1000); // suneditor 대기 await this.contentEditor.fill(content); + await this.page.waitForTimeout(1000); // suneditor 대기 + } + + async addTag(tag: string) { + await this.tagInput.fill(tag); + await this.page.keyboard.press('Enter'); + } + + async uploadAttachment(filePath: string) { + await this.attachmentInput.setInputFiles(filePath); + } + + async toggleImportant() { + await this.importantToggle.click(); + } + + async togglePrivate() { + await this.privateToggle.click(); + } + + async togglePinned() { + await this.pinnedToggle.click(); + } + + async toggleMainDisplay() { + await this.mainDisplayToggle.click(); } async clickSubmitButton() { diff --git a/e2e/pages/community/notice/detail.page.ts b/e2e/pages/community/notice/detail.page.ts new file mode 100644 index 00000000..a733f606 --- /dev/null +++ b/e2e/pages/community/notice/detail.page.ts @@ -0,0 +1,74 @@ +import { type Locator, type Page } from '@playwright/test'; + +export class NoticeDetailPage { + readonly page: Page; + readonly title: Locator; + readonly content: Locator; + readonly editButton: Locator; + readonly deleteButton: Locator; + readonly listButton: Locator; + readonly prevButton: Locator; + readonly nextButton: Locator; + readonly attachments: Locator; + readonly tags: Locator; + + constructor(page: Page) { + this.page = page; + this.title = page.locator('TODO_NOTICE_TITLE_SELECTOR'); // TODO: 공지사항 제목 영역 selector 확인 필요 + this.content = page.locator('TODO_NOTICE_CONTENT_SELECTOR'); // TODO: 공지사항 내용 영역 selector 확인 필요 + this.editButton = page.getByRole('button', { name: '편집' }); + this.deleteButton = page.getByRole('button', { name: '삭제' }); + this.listButton = page.getByRole('button', { name: '목록' }); + this.prevButton = page.locator('TODO_PREV_BUTTON_SELECTOR'); // TODO: 이전글 버튼 selector 확인 필요 + this.nextButton = page.locator('TODO_NEXT_BUTTON_SELECTOR'); // TODO: 다음글 버튼 selector 확인 필요 + this.attachments = page.locator('TODO_ATTACHMENTS_SECTION_SELECTOR'); // TODO: 첨부파일 영역 selector 확인 필요 + this.tags = page.locator('TODO_TAGS_SECTION_SELECTOR'); // TODO: 태그 영역 selector 확인 필요 + } + + async goto(id: string) { + await this.page.goto(`/community/notice/${id}`); + } + + async clickEditButton() { + await this.editButton.click(); + } + + async clickDeleteButton() { + await this.deleteButton.click(); + } + + async confirmDelete() { + // 삭제 확인 모달에서 확인 버튼 클릭 + await this.page.getByRole('button', { name: '확인' }).click(); + } + + async clickListButton() { + await this.listButton.click(); + } + + async clickPrevButton() { + await this.prevButton.click(); + } + + async clickNextButton() { + await this.nextButton.click(); + } + + async getTitle(): Promise { + return (await this.title.textContent()) || ''; + } + + async getContent(): Promise { + return (await this.content.textContent()) || ''; + } + + async getTags(): Promise { + const tagElements = await this.tags.locator('TODO_TAG_ITEM_SELECTOR').all(); // TODO: 개별 태그 item selector 확인 필요 + const tags = []; + for (const tag of tagElements) { + const text = await tag.textContent(); + if (text) tags.push(text); + } + return tags; + } +} diff --git a/e2e/pages/community/notice/edit.page.ts b/e2e/pages/community/notice/edit.page.ts new file mode 100644 index 00000000..c4f1168e --- /dev/null +++ b/e2e/pages/community/notice/edit.page.ts @@ -0,0 +1,68 @@ +import { type Locator, type Page } from '@playwright/test'; + +export class NoticeEditPage { + readonly page: Page; + readonly titleInput: Locator; + readonly contentEditor: Locator; + readonly tagInput: Locator; + readonly attachmentInput: Locator; + readonly importantToggle: Locator; + readonly privateToggle: Locator; + readonly pinnedToggle: Locator; + readonly mainDisplayToggle: Locator; + readonly submitButton: Locator; + + constructor(page: Page) { + this.page = page; + this.titleInput = page.getByPlaceholder('제목을 입력하세요.'); + this.contentEditor = page.locator('div.sun-editor-editable'); + this.tagInput = page.locator('TODO_TAG_INPUT_SELECTOR'); // TODO: 실제 태그 입력 필드 selector 확인 필요 + this.attachmentInput = page.locator('input[type="file"]'); + this.importantToggle = page.locator('TODO_IMPORTANT_TOGGLE_SELECTOR'); // TODO: 중요 안내 토글 selector 확인 필요 + this.privateToggle = page.locator('TODO_PRIVATE_TOGGLE_SELECTOR'); // TODO: 비공개 토글 selector 확인 필요 + this.pinnedToggle = page.locator('TODO_PINNED_TOGGLE_SELECTOR'); // TODO: 상단고정 토글 selector 확인 필요 + this.mainDisplayToggle = page.locator('TODO_MAIN_DISPLAY_TOGGLE_SELECTOR'); // TODO: 메인표시 토글 selector 확인 필요 + this.submitButton = page.getByRole('button', { name: /수정/ }); + } + + async goto(id: string) { + await this.page.goto(`/community/notice/${id}/edit`); + } + + async fillTitle(title: string) { + await this.titleInput.fill(title); + } + + async fillContent(content: string) { + await this.contentEditor.fill(content); + } + + async addTag(tag: string) { + await this.tagInput.fill(tag); + await this.page.keyboard.press('Enter'); + } + + async uploadAttachment(filePath: string) { + await this.attachmentInput.setInputFiles(filePath); + } + + async toggleImportant() { + await this.importantToggle.click(); + } + + async togglePrivate() { + await this.privateToggle.click(); + } + + async togglePinned() { + await this.pinnedToggle.click(); + } + + async toggleMainDisplay() { + await this.mainDisplayToggle.click(); + } + + async clickSubmitButton() { + await this.submitButton.click(); + } +} diff --git a/e2e/pages/community/notice/index.page.ts b/e2e/pages/community/notice/index.page.ts index 0336920f..840068af 100644 --- a/e2e/pages/community/notice/index.page.ts +++ b/e2e/pages/community/notice/index.page.ts @@ -1,12 +1,28 @@ import { type Locator, type Page } from '@playwright/test'; -export class NoticePage { +export class NoticeListPage { readonly page: Page; readonly newPostButton: Locator; + readonly searchInput: Locator; + readonly searchButton: Locator; + readonly tagButtons: Locator; + readonly selectAllCheckbox: Locator; + readonly noticeCheckboxes: Locator; + readonly bulkDeleteButton: Locator; + readonly bulkUnpinButton: Locator; + readonly noticeItems: Locator; constructor(page: Page) { this.page = page; this.newPostButton = page.getByRole('button', { name: '새 게시글' }); + this.searchInput = page.locator('TODO_SEARCH_INPUT_SELECTOR'); // TODO: 검색 입력 필드 selector 확인 필요 + this.searchButton = page.getByRole('button', { name: '검색' }); + this.tagButtons = page.locator('TODO_TAG_FILTER_BUTTONS_SELECTOR'); // TODO: 태그 필터 버튼들 selector 확인 필요 + this.selectAllCheckbox = page.locator('TODO_SELECT_ALL_CHECKBOX_SELECTOR'); // TODO: 전체 선택 체크박스 selector 확인 필요 + this.noticeCheckboxes = page.locator('TODO_NOTICE_CHECKBOX_SELECTOR'); // TODO: 개별 공지사항 체크박스 selector 확인 필요 + this.bulkDeleteButton = page.locator('TODO_BULK_DELETE_BUTTON_SELECTOR'); // TODO: 일괄 삭제 버튼 selector 확인 필요 + this.bulkUnpinButton = page.locator('TODO_BULK_UNPIN_BUTTON_SELECTOR'); // TODO: 일괄 고정해제 버튼 selector 확인 필요 + this.noticeItems = page.locator('TODO_NOTICE_ITEM_SELECTOR'); // TODO: 공지사항 목록 아이템 selector 확인 필요 } async goto() { @@ -16,4 +32,41 @@ export class NoticePage { async clickNewPostButton() { await this.newPostButton.click(); } + + async searchByKeyword(keyword: string) { + await this.searchInput.fill(keyword); + await this.searchButton.click(); + } + + async filterByTag(tagName: string) { + await this.tagButtons.filter({ hasText: tagName }).click(); + } + + async selectAllNotices() { + await this.selectAllCheckbox.click(); + } + + async selectNotice(index: number) { + await this.noticeCheckboxes.nth(index).click(); + } + + async bulkDelete() { + await this.bulkDeleteButton.click(); + // 확인 모달에서 확인 버튼 클릭 + await this.page.getByRole('button', { name: '확인' }).click(); + } + + async bulkUnpin() { + await this.bulkUnpinButton.click(); + // 확인 모달에서 확인 버튼 클릭 + await this.page.getByRole('button', { name: '확인' }).click(); + } + + async getNoticeCount(): Promise { + return await this.noticeItems.count(); + } + + async clickNoticeByTitle(title: string) { + await this.page.getByText(title).click(); + } } diff --git a/e2e/tests/community/notice.test.ts b/e2e/tests/community/notice.test.ts deleted file mode 100644 index c9498724..00000000 --- a/e2e/tests/community/notice.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { expect } from '@playwright/test'; - -import { test } from '../../fixtures'; - -test.describe('Notice creation', () => { - test('should allow STAFF to create a new notice', async ({ - page, - loginAs, - noticePage, - noticeCreatePage, - }) => { - // 1. Arrange - await noticePage.goto(); - const noticeTitle = `[Test] New Notice - ${Date.now()}`; - const noticeContent = 'This is a test notice content.'; - await loginAs('STAFF'); - - // 2. Act - await noticePage.clickNewPostButton(); - await expect(page).toHaveURL('/community/notice/create'); - await noticeCreatePage.fillTitle(noticeTitle); - await noticeCreatePage.fillContent(noticeContent); - // TODO: suneditor 비동기 로드 때문인지 몇 초 대기해야 제대로 작동함 - await page.waitForTimeout(1000); - await noticeCreatePage.clickSubmitButton(); - - // 3. Assert - await expect(page.getByText(noticeTitle).first()).toBeVisible(); - }); -}); diff --git a/e2e/tests/community/notice/create.test.ts b/e2e/tests/community/notice/create.test.ts new file mode 100644 index 00000000..6a11cc88 --- /dev/null +++ b/e2e/tests/community/notice/create.test.ts @@ -0,0 +1,200 @@ +import { expect } from '@playwright/test'; + +import { test } from '../../../fixtures/index.fixture'; + +test.describe('공지사항 작성 페이지 (/community/notice/create)', () => { + test('제목과 내용을 포함한 기본 공지사항을 작성할 수 있다', async ({ + page, + loginAs, + noticeListPage, + noticeCreatePage, + }) => { + // 1. Arrange + const noticeTitle = `[테스트] 기본 공지사항 - ${Date.now()}`; + const noticeContent = '이것은 테스트용 공지사항 내용입니다.'; + + await noticeListPage.goto(); + await loginAs('STAFF'); + + // 2. Act + await noticeListPage.clickNewPostButton(); + await expect(page).toHaveURL('/community/notice/create'); + + await noticeCreatePage.fillTitle(noticeTitle); + await noticeCreatePage.fillContent(noticeContent); + await noticeCreatePage.clickSubmitButton(); + + // 3. Assert + await expect(page.getByText(noticeTitle).first()).toBeVisible(); + }); + + test.skip('제목, 내용, 태그, 첨부파일을 포함한 기본 공지사항을 작성할 수 있다', async ({ + page, + loginAs, + noticeListPage, + noticeCreatePage, + }) => { + // 1. Arrange + const noticeTitle = `[테스트] 기본 공지사항 - ${Date.now()}`; + const noticeContent = '이것은 테스트용 공지사항 내용입니다.'; + // const testTag = '테스트'; + + await loginAs('STAFF'); + await noticeListPage.goto(); + + // 2. Act + await noticeListPage.clickNewPostButton(); + await expect(page).toHaveURL('/community/notice/create'); + + await noticeCreatePage.fillTitle(noticeTitle); + await noticeCreatePage.fillContent(noticeContent); + // TODO: 태그 기능이 실제로 구현되어 있는지 확인 후 활성화 + // await noticeCreatePage.addTag(testTag); + + // TODO: 실제 테스트 파일 업로드 (테스트용 파일 경로 설정 필요) + // await noticeCreatePage.uploadAttachment('e2e/fixtures/test-files/sample.pdf'); + + await page.waitForTimeout(1000); // suneditor 로드 대기 + await noticeCreatePage.clickSubmitButton(); + + // 3. Assert + await expect(page.getByText(noticeTitle).first()).toBeVisible(); + }); + + test.skip('중요 안내로 공지사항을 작성할 수 있다', async ({ + page, + loginAs, + noticeListPage, + noticeCreatePage, + }) => { + // 1. Arrange + const importantNoticeTitle = `[중요] 중요 안내 공지사항 - ${Date.now()}`; + const noticeContent = '이것은 중요한 공지사항입니다.'; + + await loginAs('STAFF'); + await noticeListPage.goto(); + + // 2. Act + await noticeListPage.clickNewPostButton(); + await noticeCreatePage.fillTitle(importantNoticeTitle); + await noticeCreatePage.fillContent(noticeContent); + // TODO: 중요 안내 토글 기능이 실제로 구현되어 있는지 확인 후 활성화 + // await noticeCreatePage.toggleImportant(); + + await page.waitForTimeout(1000); + await noticeCreatePage.clickSubmitButton(); + + // 3. Assert + await expect(page.getByText(importantNoticeTitle).first()).toBeVisible(); + // TODO: 중요 안내 표시 UI 확인 (실제 구현에 따라 assertion 추가 필요) + }); + + test.skip('비공개 글로 공지사항을 작성할 수 있다', async ({ + page, + loginAs, + noticeListPage, + noticeCreatePage, + }) => { + // 1. Arrange + const privateNoticeTitle = `[비공개] 비공개 공지사항 - ${Date.now()}`; + const noticeContent = '이것은 비공개 공지사항입니다.'; + + await loginAs('STAFF'); + await noticeListPage.goto(); + + // 2. Act + await noticeListPage.clickNewPostButton(); + await noticeCreatePage.fillTitle(privateNoticeTitle); + await noticeCreatePage.fillContent(noticeContent); + // TODO: 비공개 토글 기능이 실제로 구현되어 있는지 확인 후 활성화 + // await noticeCreatePage.togglePrivate(); + + await page.waitForTimeout(1000); + await noticeCreatePage.clickSubmitButton(); + + // 3. Assert + await expect(page.getByText(privateNoticeTitle).first()).toBeVisible(); + // TODO: 비공개 표시 UI 확인 (실제 구현에 따라 assertion 추가 필요) + }); + + test.skip('목록 상단 고정으로 공지사항을 작성할 수 있다', async ({ + page, + loginAs, + noticeListPage, + noticeCreatePage, + }) => { + // 1. Arrange + const pinnedNoticeTitle = `[고정] 상단 고정 공지사항 - ${Date.now()}`; + const noticeContent = '이것은 상단에 고정될 공지사항입니다.'; + + await loginAs('STAFF'); + await noticeListPage.goto(); + + // 2. Act + await noticeListPage.clickNewPostButton(); + await noticeCreatePage.fillTitle(pinnedNoticeTitle); + await noticeCreatePage.fillContent(noticeContent); + // TODO: 상단 고정 토글 기능이 실제로 구현되어 있는지 확인 후 활성화 + // await noticeCreatePage.togglePinned(); + + await page.waitForTimeout(1000); + await noticeCreatePage.clickSubmitButton(); + + // 3. Assert + await expect(page.getByText(pinnedNoticeTitle).first()).toBeVisible(); + + // 목록 페이지로 이동하여 상단 고정 확인 + await noticeListPage.goto(); + // TODO: 고정된 글이 상단에 위치하는지 확인 (실제 UI 구조에 따라 수정 필요) + }); + + test.skip('메인 페이지 중요 안내에 표시되는 공지사항을 작성할 수 있다', async ({ + page, + loginAs, + noticeListPage, + noticeCreatePage, + }) => { + // 1. Arrange + const mainDisplayNoticeTitle = `[메인표시] 메인 페이지 표시 공지사항 - ${Date.now()}`; + const noticeContent = '이것은 메인 페이지에 표시될 공지사항입니다.'; + + await loginAs('STAFF'); + await noticeListPage.goto(); + + // 2. Act + await noticeListPage.clickNewPostButton(); + await noticeCreatePage.fillTitle(mainDisplayNoticeTitle); + await noticeCreatePage.fillContent(noticeContent); + // TODO: 메인 표시 토글 기능이 실제로 구현되어 있는지 확인 후 활성화 + // await noticeCreatePage.toggleMainDisplay(); + + await page.waitForTimeout(1000); + await noticeCreatePage.clickSubmitButton(); + + // 3. Assert + await expect(page.getByText(mainDisplayNoticeTitle).first()).toBeVisible(); + + // 메인 페이지로 이동하여 중요 안내 영역에 표시되는지 확인 + await page.goto('/'); + // TODO: 메인 페이지 중요 안내 영역 UI 확인 (실제 구현에 따라 assertion 추가 필요) + }); + + test.skip('필수 필드가 비어있으면 공지사항을 작성할 수 없다', async ({ + page, + loginAs, + noticeListPage, + noticeCreatePage, + }) => { + await loginAs('STAFF'); + await noticeListPage.goto(); + await noticeListPage.clickNewPostButton(); + + // 제목만 입력하고 내용 없이 제출 시도 + await noticeCreatePage.fillTitle('제목만 있는 공지사항'); + await noticeCreatePage.clickSubmitButton(); + + // 에러 메시지 또는 제출 실패 확인 + // TODO: 실제 폼 검증 방식에 따라 assertion 수정 필요 + await expect(page).toHaveURL('/community/notice/create'); // 페이지가 그대로 유지되는지 확인 + }); +}); diff --git a/e2e/tests/community/notice/edit.test.ts b/e2e/tests/community/notice/edit.test.ts new file mode 100644 index 00000000..ba52ddf6 --- /dev/null +++ b/e2e/tests/community/notice/edit.test.ts @@ -0,0 +1,48 @@ +import { expect } from '@playwright/test'; + +import { test } from '../../../fixtures/index.fixture'; + +test.describe('공지사항 수정 페이지 (/community/notice/:id/edit)', () => { + test('공지사항 제목을 수정할 수 있다', async ({ + page, + loginAs, + noticeEditPage, + noticeDetailPage, + noticeCreatePage, + noticeListPage, + }) => { + // 1. Arrange + await noticeListPage.goto(); + await loginAs('STAFF'); + await noticeCreatePage.goto(); + + const noticeTitle = `[테스트] 기본 공지사항 - ${Date.now()}`; + const noticeContent = '이것은 테스트용 공지사항 내용입니다.'; + await noticeCreatePage.fillTitle(noticeTitle); + await noticeCreatePage.fillContent(noticeContent); + await noticeCreatePage.clickSubmitButton(); + + await noticeListPage.goto(); + await page.waitForLoadState('networkidle'); + await noticeListPage.clickNoticeByTitle(noticeTitle); + + // 2. Act + const updatedTitle = `[수정완료] 수정된 공지사항 제목 - ${Date.now()}`; + await noticeDetailPage.clickEditButton(); + await noticeEditPage.fillTitle(updatedTitle); + await noticeEditPage.clickSubmitButton(); + + // 3. Assert + await expect(page.getByText(updatedTitle).first()).toBeVisible(); + }); + + test.skip('공지사항 내용을 수정할 수 있다', async () => {}); + + test.skip('공지사항에 태그를 추가할 수 있다', async () => {}); + + test.skip('공지사항 옵션들을 수정할 수 있다', async () => {}); + + test.skip('첨부파일을 추가할 수 있다', async () => {}); + + test.skip('수정 권한이 없는 사용자는 수정 페이지에 접근할 수 없다', async () => {}); +}); diff --git a/e2e/tests/community/notice/index.test.ts b/e2e/tests/community/notice/index.test.ts new file mode 100644 index 00000000..7ca298fa --- /dev/null +++ b/e2e/tests/community/notice/index.test.ts @@ -0,0 +1,133 @@ +import { expect } from '@playwright/test'; + +import { test } from '../../../fixtures/index.fixture'; + +test.describe('공지사항 목록 조회 페이지 (/community/notice)', () => { + test('생성한 공지사항을 진입할 수 있다', async ({ + page, + noticeListPage, + noticeCreatePage, + loginAs, + }) => { + // 1. Arrange + await noticeListPage.goto(); + await loginAs('STAFF'); + + // 2. Act + const noticeTitle = `[테스트] 기본 공지사항 - ${Date.now()}`; + const noticeContent = '이것은 테스트용 공지사항 내용입니다.'; + + await noticeCreatePage.goto(); + await noticeCreatePage.fillTitle(noticeTitle); + await noticeCreatePage.fillContent(noticeContent); + await noticeCreatePage.clickSubmitButton(); + await noticeListPage.goto(); + + // 3. Assert + await noticeListPage.clickNoticeByTitle(noticeTitle); + await expect(page).toHaveURL(new RegExp('/community/notice/\\d+')); + }); + + test.skip('키워드로 공지사항을 검색할 수 있다', async ({ + page, + loginAs, + noticeListPage, + noticeCreatePage, + }) => { + // 1. Arrange + await noticeListPage.goto(); + await loginAs('STAFF'); + + // 2. Act + const noticeTitle = `[테스트] 기본 공지사항 - ${Date.now()}`; + const noticeContent = '이것은 테스트용 공지사항 내용입니다.'; + + await noticeCreatePage.goto(); + await noticeCreatePage.fillTitle(noticeTitle); + await noticeCreatePage.fillContent(noticeContent); + await noticeCreatePage.clickSubmitButton(); + await noticeListPage.goto(); + await noticeListPage.searchByKeyword('검색 테스트'); + + // 3. Assert + await expect(page.getByText(noticeTitle)).toBeVisible(); + }); + + test.skip('태그로 공지사항을 필터링할 수 있다', async ({ page, loginAs, noticeListPage }) => { + // 1. Arrange + await loginAs('STAFF'); + await noticeListPage.goto(); + + // 2. Act + // TODO: 태그 필터 기능이 실제로 구현되어 있는지 확인 후 활성화 + // await noticeListPage.filterByTag('필터'); + + // 3. Assert + // TODO: 태그 필터 결과 확인 (실제 구현에 따라 assertion 추가 필요) + // await expect(page.getByText('[목록테스트2] 태그 필터 테스트')).toBeVisible(); + // await expect(page.getByText('[목록테스트1] 검색 테스트 공지사항')).not.toBeVisible(); + + // 현재는 기본 목록 표시 확인 + await expect(page.getByText('[목록테스트2] 태그 필터 테스트')).toBeVisible(); + }); + + test.skip('선택한 공지사항들을 일괄 삭제할 수 있다', async ({ loginAs, noticeListPage }) => { + // 1. Arrange + await loginAs('STAFF'); + await noticeListPage.goto(); + + // TODO: 공지사항 개수 확인 기능이 실제로 구현되어 있는지 확인 후 활성화 + // const initialCount = await noticeListPage.getNoticeCount(); + + // 2. Act + // TODO: 일괄 삭제 기능이 실제로 구현되어 있는지 확인 후 활성화 + // await noticeListPage.selectNotice(initialCount - 1); + // await noticeListPage.bulkDelete(); + + // 3. Assert + // TODO: 일괄 삭제 결과 확인 (실제 구현에 따라 assertion 추가 필요) + // const finalCount = await noticeListPage.getNoticeCount(); + // expect(finalCount).toBe(initialCount - 1); + // await expect(page.getByText('[목록테스트5] 삭제 테스트 공지사항')).not.toBeVisible(); + }); + + test.skip('선택한 공지사항들의 고정을 일괄 해제할 수 있다', async ({ + page, + loginAs, + noticeListPage, + }) => { + // 1. Arrange - 먼저 공지사항을 고정 상태로 만들어야 함 + await loginAs('STAFF'); + + // 고정할 공지사항 생성 + await page.goto('/community/notice/create'); + await page.getByPlaceholder('제목을 입력하세요.').fill('[고정해제테스트] 고정된 공지사항'); + await page.locator('div.sun-editor-editable').fill('고정 해제 테스트용 내용입니다.'); + // TODO: 고정 옵션이 실제로 구현되어 있는지 확인 후 활성화 + // await page.locator('TODO_PINNED_TOGGLE_SELECTOR').click(); // 고정 옵션 활성화 + await page.waitForTimeout(1000); + await page.getByRole('button', { name: /게시/ }).click(); + + await noticeListPage.goto(); + + // 2. Act + // TODO: 일괄 고정 해제 기능이 실제로 구현되어 있는지 확인 후 활성화 + // await page.getByText('[고정해제테스트] 고정된 공지사항').locator('..').locator('TODO_NOTICE_CHECKBOX_SELECTOR').click(); + // await noticeListPage.bulkUnpin(); + + // 3. Assert + // TODO: 고정 해제 결과 확인 (실제 UI 구조에 따라 assertion 추가 필요) + // 고정 표시가 사라졌는지 또는 고정 영역에서 제거되었는지 확인 + }); + + test.skip('페이지네이션이 올바르게 작동한다', async ({ loginAs, noticeListPage }) => { + // 1. Arrange + await loginAs('STAFF'); + await noticeListPage.goto(); + + // 2. Act & Assert + // TODO: 페이지네이션 기능이 실제로 구현되어 있는지 확인 후 테스트 추가 + // 현재 테스트 데이터가 적어서 페이지네이션이 없을 수 있음 + // 실제 페이지네이션 구현에 따라 테스트 추가 필요 + }); +});