From 07889622736d86434d7e8fc8acde1da963e7627b Mon Sep 17 00:00:00 2001 From: DDD1542 Date: Fri, 6 Mar 2026 03:21:42 +0900 Subject: [PATCH] [agent-pipeline] pipe-20260305181927-h4x5 round-1 --- .../src/controllers/approvalController.ts | 56 ++++++++---- pw-runner.mjs | 89 +++++++++++++++++++ run-pw.js | 5 ++ 3 files changed, 132 insertions(+), 18 deletions(-) create mode 100644 pw-runner.mjs create mode 100644 run-pw.js diff --git a/backend-node/src/controllers/approvalController.ts b/backend-node/src/controllers/approvalController.ts index cec2ca67..bd1bcfa4 100644 --- a/backend-node/src/controllers/approvalController.ts +++ b/backend-node/src/controllers/approvalController.ts @@ -77,9 +77,12 @@ export class ApprovalDefinitionController { } const { id } = req.params; + // SUPER_ADMIN은 company_code 필터 없이 조회 가능 const row = await queryOne( - "SELECT * FROM approval_definitions WHERE definition_id = $1 AND company_code = $2", - [id, companyCode] + companyCode === "*" + ? "SELECT * FROM approval_definitions WHERE definition_id = $1" + : "SELECT * FROM approval_definitions WHERE definition_id = $1 AND company_code = $2", + companyCode === "*" ? [id] : [id, companyCode] ); if (!row) { @@ -307,12 +310,18 @@ export class ApprovalTemplateController { } const { id } = req.params; + // SUPER_ADMIN은 company_code 필터 없이 조회 가능 const template = await queryOne( - `SELECT t.*, d.definition_name - FROM approval_line_templates t - LEFT JOIN approval_definitions d ON t.definition_id = d.definition_id AND t.company_code = d.company_code - WHERE t.template_id = $1 AND t.company_code = $2`, - [id, companyCode] + companyCode === "*" + ? `SELECT t.*, d.definition_name + FROM approval_line_templates t + LEFT JOIN approval_definitions d ON t.definition_id = d.definition_id AND t.company_code = d.company_code + WHERE t.template_id = $1` + : `SELECT t.*, d.definition_name + FROM approval_line_templates t + LEFT JOIN approval_definitions d ON t.definition_id = d.definition_id AND t.company_code = d.company_code + WHERE t.template_id = $1 AND t.company_code = $2`, + companyCode === "*" ? [id] : [id, companyCode] ); if (!template) { @@ -320,8 +329,10 @@ export class ApprovalTemplateController { } const steps = await query( - "SELECT * FROM approval_line_template_steps WHERE template_id = $1 AND company_code = $2 ORDER BY step_order ASC", - [id, companyCode] + companyCode === "*" + ? "SELECT * FROM approval_line_template_steps WHERE template_id = $1 ORDER BY step_order ASC" + : "SELECT * FROM approval_line_template_steps WHERE template_id = $1 AND company_code = $2 ORDER BY step_order ASC", + companyCode === "*" ? [id] : [id, companyCode] ); return res.json({ success: true, data: { ...template, steps } }); @@ -640,14 +651,15 @@ export class ApprovalRequestController { const offset = (parseInt(page as string) - 1) * parseInt(limit as string); const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : ""; - // 카운트 쿼리는 LIMIT/OFFSET 파라미터 없이 실행 + // countParams는 WHERE 조건 파라미터만 포함 (LIMIT/OFFSET 제외) + // my_approvals 파라미터도 포함된 후 복사해야 함 const countParams = [...params]; const [countRow] = await query( `SELECT COUNT(*) as total FROM approval_requests r ${whereClause}`, countParams ); - // LIMIT/OFFSET 파라미터 인덱스를 미리 계산 + // LIMIT/OFFSET 파라미터 인덱스를 미리 계산 (countParams 복사 후에 idx 증가) const limitIdx = idx++; const offsetIdx = idx++; params.push(parseInt(limit as string), offset); @@ -688,12 +700,18 @@ export class ApprovalRequestController { } const { id } = req.params; + // SUPER_ADMIN은 company_code 필터 없이 모든 요청 조회 가능 const request = await queryOne( - `SELECT r.*, d.definition_name - FROM approval_requests r - LEFT JOIN approval_definitions d ON r.definition_id = d.definition_id AND r.company_code = d.company_code - WHERE r.request_id = $1 AND r.company_code = $2`, - [id, companyCode] + companyCode === "*" + ? `SELECT r.*, d.definition_name + FROM approval_requests r + LEFT JOIN approval_definitions d ON r.definition_id = d.definition_id AND r.company_code = d.company_code + WHERE r.request_id = $1` + : `SELECT r.*, d.definition_name + FROM approval_requests r + LEFT JOIN approval_definitions d ON r.definition_id = d.definition_id AND r.company_code = d.company_code + WHERE r.request_id = $1 AND r.company_code = $2`, + companyCode === "*" ? [id] : [id, companyCode] ); if (!request) { @@ -701,8 +719,10 @@ export class ApprovalRequestController { } const lines = await query( - "SELECT * FROM approval_lines WHERE request_id = $1 AND company_code = $2 ORDER BY step_order ASC", - [id, companyCode] + companyCode === "*" + ? "SELECT * FROM approval_lines WHERE request_id = $1 ORDER BY step_order ASC" + : "SELECT * FROM approval_lines WHERE request_id = $1 AND company_code = $2 ORDER BY step_order ASC", + companyCode === "*" ? [id] : [id, companyCode] ); return res.json({ success: true, data: { ...request, lines } }); diff --git a/pw-runner.mjs b/pw-runner.mjs new file mode 100644 index 00000000..f8540a5a --- /dev/null +++ b/pw-runner.mjs @@ -0,0 +1,89 @@ +import { chromium } from '/Users/gbpark/ERP-node/node_modules/playwright/index.mjs'; +import { writeFileSync } from 'fs'; + +const results = []; +let passed = true; +let failReason = ''; + +async function run() { + const browser = await chromium.launch({ headless: true }); + const page = await browser.newPage(); + + try { + // 로그인 + await page.goto('http://localhost:9771/login'); + await page.waitForLoadState('networkidle'); + + await page.getByPlaceholder('사용자 ID를 입력하세요').fill('wace'); + await page.getByPlaceholder('비밀번호를 입력하세요').fill('qlalfqjsgh11'); + + await Promise.all([ + page.waitForURL(url => !url.toString().includes('/login'), { timeout: 30000 }), + page.getByRole('button', { name: '로그인' }).click(), + ]); + await page.waitForLoadState('networkidle'); + results.push('✅ 로그인 성공'); + + // approvalBox 페이지 접속 + await page.goto('http://localhost:9771/admin/approvalBox'); + await page.waitForLoadState('domcontentloaded'); + await page.waitForTimeout(3000); + results.push('✅ approvalBox 페이지 접속 성공'); + + // "보낸 결재" 탭 확인 + const sentTabVisible = await page.getByRole('tab', { name: '보낸 결재' }).isVisible().catch(async () => { + return await page.getByText('보낸 결재').first().isVisible().catch(() => false); + }); + if (!sentTabVisible) { + throw new Error('"보낸 결재" 탭이 보이지 않음'); + } + results.push('✅ "보낸 결재" 탭 확인'); + + // 결재 목록 또는 빈 상태 확인 + const rowCount = await page.locator('table tbody tr').count(); + const hasEmpty = await page.getByText(/결재 요청이 없습니다|데이터가 없습니다|없습니다/).isVisible().catch(() => false); + results.push(`✅ 목록 확인 (rows: ${rowCount}, emptyMsg: ${hasEmpty})`); + + // "받은 결재" 탭 클릭 + const receivedTab = page.getByRole('tab', { name: '받은 결재' }); + const receivedVisible = await receivedTab.isVisible().catch(() => false); + if (!receivedVisible) { + throw new Error('"받은 결재" 탭이 보이지 않음'); + } + await receivedTab.click(); + await page.waitForTimeout(2000); + results.push('✅ "받은 결재" 탭 클릭'); + + // 에러 오버레이 확인 + const hasError = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); + if (hasError) { + throw new Error('에러 오버레이 발견'); + } + results.push('✅ 에러 오버레이 없음'); + + // 스크린샷 + await page.screenshot({ path: '/Users/gbpark/ERP-node/.agent-pipeline/browser-tests/result.png', fullPage: true }); + results.push('✅ 스크린샷 저장'); + + } catch (err) { + passed = false; + failReason = err.message; + results.push(`❌ 실패: ${err.message}`); + await page.screenshot({ path: '/Users/gbpark/ERP-node/.agent-pipeline/browser-tests/result-fail.png', fullPage: true }).catch(() => {}); + } finally { + await browser.close(); + } +} + +run().then(() => { + const output = results.join('\n'); + console.log(output); + writeFileSync('/tmp/pw-runner-result.txt', output + '\n' + (passed ? 'RESULT: PASS' : `RESULT: FAIL - ${failReason}`)); + console.log(passed ? 'RESULT: PASS' : `RESULT: FAIL - ${failReason}`); + process.exit(passed ? 0 : 1); +}).catch(err => { + const msg = `치명적 오류: ${err.message}`; + console.error(msg); + writeFileSync('/tmp/pw-runner-result.txt', msg + '\nRESULT: FAIL - ' + err.message); + process.exit(1); +}); diff --git a/run-pw.js b/run-pw.js new file mode 100644 index 00000000..cd325753 --- /dev/null +++ b/run-pw.js @@ -0,0 +1,5 @@ +const { execSync } = require('child_process'); +const result = execSync( + './node_modules/.bin/playwright test ".agent-pipeline/browser-tests/e2e-test.spec.ts" --config=".agent-pipeline/browser-tests/playwright.config.ts" --reporter=line', + { cwd: '/Users/gbpark/ERP-node', stdio: 'inherit', timeout: 120000 } +);