tool-mutation.spec.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import { expect, test } from "@playwright/test"
  2. import {
  3. defineVisualRegions,
  4. reportVisualStability,
  5. startVisualProbe,
  6. stopVisualProbe,
  7. visualPlan,
  8. } from "../../utils/visual-stability"
  9. import {
  10. assistantMessage,
  11. partUpdated,
  12. session,
  13. sessionID,
  14. setupTimeline,
  15. textPart,
  16. toolPart,
  17. userMessage,
  18. } from "./fixture"
  19. test("adds a task child-session link without replacing the task row", async ({ page }, testInfo) => {
  20. const taskID = "prt_task_link"
  21. const childID = "ses_task_child"
  22. const input = { description: "Inspect child", subagent_type: "explore" }
  23. const timeline = await setupTimeline(page, {
  24. messages: [userMessage(), assistantMessage([toolPart(taskID, "task", "running", input)], { completed: false })],
  25. sessions: [session(), session({ id: childID, parentID: sessionID, title: "Inspect child" })],
  26. cpuRate: 4,
  27. })
  28. const regions = defineVisualRegions({
  29. task: { selector: `[data-timeline-part-id="${taskID}"] [data-slot="collapsible-trigger"]` },
  30. })
  31. await startVisualProbe(page, regions)
  32. await timeline.send(
  33. partUpdated(toolPart(taskID, "task", "completed", input, { metadata: { sessionId: childID } })),
  34. 500,
  35. )
  36. const trace = await stopVisualProbe<keyof typeof regions>(page)
  37. await reportVisualStability(
  38. testInfo,
  39. "task-link",
  40. trace,
  41. visualPlan(regions, [
  42. { type: "required", regions: ["task"] },
  43. { type: "unique", regions: ["task"] },
  44. { type: "stable", regions: ["task"] },
  45. { type: "opacity", regions: "all" },
  46. { type: "continuity", regions: "all" },
  47. { type: "motion", regions: "all", maxPositionReversals: 0 },
  48. { type: "label-stability", regions: "all" },
  49. ]),
  50. )
  51. await expect(
  52. page.locator(`a[href$="/session/${childID}"]`, { has: page.locator('[data-component="task-tool-card"]') }),
  53. ).toBeVisible()
  54. })
  55. test("changes generic tool arguments without replacing the row", async ({ page }, testInfo) => {
  56. const toolID = "prt_generic_mutation"
  57. const followingID = "prt_generic_mutation_following"
  58. const timeline = await setupTimeline(page, {
  59. messages: [
  60. userMessage(),
  61. assistantMessage(
  62. [
  63. toolPart(toolID, "mcp_probe", "running", { target: "one", count: 1 }),
  64. textPart(followingID, "Following generic tool"),
  65. ],
  66. { completed: false },
  67. ),
  68. ],
  69. cpuRate: 4,
  70. })
  71. const regions = defineVisualRegions({
  72. tool: { selector: `[data-timeline-part-id="${toolID}"]`, closest: '[data-timeline-row="AssistantPart"]' },
  73. following: { selector: `[data-timeline-part-id="${followingID}"]`, closest: '[data-timeline-row="AssistantPart"]' },
  74. })
  75. await startVisualProbe(page, regions)
  76. await timeline.send(
  77. partUpdated(toolPart(toolID, "mcp_probe", "running", { target: "two", count: 2, mode: "deep" })),
  78. 200,
  79. )
  80. await timeline.send(
  81. partUpdated(toolPart(toolID, "mcp_probe", "completed", { target: "two", count: 2, mode: "deep" })),
  82. 400,
  83. )
  84. const trace = await stopVisualProbe<keyof typeof regions>(page)
  85. await reportVisualStability(
  86. testInfo,
  87. "generic-mutation",
  88. trace,
  89. visualPlan(
  90. regions,
  91. [
  92. { type: "required", regions: ["tool", "following"] },
  93. { type: "unique", regions: ["tool", "following"] },
  94. { type: "stable", regions: ["tool", "following"] },
  95. { type: "opacity", regions: "all" },
  96. { type: "continuity", regions: "all" },
  97. { type: "motion", regions: "all", maxPositionReversals: 0 },
  98. { type: "label-stability", regions: "all" },
  99. { type: "flow", regions: ["tool", "following"] },
  100. ],
  101. { perMarker: true },
  102. ),
  103. )
  104. })