summaryrefslogtreecommitdiff
path: root/qa
diff options
context:
space:
mode:
Diffstat (limited to 'qa')
-rw-r--r--qa/driver/browser-test.js63
1 files changed, 62 insertions, 1 deletions
diff --git a/qa/driver/browser-test.js b/qa/driver/browser-test.js
index 677ae54..acc576d 100644
--- a/qa/driver/browser-test.js
+++ b/qa/driver/browser-test.js
@@ -13,6 +13,8 @@ const confirmPosts = [];
const confirmPostStatuses = [];
const redemptionPosts = [];
const redemptionConfirmResponses = [];
+const capitalPosts = [];
+const capitalPostStatuses = [];
let positionGets = 0;
let lastPositionsResponse = null;
let fundPostResponses = 0;
@@ -109,6 +111,7 @@ async function summaryLabelExists(page, label, name) {
} catch {}
}
if (r.url().includes("/orders") && r.request().method() === "POST") orderPostStatuses.push(r.status());
+ if (r.url().includes("/capital") && r.request().method() === "POST") capitalPostStatuses.push(r.status());
if (r.url().includes("/confirm") && r.url().includes("/redemptions") && r.request().method() === "POST") {
try {
lastRedemptionConfirmResponse = await r.json();
@@ -126,7 +129,7 @@ async function summaryLabelExists(page, label, name) {
lastPositionsResponse = await r.json();
} catch {}
}
- if (r.status() >= 400 && !/\/api\/instruments\//.test(r.url()) && !/\/orders/.test(r.url()) && !/\/redemptions/.test(r.url())) {
+ if (r.status() >= 400 && !/\/api\/instruments\//.test(r.url()) && !/\/orders/.test(r.url()) && !/\/redemptions/.test(r.url()) && !/\/capital/.test(r.url())) {
consoleErrors.push("resource " + r.status() + ": " + r.url());
}
});
@@ -143,6 +146,9 @@ async function summaryLabelExists(page, label, name) {
if (r.url().includes("/redemptions") && r.method() === "POST" && !r.url().includes("/confirm")) {
redemptionPosts.push({ url: r.url(), key: r.headers()["idempotency-key"] || "", body: r.postData() || "" });
}
+ if (r.url().includes("/capital") && r.method() === "POST") {
+ capitalPosts.push({ url: r.url(), key: r.headers()["idempotency-key"] || "", body: r.postData() || "" });
+ }
});
page.on("console", (m) => {
if (/^DBG /.test(m.text())) console.log("[dbg] " + m.text());
@@ -167,6 +173,7 @@ async function summaryLabelExists(page, label, name) {
await orderScenario(page);
await confirmScenario(page);
await redeemScenario(page);
+ await capitalScenario(page);
} finally {
check("G1 无浏览器控制台/页面错误", consoleErrors.length === 0, consoleErrors.slice(0, 3).join(" | "));
await browser.close();
@@ -280,6 +287,60 @@ async function redeemScenario(page) {
await page.screenshot({ path: SHOTS + "/13-redemption-settled.png" });
}
+
+async function capitalScenario(page) {
+ // J12-J17: 追加资金(接 K 场景:现金 4937.99,持仓 50.00000000/成本 59.51 不变)
+ const initialCash = "4937.99";
+ await page.waitForSelector(".capital-deposit-action", { timeout: 10000 });
+
+ await page.fill(".capital-amount-input", "0.00");
+ await page.click(".capital-deposit-action");
+ await expectText(page, ".error-banner", "追加金额必须是大于零的两位小数金额,例如 1000.00", "J12 非法金额(0.00)拒绝");
+ await page.fill(".capital-amount-input", "abc");
+ await page.click(".capital-deposit-action");
+ await expectText(page, ".error-banner", "追加金额必须是大于零的两位小数金额,例如 1000.00", "J13 非法金额(abc)拒绝");
+ check("J14 校验阶段无追加 POST", capitalPosts.length === 0, `posts=${capitalPosts.length}`);
+
+ await page.fill(".capital-amount-input", "500.00");
+ await page.click(".capital-deposit-action");
+ for (let i = 0; i < 100 && capitalPostStatuses.length === 0; i++) {
+ await page.waitForTimeout(100);
+ }
+ check("J15 追加 POST 恰好 1 次且幂等键 32 位十六进制", capitalPosts.length === 1 && /^[0-9a-f]{32}$/.test(capitalPosts[0].key), JSON.stringify(capitalPosts));
+ await page.waitForFunction(
+ (want) => {
+ const rows = Array.from(document.querySelectorAll(".fund-detail-row"));
+ return rows.some((r) => (r.querySelector(".fund-detail-label")?.textContent || "").trim() === "可用现金" && (r.querySelector(".fund-detail-value")?.textContent || "").trim() === want);
+ },
+ "5437.99",
+ { timeout: 15000 }
+ );
+ check("J16 追加后现金即时刷新 5437.99", true);
+
+ // 现金守恒:持仓份额与成本不变,现金差等于追加额
+ const summary = ((await page.textContent(".fund-summary")) || "");
+ const holdings = ((await page.textContent(".positions-panel")) || "");
+ check(
+ "J17 追加不计入已实现收益(份额与成本不变)",
+ holdings.includes("份额 50.00000000") && holdings.includes("成本 59.51") && summary.includes("5437.99"),
+ holdings.slice(0, 200)
+ );
+
+ // 幂等 ×1:同金额重试复用键 → replay 200,现金只入账一次
+ await page.click(".capital-deposit-action");
+ for (let i = 0; i < 100 && capitalPostStatuses.length < 2; i++) {
+ await page.waitForTimeout(100);
+ }
+ await page.waitForTimeout(500);
+ const summaryAfterRetry = ((await page.textContent(".fund-summary")) || "");
+ check(
+ "J18 同键重放只入账一次",
+ capitalPostStatuses.length === 2 && capitalPosts[0].key === capitalPosts[1].key && summaryAfterRetry.includes("5437.99"),
+ JSON.stringify({ statuses: capitalPostStatuses, keys: capitalPosts.map((p) => p.key), summary: summaryAfterRetry.slice(0, 160) })
+ );
+ await page.screenshot({ path: SHOTS + "/14-capital-deposit.png" });
+}
+
const failed = results.filter((r) => !r.ok);
console.log(`\n==== ${results.length - failed.length}/${results.length} passed ====`);
process.exit(failed.length > 0 ? 1 : 0);