143 lines
5.2 KiB
TypeScript
143 lines
5.2 KiB
TypeScript
import { activityProposals, expect, test } from "../activity-fixtures";
|
|
import { ActivitiesPage } from "../models/ActivitiesPage";
|
|
|
|
test("shares published chores and reply activities with Home, Messages and MCP without losing user changes", async ({
|
|
page,
|
|
context,
|
|
homePage,
|
|
workspacePage,
|
|
messagesPage,
|
|
activitiesPage,
|
|
activityMcp,
|
|
}) => {
|
|
await homePage.goTo();
|
|
await expect(activitiesPage.card("buy-detergent")).toHaveCount(0);
|
|
expect((await activityMcp.context()).activities).toEqual([]);
|
|
await activityMcp.publish();
|
|
await expect(activitiesPage.card("buy-detergent")).toBeVisible();
|
|
await expect(activitiesPage.card("review-times")).toBeVisible();
|
|
await expect(activitiesPage.card("send-documents")).toBeVisible();
|
|
await activitiesPage.complete("Buy detergent").click();
|
|
await expect(activitiesPage.card("buy-detergent")).toHaveAttribute("data-status", "done");
|
|
await activitiesPage.openReply("review-times");
|
|
await expect(page).toHaveURL(/\/support\?.*activity=review-times/);
|
|
await expect(activitiesPage.details).toContainText(
|
|
"Read the review materials and choose two times",
|
|
);
|
|
await expect(activitiesPage.sources).toContainText(
|
|
"Please review the materials and suggest two times.",
|
|
);
|
|
await expect(activitiesPage.sources).toContainText("not the full conversation history");
|
|
await expect(activitiesPage.action("Send the completed documents")).toBeVisible();
|
|
const edited = "I checked the materials. Thursday at 14:00 works for me.";
|
|
await activitiesPage.saveReply(edited);
|
|
await expect
|
|
.poll(
|
|
async () =>
|
|
(await activityMcp.context()).activities.find((item) => item.id === "review-times")
|
|
?.userOverrides.draft,
|
|
)
|
|
.toBe(edited);
|
|
await activitiesPage.later.click();
|
|
await expect
|
|
.poll(
|
|
async () =>
|
|
(await activityMcp.context()).activities.find((item) => item.id === "review-times")
|
|
?.userState,
|
|
)
|
|
.toBe("deferred");
|
|
const before = await activityMcp.context();
|
|
expect(before.activities.find((item) => item.id === "send-documents")?.userState).toBe(
|
|
"available",
|
|
);
|
|
const refreshed = activityProposals.map((proposal) =>
|
|
proposal.kind === "reply"
|
|
? { ...proposal, reply: { ...proposal.reply, draft: "A newer agent suggestion" } }
|
|
: proposal,
|
|
);
|
|
await activityMcp.publish(refreshed);
|
|
const after = await activityMcp.context();
|
|
expect(after.activities).toHaveLength(3);
|
|
expect(after.activities.find((item) => item.id === "review-times")).toMatchObject({
|
|
userState: "deferred",
|
|
userOverrides: { draft: edited },
|
|
effective: { reply: { draft: edited } },
|
|
proposal: { reply: { draft: "A newer agent suggestion" } },
|
|
});
|
|
expect(after.activities.find((item) => item.id === "buy-detergent")?.userState).toBe("completed");
|
|
await page.reload();
|
|
await expect(activitiesPage.draft).toHaveValue(edited);
|
|
await expect(
|
|
activitiesPage.details.getByRole("button", {
|
|
name: /^(Send|Mark complete|Try sending)$/,
|
|
exact: true,
|
|
}),
|
|
).toHaveCount(0);
|
|
await activitiesPage.action("Send the completed documents").click();
|
|
await expect(activitiesPage.draft).toHaveValue("A newer agent suggestion");
|
|
await messagesPage.showConversationList();
|
|
await workspacePage.navigateTo("Home");
|
|
await expect(activitiesPage.card("review-times")).toContainText("Set aside");
|
|
const device = await context.newPage();
|
|
try {
|
|
const other = new ActivitiesPage(device);
|
|
await device.goto("/");
|
|
await expect(other.card("buy-detergent")).toHaveAttribute("data-status", "done");
|
|
await activityMcp.publish([
|
|
{
|
|
id: "later-chore",
|
|
kind: "task",
|
|
title: "Take the recycling out",
|
|
nextAction: "Bring the paper bag downstairs",
|
|
sources: [],
|
|
},
|
|
]);
|
|
await expect(activitiesPage.card("later-chore")).toBeVisible();
|
|
await expect(other.card("later-chore")).toBeVisible();
|
|
await device.reload();
|
|
await expect(other.card("later-chore")).toBeVisible();
|
|
} finally {
|
|
await device.close();
|
|
}
|
|
});
|
|
|
|
test("retries publication without duplicates and rejects invalid batches without partial writes", async ({
|
|
activityMcp,
|
|
}) => {
|
|
const input = {
|
|
requestId: "stable-publication",
|
|
expectedRevision: 0,
|
|
activities: activityProposals,
|
|
};
|
|
const receipt = await activityMcp.tool("publish_activities", input);
|
|
expect(receipt.ok).toBe(true);
|
|
expect(await activityMcp.tool("publish_activities", input)).toEqual(receipt);
|
|
expect(
|
|
await activityMcp.tool("publish_activities", { ...input, requestId: "stale" }),
|
|
).toMatchObject({ ok: false, error: { code: "revision-conflict" } });
|
|
const snapshot = await activityMcp.context();
|
|
expect(
|
|
await activityMcp.tool("publish_activities", {
|
|
requestId: "invalid",
|
|
expectedRevision: snapshot.revision,
|
|
activities: [
|
|
{
|
|
id: "valid-sibling",
|
|
kind: "task",
|
|
title: "Valid sibling",
|
|
nextAction: "Do the chore",
|
|
sources: [],
|
|
},
|
|
{
|
|
id: "invalid-sibling",
|
|
kind: "reply",
|
|
title: "Missing reply target",
|
|
nextAction: "Check",
|
|
sources: [],
|
|
},
|
|
],
|
|
}),
|
|
).toMatchObject({ ok: false, error: { code: "invalid-input" } });
|
|
expect(await activityMcp.context()).toEqual(snapshot);
|
|
});
|