From 7f1c512016ad4b76271a02ad90f31279595e5b6c Mon Sep 17 00:00:00 2001 From: eunnbi Date: Wed, 5 Feb 2025 17:42:55 +0900 Subject: [PATCH 1/5] chore(fe): add setup test file --- apps/frontend/components/auth/SignIn.tsx | 1 + apps/frontend/playwright/pages/auth.page.ts | 180 ------------------ .../playwright/tests/admin/auth.setup.ts | 16 ++ .../playwright/tests/auth/login.spec.ts | 25 --- .../playwright/tests/user/auth.setup.ts | 16 ++ 5 files changed, 33 insertions(+), 205 deletions(-) delete mode 100644 apps/frontend/playwright/pages/auth.page.ts create mode 100644 apps/frontend/playwright/tests/admin/auth.setup.ts delete mode 100644 apps/frontend/playwright/tests/auth/login.spec.ts create mode 100644 apps/frontend/playwright/tests/user/auth.setup.ts diff --git a/apps/frontend/components/auth/SignIn.tsx b/apps/frontend/components/auth/SignIn.tsx index 72cb7a96d2..6970fa5766 100644 --- a/apps/frontend/components/auth/SignIn.tsx +++ b/apps/frontend/components/auth/SignIn.tsx @@ -72,6 +72,7 @@ export function SignIn() {

Log in diff --git a/apps/frontend/playwright/pages/auth.page.ts b/apps/frontend/playwright/pages/auth.page.ts deleted file mode 100644 index b373b06cbc..0000000000 --- a/apps/frontend/playwright/pages/auth.page.ts +++ /dev/null @@ -1,180 +0,0 @@ -import type { Page, Locator } from '@playwright/test' - -export class Auth { - public readonly page: Page - public readonly loginButton: Locator - public readonly loginModal: Locator - public readonly loginModalCloseButton: Locator - public readonly loginButtonInSignUpModal: Locator - public readonly loginSubmitButton: Locator - - public readonly forgotIDPasswordButton: Locator - public readonly forgotIDPasswordModalCloseButton: Locator - public readonly forgotEmailAddressSelector: Locator - public readonly forgotFindUserIDButton: Locator - public readonly forgotResetPasswordButton: Locator - public readonly forgotRegisterNowButton: Locator - - public readonly signUpButton: Locator - public readonly signUpModal: Locator - public readonly signUpModalCloseButton: Locator - public readonly signUpButtonInLoginModal: Locator - public readonly sendEmailButton: Locator - public readonly signUpEmailAdressSelector: Locator - public readonly signUpVerificationCode: Locator - public readonly signUpVerificationCodeNextButton: Locator - public readonly signUpYourName: Locator - public readonly signUpUserID: Locator - public readonly signUpPassword: Locator - public readonly signUpReenterPassword: Locator - public readonly signUpRegisterButton: Locator - - public readonly usernameSelector: Locator - public readonly passwordSelector: Locator - - public readonly userProfile: Locator - public readonly logoutButton: Locator - public readonly managementButton: Locator - - constructor(page: Page) { - this.page = page - this.loginButton = page.getByRole('button', { name: 'Log In' }) - this.loginModal = page.locator( - 'div.flex.h-full.w-full.flex-col.justify-between' - ) - this.loginModalCloseButton = page.locator('svg.lucide.lucide-x.h-4.w-4') - this.loginButtonInSignUpModal = page.locator( - 'button.inline-flex.items-center.justify-center.text-xs.text-gray-500' - ) - this.loginSubmitButton = page - .locator('form') - .getByRole('button', { name: 'Log In' }) - - this.forgotIDPasswordButton = page.locator( - 'button.inline-flex.items-center.justify-center.rounded-md.font-medium.h-5.w-fit.p-0.py-2.text-xs.text-gray-500' - ) - this.forgotIDPasswordModalCloseButton = page.locator( - 'button.absolute.left-4.top-4' - ) - this.forgotEmailAddressSelector = page.locator('input.bg-white.text-sm') - this.forgotFindUserIDButton = page.locator('button.bg-primary.text-gray-50') - this.forgotResetPasswordButton = page.locator( - 'button.bg-gray-400.text-gray-50.h-10.px-4.py-2' - ) - this.forgotRegisterNowButton = page.locator( - 'button.text-xs.text-gray-500.h-5.w-fit.p-0.py-2' - ) - - this.signUpButton = page.locator('button.bg-primary.text-gray-50.font-bold') - this.signUpModal = page.locator( - 'div.flex.h-full.flex-col.items-center.justify-center' - ) - this.signUpModalCloseButton = page.locator('button .lucide.lucide-x') - this.signUpButtonInLoginModal = page.locator( - 'button.inline-flex.items-center.justify-center' - ) - //not yet - this.sendEmailButton = page.locator('button#signUp-with-email') - this.signUpEmailAdressSelector = page.locator('input[name="Email Address"]') - this.signUpVerificationCode = page.locator( - 'input[name="Verification Code"]' - ) - this.signUpVerificationCodeNextButton = page.locator('button#next') - this.signUpYourName = page.locator('input[name="Your name"]') - this.signUpUserID = page.locator('input[name="User ID"]') - this.signUpPassword = page.locator('input[name="Password"]') - this.signUpReenterPassword = page.locator('input[name="Re-enter password"]') - this.signUpRegisterButton = page.locator('button#signUpRegister') - - this.usernameSelector = page.locator('input[name="username"]') - this.passwordSelector = page.locator('input[name="password"]') - - this.userProfile = page.locator('p.font-semibold.text-white') - this.logoutButton = page.locator('button#logout') - this.managementButton = page.locator('button#management') - } - async goToMain() { - await this.page.goto('https://coolify.codedang.com/') - } - async clickLoginButton() { - await this.loginButton.click() - } - - async isLogInModalVisible(): Promise { - await this.loginModal.waitFor() - return await this.loginModal.isVisible() - } - - async isLoggedIn() { - return await this.loginButton.isHidden() - } - - async login(username: string, password: string) { - await this.usernameSelector.fill(username) - await this.passwordSelector.fill(password) - await this.loginSubmitButton.click() - await this.userProfile.waitFor() // appear when login success - } - - async getJWTToken(): Promise { - const cookies = await this.page.context().cookies() - const tokenCookie = cookies.find( - (cookie) => cookie.name === '__Secure-next-auth.session-token' - ) - return tokenCookie ? tokenCookie.value : null - } - - async clickForgotIDPasswordButton() { - await this.forgotIDPasswordButton.click() - } - - async forgotIDPasswordModalVisible(): Promise { - return await this.signUpModal.isVisible() - } - - async closeForgotIDPasswordModal() { - if (await this.forgotIDPasswordModalVisible()) { - await this.forgotIDPasswordModalCloseButton.click() - } - } - - async findUserID(emailAddress: string) { - await this.forgotEmailAddressSelector.fill(emailAddress) - await this.forgotFindUserIDButton.click() - await this.forgotResetPasswordButton.click() - } - - async registerNow() { - await this.forgotRegisterNowButton.click() - } - - async closeLoginModal() { - if (await this.isLogInModalVisible()) { - await this.loginModalCloseButton.click() - } - } - - async logout() { - await this.logoutButton.click() - await this.loginButton.waitFor() - } - - async clickSignUpButton() { - await this.signUpButton.click() - } - - async signUp(emailAddress: string) { - await this.signUpEmailAdressSelector.fill(emailAddress) - await this.sendEmailButton.click() - } - - async clickLoginButtonInSignUpModal() { - await this.loginButtonInSignUpModal.click() - } - - async closeSignUpModal() { - if (await this.signUpModal.isVisible()) { - await this.signUpModalCloseButton.click() - } - } -} diff --git a/apps/frontend/playwright/tests/admin/auth.setup.ts b/apps/frontend/playwright/tests/admin/auth.setup.ts new file mode 100644 index 0000000000..2d28e68db2 --- /dev/null +++ b/apps/frontend/playwright/tests/admin/auth.setup.ts @@ -0,0 +1,16 @@ +import { test as setup, expect } from '@playwright/test' + +const adminFile = 'playwright/.auth/admin.json' + +setup('Authenticate as admin', { tag: '@login' }, async ({ page }) => { + await page.goto('/') + await page.getByRole('button', { name: 'Log In' }).click() + await expect(page.getByRole('dialog')).toBeVisible() + await page.getByPlaceholder('User ID').fill('admin') + await page.getByPlaceholder('Password').fill('Adminadmin') + await page.getByRole('button', { name: 'Log In' }).click() + + await expect(page.getByRole('button', { name: 'admin' })).toBeVisible() + + await page.context().storageState({ path: adminFile }) +}) diff --git a/apps/frontend/playwright/tests/auth/login.spec.ts b/apps/frontend/playwright/tests/auth/login.spec.ts deleted file mode 100644 index 073c6221b3..0000000000 --- a/apps/frontend/playwright/tests/auth/login.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { test, expect } from '@playwright/test' -import { Auth } from '../../pages/auth.page' - -test.describe('Login Tests', { tag: '@login' }, () => { - let auth: Auth - - test.beforeEach(async ({ page }) => { - auth = new Auth(page) - await auth.goToMain() - }) - - test('Log in via homepage', async () => { - await auth.clickLoginButton() - expect(await auth.loginModal).toBeVisible() - await auth.login('user01', 'Useruser') - }) - - test('Log in via sign up modal', async () => { - await auth.clickSignUpButton() - expect(await auth.signUpModal).toBeVisible() - await auth.clickLoginButtonInSignUpModal() - expect(await auth.loginModal).toBeVisible() - await auth.login('user01', 'Useruser') - }) -}) diff --git a/apps/frontend/playwright/tests/user/auth.setup.ts b/apps/frontend/playwright/tests/user/auth.setup.ts new file mode 100644 index 0000000000..41113776fd --- /dev/null +++ b/apps/frontend/playwright/tests/user/auth.setup.ts @@ -0,0 +1,16 @@ +import { test as setup, expect } from '@playwright/test' + +const userFile = 'playwright/.auth/user.json' + +setup('Authenticate as user', { tag: '@login' }, async ({ page }) => { + await page.goto('/') + await page.getByRole('button', { name: 'Log In' }).click() + await expect(page.getByRole('dialog')).toBeVisible() + await page.getByPlaceholder('User ID').fill('user01') + await page.getByPlaceholder('Password').fill('Useruser') + await page.getByRole('button', { name: 'Log In' }).click() + + await expect(page.getByRole('button', { name: 'user01' })).toBeVisible() + + await page.context().storageState({ path: userFile }) +}) From 82e0fbf22c6b6e527c14bbcb971f86c6d2996979 Mon Sep 17 00:00:00 2001 From: eunnbi Date: Wed, 5 Feb 2025 17:43:17 +0900 Subject: [PATCH 2/5] chore(fe): add home admin test --- apps/frontend/playwright/pages/home.page.ts | 67 ----------------- .../playwright/tests/admin/home.test.ts | 16 +++++ .../playwright/tests/home/home.test.ts | 71 ------------------- 3 files changed, 16 insertions(+), 138 deletions(-) delete mode 100644 apps/frontend/playwright/pages/home.page.ts create mode 100644 apps/frontend/playwright/tests/admin/home.test.ts delete mode 100644 apps/frontend/playwright/tests/home/home.test.ts diff --git a/apps/frontend/playwright/pages/home.page.ts b/apps/frontend/playwright/pages/home.page.ts deleted file mode 100644 index 0b99229920..0000000000 --- a/apps/frontend/playwright/pages/home.page.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { Page, Locator } from '@playwright/test' - -export class HomePage { - public readonly homePageUrl: string = 'https://stage.codedang.com/' - public readonly noticePageUrl: string = 'https://stage.codedang.com/notice' - public readonly contestPageUrl: string = 'https://stage.codedang.com/contest' - public readonly problemPageUrl: string = 'https://stage.codedang.com/problem' - public readonly seeMorePageUrl: string = 'https://stage.codedang.com/problem' - - public readonly mainLogo: Locator - public readonly noticeButton: Locator - public readonly contestButton: Locator - public readonly problemButton: Locator - public readonly seemoreButton: Locator - - constructor(public page: Page) { - this.mainLogo = page.locator('img[alt="코드당"]') - this.noticeButton = page.locator('a[href="/notice"]') - this.contestButton = page.locator('a[href="/contest"]') - this.problemButton = page.locator('a[href="/problem"]') - this.seemoreButton = page.locator('button:has-text("See More")') - } - - async goTo() { - await this.page.goto(this.homePageUrl) - } - - async clickMainLogo(): Promise { - await this.mainLogo.click() - } - - async clickNoticeButton(): Promise { - await this.noticeButton.click() - } - - async clickContestButton(): Promise { - await this.contestButton.click() - } - - async clickProblemButton(): Promise { - await this.problemButton.click() - } - - async clickSeemoreButton(): Promise { - await this.seemoreButton.click() - } - - async getCurrentUrl() { - return await this.page.url() - } - - async goToNoticePage() { - await this.page.goto(this.noticePageUrl) - } - - async goToContestPage() { - await this.page.goto(this.contestPageUrl) - } - - async goToProblemPage() { - await this.page.goto(this.problemPageUrl) - } - - async goToSeeMorePage() { - await this.page.goto(this.seeMorePageUrl) - } -} diff --git a/apps/frontend/playwright/tests/admin/home.test.ts b/apps/frontend/playwright/tests/admin/home.test.ts new file mode 100644 index 0000000000..dae39c8ed1 --- /dev/null +++ b/apps/frontend/playwright/tests/admin/home.test.ts @@ -0,0 +1,16 @@ +import { expect, test } from '@playwright/test' + +test.describe('Home page test', () => { + test('Admin can go to the management page through the account menu', async ({ + page + }) => { + await page.goto('/') + await page.getByRole('button', { name: 'admin' }).click() + + await expect(page.getByRole('menu')).toBeVisible() + + await page.getByRole('menuitem', { name: 'Management' }).click() + + await page.waitForURL('/admin') + }) +}) diff --git a/apps/frontend/playwright/tests/home/home.test.ts b/apps/frontend/playwright/tests/home/home.test.ts deleted file mode 100644 index b7ce2d7649..0000000000 --- a/apps/frontend/playwright/tests/home/home.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { test, expect } from '@playwright/test' -import { HomePage } from '../../pages/home.page' - -test.describe('Homepage Tests', () => { - let homePage: HomePage - - test.beforeEach(async ({ page }) => { - homePage = new HomePage(page) - await homePage.goTo() - }) - - test('go to home page', async () => { - const currentUrl = await homePage.getCurrentUrl() - expect(currentUrl).toBe(homePage.homePageUrl) - }) - - test('go to homepage when clicking main logo', async () => { - await homePage.clickMainLogo() - const currentUrl = await homePage.getCurrentUrl() - expect(currentUrl).toBe(homePage.homePageUrl) - }) - - test('go to notice page when clicking notice button', async () => { - await homePage.clickNoticeButton() - const currentUrl = await homePage.getCurrentUrl() - expect(currentUrl).toBe(homePage.noticePageUrl) - }) - - test('go to contest page when clicking contest button', async () => { - await homePage.clickContestButton() - const currentUrl = await homePage.getCurrentUrl() - expect(currentUrl).toBe(homePage.contestPageUrl) - }) - - test('go to problem page when clicking problem button', async () => { - await homePage.clickProblemButton() - const currentUrl = await homePage.getCurrentUrl() - expect(currentUrl).toBe(homePage.problemPageUrl) - }) - - test('go to problem page when clicking seemore button', async () => { - await homePage.clickSeemoreButton() - const currentUrl = await homePage.getCurrentUrl() - expect(currentUrl).toBe(homePage.seeMorePageUrl) - }) - - test('open forgot ID/password modal from login modal', async () => { - await homePage.clickLoginButton() - expect(await homePage.loginModalVisible()).toBe(true) - - await homePage.clickForgotIDPasswordButton() - expect(await homePage.forgotIDPasswordModalVisible()).toBe(true) - }) - - //test('close login modal when clicking close button', async () => { - // await homePage.clickLoginButton() - //await homePage.closeLoginModal() - //expect(await homePage.loginModalVisible()).toBe(false) - //}) - - test('open signup modal when clicking signup button', async () => { - await homePage.clickSignupButton() - expect(await homePage.signupModalVisible()).toBe(true) - }) - - //test('close signup modal when clicking close button', async () => { - //await homePage.clickSignupButton() - //await homePage.closeSignupModal() - //expect(await homePage.signupModalVisible()).toBe(false) - //}) -}) From c6254c634c5f37bc3ba6fe9dbd520e2d80da4bde Mon Sep 17 00:00:00 2001 From: eunnbi Date: Wed, 5 Feb 2025 17:43:41 +0900 Subject: [PATCH 3/5] chore(fe): add problem detail page test for non auth users --- .../_components/EditorHeader/EditorHeader.tsx | 30 +++++++------ .../EditorHeader/RunTestButton.tsx | 8 ++++ apps/frontend/app/layout.tsx | 5 ++- .../frontend/components/auth/AuthProvider.tsx | 21 +++++++++ apps/frontend/libs/hooks/useSession.ts | 12 +++++ .../playwright/tests/non-auth/problem.test.ts | 44 +++++++++++++++++++ 6 files changed, 106 insertions(+), 14 deletions(-) create mode 100644 apps/frontend/components/auth/AuthProvider.tsx create mode 100644 apps/frontend/libs/hooks/useSession.ts create mode 100644 apps/frontend/playwright/tests/non-auth/problem.test.ts diff --git a/apps/frontend/app/(client)/(code-editor)/_components/EditorHeader/EditorHeader.tsx b/apps/frontend/app/(client)/(code-editor)/_components/EditorHeader/EditorHeader.tsx index 484c487f18..0f0c713c2d 100644 --- a/apps/frontend/app/(client)/(code-editor)/_components/EditorHeader/EditorHeader.tsx +++ b/apps/frontend/app/(client)/(code-editor)/_components/EditorHeader/EditorHeader.tsx @@ -23,7 +23,7 @@ import { SelectTrigger, SelectValue } from '@/components/shadcn/select' -import { auth } from '@/libs/auth' +import { useSession } from '@/libs/hooks/useSession' import { fetcherWithAuth } from '@/libs/utils' import submitIcon from '@/public/icons/submit.svg' import { useAuthModalStore } from '@/stores/authModal' @@ -81,7 +81,8 @@ export function EditorHeader({ const storageKey = useRef( getStorageKey(language, problem.id, userName, contestId) ) - const { currentModal, showSignIn } = useAuthModalStore((state) => state) + const session = useSession() + const showSignIn = useAuthModalStore((state) => state.showSignIn) const [showModal, setShowModal] = useState(false) //const pushed = useRef(false) const whereToPush = useRef('') @@ -118,18 +119,15 @@ export function EditorHeader({ loading && submissionId ? 500 : null ) - const checkSession = async () => { - const session = await auth() + useEffect(() => { if (!session) { - toast.info('Log in to use submission & save feature') + setTimeout(() => { + toast.info('Log in to use submission & save feature') + }) } else { setUserName(session.user.username) } - } - - useEffect(() => { - checkSession() - }, [currentModal]) + }, [session]) useEffect(() => { if (!templateString) { @@ -168,6 +166,12 @@ export function EditorHeader({ const submit = async () => { const code = getCode() + if (session === null) { + showSignIn() + toast.error('Log in first to submit your code') + return + } + if (code === '') { toast.error('Please write code before submission') return @@ -225,12 +229,12 @@ export function EditorHeader({ } } - const saveCode = async () => { - const session = await auth() + const saveCode = () => { const code = getCode() - if (!session) { + if (session === null) { toast.error('Log in first to save your code') + showSignIn() } else if (storageKey.current !== undefined) { localStorage.setItem(storageKey.current, code) toast.success('Successfully saved the code') diff --git a/apps/frontend/app/(client)/(code-editor)/_components/EditorHeader/RunTestButton.tsx b/apps/frontend/app/(client)/(code-editor)/_components/EditorHeader/RunTestButton.tsx index 3ace00a1ba..6ed16f9787 100644 --- a/apps/frontend/app/(client)/(code-editor)/_components/EditorHeader/RunTestButton.tsx +++ b/apps/frontend/app/(client)/(code-editor)/_components/EditorHeader/RunTestButton.tsx @@ -1,4 +1,5 @@ import { Button, type ButtonProps } from '@/components/shadcn/button' +import { useSession } from '@/libs/hooks/useSession' import { isHttpError, safeFetcherWithAuth } from '@/libs/utils' import { useAuthModalStore } from '@/stores/authModal' import { useCodeStore } from '@/stores/editor' @@ -21,6 +22,7 @@ export function RunTestButton({ saveCode, ...props }: RunTestButtonProps) { + const session = useSession() const setIsTesting = useTestPollingStore((state) => state.setIsTesting) const startPolling = useTestPollingStore((state) => state.startPolling) const showSignIn = useAuthModalStore((state) => state.showSignIn) @@ -97,6 +99,12 @@ export function RunTestButton({ const code = getCode() const testcases = getUserTestcases() + if (session === null) { + showSignIn() + toast.error('Log in first to test your code') + return + } + if (code === '') { toast.error('Please write code before test') return diff --git a/apps/frontend/app/layout.tsx b/apps/frontend/app/layout.tsx index c1edee36cd..473ade5429 100644 --- a/apps/frontend/app/layout.tsx +++ b/apps/frontend/app/layout.tsx @@ -1,4 +1,6 @@ +import { AuthProvider } from '@/components/auth/AuthProvider' import { Toaster } from '@/components/shadcn/sonner' +import { auth } from '@/libs/auth' import { metaBaseUrl } from '@/libs/constants' import { getBootstrapData } from '@/libs/posthog.server' import type { Metadata, Viewport } from 'next' @@ -36,6 +38,7 @@ export default async function RootLayout({ children: React.ReactNode }) { const bootstrapData = await getBootstrapData() + const session = await auth() return ( @@ -43,7 +46,7 @@ export default async function RootLayout({ {/**NOTE: remove comment if you want to track page view of users */} {/* */} - {children} + {children} ( + undefined +) + +interface AuthProviderProps { + children: ReactNode + session: Session | null +} + +export function AuthProvider({ children, session }: AuthProviderProps) { + return ( + + {children} + + ) +} diff --git a/apps/frontend/libs/hooks/useSession.ts b/apps/frontend/libs/hooks/useSession.ts new file mode 100644 index 0000000000..b6540ae2be --- /dev/null +++ b/apps/frontend/libs/hooks/useSession.ts @@ -0,0 +1,12 @@ +import { SessionContext } from '@/components/auth/AuthProvider' +import { useContext } from 'react' + +export const useSession = () => { + const context = useContext(SessionContext) + + if (context === undefined) { + throw new Error('useSession should be used within the AuthProvider') + } + + return context +} diff --git a/apps/frontend/playwright/tests/non-auth/problem.test.ts b/apps/frontend/playwright/tests/non-auth/problem.test.ts new file mode 100644 index 0000000000..8e455f74aa --- /dev/null +++ b/apps/frontend/playwright/tests/non-auth/problem.test.ts @@ -0,0 +1,44 @@ +import { expect, test } from '@playwright/test' + +const PROBLEM_ID = 101 +test.describe('Problem detail page test', () => { + test.beforeEach(async ({ page }) => { + await page.goto(`/problem/${PROBLEM_ID}`) + }) + test('Toast message should be displayed to guide log in when accessing the page', async ({ + page + }) => { + await expect( + page.getByText(/Log in to use submission & save feature/).first() + ).toBeVisible() + }) + + test('Login form and toast message should be displayed to guide log in when clicking save button', async ({ + page + }) => { + await page.getByRole('textbox').fill('asdf') + await page.getByRole('button', { name: 'Save' }).click() + await expect(page.getByRole('form', { name: 'Log in' })).toBeVisible() + await expect(page.getByText(/Log in first to save your code/)).toBeVisible() + }) + + test('Login form and toast message should be displayed when clicking submit button', async ({ + page + }) => { + await page.getByRole('textbox').fill('asdf') + await page.getByRole('button', { name: 'Submit' }).click() + await expect(page.getByRole('form', { name: 'Log in' })).toBeVisible() + await expect( + page.getByText(/Log in first to submit your code/) + ).toBeVisible() + }) + + test('Login form and toast message should be displayed when clicking test button', async ({ + page + }) => { + await page.getByRole('textbox').fill('asdf') + await page.getByRole('button', { name: 'Test', exact: true }).click() + await expect(page.getByRole('form', { name: 'Log in' })).toBeVisible() + await expect(page.getByText(/Log in first to test your code/)).toBeVisible() + }) +}) From 4fff0dc83b67c2f69fda4c483910d9ec55b6524e Mon Sep 17 00:00:00 2001 From: eunnbi Date: Wed, 5 Feb 2025 17:44:09 +0900 Subject: [PATCH 4/5] chore(fe): update playwright config --- apps/frontend/.gitignore | 3 +- apps/frontend/playwright.config.ts | 76 ++++++++++++++++++++++++------ 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/apps/frontend/.gitignore b/apps/frontend/.gitignore index c13129bc3a..77154f37f1 100644 --- a/apps/frontend/.gitignore +++ b/apps/frontend/.gitignore @@ -43,7 +43,8 @@ __generated__/ # Playwright test-results/* -test-results.json +playwright/test-results.json +playwright/.auth # Next.js Bundle Analyzer analyze diff --git a/apps/frontend/playwright.config.ts b/apps/frontend/playwright.config.ts index 2fcf8985ca..993b63cdbe 100644 --- a/apps/frontend/playwright.config.ts +++ b/apps/frontend/playwright.config.ts @@ -1,26 +1,74 @@ -import { defineConfig, devices } from '@playwright/test' +import { + defineConfig, + devices, + type PlaywrightTestConfig +} from '@playwright/test' export default defineConfig({ - timeout: 30000, - retries: 3, - reporter: [['list'], ['json', { outputFile: 'test-results.json' }]], testDir: './playwright/tests/', + retries: 3, + reporter: [ + ['list'], + ['json', { outputFile: 'playwright/test-results.json' }] + ], + webServer: { + command: 'pnpm dev', + url: 'http://localhost:5525', + reuseExistingServer: !process.env.CI, + timeout: 120 * 1000 + }, + use: { + baseURL: 'http://localhost:5525' + }, projects: [ + // Setup projects { - name: 'firefox', - use: { ...devices['Desktop Firefox'] } + name: 'Admin Setup', + testDir: './playwright/tests/admin', + testMatch: /.*\.setup\.ts/ }, { - name: 'webkit', - use: { ...devices['Desktop Safari'] } + name: 'User Setup', + testDir: './playwright/tests/user', + testMatch: /.*\.setup\.ts/ + }, + ...getProjectsByBrowser() + ] +}) + +function getProjectsByBrowser(): NonNullable { + const browsers = [ + 'Desktop Safari', + 'Desktop Firefox', + 'Desktop Chrome', + 'Desktop Edge' + ] as const + + return browsers.flatMap((browser) => [ + { + name: browser, + testDir: './playwright/tests/non-auth', + use: { + ...devices[browser] + } }, { - name: 'Google Chrome', - use: { ...devices['Desktop Chrome'] } + name: `${browser} (User)`, + testDir: './playwright/tests/user', + dependencies: ['User Setup'], + use: { + ...devices[browser], + storageState: 'playwright/.auth/user.json' + } }, { - name: 'Microsoft Edge', - use: { ...devices['Desktop Edge'] } + name: `${browser} (Admin)`, + testDir: './playwright/tests/admin', + dependencies: ['Admin Setup'], + use: { + ...devices[browser], + storageState: 'playwright/.auth/admin.json' + } } - ] -}) + ]) +} From f4bc6508dc14327c37c8bc1d174cfaa0b8c1b0f6 Mon Sep 17 00:00:00 2001 From: eunnbi Date: Thu, 6 Feb 2025 13:59:23 +0900 Subject: [PATCH 5/5] chore(fe): update guide text --- apps/frontend/playwright/tests/non-auth/problem.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/playwright/tests/non-auth/problem.test.ts b/apps/frontend/playwright/tests/non-auth/problem.test.ts index 8e455f74aa..205f5e697f 100644 --- a/apps/frontend/playwright/tests/non-auth/problem.test.ts +++ b/apps/frontend/playwright/tests/non-auth/problem.test.ts @@ -13,7 +13,7 @@ test.describe('Problem detail page test', () => { ).toBeVisible() }) - test('Login form and toast message should be displayed to guide log in when clicking save button', async ({ + test('Login form and toast message should be displayed when clicking save button', async ({ page }) => { await page.getByRole('textbox').fill('asdf')