Skip to content

Commit 6150678

Browse files
Merge pull request #22 from SteveLin100132/test/e2e-generator-chart
test: Add end-to-end tests for Notion Chart Generator with status property aggregation
2 parents ce29455 + 232ae7e commit 6150678

File tree

1 file changed

+247
-0
lines changed

1 file changed

+247
-0
lines changed
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
import { test } from "@playwright/test";
2+
import dotenv from "dotenv";
3+
4+
dotenv.config();
5+
6+
// Playwright 測試設定
7+
test.use({
8+
trace: "on",
9+
screenshot: "on",
10+
video: {
11+
mode: "on",
12+
size: { width: 1920, height: 1080 },
13+
},
14+
permissions: ["clipboard-read", "clipboard-write"],
15+
});
16+
17+
/**
18+
* Notion Chart Generator E2E 測試
19+
* 測試透過 Notion Status 屬性進行聚合
20+
* 確保在選擇「合約」資料庫後,能夠正確載入資料並顯示相關圖表設定選項
21+
*/
22+
test("Notion Chart Generator - 透過 Notion Status 屬性進行聚合", async ({
23+
page,
24+
}) => {
25+
// 設定測試超時時間,這裡設定為 120 秒,確保有足夠時間
26+
test.setTimeout(120000);
27+
28+
// 設定視窗大小
29+
await page.setViewportSize({ width: 1920, height: 1080 });
30+
31+
// 前往 Notion Chart Generator 網站
32+
await page.goto(process.env.E2E_TEST_HOST || "http://localhost:3000/");
33+
34+
// 注入滑鼠軌跡顯示腳本
35+
await page.evaluate(() => {
36+
const style = document.createElement("style");
37+
style.innerHTML = `
38+
.__pw-mouse-pointer {
39+
pointer-events: none;
40+
position: fixed;
41+
z-index: 9999;
42+
width: 24px;
43+
height: 24px;
44+
margin-left: -12px;
45+
margin-top: -12px;
46+
border-radius: 12px;
47+
background: rgba(255,0,0,0.5);
48+
box-shadow: 0 0 8px 2px rgba(255,0,0,0.3);
49+
transition: left 0.05s linear, top 0.05s linear;
50+
}
51+
`;
52+
document.head.appendChild(style);
53+
const pointer = document.createElement("div");
54+
pointer.className = "__pw-mouse-pointer";
55+
document.body.appendChild(pointer);
56+
document.addEventListener(
57+
"mousemove",
58+
(event) => {
59+
pointer.style.left = event.clientX + "px";
60+
pointer.style.top = event.clientY + "px";
61+
},
62+
true
63+
);
64+
});
65+
66+
// 輸入 Notion API 金鑰
67+
const notionApiKeyInput = page.getByRole("textbox", {
68+
name: "secret_xxx 或 ntn_xxx",
69+
});
70+
const notionApiKeyInputBoundingBox = await notionApiKeyInput.boundingBox();
71+
if (notionApiKeyInputBoundingBox) {
72+
await page.mouse.move(
73+
(notionApiKeyInputBoundingBox?.x ?? 0) +
74+
notionApiKeyInputBoundingBox.width / 2,
75+
(notionApiKeyInputBoundingBox?.y ?? 0) +
76+
notionApiKeyInputBoundingBox.height / 2,
77+
{ steps: 20 }
78+
);
79+
await page.waitForTimeout(250);
80+
}
81+
await notionApiKeyInput.fill(process.env.E2E_TEST_NOTION_API_KEY || "");
82+
await page.waitForTimeout(750);
83+
84+
// 點擊「載入資料庫」按鈕
85+
const loadDatabaseButton = page.getByRole("button", { name: "載入資料庫" });
86+
const loadDatabaseButtonBoundingBox = await loadDatabaseButton.boundingBox();
87+
if (loadDatabaseButtonBoundingBox) {
88+
await page.mouse.move(
89+
(loadDatabaseButtonBoundingBox?.x ?? 0) +
90+
loadDatabaseButtonBoundingBox.width / 2,
91+
(loadDatabaseButtonBoundingBox?.y ?? 0) +
92+
loadDatabaseButtonBoundingBox.height / 2,
93+
{ steps: 20 }
94+
);
95+
await page.waitForTimeout(250);
96+
}
97+
await loadDatabaseButton.click();
98+
await page.waitForTimeout(750);
99+
100+
// 等待資料庫載入完成
101+
const databaseListComboBox = page.getByRole("combobox");
102+
await databaseListComboBox.waitFor({ state: "visible" });
103+
const databaseListComboBoxBoundingBox =
104+
await databaseListComboBox.boundingBox();
105+
if (databaseListComboBoxBoundingBox) {
106+
await page.mouse.move(
107+
(databaseListComboBoxBoundingBox?.x ?? 0) +
108+
databaseListComboBoxBoundingBox.width / 2,
109+
(databaseListComboBoxBoundingBox?.y ?? 0) +
110+
databaseListComboBoxBoundingBox.height / 2,
111+
{ steps: 20 }
112+
);
113+
await page.waitForTimeout(250);
114+
}
115+
await databaseListComboBox.click();
116+
117+
// 選擇「合約」資料庫
118+
const contractOption = page.getByRole("option", { name: "合約" });
119+
const contractOptionBoundingBox = await contractOption.boundingBox();
120+
if (contractOptionBoundingBox) {
121+
await page.mouse.move(
122+
(contractOptionBoundingBox?.x ?? 0) + contractOptionBoundingBox.width / 2,
123+
(contractOptionBoundingBox?.y ?? 0) +
124+
contractOptionBoundingBox.height / 2,
125+
{ steps: 20 }
126+
);
127+
await page.waitForTimeout(250);
128+
}
129+
await contractOption.click();
130+
await page.waitForTimeout(750);
131+
132+
// 選擇 X 軸屬性
133+
const xAxisCombobox = page
134+
.getByRole("combobox")
135+
.filter({ hasText: "選擇 X 軸屬性" });
136+
const xAxisComboboxBoundingBox = await xAxisCombobox.boundingBox();
137+
if (xAxisComboboxBoundingBox) {
138+
await page.mouse.move(
139+
(xAxisComboboxBoundingBox?.x ?? 0) + xAxisComboboxBoundingBox.width / 2,
140+
(xAxisComboboxBoundingBox?.y ?? 0) + xAxisComboboxBoundingBox.height / 2,
141+
{ steps: 20 }
142+
);
143+
await page.waitForTimeout(250);
144+
}
145+
await xAxisCombobox.click();
146+
147+
const xAxisOption = page.getByRole("option", {
148+
name: "合約處理狀態 (status)",
149+
});
150+
const xAxisOptionBoundingBox = await xAxisOption.boundingBox();
151+
if (xAxisOptionBoundingBox) {
152+
await page.mouse.move(
153+
(xAxisOptionBoundingBox?.x ?? 0) + xAxisOptionBoundingBox.width / 2,
154+
(xAxisOptionBoundingBox?.y ?? 0) + xAxisOptionBoundingBox.height / 2,
155+
{ steps: 20 }
156+
);
157+
await page.waitForTimeout(250);
158+
}
159+
await xAxisOption.click();
160+
await page.waitForTimeout(750);
161+
162+
// 選擇 Y 軸屬性
163+
const yAxisCombobox = page
164+
.getByRole("combobox")
165+
.filter({ hasText: "選擇 Y 軸屬性(可選)" });
166+
const yAxisComboboxBoundingBox = await yAxisCombobox.boundingBox();
167+
if (yAxisComboboxBoundingBox) {
168+
await page.mouse.move(
169+
(yAxisComboboxBoundingBox?.x ?? 0) + yAxisComboboxBoundingBox.width / 2,
170+
(yAxisComboboxBoundingBox?.y ?? 0) + yAxisComboboxBoundingBox.height / 2,
171+
{ steps: 20 }
172+
);
173+
await page.waitForTimeout(250);
174+
}
175+
await yAxisCombobox.click();
176+
177+
const yAxisOption = page.getByRole("option", {
178+
name: "合約金額 (含稅價) (number)",
179+
});
180+
const yAxisOptionBoundingBox = await yAxisOption.boundingBox();
181+
if (yAxisOptionBoundingBox) {
182+
await page.mouse.move(
183+
(yAxisOptionBoundingBox?.x ?? 0) + yAxisOptionBoundingBox.width / 2,
184+
(yAxisOptionBoundingBox?.y ?? 0) + yAxisOptionBoundingBox.height / 2,
185+
{ steps: 20 }
186+
);
187+
await page.waitForTimeout(250);
188+
}
189+
await yAxisOption.click();
190+
await page.waitForTimeout(750);
191+
192+
// 點擊「生成圖表」按鈕
193+
const generateChartButton = page.getByRole("button", { name: "生成圖表" });
194+
const generateChartButtonBoundingBox =
195+
await generateChartButton.boundingBox();
196+
if (generateChartButtonBoundingBox) {
197+
await page.mouse.move(
198+
(generateChartButtonBoundingBox?.x ?? 0) +
199+
generateChartButtonBoundingBox.width / 2,
200+
(generateChartButtonBoundingBox?.y ?? 0) +
201+
generateChartButtonBoundingBox.height / 2,
202+
{ steps: 20 }
203+
);
204+
await page.waitForTimeout(250);
205+
}
206+
await generateChartButton.click();
207+
208+
// 等待圖表生成完成
209+
const chartCanvas = page.locator("canvas");
210+
await chartCanvas.waitFor({ state: "visible" });
211+
await page.waitForTimeout(250);
212+
213+
// 確保圖表畫布可見並且有內容
214+
const chartCanvasBoundingBox = await chartCanvas.boundingBox();
215+
if (chartCanvasBoundingBox) {
216+
await page.mouse.move(chartCanvasBoundingBox.x, chartCanvasBoundingBox.y, {
217+
steps: 20,
218+
});
219+
await page.mouse.move(
220+
chartCanvasBoundingBox.x + chartCanvasBoundingBox.width,
221+
chartCanvasBoundingBox.y,
222+
{
223+
steps: 20,
224+
}
225+
);
226+
await page.mouse.move(
227+
chartCanvasBoundingBox.x + chartCanvasBoundingBox.width,
228+
chartCanvasBoundingBox.y + chartCanvasBoundingBox.height,
229+
{
230+
steps: 20,
231+
}
232+
);
233+
await page.mouse.move(
234+
chartCanvasBoundingBox.x,
235+
chartCanvasBoundingBox.y + chartCanvasBoundingBox.height,
236+
{
237+
steps: 20,
238+
}
239+
);
240+
await page.mouse.move(chartCanvasBoundingBox.x, chartCanvasBoundingBox.y, {
241+
steps: 20,
242+
});
243+
await page.waitForTimeout(250);
244+
}
245+
246+
await page.waitForTimeout(2000);
247+
});

0 commit comments

Comments
 (0)