import { test, expect } from '@playwright/test'; import { login } from './helpers/auth'; import * as fs from 'fs'; /** * 採購模組端到端測試 * 驗證「批量寫入」(多筆明細 bulk insert) 與「併發鎖定」(狀態變更 lockForUpdate) */ test.describe('採購管理 - 採購單建立', () => { // 登入 + 導航 + 表單操作需要較長時間 test.use({ actionTimeout: 15000 }); test.beforeEach(async ({ page }) => { await login(page); }); test('應能成功建立含多筆明細的採購單', async ({ page }) => { // 整體測試逾時設定為 90 秒(含多次選單互動) test.setTimeout(90000); // 1. 前往採購單列表 await page.goto('/purchase-orders'); await expect(page.getByRole('heading', { name: '採購單管理' })).toBeVisible(); // 2. 點擊「建立採購單」按鈕 await page.getByRole('button', { name: /建立採購單/ }).click(); await expect(page.getByRole('heading', { name: '建立採購單' })).toBeVisible({ timeout: 10000 }); // 3. 選擇倉庫 (使用 SearchableSelect combobox) const warehouseCombobox = page.locator('label:has-text("預計入庫倉庫")').locator('..').getByRole('combobox'); await warehouseCombobox.click(); await page.getByRole('option', { name: '中央倉庫' }).click(); // 4. 選擇供應商 const supplierCombobox = page.locator('label:has-text("供應商")').locator('..').getByRole('combobox'); await supplierCombobox.click(); await page.getByRole('option', { name: '台積電' }).click(); // 5. 填寫下單日期(應該已有預設值,但確保有值) const orderDateInput = page.locator('label:has-text("下單日期")').locator('..').locator('input[type="date"]'); const currentDate = await orderDateInput.inputValue(); if (!currentDate) { const today = new Date().toISOString().split('T')[0]; await orderDateInput.fill(today); } // 6. 填寫備註 await page.getByPlaceholder('備註這筆採購單的特殊需求...').fill('E2E 自動化測試 - 批量寫入驗證'); // 7. 新增第一個品項 await page.getByRole('button', { name: '新增一個品項' }).click(); // 選擇商品(第一行) const firstRow = page.locator('table tbody tr').first(); const firstProductCombobox = firstRow.getByRole('combobox').first(); await firstProductCombobox.click(); await page.getByRole('option', { name: '紅糖' }).click(); // 填寫數量 const firstQtyInput = firstRow.locator('input[type="number"]').first(); await firstQtyInput.clear(); await firstQtyInput.fill('5'); // 填寫小計(主要金額欄位) const firstSubtotalInput = firstRow.locator('input[type="number"]').nth(1); await firstSubtotalInput.fill('500'); // 8. 新增第二個品項(驗證批量寫入) await page.getByRole('button', { name: '新增一個品項' }).click(); const secondRow = page.locator('table tbody tr').nth(1); const secondProductCombobox = secondRow.getByRole('combobox').first(); await secondProductCombobox.click(); await page.getByRole('option', { name: '粗吸管' }).click(); const secondQtyInput = secondRow.locator('input[type="number"]').first(); await secondQtyInput.clear(); await secondQtyInput.fill('10'); const secondSubtotalInput = secondRow.locator('input[type="number"]').nth(1); await secondSubtotalInput.fill('200'); // 9. 點擊「確認發布採購單」 await page.getByRole('button', { name: '確認發布採購單' }).click(); // 10. 驗證結果 — 應跳轉回列表頁或顯示詳情頁 // Inertia.js 的 onSuccess 會觸發頁面導航 await expect( page.getByRole('heading', { name: '採購單管理' }).or(page.getByRole('heading', { name: /PO-/ })) ).toBeVisible({ timeout: 15000 }); // 11. 截圖留存 if (!fs.existsSync('e2e/screenshots')) fs.mkdirSync('e2e/screenshots', { recursive: true }); await page.screenshot({ path: 'e2e/screenshots/procurement-po-create-success.png', fullPage: true }); }); test('應能成功編輯採購單', async ({ page }) => { test.setTimeout(60000); // 1. 前往採購單列表 await page.goto('/purchase-orders'); await expect(page.getByRole('heading', { name: '採購單管理' })).toBeVisible(); // 2. 找到並點擊第一個可編輯的採購單 (草稿或待審核狀態) const editLink = page.locator('button[title="編輯"], a[title="編輯"]').first(); await expect(editLink).toBeVisible({ timeout: 10000 }); await editLink.click(); // 3. 驗證已進入編輯頁 await expect(page.getByRole('heading', { name: '編輯採購單' })).toBeVisible({ timeout: 15000 }); // 4. 修改備註 await page.getByPlaceholder('備註這筆採購單的特殊需求...').fill('E2E 自動化測試 - 已被編輯過'); // 5. 點擊「更新採購單」 await page.getByRole('button', { name: '更新採購單' }).click(); // 6. 驗證結果 — 返回列表或詳情頁 await expect( page.getByRole('heading', { name: '採購單管理' }).or(page.getByRole('heading', { name: /PO-/ })) ).toBeVisible({ timeout: 15000 }); // 7. 截圖留存 if (!fs.existsSync('e2e/screenshots')) fs.mkdirSync('e2e/screenshots', { recursive: true }); await page.screenshot({ path: 'e2e/screenshots/procurement-po-edit-success.png', fullPage: true }); }); });