Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
94f43d9
반복 일정 서버 코드 추가
soyalattee Aug 27, 2025
97e8f7e
test: 반복일정 유닛 테스트 작성
soyalattee Aug 27, 2025
a7f4ea7
test: 반복일정 통합 테스트 작성
soyalattee Aug 27, 2025
ebbc47d
test: 반복일정 유닛테스트 성공하는 테스트코드 구현
soyalattee Aug 27, 2025
93b780c
test: 반복일정 통합 테스트 성공하는 테스트코드 구현
soyalattee Aug 27, 2025
684d166
feat: 테스트코드 통과하도록 expandRepeats 함수 구현 (리팩토링X)
soyalattee Aug 27, 2025
bc1b1ff
fix: 통합테스트코드에 event-list POST API 추가 하고 테스트코드 수정
soyalattee Aug 27, 2025
6f17eb4
fix: 통합 테스트코드 전용 목핸들러 생성하고 이걸 사용해 반복 설정 통합테스트코드 작성
soyalattee Aug 27, 2025
3dc32ab
test: 심화 테스트 추가
soyalattee Aug 28, 2025
2b45d4a
style: 임포트 줄바꿈 수정
soyalattee Aug 28, 2025
fdf7420
rename: 테스트파일명 수정
soyalattee Aug 28, 2025
9da803c
test: 테스트명에 '시나리오{number}' 추가
soyalattee Aug 28, 2025
f8cc334
test: 알림설정 통합테스트 추가
soyalattee Aug 28, 2025
68a60eb
test: 화면 싱크 통합테스트 추가
soyalattee Aug 28, 2025
917c1cf
chore: e2e 테스트를 위해 playwrite 추가
soyalattee Aug 28, 2025
2d8f7f5
chore: e2e 생성물 올리지 않음
soyalattee Aug 28, 2025
1a96222
test: e2e 테스트 일정 등록 조회 수정 삭제 추가
soyalattee Aug 28, 2025
b584c36
e2e 파일 최상단으로 이동
soyalattee Aug 28, 2025
4322195
fix: 타임아웃시간 변경
soyalattee Aug 28, 2025
3d55cd9
fix: e2e 로 파일명 변경
soyalattee Aug 28, 2025
a547667
test: e2e 검색 테스트 추가
soyalattee Aug 28, 2025
ec199a0
chore: test ci 에 test:e2e 추가하여 e2e 테스트 를 실행
soyalattee Aug 28, 2025
603cafa
chore: e2e 스텝 추가
soyalattee Aug 28, 2025
6c582c6
ci 수정
soyalattee Aug 28, 2025
c1f8f02
ci 에 steps 두개
soyalattee Aug 28, 2025
8e545ea
ci 트리거추가
soyalattee Aug 28, 2025
09243c2
name 으로 e2e 추가
soyalattee Aug 28, 2025
3850455
트리거 제거
soyalattee Aug 28, 2025
9fea6ff
test: 뷰 전환 관련 e2e테스트 작성
soyalattee Aug 28, 2025
8bea9de
ci e2e 잡 다시 추가 + github.sha
soyalattee Aug 28, 2025
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
22 changes: 21 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
- synchronize
- opened
- reopened
push:

workflow_dispatch:

Expand Down Expand Up @@ -45,4 +46,23 @@ jobs:
- name: test basic
run: |
pnpm install
pnpm run test
pnpm run test
e2e:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- uses: pnpm/action-setup@v4
with:
version: latest
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: |
pnpm install
npx playwright install --with-deps
pnpm exec playwright test
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
.vscode
node_modules
.coverage
playwright-report
test-results
60 changes: 60 additions & 0 deletions e2e/event.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { test, expect } from '@playwright/test';

