Skip to content

Commit 023236c

Browse files
committed
feat: add Playwright tests
1 parent 518f497 commit 023236c

File tree

4 files changed

+761
-0
lines changed

4 files changed

+761
-0
lines changed

apps/client/tests/HomePage.spec.ts

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
import { expect, test } from '@playwright/test';
2+
3+
test.beforeEach(async ({ page }) => {
4+
await page.goto('/', { waitUntil: 'domcontentloaded' });
5+
});
6+
7+
test('헤더와 설명 텍스트가 올바르게 표시되는지 확인', async ({ page }) => {
8+
await expect(page.getByText('질문과 답변을 넘어,')).toBeVisible();
9+
await expect(page.getByText('함께 만드는 인사이트')).toBeVisible();
10+
await expect(
11+
page.getByText('실시간 Q&A와 소통을 위한 최적의 플랫폼'),
12+
).toBeVisible();
13+
});
14+
15+
test('기능 카드들이 모두 표시되는지 확인', async ({ page }) => {
16+
const features = [
17+
{ title: '실시간 Q&A', desc: '연사자와 익명의 청중의 실시간 응답' },
18+
{ title: '채팅', desc: '실시간 채팅으로 즉각적인 소통' },
19+
{
20+
title: '권한 관리',
21+
desc: '연사자와 참가자를 위한 세분화된 권한 시스템',
22+
},
23+
{ title: '아카이빙', desc: '세션 내용 보존과 효율적인 자료화' },
24+
];
25+
26+
await Promise.all(
27+
features.map(async (feature) => {
28+
await expect(
29+
page.locator(`text=${feature.title} >> .. >> text=${feature.desc}`),
30+
).toBeVisible();
31+
}),
32+
);
33+
});
34+
35+
test('회원가입 플로우 전체 테스트', async ({ page }) => {
36+
await page.click('text=회원가입');
37+
38+
const signUpButton = page.locator('text=회원 가입');
39+
await expect(signUpButton).toBeDisabled();
40+
41+
await page.route('**/api/users/emails/**', async (route) => {
42+
await route.fulfill({
43+
status: 200,
44+
contentType: 'application/json',
45+
body: JSON.stringify({ exists: false }),
46+
});
47+
});
48+
49+
await page.route('**/api/users/nicknames/**', async (route) => {
50+
await route.fulfill({
51+
status: 200,
52+
contentType: 'application/json',
53+
body: JSON.stringify({ exists: false }),
54+
});
55+
});
56+
57+
await page.route('**/api/users', async (route) => {
58+
await route.fulfill({
59+
status: 201,
60+
contentType: 'application/json',
61+
});
62+
});
63+
64+
await page.fill('input[placeholder="[email protected]"]', '[email protected]');
65+
await page.waitForResponse('**/api/users/emails/**');
66+
67+
await page.fill('input[placeholder="닉네임을 입력해주세요"]', 'testUser');
68+
await page.waitForResponse('**/api/users/nicknames/**');
69+
70+
await page.fill(
71+
'input[placeholder="비밀번호를 입력해주세요"]',
72+
'Password123!',
73+
);
74+
75+
await expect(signUpButton).toBeEnabled();
76+
77+
const response = page.waitForResponse('**/api/users');
78+
await signUpButton.click();
79+
expect((await response).status()).toBe(201);
80+
81+
await expect(page.locator('text=회원가입 되었습니다.')).toBeVisible();
82+
});
83+
84+
test('회원 가입이 이미 중복된 이메일이 있어서 실패하는 경우', async ({
85+
page,
86+
}) => {
87+
await page.click('text=회원가입');
88+
89+
const signUpButton = page.locator('text=회원 가입');
90+
await expect(signUpButton).toBeDisabled();
91+
92+
await page.route('**/api/users/emails/**', async (route) => {
93+
await route.fulfill({
94+
status: 200,
95+
contentType: 'application/json',
96+
body: JSON.stringify({ exists: true }),
97+
});
98+
});
99+
100+
await page.fill(
101+
'input[placeholder="[email protected]"]',
102+
103+
);
104+
await page.waitForResponse('**/api/users/emails/**');
105+
106+
await expect(page.locator('text=이미 사용 중인 이메일입니다.')).toBeVisible();
107+
await expect(signUpButton).toBeDisabled();
108+
});
109+
110+
test('회원 가입이 이미 중복된 닉네임이 있어서 실패하는 경우', async ({
111+
page,
112+
}) => {
113+
await page.click('text=회원가입');
114+
115+
const signUpButton = page.locator('text=회원 가입');
116+
await expect(signUpButton).toBeDisabled();
117+
118+
await page.route('**/api/users/emails/**', async (route) => {
119+
await route.fulfill({
120+
status: 200,
121+
contentType: 'application/json',
122+
body: JSON.stringify({ exists: false }),
123+
});
124+
});
125+
126+
await page.route('**/api/users/nicknames/**', async (route) => {
127+
await route.fulfill({
128+
status: 200,
129+
contentType: 'application/json',
130+
body: JSON.stringify({ exists: true }),
131+
});
132+
});
133+
134+
await page.fill(
135+
'input[placeholder="[email protected]"]',
136+
137+
);
138+
await page.waitForResponse('**/api/users/emails/**');
139+
await expect(page.locator('text=사용 가능한 이메일입니다.')).toBeVisible();
140+
141+
await page.fill('input[placeholder="닉네임을 입력해주세요"]', 'testUser');
142+
await page.waitForResponse('**/api/users/nicknames/**');
143+
await expect(page.locator('text=이미 사용 중인 닉네임입니다.')).toBeVisible();
144+
145+
await expect(signUpButton).toBeDisabled();
146+
});
147+
148+
test('로그인 / 로그아웃 플로우 전체 테스트', async ({ page }) => {
149+
await page.click('text=로그인');
150+
151+
const loginButton = page.locator('text=로그인').nth(1);
152+
153+
await page.fill('input[placeholder="[email protected]"]', '[email protected]');
154+
await page.fill(
155+
'input[placeholder="비밀번호를 입력해주세요"]',
156+
'Password123!',
157+
);
158+
159+
await expect(loginButton).toBeEnabled();
160+
161+
await page.route('**/api/auth/login', async (route) => {
162+
route.fulfill({
163+
status: 200,
164+
contentType: 'application/json',
165+
body: JSON.stringify({ accessToken: 'fake-jwt-token' }),
166+
});
167+
});
168+
169+
const response = page.waitForResponse('**/api/auth/login');
170+
await loginButton.click();
171+
expect((await response).status()).toBe(200);
172+
173+
await expect(page.locator('text=로그인 되었습니다.')).toBeVisible();
174+
175+
await page.click('text=로그아웃');
176+
await expect(page.locator('text=로그아웃 되었습니다.')).toBeVisible();
177+
});

