From ac88ebdedbedecaf43f255a46a374f12f1a878b8 Mon Sep 17 00:00:00 2001 From: javamon1174 Date: Thu, 26 Mar 2026 18:16:12 +0900 Subject: [PATCH] =?UTF-8?q?=EC=8B=A0=EA=B7=9C=20=EA=B8=B0=EB=8A=A5=20E2E?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=2029=EA=B0=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=E2=80=94=20=EB=8F=84=EB=A9=94=EC=9D=B8=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=ED=86=B5=EA=B3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CT-01 페이지네이션, CT-02 엑셀, CT-03 인쇄, CT-06 대시보드 P2-15 다조건조회, P2-17 지도, P2-18 현황 P5-04 년판매, P5-05 판매소별, P5-06 홈택스, P5-08 반품파기 P5-10 LOT수불, P5-11 기타입출고 사이트 CRUD 레이아웃 검증 6개, 엑셀 다운로드 2개 Co-Authored-By: Claude Opus 4.6 (1M context) --- e2e/new-features.spec.js | 297 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 e2e/new-features.spec.js diff --git a/e2e/new-features.spec.js b/e2e/new-features.spec.js new file mode 100644 index 0000000..37a4fae --- /dev/null +++ b/e2e/new-features.spec.js @@ -0,0 +1,297 @@ +// @ts-check +const { test, expect } = require('@playwright/test'); +const { login } = require('./helpers/auth'); + +async function loginAsAdmin(page) { + await login(page, 'admin'); + await page.locator('input[name="lg_idx"]').first().check(); + await page.click('button[type="submit"]'); + await page.waitForURL(url => !url.pathname.includes('select-local-government'), { timeout: 30000 }); +} + +async function loginAsLocal(page) { + await login(page, 'local'); +} + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// CT-01: 페이지네이션 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +test.describe('CT-01: 페이지네이션', () => { + test('발주 목록에 데이터 테이블 존재', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/bag-orders'); + await expect(page.locator('table.data-table')).toBeVisible(); + }); + + test('판매 목록에 데이터 테이블 존재', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/bag-sales'); + await expect(page.locator('table.data-table')).toBeVisible(); + }); +}); + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// CT-02: 엑셀 저장 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +test.describe('CT-02: 엑셀 저장', () => { + test('발주 엑셀 다운로드', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/bag-orders'); + const downloadPromise = page.waitForEvent('download', { timeout: 10000 }); + await page.locator('a[href*="export"]').first().click(); + const download = await downloadPromise; + expect(download.suggestedFilename()).toContain('.csv'); + }); + + test('재고 엑셀 다운로드', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/bag-inventory'); + const downloadPromise = page.waitForEvent('download', { timeout: 10000 }); + await page.locator('a[href*="export"]').first().click(); + const download = await downloadPromise; + expect(download.suggestedFilename()).toContain('.csv'); + }); +}); + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// CT-03: 인쇄 버튼 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +test.describe('CT-03: 인쇄 버튼', () => { + test('발주 목록에 인쇄 버튼 존재', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/bag-orders'); + await expect(page.locator('button:has-text("인쇄"), a:has-text("인쇄")')).toBeVisible(); + }); +}); + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// CT-06: 대시보드 실 데이터 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +test.describe('CT-06: 대시보드 실 데이터', () => { + test('대시보드에 통계 표시', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin'); + const content = await page.textContent('main'); + expect(content).toBeTruthy(); + expect(content.length).toBeGreaterThan(50); + }); +}); + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// P2-15: 지정판매소 다조건 조회 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +test.describe('P2-15: 지정판매소 다조건 조회', () => { + test('이름 검색 필터', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/designated-shops?ds_name=CU'); + await expect(page).toHaveURL(/ds_name=CU/); + await expect(page.locator('table.data-table')).toBeVisible(); + }); + + test('상태 필터', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/designated-shops?ds_state=1'); + await expect(page.locator('table.data-table')).toBeVisible(); + }); + + test('검색 폼에서 이름 입력 후 조회', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/designated-shops'); + const nameInput = page.locator('input[name="ds_name"]'); + if (await nameInput.count() > 0) { + await nameInput.fill('GS'); + await page.click('button:has-text("조회")'); + await expect(page).toHaveURL(/ds_name=GS/); + } + }); +}); + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// P2-17: 지정판매소 지도 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +test.describe('P2-17: 지정판매소 지도', () => { + test('지도 페이지 접근', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/designated-shops/map'); + await expect(page).toHaveURL(/\/map/); + }); +}); + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// P2-18: 지정판매소 현황 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +test.describe('P2-18: 지정판매소 현황', () => { + test('현황 페이지 접근', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/designated-shops/status'); + await expect(page).toHaveURL(/\/status/); + await expect(page.locator('table.data-table').first()).toBeVisible(); + }); +}); + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// P5-04: 년 판매 현황 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +test.describe('P5-04: 년 판매 현황', () => { + test('년 판매 현황 접근', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/reports/yearly-sales'); + await expect(page).toHaveURL(/yearly-sales/); + await expect(page.locator('table.data-table')).toBeVisible(); + }); + + test('연도 변경 조회', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/reports/yearly-sales?year=2025'); + await expect(page).toHaveURL(/year=2025/); + }); +}); + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// P5-05: 지정판매소별 판매현황 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +test.describe('P5-05: 판매소별 판매현황', () => { + test('판매소별 현황 접근', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/reports/shop-sales'); + await expect(page).toHaveURL(/shop-sales/); + await expect(page.locator('table.data-table')).toBeVisible(); + }); +}); + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// P5-06: 홈택스 엑셀 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +test.describe('P5-06: 홈택스 세금계산서 엑셀', () => { + test('홈택스 엑셀 내보내기', async ({ page }) => { + await loginAsLocal(page); + // 다운로드를 트리거하는 URL이므로 evaluate로 fetch 테스트 + const status = await page.evaluate(async () => { + const res = await fetch('/admin/reports/hometax-export'); + return res.status; + }); + expect(status).toBe(200); + }); +}); + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// P5-08: 반품/파기 현황 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +test.describe('P5-08: 반품/파기 현황', () => { + test('반품/파기 목록 접근', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/reports/returns'); + await expect(page).toHaveURL(/returns/); + await expect(page.locator('table.data-table')).toBeVisible(); + }); + + test('기간 필터 조회', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/reports/returns?start_date=2026-01-01&end_date=2026-12-31'); + await expect(page.locator('table.data-table')).toBeVisible(); + }); +}); + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// P5-10: LOT 수불 조회 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +test.describe('P5-10: LOT 수불 조회', () => { + test('LOT 수불 페이지 접근', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/reports/lot-flow'); + await expect(page).toHaveURL(/lot-flow/); + }); + + test('LOT 번호 검색', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/reports/lot-flow?lot=LOT-2025'); + await expect(page).toHaveURL(/lot=LOT/); + }); +}); + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// P5-11: 기타 입출고 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +test.describe('P5-11: 기타 입출고', () => { + test('기타 입출고 페이지 접근', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/reports/misc-flow'); + await expect(page).toHaveURL(/misc-flow/); + }); + + test('기타 입출고 등록 폼 표시', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/reports/misc-flow'); + await expect(page.locator('select[name="bmf_type"]')).toBeVisible(); + await expect(page.locator('input[name="bmf_qty"]')).toBeVisible(); + }); +}); + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// 사이트 메뉴 CRUD (DOM 조작) +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +test.describe('사이트 메뉴 CRUD 동작', () => { + test.beforeEach(async ({ page }) => { + await loginAsLocal(page); + }); + + test('불출 처리 폼 → 사이트 레이아웃', async ({ page }) => { + await page.goto('/bag/issue/create'); + await expect(page.locator('a:has-text("발주 입고 관리")')).toBeVisible(); + expect(await page.locator('a:has-text("회원 관리")').count()).toBe(0); + await expect(page.locator('select[name="bi2_bag_code"]')).toBeVisible(); + }); + + test('발주 등록 폼 → 사이트 레이아웃', async ({ page }) => { + await page.goto('/bag/order/create'); + await expect(page.locator('a:has-text("불출 관리")')).toBeVisible(); + await expect(page.locator('input[name="bo_order_date"]')).toBeVisible(); + }); + + test('입고 처리 폼 → 사이트 레이아웃', async ({ page }) => { + await page.goto('/bag/receiving/create'); + await expect(page.locator('a:has-text("재고 관리")')).toBeVisible(); + }); + + test('판매 등록 폼 → 사이트 레이아웃', async ({ page }) => { + await page.goto('/bag/sale/create'); + await expect(page.locator('a:has-text("판매 현황")')).toBeVisible(); + await expect(page.locator('select[name="bs_ds_idx"]')).toBeVisible(); + }); + + test('주문 접수 폼 → 사이트 레이아웃', async ({ page }) => { + await page.goto('/bag/shop-order/create'); + await expect(page.locator('a:has-text("봉투 수불 관리")')).toBeVisible(); + await expect(page.locator('select[name="so_ds_idx"]')).toBeVisible(); + }); + + test('재고 조정 폼', async ({ page }) => { + await page.goto('/bag/inventory/adjust'); + await expect(page.locator('select[name="bag_code"]')).toBeVisible(); + await expect(page.locator('select[name="adjust_type"]')).toBeVisible(); + await expect(page.locator('input[name="qty"]')).toBeVisible(); + }); +}); + +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// 엑셀 내보내기 다운로드 +// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +test.describe('엑셀 내보내기 다운로드', () => { + test('지정판매소 엑셀', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/designated-shops'); + const downloadPromise = page.waitForEvent('download', { timeout: 10000 }); + await page.locator('a[href*="export"]').first().click(); + const download = await downloadPromise; + expect(download.suggestedFilename()).toContain('.csv'); + }); + + test('판매 엑셀', async ({ page }) => { + await loginAsLocal(page); + await page.goto('/admin/bag-sales'); + const downloadPromise = page.waitForEvent('download', { timeout: 10000 }); + await page.locator('a[href*="export"]').first().click(); + const download = await downloadPromise; + expect(download.suggestedFilename()).toContain('.csv'); + }); +});