test.describe('E2E: 일정 등록 시나리오', () => {
test.beforeEach(async ({ page }) => {
// 로컬 스토리지 초기화
await page.goto('/');
// 기본 렌더 확인 (리스트가 보이는지)
await expect(page.getByTestId('event-list')).toBeVisible();
});

test.describe('일정 등록 시나리오', () => {
test('일정 등록 테스트', async ({ page }) => {
const currentYear = new Date().getFullYear();
const currentMonth = (new Date().getMonth() + 1).toString().padStart(2, '0');
const currentDay = new Date().getDate().toString().padStart(2, '0');
await page.getByRole('textbox', { name: '제목' }).click();
await page.getByRole('textbox', { name: '제목' }).fill('2팀 회식');
await page
.getByRole('textbox', { name: '날짜' })
.fill(`${currentYear}-${currentMonth}-${currentDay}`);
await page.getByRole('textbox', { name: '시작 시간' }).click();
await page.getByRole('textbox', { name: '시작 시간' }).press('ArrowDown');
await page.getByRole('textbox', { name: '시작 시간' }).press('ArrowRight');
await page.getByRole('textbox', { name: '시작 시간' }).fill('23:11');
await page.getByRole('textbox', { name: '종료 시간' }).click();
await page.getByRole('textbox', { name: '종료 시간' }).press('ArrowDown');
await page.getByRole('textbox', { name: '종료 시간' }).press('Tab');
await page.getByRole('textbox', { name: '종료 시간' }).fill('23:20');
await page.getByRole('combobox', { name: '업무' }).click();
await page.getByRole('option', { name: '개인-option' }).click();
await page.getByTestId('event-submit-button').click();

// 5) 리스트에 제목 노출 확인
const list = page.getByTestId('event-list');
await expect(list.getByText('점심 약속')).toBeVisible();
});
test('일정 수정 테스트', async ({ page }) => {
const list = page.getByTestId('event-list');
// 1) 수정 버튼 클릭
await page.getByRole('button', { name: 'Edit event' }).last().click();
await page.getByRole('textbox', { name: '설명' }).click();
await page.getByRole('textbox', { name: '설명' }).fill('2팀 점심 모임');
await page.getByTestId('event-submit-button').click();

// 2) 리스트에 설명 노출 확인
await expect(list.getByText('2팀 점심 모임')).toBeVisible();
});
test('일정 삭제 테스트', async ({ page }) => {
// 1) 리스트에 제목 노출 확인
const list = page.getByTestId('event-list');
await expect(list.getByText('2팀 회식')).toBeVisible();

// 2) 마지막 이벤트 삭제 버튼 클릭
await page.getByRole('button', { name: 'Delete event' }).last().click();

// 7) 리스트에 제목 사라짐 확인
await expect(list.getByText('2팀 회식')).not.toBeVisible();
});
});
});
79 changes: 79 additions & 0 deletions e2e/event.flow.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { test, expect } from '@playwright/test';

test('사용자가 캘린더 뷰를 전환하고 날짜를 탐색할 수 있다', async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');

// 1. 초기 상태 확인 - 사용자가 월간 뷰를 보고 있음
await expect(page.locator('[data-testid="month-view"]')).toBeVisible();

// 현재 날짜가 표시되는지 확인 (사용자가 오늘 날짜를 볼 수 있음)
const today = new Date();
const currentDay = today.getDate().toString();
await expect(page.locator(`text=${currentDay}`)).toBeVisible();

// 2. 사용자가 주간 뷰로 전환하고 싶어함
await page.click('[aria-label="뷰 타입 선택"]');
await page.click('[aria-label="week-option"]');

// 주간 뷰로 전환되어 현재 주가 표시됨
await expect(page.locator('[data-testid="week-view"]')).toBeVisible();
await expect(page.locator(`text=${currentDay}`)).toBeVisible();

// 3. 사용자가 다음 주로 이동하고 싶어함
await page.click('[aria-label="Next"]');
// 다음 주로 이동했는지 확인 (뷰가 변경되었는지만 확인)
await expect(page.locator('[data-testid="week-view"]')).toBeVisible();

// 4. 사용자가 이전 주로 돌아가고 싶어함
await page.click('[aria-label="Previous"]');
// 다시 현재 주로 돌아왔으므로 현재 날짜가 보임
await expect(page.locator(`text=${currentDay}`)).toBeVisible();

// 5. 사용자가 다시 월간 뷰로 돌아가고 싶어함
await page.click('[aria-label="뷰 타입 선택"]');
await page.click('[aria-label="month-option"]');

// 월간 뷰로 돌아와서 전체 월을 볼 수 있음
await expect(page.locator('[data-testid="month-view"]')).toBeVisible();
await expect(page.locator(`text=${currentDay}`)).toBeVisible();

// 6. 사용자가 다음 달로 이동하고 싶어함
await page.click('[aria-label="Next"]');
// 다음 달로 이동했는지 확인 (뷰가 변경되었는지만 확인)
await expect(page.locator('[data-testid="month-view"]')).toBeVisible();

// 7. 사용자가 이전 달로 돌아가고 싶어함
await page.click('[aria-label="Previous"]');
// 다시 현재 달로 돌아왔으므로 현재 날짜가 보임
await expect(page.locator(`text=${currentDay}`)).toBeVisible();
});