apps/client/tests/MyPage.spec.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { expect, test } from '@playwright/test';
2+
3+
import { PostRefreshResponseDTO } from '@/features/auth/auth.dto';
4+
5+
test.beforeEach(async ({ page }) => {
6+
page.route('**/api/auth/token', async (route) => {
7+
await route.fulfill({
8+
status: 200,
9+
contentType: 'application/json',
10+
body: JSON.stringify({
11+
accessToken: 'new-access-token',
12+
userId: 1,
13+
} as PostRefreshResponseDTO),
14+
});
15+
});
16+
17+
page.route('**/api/sessions', async (route) => {
18+
await route.fulfill({
19+
status: 200,
20+
contentType: 'application/json',
21+
body: JSON.stringify({
22+
sessionData: [
23+
{
24+
sessionId: '1',
25+
title: '세션 1',
26+
createdAt: {
27+
year: 2024,
28+
month: 12,
29+
date: 1,
30+
},
31+
expired: false,
32+
},
33+
{
34+
sessionId: '2',
35+
title: '세션 2',
36+
createdAt: {
37+
year: 2024,
38+
month: 12,
39+
date: 2,
40+
},
41+
expired: true,
42+
},
43+
{
44+
sessionId: '3',
45+
title: '세션 3',
46+
createdAt: {
47+
year: 2024,
48+
month: 12,
49+
date: 3,
50+
},
51+
expired: false,
52+
},
53+
],
54+
}),
55+
});
56+
});
57+
58+
await page.goto('/my', { waitUntil: 'domcontentloaded' });
59+
});
60+
61+
test('세션 목록 확인', async ({ page }) => {
62+
await expect(page.locator('text=참여한 세션 기록')).toBeVisible();
63+
64+
const sessionTitles = ['세션 1', '세션 2', '세션 3'];
65+
await Promise.all(
66+
sessionTitles.map(async (title) => {
67+
await expect(page.locator(`text=${title}`)).toBeVisible();
68+
}),
69+
);
70+
});

0 commit comments

Comments
 (0)