|
1 | 1 | import { expect, test } from "../../../../fixtures/fixtures"; |
2 | 2 | import { awaitBootstrapTest } from "../../../../helpers/other/await-bootstrap-test"; |
3 | 3 |
|
4 | | -test.describe("Output Modal Copy Button", () => { |
5 | | - test( |
6 | | - "user should be able to copy text output from component output modal", |
7 | | - { tag: ["@release", "@workspace", "@playground"] }, |
8 | | - async ({ page }) => { |
9 | | - await awaitBootstrapTest(page); |
10 | | - |
11 | | - await page.getByTestId("blank-flow").click(); |
12 | | - |
13 | | - await page.waitForSelector('[data-testid="sidebar-search-input"]', { |
14 | | - timeout: 30000, |
15 | | - state: "visible", |
16 | | - }); |
17 | | - |
18 | | - // Add a Text Input component |
19 | | - await page.getByTestId("sidebar-search-input").click(); |
20 | | - await page.getByTestId("sidebar-search-input").fill("text input"); |
21 | | - |
22 | | - await page.waitForSelector('[data-testid="input_outputText Input"]', { |
23 | | - timeout: 10000, |
24 | | - state: "visible", |
25 | | - }); |
26 | | - |
27 | | - await page |
28 | | - .getByTestId("input_outputText Input") |
29 | | - .hover() |
30 | | - .then(async () => { |
31 | | - await page.getByTestId("add-component-button-text-input").click(); |
32 | | - }); |
33 | | - |
34 | | - await page.waitForTimeout(500); |
35 | | - |
36 | | - // Fill in some test text |
37 | | - await page |
38 | | - .getByTestId("textarea_str_input_value") |
39 | | - .fill("Test content to copy"); |
40 | | - |
41 | | - // Run the component |
42 | | - await page.getByTestId("button_run_text input").click(); |
43 | | - |
44 | | - await page.waitForSelector("text=built successfully", { timeout: 30000 }); |
45 | | - |
46 | | - // Open the output modal |
47 | | - await page.locator('[data-testid^="output-inspection-"]').first().click(); |
48 | | - |
49 | | - await page.waitForSelector("text=Component Output", { timeout: 30000 }); |
50 | | - |
51 | | - // Verify the copy button exists |
52 | | - const copyButton = page.getByTestId("copy-output-button"); |
53 | | - await expect(copyButton).toBeVisible(); |
54 | | - |
55 | | - // Click the copy button |
56 | | - await copyButton.click(); |
57 | | - |
58 | | - // Verify the success message appears |
59 | | - await page.waitForSelector("text=Copied to clipboard", { |
60 | | - timeout: 5000, |
61 | | - }); |
62 | | - |
63 | | - // Verify the check icon appears (button changes state) |
64 | | - await expect( |
65 | | - copyButton.locator('[data-testid="icon-Check"]'), |
66 | | - ).toBeVisible(); |
67 | | - |
68 | | - // Wait for the icon to revert back to copy icon |
69 | | - await page.waitForTimeout(2500); |
70 | | - await expect( |
71 | | - copyButton.locator('[data-testid="icon-Copy"]'), |
72 | | - ).toBeVisible(); |
73 | | - }, |
74 | | - ); |
| 4 | +test.describe("Output Modal — Copy Button", () => { |
| 5 | + test.describe.configure({ mode: "serial" }); |
| 6 | + |
| 7 | + let createdFlowId: string | null = null; |
| 8 | + |
| 9 | + test.afterEach(async ({ page }) => { |
| 10 | + if (createdFlowId) { |
| 11 | + // Navigate to home before deleting to stop background browser requests |
| 12 | + // for the current flow; without this, pending polling GETs complete |
| 13 | + // after the DELETE and trigger spurious 404 fixture errors. |
| 14 | + await page.goto("/"); |
| 15 | + await page.request.delete(`/api/v1/flows/${createdFlowId}`); |
| 16 | + createdFlowId = null; |
| 17 | + } |
| 18 | + }); |
75 | 19 |
|
76 | 20 | test( |
77 | | - "copy button should work with JSON output from API Request component", |
78 | | - { tag: ["@release", "@workspace", "@playground"] }, |
| 21 | + "copy button copies Text Input output and toggles Check icon", |
| 22 | + { tag: ["@stable", "@release", "@workspace", "@playground"] }, |
79 | 23 | async ({ page }) => { |
80 | | - await awaitBootstrapTest(page); |
81 | | - |
82 | | - await page.getByTestId("blank-flow").click(); |
83 | | - |
84 | | - await page.waitForSelector('[data-testid="sidebar-search-input"]', { |
85 | | - timeout: 30000, |
86 | | - state: "visible", |
| 24 | + await test.step("create blank flow and capture flow id", async () => { |
| 25 | + await awaitBootstrapTest(page); |
| 26 | + |
| 27 | + const flowCreationPromise = page.waitForResponse( |
| 28 | + (resp) => |
| 29 | + resp.url().includes("/api/v1/flows") && |
| 30 | + resp.request().method() === "POST" && |
| 31 | + resp.status() === 201, |
| 32 | + { timeout: 15000 }, |
| 33 | + ); |
| 34 | + |
| 35 | + await page.getByTestId("blank-flow").click(); |
| 36 | + |
| 37 | + const creationResponse = await flowCreationPromise; |
| 38 | + const flowData = await creationResponse.json(); |
| 39 | + // Capture id before asserting format so afterEach can still clean up |
| 40 | + // if the regex assertion fails on an unexpected id shape. |
| 41 | + createdFlowId = flowData.id ?? null; |
| 42 | + expect(flowData.id, "flow creation response missing id").toMatch( |
| 43 | + /^[0-9a-f-]{36}$/, |
| 44 | + ); |
87 | 45 | }); |
88 | 46 |
|
89 | | - await page.getByTestId("sidebar-search-input").fill("api request"); |
| 47 | + await test.step("add Text Input and fill its value", async () => { |
| 48 | + await page.getByTestId("sidebar-search-input").fill("text input"); |
| 49 | + await page |
| 50 | + .getByTestId("input_outputText Input") |
| 51 | + .hover() |
| 52 | + .then(async () => { |
| 53 | + await page.getByTestId("add-component-button-text-input").click(); |
| 54 | + }); |
| 55 | + |
| 56 | + await expect(page.locator(".react-flow__node")).toHaveCount(1, { |
| 57 | + timeout: 10000, |
| 58 | + }); |
90 | 59 |
|
91 | | - await page.waitForSelector('[data-testid="data_sourceAPI Request"]', { |
92 | | - timeout: 10000, |
93 | | - state: "visible", |
| 60 | + await page |
| 61 | + .getByTestId("textarea_str_input_value") |
| 62 | + .fill("Test content to copy"); |
94 | 63 | }); |
95 | 64 |
|
96 | | - await page |
97 | | - .getByTestId("data_sourceAPI Request") |
98 | | - .hover() |
99 | | - .then(async () => { |
100 | | - await page.getByTestId("add-component-button-api-request").click(); |
101 | | - |
102 | | - await page.waitForTimeout(500); |
103 | | - |
104 | | - await page |
105 | | - .getByTestId("popover-anchor-input-url_input") |
106 | | - .first() |
107 | | - .fill("https://httpbin.org/json"); |
| 65 | + await test.step("run component and open output modal", async () => { |
| 66 | + await page.getByTestId("button_run_text input").click(); |
| 67 | + await expect(page.getByText("built successfully").last()).toBeVisible({ |
| 68 | + timeout: 30000, |
108 | 69 | }); |
109 | 70 |
|
110 | | - await page.getByTestId("button_run_api request").click(); |
111 | | - |
112 | | - await page.waitForSelector("text=Running", { |
113 | | - timeout: 30000, |
114 | | - state: "visible", |
| 71 | + await page |
| 72 | + .locator('[data-testid^="output-inspection-"]') |
| 73 | + .first() |
| 74 | + .click(); |
| 75 | + await expect(page.getByText("Component Output").first()).toBeVisible({ |
| 76 | + timeout: 30000, |
| 77 | + }); |
115 | 78 | }); |
116 | 79 |
|
117 | | - await page.waitForSelector("text=built successfully", { timeout: 30000 }); |
118 | | - |
119 | | - await page |
120 | | - .getByTestId("output-inspection-api response-apirequest") |
121 | | - .click(); |
| 80 | + await test.step("click copy and verify Check → Copy icon transition", async () => { |
| 81 | + const copyButton = page.getByTestId("copy-output-button"); |
| 82 | + await expect(copyButton).toBeVisible(); |
| 83 | + await copyButton.click(); |
122 | 84 |
|
123 | | - await page.waitForSelector("text=Component Output", { timeout: 30000 }); |
124 | | - |
125 | | - // Verify the copy button exists and click it |
126 | | - const copyButton = page.getByTestId("copy-output-button"); |
127 | | - await expect(copyButton).toBeVisible(); |
128 | | - |
129 | | - await copyButton.click(); |
130 | | - |
131 | | - // Verify the success message appears |
132 | | - await page.waitForSelector("text=Copied to clipboard", { |
133 | | - timeout: 5000, |
| 85 | + await expect(page.getByText("Copied to clipboard")).toBeVisible({ |
| 86 | + timeout: 5000, |
| 87 | + }); |
| 88 | + await expect( |
| 89 | + copyButton.locator('[data-testid="icon-Check"]'), |
| 90 | + ).toBeVisible(); |
| 91 | + |
| 92 | + // Icon reverts to Copy after the success state expires (~2s in UI). |
| 93 | + // Web-first assertion polls until the Copy icon reappears. |
| 94 | + await expect( |
| 95 | + copyButton.locator('[data-testid="icon-Copy"]'), |
| 96 | + ).toBeVisible({ timeout: 5000 }); |
134 | 97 | }); |
135 | 98 | }, |
136 | 99 | ); |
|
0 commit comments