test('뷰 전환 시 현재 날짜가 올바른 주/월로 이동한다', async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');

// 1. 현재 날짜 정보 가져오기
const today = new Date();
const currentDay = today.getDate().toString();

// 2. 월간 뷰에서 현재 날짜가 표시되는지 확인
await expect(page.locator('[data-testid="month-view"]')).toBeVisible();
await expect(page.locator(`text=${currentDay}`)).toBeVisible();

// 3. 주간 뷰로 전환했을 때 현재 날짜가 속한 주로 이동하는지 확인
await page.click('[aria-label="뷰 타입 선택"]');
await page.click('[aria-label="week-option"]');

// 주간 뷰에서 현재 날짜가 표시되는지 확인 (현재 주로 이동했는지 검증)
await expect(page.locator('[data-testid="week-view"]')).toBeVisible();
await expect(page.locator(`text=${currentDay}`)).toBeVisible();

// 4. 다시 월간 뷰로 전환했을 때 현재 날짜가 속한 월로 이동하는지 확인
await page.click('[aria-label="뷰 타입 선택"]');
await page.click('[aria-label="month-option"]');

// 월간 뷰에서 현재 날짜가 표시되는지 확인 (현재 월로 이동했는지 검증)
await expect(page.locator('[data-testid="month-view"]')).toBeVisible();
await expect(page.locator(`text=${currentDay}`)).toBeVisible();
});
40 changes: 40 additions & 0 deletions e2e/event.serach.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { test, expect } from '@playwright/test';

test('검색어가 있는 경우 해당 검색어를 필터링 한다.', async ({ page }) => {
await page.goto('/');

// 검색용 일정 생성
await page.getByRole('textbox', { name: '제목' }).fill('검색용');
await page.getByRole('textbox', { name: '날짜' }).fill('2025-08-26');
await page.getByRole('textbox', { name: '시작 시간' }).fill('09:00');
await page.getByRole('textbox', { name: '종료 시간' }).fill('10:00');
await page.getByRole('textbox', { name: '설명' }).fill('검색하려고 만든 이벤트');
await page.getByTestId('event-submit-button').click();

// 해당 일정이 추가되었는지 확인
await expect(page.getByTestId('event-list').getByText('검색용')).toBeVisible();

// 검색 후 해당 키워드가 표시되는지 확인
await page.getByRole('textbox', { name: '일정 검색' }).fill('검색용');
await expect(page.getByTestId('event-list').getByText('검색용')).toBeVisible();

// 검색 초기화 후 일정이 다시 표시되는지 확인
await page.getByRole('textbox', { name: '일정 검색' }).fill('');
await expect(page.getByTestId('event-list').getByText('검색용')).toBeVisible();

// 존재하지 않는 검색어 검색
await page.getByRole('textbox', { name: '일정 검색' }).fill('없는 검색어');
await expect(page.getByText('검색 결과가 없습니다')).toBeVisible();

// 검색 초기화 후 원래 일정이 표시되는지 확인
await page.getByRole('textbox', { name: '일정 검색' }).fill('');
await expect(page.getByTestId('event-list').getByText('검색용')).toBeVisible();

//추가된 일정 삭제
const searchEventCard = page
.locator('[data-testid="event-list"] .MuiBox-root')
.filter({ hasText: '검색용' });
await searchEventCard.getByRole('button', { name: 'Delete event' }).click();

await expect(page.getByTestId('event-list').getByText('검색용')).not.toBeVisible();
});
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"dev": "concurrently \"pnpm run server:watch\" \"pnpm run start\"",
"test": "vitest",
"test:ui": "vitest --ui",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"test:coverage": "vitest run --coverage",
"build": "tsc -b && vite build",
"lint:eslint": "eslint . --ext ts,tsx --report-unused-disable-directives",
Expand All @@ -30,6 +32,7 @@
},
"devDependencies": {
"@eslint/js": "9.33.0",
"@playwright/test": "^1.55.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.5.2",
Expand Down
37 changes: 37 additions & 0 deletions playwright.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './e2e', // 경로 수정
testMatch: '**/*.e2e.ts', // e2e 테스트 파일만 실행
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
timeout: 120000, // 2분
reporter: 'html',
use: {
baseURL: 'http://localhost:5173', // Vite 기본 포트
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
webServer: {
command: 'pnpm dev',
url: 'http://localhost:5173',
reuseExistingServer: !process.env.CI,
timeout: 6 * 120 * 1000, // 6분
},
testIgnore: ['**/node_modules/**', '**/playwright.config.js'], // 설정 파일 제외
});
38 changes: 38 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading