Sentry Integration (補強 SignOz): - Add @sentry/nextjs for frontend error tracking + session replay - Add sentry-sdk[fastapi] for backend error tracking - Create sentry.client/server/edge.config.ts - Integrate with next.config.js + instrumentation.ts - Add Sentry exception capture in FastAPI error handler - Create deployment scripts for Self-Hosted @ 192.168.0.110 CI/CD Fixes: - Fix F821 Undefined name 'Field' in incidents.py - Add NEXT_PUBLIC_API_URL env var to CI build step - Add build-arg to Docker build verification E2E Test Improvements: - Fix strict mode violations in dashboard-acceptance tests - Add timeout increase for Phase 4 demo tests - Make tests more resilient to UI variations Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
110 lines
3.8 KiB
TypeScript
110 lines
3.8 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
||
|
||
/**
|
||
* Phase 4: Action Timeline 完整流程驗證
|
||
* =====================================
|
||
* 模擬: AI 提案 ➡️ DevOps 被拒絕 ➡️ CTO 成功批准
|
||
*/
|
||
|
||
test('Phase 4: Full Action Timeline Demo Flow', async ({ page }) => {
|
||
// 增加超時到 60 秒 (互動式 demo 需要更長時間)
|
||
test.setTimeout(60000)
|
||
// Set larger viewport
|
||
await page.setViewportSize({ width: 1920, height: 1200 });
|
||
|
||
// Navigate to demo page (使用相對路徑,baseURL 由 playwright.config.ts 控制)
|
||
await page.goto('/zh-TW/demo');
|
||
await page.waitForTimeout(4000);
|
||
|
||
// Screenshot 1: Initial state
|
||
await page.screenshot({ path: 'test-results/phase4-01-initial.png', fullPage: true });
|
||
|
||
// Step 1: Trigger a CRITICAL alert (AI Decision) - use Chinese text
|
||
console.log('Step 1: Triggering CRITICAL alert...');
|
||
// The button says "+ 嚴重" in Chinese
|
||
const criticalButton = page.locator('button:has-text("嚴重")').first();
|
||
await criticalButton.click({ timeout: 5000 });
|
||
await page.waitForTimeout(4000); // Wait for AI thinking animation
|
||
|
||
// Screenshot 2: After AI decision
|
||
await page.evaluate(() => window.scrollTo(0, 400));
|
||
await page.waitForTimeout(500);
|
||
await page.screenshot({ path: 'test-results/phase4-02-ai-decision.png' });
|
||
|
||
// Scroll to see approval cards
|
||
await page.evaluate(() => window.scrollTo(0, 500));
|
||
await page.waitForTimeout(1000);
|
||
|
||
// Screenshot 3: Approval cards visible
|
||
await page.screenshot({ path: 'test-results/phase4-03-approval-cards.png' });
|
||
|
||
// Step 2: Try to approve as DevOps (should be denied)
|
||
console.log('Step 2: DevOps attempting to sign CRITICAL...');
|
||
|
||
// Look for the hold-to-approve button
|
||
const approveButton = page.locator('button:has-text("長按批准")').first();
|
||
|
||
// Try to click and hold
|
||
if (await approveButton.isVisible()) {
|
||
const box = await approveButton.boundingBox();
|
||
if (box) {
|
||
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
|
||
await page.mouse.down();
|
||
await page.waitForTimeout(2500); // Hold for 2.5 seconds
|
||
await page.mouse.up();
|
||
}
|
||
}
|
||
|
||
await page.waitForTimeout(500);
|
||
|
||
// Screenshot 4: Access Denied modal (should be visible)
|
||
await page.screenshot({ path: 'test-results/phase4-04-access-denied.png' });
|
||
|
||
// Close the modal if visible
|
||
const closeButton = page.locator('button:has-text("了解,返回")');
|
||
if (await closeButton.isVisible()) {
|
||
await closeButton.click();
|
||
await page.waitForTimeout(500);
|
||
}
|
||
|
||
// Step 3: Switch to CTO role
|
||
console.log('Step 3: Switching to CTO role...');
|
||
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
||
await page.waitForTimeout(500);
|
||
|
||
const ctoButton = page.locator('button:has-text("CTO")');
|
||
await ctoButton.click();
|
||
await page.waitForTimeout(500);
|
||
|
||
// Screenshot 5: Role switched to CTO
|
||
await page.screenshot({ path: 'test-results/phase4-05-cto-role.png' });
|
||
|
||
// Scroll back to approval cards
|
||
await page.evaluate(() => window.scrollTo(0, 500));
|
||
await page.waitForTimeout(500);
|
||
|
||
// Step 4: CTO approves (should succeed)
|
||
console.log('Step 4: CTO signing CRITICAL...');
|
||
|
||
const approveButtonCTO = page.locator('button:has-text("長按批准")').first();
|
||
if (await approveButtonCTO.isVisible()) {
|
||
const box = await approveButtonCTO.boundingBox();
|
||
if (box) {
|
||
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
|
||
await page.mouse.down();
|
||
await page.waitForTimeout(2500);
|
||
await page.mouse.up();
|
||
}
|
||
}
|
||
|
||
await page.waitForTimeout(1000);
|
||
|
||
// Screenshot 6: After CTO signature
|
||
await page.screenshot({ path: 'test-results/phase4-06-cto-signed.png' });
|
||
|
||
// Final screenshot with full page showing timeline
|
||
await page.screenshot({ path: 'test-results/phase4-07-final.png', fullPage: true });
|
||
|
||
console.log('Phase 4 demo flow completed!');
|
||
});
|