Skip to content

Commit 4ba99bb

Browse files
committed
test(lark): cover blocked notice delivery and settings
Signed-off-by: jackie-cqz <2557911191@qq.com>
1 parent f1fe9d2 commit 4ba99bb

5 files changed

Lines changed: 470 additions & 2 deletions

File tree

‎examples/loopx-chat-server-smoke.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,23 @@ def main() -> None:
390390
assert code == 400, configure_invalid
391391
assert configure_invalid["error_code"] == "invalid_goal_channel_configure", configure_invalid
392392

393+
code, blocked_notice_missing = request_json(
394+
f"{base_url}/api/chat/goal-channel/configure",
395+
method="POST",
396+
body={"goal_id": GOAL_ID, "auto_notify_blocked_notices": False},
397+
)
398+
assert code == 400, blocked_notice_missing
399+
assert blocked_notice_missing["blocker"] == "channel_binding_missing", blocked_notice_missing
400+
401+
code, configure_conflict = request_json(
402+
f"{base_url}/api/chat/goal-channel/configure",
403+
method="POST",
404+
body={"goal_id": GOAL_ID, "auto_notify_human_gates": True,
405+
"auto_notify_blocked_notices": True},
406+
)
407+
assert code == 400, configure_conflict
408+
assert configure_conflict["error_code"] == "invalid_goal_channel_configure", configure_conflict
409+
393410
registry_before_subagent_preview = registry.read_text(encoding="utf-8")
394411
code, invalid_domain = request_json(
395412
f"{base_url}/api/chat/goal-subagents/dry-run",

‎examples/personal-workspace-browser-smoke.mjs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
} from "./personal-workspace-browser/fixture.mjs";
3131
import { navigationSortingScenario } from "./personal-workspace-browser/navigation-sorting.mjs";
3232
import { capabilityScopeScenario } from "./personal-workspace-browser/capability-scope.mjs";
33+
import { blockedNoticeSettingsScenario } from "./personal-workspace-browser/blocked-notice-settings.mjs";
3334
import { automationCadenceScenario } from "./personal-workspace-browser/automation-cadence.mjs";
3435
import { turnStepsScenario } from "./personal-workspace-browser/turn-steps.mjs";
3536
import { monitorReadbackScenario } from "./personal-workspace-browser/monitor-readback.mjs";
@@ -66,6 +67,7 @@ scenarioCatalog.push(workspaceViewRecoveryScenario);
6667
scenarioCatalog.push(monitorReadbackScenario);
6768
scenarioCatalog.push(turnStepsScenario);
6869
scenarioCatalog.push(goalWorkMapScenario);
70+
scenarioCatalog.push(blockedNoticeSettingsScenario);
6971
const requestedScenario = process.env.LOOPX_PERSONAL_WORKSPACE_SCENARIO;
7072
const scenarios = requestedScenario
7173
? scenarioCatalog.filter((scenario) => scenario.id === requestedScenario)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { resolve } from "node:path";
2+
import { outputDir } from "./fixture.mjs";
3+
import { openWorkspacePage } from "./scenario-context.mjs";
4+
5+
export const blockedNoticeSettingsScenario = {
6+
id: "blocked-notice-settings",
7+
async run({ browser, collectCoverage, url }) {
8+
const notificationProjection = {
9+
schema_version: "loopx_goal_channel_notification_projection_v0",
10+
goals: [{
11+
goal_id: "product-release", configured: true, enabled: true,
12+
human_gate_auto_notify_enabled: false, blocked_notice_auto_notify_enabled: false,
13+
receipt_count: 1,
14+
blocked_notice_delivery: { delivered_count: 1, unverified_count: 0, resolved_count: 0 },
15+
}],
16+
};
17+
const requests = [];
18+
const context = await openWorkspacePage(browser, url, {
19+
collectCoverage,
20+
apiOptions: { notificationProjection },
21+
beforeGoto: async (_api, page) => {
22+
await page.route("**/api/chat/goal-channel/configure", async (route) => {
23+
const body = route.request().postDataJSON();
24+
requests.push(body);
25+
notificationProjection.goals[0].blocked_notice_auto_notify_enabled = body.auto_notify_blocked_notices === true;
26+
await route.fulfill({ contentType: "application/json", json: {
27+
ok: true, status: "configured", public_summary: "updated", readback_verified: true,
28+
}, status: 200 });
29+
});
30+
},
31+
});
32+
const { page } = context;
33+
try {
34+
await page.getByRole("button", { name: "设置", exact: true }).click();
35+
await page.getByRole("button", { name: "能力中心", exact: true }).click();
36+
await page.getByRole("radio", { name: "单个 Goal", exact: true }).check();
37+
await page.getByRole("combobox", { name: "目标 Goal", exact: true }).selectOption("product-release");
38+
await page.getByRole("navigation", { name: "Goal 能力目录" })
39+
.getByRole("button", { name: /飞书事件收件箱/ }).click();
40+
const toggle = page.getByLabel("任务受阻时推送到此目标群");
41+
await toggle.waitFor();
42+
await page.getByText("受阻通知:已核验 1 条,未核验 0 条,已解除 0 条").waitFor();
43+
await page.screenshot({ path: resolve(outputDir, "blocked-notice-settings.png"), animations: "disabled" });
44+
await toggle.click();
45+
if (requests.length !== 1 || requests[0].goal_id !== "product-release"
46+
|| requests[0].auto_notify_blocked_notices !== true
47+
|| "auto_notify_human_gates" in requests[0]) {
48+
throw new Error(`Blocked notice toggle sent the wrong request: ${JSON.stringify(requests)}`);
49+
}
50+
await page.waitForFunction(() => {
51+
const input = [...document.querySelectorAll("input[type=checkbox]")]
52+
.find((item) => item.closest("label")?.textContent?.includes("任务受阻时推送到此目标群"));
53+
return input?.checked === true;
54+
});
55+
await page.setViewportSize({ width: 390, height: 844 });
56+
await page.screenshot({ path: resolve(outputDir, "blocked-notice-settings-mobile.png"), animations: "disabled" });
57+
if (await page.evaluate(() => document.documentElement.scrollWidth > innerWidth + 1)) {
58+
throw new Error("Blocked notice settings overflow the narrow viewport");
59+
}
60+
if (context.errors.length) throw new Error(context.errors.join(" | "));
61+
return { coverageEntries: await context.close(), note: "Goal Channel blocked notice opt-in, readback, and mobile layout verified." };
62+
} catch (error) { await context.close(); throw error; }
63+
},
64+
};

