-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
핵심기능 E2E 테스트 - 카테고리, 검색 #648
Closed
Closed
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
0e596b8
chore: 불필요한 의존성 제거
Hain-tain b5be640
feat(e2eTests): loginToCodezap, waitForSuccess 생성
Hain-tain 340b25c
feat(e2eTests): 카테고리 관련 e2e 테스트 작성
Hain-tain e2150c9
feat(e2eTests): 검색 관련 e2e 테스트 작성
Hain-tain 351865d
refactor(e2eTest): waitForSuccess 의 parameter를 url => apiUrl 로 변경
Hain-tain 8f111be
chore: playwright dotenv 설정 추가
Hain-tain 581911c
feat(e2eTest): actions 파일 분리
Hain-tain 8721b42
refactor(e2eTest): testUtils => utils 로 이름 변경, loginToCodezap 의 파라미터 …
Hain-tain 11aa70e
refactor(e2eTest): 변수 할당하여 재사용하도록 변경, 각 actions 적용 및 env 적용
Hain-tain File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { Page } from '@playwright/test'; | ||
|
||
interface Props { | ||
page: Page; | ||
categoryName: string; | ||
} | ||
|
||
export const getCategoryButton = ({ page, categoryName }: Props) => page.getByRole('button', { name: categoryName }); | ||
|
||
export const createCategory = async ({ page, categoryName }: Props) => { | ||
await page.getByRole('button', { name: '카테고리 편집' }).click(); | ||
|
||
await page.getByRole('button', { name: '+ 카테고리 추가' }).click(); | ||
await page.getByPlaceholder('카테고리 입력').fill(categoryName); | ||
await page.getByPlaceholder('카테고리 입력').press('Enter'); | ||
|
||
await page.getByRole('button', { name: '저장' }).click(); | ||
}; | ||
|
||
export const deleteCategory = async ({ page, categoryName }: Props) => { | ||
await page.getByRole('button', { name: '카테고리 편집' }).click(); | ||
|
||
const categoryInEditModal = page.getByText(categoryName).nth(1); | ||
|
||
await categoryInEditModal.hover(); | ||
await page.getByRole('button', { name: '카테고리 삭제' }).click(); | ||
|
||
await page.getByRole('button', { name: '저장' }).click(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { test, expect } from '@playwright/test'; | ||
|
||
import { createCategory, deleteCategory, getCategoryButton } from './category.actions'; | ||
import { loginToCodezap, waitForSuccess } from './utils'; | ||
|
||
test.beforeEach(async ({ page }) => { | ||
await loginToCodezap({ | ||
page, | ||
id: process.env.PLAYWRIGHT_TEST_ID || '', | ||
password: process.env.PLAYWRIGHT_TEST_PASSWORD || '', | ||
}); | ||
}); | ||
|
||
test('카테고리 편집 모달에서 새 카테고리를 추가 및 삭제할 수 있다.', async ({ page, browserName }) => { | ||
const newCategoryName = `생성테스트-${browserName}`; | ||
|
||
await createCategory({ page, categoryName: newCategoryName }); | ||
|
||
await waitForSuccess({ page, apiUrl: '/categories' }); | ||
|
||
const newCategoryButton = getCategoryButton({ page, categoryName: newCategoryName }); | ||
|
||
await expect(newCategoryButton).toBeVisible(); | ||
|
||
// 다음 테스트를 위해 테스트용 카테고리 삭제 | ||
await deleteCategory({ page, categoryName: newCategoryName }); | ||
|
||
await waitForSuccess({ page, apiUrl: '/categories' }); | ||
|
||
await expect(newCategoryButton).not.toBeVisible(); | ||
}); | ||
|
||
test('카테고리 편집 모달에서 카테고리명을 수정 및 삭제할 수 있다.', async ({ page, browserName }) => { | ||
const newCategoryName = `수정테스트-${browserName}`; | ||
const editedCategoryName = `수정완료-${browserName}`; | ||
|
||
// 수정할 카테고리 생성 | ||
await createCategory({ page, categoryName: newCategoryName }); | ||
|
||
await waitForSuccess({ page, apiUrl: '/categories' }); | ||
|
||
const newCategoryButton = getCategoryButton({ page, categoryName: newCategoryName }); | ||
|
||
await expect(newCategoryButton).toBeVisible(); | ||
|
||
// 카테고리 수정 | ||
await page.getByRole('button', { name: '카테고리 편집' }).click(); | ||
|
||
const newCategoryInEditModal = page.getByText(newCategoryName).nth(1); | ||
|
||
await newCategoryInEditModal.hover(); | ||
await page.getByRole('button', { name: '카테고리 이름 변경' }).click(); | ||
await page.getByPlaceholder('카테고리 입력').click(); | ||
await page.getByPlaceholder('카테고리 입력').fill(editedCategoryName); | ||
await page.getByRole('button', { name: '저장' }).click(); | ||
|
||
const editedCategoryButton = getCategoryButton({ page, categoryName: editedCategoryName }); | ||
|
||
await expect(editedCategoryButton).toBeVisible(); | ||
|
||
// 다음 테스트를 위해 테스트용 카테고리 삭제 | ||
await deleteCategory({ page, categoryName: editedCategoryName }); | ||
|
||
await waitForSuccess({ page, apiUrl: '/categories' }); | ||
await expect(editedCategoryButton).not.toBeVisible(); | ||
}); | ||
|
||
test('카테고리는 최대 15글자까지만 입력할 수 있다.', async ({ page, browserName }) => { | ||
const rawCategoryName = `최대글자수테스트-${browserName}`; | ||
const expectedCategoryName = rawCategoryName.slice(0, 15); | ||
|
||
await page.getByRole('button', { name: '카테고리 편집' }).click(); | ||
await page.getByRole('button', { name: '+ 카테고리 추가' }).click(); | ||
const categoryInput = page.getByPlaceholder('카테고리 입력'); | ||
|
||
await categoryInput.click(); | ||
|
||
for (const char of rawCategoryName) { | ||
await page.keyboard.type(char); | ||
} | ||
|
||
await page.getByRole('button', { name: '저장' }).click(); | ||
|
||
await waitForSuccess({ page, apiUrl: '/categories' }); | ||
|
||
const newCategoryButton = getCategoryButton({ page, categoryName: expectedCategoryName }); | ||
|
||
await expect(newCategoryButton).toBeVisible(); | ||
|
||
// 다음 테스트를 위해 테스트용 카테고리 삭제 | ||
await deleteCategory({ page, categoryName: expectedCategoryName }); | ||
|
||
await waitForSuccess({ page, apiUrl: '/categories' }); | ||
await expect(newCategoryButton).not.toBeVisible(); | ||
}); |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { Page } from '@playwright/test'; | ||
|
||
interface Props { | ||
page: Page; | ||
keyword: string; | ||
} | ||
|
||
export const searchTemplates = async ({ page, keyword }: Props) => { | ||
const searchInput = page.getByPlaceholder('검색'); | ||
|
||
await searchInput.waitFor({ state: 'visible' }); | ||
await searchInput.click(); | ||
await searchInput.fill(keyword); | ||
await searchInput.press('Enter'); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { test, expect } from '@playwright/test'; | ||
|
||
import { searchTemplates } from './search.actions'; | ||
import { loginToCodezap, waitForSuccess } from './utils'; | ||
|
||
test.beforeEach(async ({ page }) => { | ||
await loginToCodezap({ | ||
page, | ||
id: process.env.PLAYWRIGHT_TEST_ID || '', | ||
password: process.env.PLAYWRIGHT_TEST_PASSWORD || '', | ||
}); | ||
}); | ||
|
||
test('검색창에 `테스트`를 입력하면 `테스트`가 내용에 포함된 템플릿 목록을 확인할 수 있다.', async ({ page }) => { | ||
const keyword = '테스트'; | ||
|
||
await searchTemplates({ page, keyword }); | ||
|
||
await waitForSuccess({ page, apiUrl: '/templates?keyword' }); | ||
await expect(page.getByRole('link', { name: /테스트/ })).toBeVisible(); | ||
}); | ||
|
||
test('검색창에 `ㅁㅅㅌㅇ`를 입력할 경우 `검색 결과가 없습니다`가 나온다.', async ({ page }) => { | ||
const keyword = 'ㅁㅅㅌㅇ'; | ||
|
||
await searchTemplates({ page, keyword }); | ||
|
||
await waitForSuccess({ page, apiUrl: '/templates?keyword' }); | ||
await expect(page.locator('div').filter({ hasText: /^검색 결과가 없습니다\.$/ })).toBeVisible(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { Page } from '@playwright/test'; | ||
|
||
interface LoginToCodezapProps { | ||
page: Page; | ||
id: string; | ||
password: string; | ||
} | ||
|
||
export const loginToCodezap = async ({ page, id, password }: LoginToCodezapProps) => { | ||
await page.goto('/'); | ||
await page.getByRole('link', { name: '로그인', exact: true }).getByRole('button').click(); | ||
await page | ||
.locator('div') | ||
.filter({ hasText: /^아이디 \(닉네임\)$/ }) | ||
.locator('div') | ||
.click(); | ||
await page.locator('input[type="text"]').fill(id); | ||
await page.locator('input[type="text"]').press('Tab'); | ||
await page.locator('input[type="password"]').fill(password); | ||
await page.locator('form').getByRole('button', { name: '로그인' }).click(); | ||
|
||
await waitForSuccess({ page, apiUrl: '/login' }); | ||
}; | ||
|
||
interface WaitForSuccessProps { | ||
page: Page; | ||
apiUrl: string; | ||
} | ||
|
||
export const waitForSuccess = async ({ page, apiUrl }: WaitForSuccessProps) => { | ||
await page.waitForResponse((response) => response.url().includes(apiUrl) && response.status() === 200); | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기서 Locator.fill()로 입력하지 않고 page.keyboard.type으로 입력한 이유가 있나요? 다음 링크의 caution에서는 fill을 권장하는 것 같아서요!
https://playwright.dev/docs/api/class-keyboard#keyboard-type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Locator.fill()은 복사붙여넣기 처럼 동작하기 때문에 16자 이상의 글자를 fill로 넣으면 아무것도 입력되지않아 해당 테스트에서 실패하였습니다. 🤣
(16자 이상의 글자를 복사붙여넣기하면 아예 복사붙여넣기가 되지 않는데, 그 이유는 validate 함수에서 아예 16자 이상의 입력을 막고있기 때문인 것 같습니다.)
따라서 15자 이후에 계속 입력하여도 15자까지만 카테고리가 생성된다는 것을 테스트하기 위해 page.keyboard.type를 사용하였습니다!