Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
483 changes: 483 additions & 0 deletions .pnp.cjs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions apps/admin/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ dist-ssr
*.njsproj
*.sln
*.sw?
.last-run.json
21 changes: 21 additions & 0 deletions apps/admin/e2e/functional/auth-routing.e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { expect, test } from '@playwright/test';

import { prepareE2EContext } from '../helpers/msw';

test.describe('Auth routing guard', () => {
test.beforeEach(async ({ page }) => {
await prepareE2EContext(page);
});

test('비로그인 + 일반 브라우저는 /home 접근 시 /login으로 이동한다', async ({ page }) => {
await page.addInitScript(() => {
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
});

await page.goto('/home');
await expect(page).toHaveURL(/\/login$/);
await expect(page.getByRole('heading', { name: '로그인' })).toBeVisible();
await expect(page.getByRole('button', { name: '카카오톡으로 시작하기' })).toBeVisible();
});
});
37 changes: 37 additions & 0 deletions apps/admin/e2e/functional/prequestion-response-flow.e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { expect, test } from '@playwright/test';

import { prepareE2EContext } from '../helpers/msw';

test.describe('Pre-question response flow', () => {
test.beforeEach(async ({ page }) => {
await prepareE2EContext(page);
await page.addInitScript(() => {
localStorage.setItem('accessToken', 'access-token');
localStorage.setItem('refreshToken', 'refresh-token');
});
});

test('응답 확인 탭에서 참여자별 응답/검색 플로우가 동작한다', async ({ page }) => {
await page.goto('/show/1/pre-question');
await expect(page.getByText('응답 확인')).toBeVisible();

await page.getByText('응답 확인').click();
await page.getByRole('button', { name: '참여자별 응답' }).click();

await expect(page.getByText('홍길동', { exact: true })).toBeVisible();
await page.getByPlaceholder('참여자명 검색').fill('홍길동');
await page.waitForTimeout(400);
await expect(page.getByText('홍길동', { exact: true })).toBeVisible();
});

test('검색 결과가 없으면 빈 상태 메시지가 노출된다', async ({ page }) => {
await page.goto('/show/1/pre-question');
await page.getByText('응답 확인').click();
await page.getByRole('button', { name: '참여자별 응답' }).click();

await page.getByPlaceholder('참여자명 검색').fill('없는이름');
await page.waitForTimeout(400);

await expect(page.getByText('검색 결과가 없어요.')).toBeVisible();
});
});
36 changes: 36 additions & 0 deletions apps/admin/e2e/functional/settlement-request-flow.e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { expect, test } from '@playwright/test';

import { prepareE2EContext } from '../helpers/msw';

test.describe('Settlement request flow', () => {
test.beforeEach(async ({ page }) => {
await prepareE2EContext(page);
await page.addInitScript(() => {
localStorage.setItem('accessToken', 'access-token');
localStorage.setItem('refreshToken', 'refresh-token');
});
});

test('동의 체크 전 비활성, 체크 후 활성화되고 요청이 전송된다', async ({ page }) => {
await page.goto('/show/1/settlement');
const submitButton = page.getByRole('button', { name: '정산 요청하기' });
await expect(submitButton).toBeDisabled();

await page.getByText('정산 내역 및 안내사항을 모두 확인하였으며 정산을 요청합니다.').click();
await expect(submitButton).toBeEnabled();
await submitButton.click();

await expect(page.getByText('정산을 요청했습니다')).toBeVisible();
});

test('요청 실패 시 에러 토스트가 노출된다', async ({ page }) => {
await page.goto('/show/1/settlement');
await page.evaluate(() => {
localStorage.setItem('__E2E_SCENARIO__', 'settlement-request-fail');
});
await page.getByText('정산 내역 및 안내사항을 모두 확인하였으며 정산을 요청합니다.').click();
await page.getByRole('button', { name: '정산 요청하기' }).click();

await expect(page.getByText('정산 요청에 실패했습니다. 잠시 후에 다시 시도해주세요.')).toBeVisible();
});
});
12 changes: 12 additions & 0 deletions apps/admin/e2e/helpers/msw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Page } from '@playwright/test';

export const prepareE2EContext = async (page: Page, scenario: string = 'default') => {
await page.addInitScript((currentScenario) => {
(window as Window & { __ENABLE_E2E_MSW__?: boolean }).__ENABLE_E2E_MSW__ = true;
localStorage.setItem('__E2E_SCENARIO__', currentScenario);
}, scenario);

await page.goto('/');
await page.waitForFunction(() => window.__E2E_MSW_READY__ === true, null, { timeout: 5000 });
await page.reload();
};
11 changes: 10 additions & 1 deletion apps/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
"type-check": "tsc --noEmit",
"preview": "vite preview",
"test": "vitest run",
"test:watch": "vitest"
"test:watch": "vitest",
"test:e2e:functional": "playwright test -c playwright.config.ts",
"test:e2e:ui": "playwright test -c playwright.config.ts --ui"
},
"dependencies": {
"@boolti/api": "*",
Expand Down Expand Up @@ -56,6 +58,7 @@
"@boolti/eslint-config": "*",
"@boolti/typescript-config": "*",
"@emotion/babel-plugin": "^11.11.0",
"@playwright/test": "^1.54.1",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.0.1",
"@types/js-cookie": "^3.0.6",
Expand All @@ -64,8 +67,14 @@
"@types/react-dom": "^18.2.17",
"@vitejs/plugin-react": "^4.2.1",
"jsdom": "^26.1.0",
"msw": "^2.14.2",
"typescript": "^5.2.2",
"vite": "^5.0.8",
"vitest": "^2.1.9"
},
"msw": {
"workerDirectory": [
"public"
]
}
}
26 changes: 26 additions & 0 deletions apps/admin/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './e2e/functional',
timeout: 30_000,
use: {
baseURL: 'http://127.0.0.1:4173',
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
},
webServer: {
command: 'VITE_E2E_MSW=true VITE_BASE_API_URL=http://127.0.0.1:4173/ yarn workspace admin dev --host 127.0.0.1 --port 4173',
url: 'http://127.0.0.1:4173',
reuseExistingServer: true,
timeout: 120_000,
},
projects: [
{
name: 'desktop',
use: {
...devices['Desktop Chrome'],
viewport: { width: 1280, height: 800 },
},
},
],
});
Loading
Loading