‎examples/personal-workspace-browser/fixture.mjs‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ server.serve_forever()
301301
server.stderr.on("data", (chunk) => { diagnostic = (diagnostic + chunk).slice(-8000); });
302302
server.stdout.on("data", (chunk) => {
303303
output += chunk;
304-
if (output.includes("loopx-packaged-smoke-ready\n")) {
304+
if (/loopx-packaged-smoke-ready\r?\n/u.test(output)) {
305305
clearTimeout(timer);
306306
resolveReady();
307307
}
@@ -391,7 +391,7 @@ function filterStatusFixtureToScope(fixture, matchesScope) {
391391
}
392392
}
393393

394-
export async function installApi(page, { goalSubagentConfigurationEnabled = true, initialActionProposals = [], managerChannelBinding = null, progressiveWorkspace = false, runtimeAgents = null } = {}) {
394+
export async function installApi(page, { goalSubagentConfigurationEnabled = true, initialActionProposals = [], managerChannelBinding = null, notificationProjection = null, progressiveWorkspace = false, runtimeAgents = null } = {}) {
395395
let turnCounter = 0;
396396
const runtime = page.__loopxRuntime ??= { actionProposals: new Map(), goalSubagentConfigurations: new Map(), larkConnections: [], messages: new Map(), sessions: new Map(), turnMessages: new Map() };
397397
const actionProposals = runtime.actionProposals;
@@ -506,6 +506,7 @@ export async function installApi(page, { goalSubagentConfigurationEnabled = true
506506
}
507507
if (progressiveWorkspace && requestedGoalId) state.goalStatusRequests.push(requestedGoalId);
508508
const fixture = structuredClone(require(resolve(repoRoot, "examples/status.example.json")));
509+
if (notificationProjection) fixture.goal_channel_notification_projection = structuredClone(notificationProjection);
509510
const defaultSubagentConfiguration = { mode: "default", spawn_allowed: false, max_children: 0, allowed_domains: [] };
510511
const projectedSubagentConfiguration = (goalId, fallback) => state.freezeGoalSubagentStatusProjection
511512
? fallback ?? defaultSubagentConfiguration

0 commit comments

Comments
 (0)