test: remove duplicate Playwright e2e coverage (#13819)

* test: remove duplicate test coverage (safe dedup)

LE-1620 Tier 3 (phase A) - three duplicate-coverage removals, each verified to lose no unique assertion:

Delete outdated-message.spec.ts: its only assertion (the "components need updates" banner) is identical to the first assertion of outdated-actions.spec.ts.

output-modal-copy-button.spec.ts: drop the "JSON output from API Request component" sub-test (duplicated by copy-button-in-output.spec.ts); keep the text-output icon-state test.

general-bugs-move-flow-from-folder.spec.ts: drop the happy-path "move flow from folder" test (covered more thoroughly by folders.spec.ts change-flow-folder); keep the unique stale-cache regression test.

No coverage loss: each removed assertion has a home in a surviving test.

Refs LE-1620 (Tier 3 / phase A)

* test: fold minimize handle-visibility check into the 5-cycle test

minimize.spec.ts checked a single minimize/expand cycle and uniquely asserted that the node connection handles get the no-show class. general-bugs-minimize-state-error already runs 5 cycles but only checked hide-node-content; this migrates the no-show handle assertion into its toggle helper, then deletes minimize.spec.ts.

Refs LE-1620 (Tier 3 / C1)

* test: merge store share-button test into generalBugs-shard-13

store-shard-2 and generalBugs-shard-13 both exercised share-on-canvas but checked different things: store-shard-2 validated the share modal contents (labels, public checkbox, category tags, name/description inputs); generalBugs-shard-13 checked idempotency (re-share + replace). Move the modal-validation test into generalBugs-shard-13 (guard adapted to skipIfMissing.storeApiKey + loadDotenvIfLocal), preserving every modal assertion. store-shard-2 keeps its filter-by-tag test.

Refs LE-1620 (Tier 3 / M4)

* test: actually trigger share before asserting success toast

The store share test (moved here from store-shard-2 in this PR) asserted the "Flow shared successfully" toast without ever clicking the share confirm button, so it never exercised the real share. It went unnoticed because the test is STORE_API_KEY-gated and skipped in CI. Add the share-modal-button-flow click + replace handling after the modal validation, matching the existing share test.

Refs LE-1620 (Tier 3 / M4 review)

* test: assert modal checks instead of ignoring their booleans

The API-keys warning and the public checkbox in the moved store share test called .isVisible()/.isChecked() but ignored the returned booleans, so they asserted nothing. Make them real web-first assertions. Pre-existing from store-shard-2; flagged in review.

Refs LE-1620 (Tier 3 / M4 review)
This commit is contained in:
Tarcio
2026-06-25 10:28:28 -03:00
committed by GitHub
parent ce71c72868
commit ffd119cf3f
7 changed files with 96 additions and 314 deletions

View File

@ -1,9 +1,7 @@
import * as dotenv from "dotenv";
import path from "path";
import { expect, test } from "../../fixtures";
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { TEXTS } from "../../utils/constants/texts";
import { renameFlow } from "../../utils/rename-flow";
test(
"should filter by tag",
@ -69,87 +67,3 @@ test(
await expect(page.getByText(TEXTS.templateBasicRag)).toBeVisible();
},
);
test("should share component with share button", async ({ page }) => {
test.skip(
!process?.env?.STORE_API_KEY,
"STORE_API_KEY required to run this test",
);
if (!process.env.CI) {
dotenv.config({ path: path.resolve(__dirname, "../../.env") });
}
await page.goto("/");
await page.waitForTimeout(1000);
await page.getByTestId("button-store").click();
await page.waitForTimeout(1000);
await page.getByTestId("api-key-button-store").click({
timeout: 200000,
});
await page
.getByPlaceholder(TEXTS.placeholderApiKey)
.fill(process.env.STORE_API_KEY ?? "");
await page.getByTestId("api-key-save-button-store").click();
await page.waitForTimeout(1000);
await expect(page.getByText(TEXTS.toastApiKeySaved)).toBeVisible();
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
timeout: 100000,
});
await page.getByTestId("icon-ChevronLeft").first().click();
await awaitBootstrapTest(page, {
skipGoto: true,
});
await page.waitForTimeout(1000);
const randomName = Math.random().toString(36).substring(2);
await page.getByTestId("side_nav_options_all-templates").click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await renameFlow(page, { flowName: randomName });
await page.waitForSelector('[data-testid="shared-button-flow"]', {
timeout: 100000,
});
await page.getByTestId("shared-button-flow").first().click();
await expect(page.getByText("Name:")).toBeVisible();
await expect(page.getByText("Description:")).toBeVisible();
await expect(page.getByText("Set workflow status to public")).toBeVisible();
await page
.getByText(
"Attention: API keys in specified fields are automatically removed upon sharing.",
)
.isVisible();
await expect(page.getByText("Export").first()).toBeVisible();
await expect(page.getByText("Share Flow").first()).toBeVisible();
await page.waitForTimeout(3000);
await expect(page.getByText("Agent").first()).toBeVisible();
await expect(page.getByText("Memory").first()).toBeVisible();
await expect(page.getByText("Chain").first()).toBeVisible();
await expect(page.getByText("Vector Store").first()).toBeVisible();
await expect(page.getByText("Prompt").last()).toBeVisible();
await page.getByTestId("public-checkbox").isChecked();
const flowName = await page.getByTestId("input-flow-name").inputValue();
const flowDescription = await page
.getByPlaceholder("Flow description")
.inputValue();
await expect(page.getByText(flowName).last()).toBeVisible();
await expect(page.getByText(flowDescription).last()).toBeVisible();
await page.waitForTimeout(1000);
await expect(page.getByText("Flow shared successfully").last()).toBeVisible();
});

View File

@ -1,72 +0,0 @@
import { expect, test } from "../../fixtures";
import { addLegacyComponents } from "../../utils/add-legacy-components";
import { adjustScreenView } from "../../utils/adjust-screen-view";
import { TEXTS } from "../../utils/constants/texts";
import { openBlankFlow } from "../../utils/flow/open-blank-flow";
import { zoomOut } from "../../utils/zoom-out";
test(
"user must be able to minimize and expand a component",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
await openBlankFlow(page);
await addLegacyComponents(page);
await page.getByTestId("sidebar-search-input").click();
await page.getByTestId("sidebar-search-input").fill(TEXTS.searchTextInput);
await page.waitForSelector('[data-testid="input_outputText Input"]', {
timeout: 30000,
});
await page
.getByTestId("input_outputText Input")
.dragTo(page.locator('//*[@id="react-flow-id"]'));
await page
.locator('//*[@id="react-flow-id"]')
.hover()
.then(async () => {
await page.mouse.down();
await page.mouse.move(-800, 300);
});
await page.mouse.up();
await adjustScreenView(page);
await zoomOut(page, 4);
await page.getByTestId("more-options-modal").click();
await page.waitForSelector('[data-testid="minimize-button-modal"]', {
timeout: 10000,
});
await page.getByTestId("minimize-button-modal").first().click();
await expect(
page.locator(".react-flow__handle-left.no-show").first(),
).toBeVisible({ timeout: 10000 });
await expect(
page.locator(".react-flow__handle-right.no-show").first(),
).toBeVisible({ timeout: 10000 });
await page.getByTestId("more-options-modal").click();
await page.waitForSelector('[data-testid="expand-button-modal"]', {
timeout: 10000,
});
await page.getByTestId("expand-button-modal").first().click();
await expect(page.locator(".react-flow__handle-left").first()).toBeVisible({
timeout: 10000,
});
await expect(page.locator(".react-flow__handle-right").first()).toBeVisible(
{ timeout: 10000 },
);
},
);

View File

@ -1,46 +0,0 @@
import { readFileSync } from "fs";
import { expect, test } from "../../fixtures";
import { openFlowsList } from "../../utils/flow/open-flows-list";
test("user must be able outdated message on error", async ({ page }) => {
const dropTarget = await openFlowsList(page);
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
// Read the asset and rename the flow uniquely so we can wait for THIS
// upload to appear in the list — avoids racing against the bootstrap-seeded
// "Basic Prompting" card or stale "Memory Chatbot" entries from sibling tests.
const rawJson = readFileSync("tests/assets/outdated_flow.json", "utf-8");
const flowName = `Outdated Test Flow ${Date.now()}`;
const jsonContent = JSON.stringify({
...JSON.parse(rawJson),
name: flowName,
});
// Create the DataTransfer and File
const dataTransfer = await page.evaluateHandle((data) => {
const dt = new DataTransfer();
// Convert the buffer to a hex array
const file = new File([data], "outdated_flow.json", {
type: "application/json",
});
dt.items.add(file);
return dt;
}, jsonContent);
// Now dispatch
await dropTarget.dispatchEvent("drop", {
dataTransfer,
});
// Wait for the freshly-dropped flow card (by unique name) to appear, then click it.
const droppedCard = page
.getByTestId("list-card")
.filter({ hasText: flowName });
await droppedCard.waitFor({ state: "visible", timeout: 30000 });
await droppedCard.click();
// Verify the outdated components banner appears on the canvas.
await expect(page.getByText(/\d+ components? needs? updates?/)).toBeVisible({
timeout: 30000,
});
});

View File

@ -78,61 +78,4 @@ test.describe("Output Modal Copy Button", () => {
).toBeVisible();
},
);
test(
"copy button should work with JSON output from API Request component",
{ tag: ["@release", "@workspace"] },
async ({ page }) => {
await openBlankFlow(page);
await page.waitForSelector('[data-testid="disclosure-data sources"]', {
timeout: 3000,
state: "visible",
});
await page.getByTestId("disclosure-data sources").click();
await page
.getByTestId("data_sourceAPI Request")
.hover()
.then(async () => {
await page.getByTestId("add-component-button-api-request").click();
await page.waitForTimeout(500);
await page
.getByTestId("popover-anchor-input-url_input")
.first()
.fill("https://httpbin.org/json");
});
await page.getByTestId("button_run_api request").click();
await page.waitForSelector("text=Running", {
timeout: 30000,
state: "visible",
});
await page.waitForSelector(`text=${TEXTS.toastBuiltSuccessfully}`, {
timeout: 30000,
});
await page
.getByTestId("output-inspection-api response-apirequest")
.click();
await page.waitForSelector("text=Component Output", { timeout: 30000 });
// Verify the copy button exists and click it
const copyButton = page.getByTestId("copy-output-button");
await expect(copyButton).toBeVisible();
await copyButton.click();
// Verify the success message appears
await page.waitForSelector("text=Copied to clipboard", {
timeout: 5000,
});
},
);
});

View File

@ -13,6 +13,15 @@ async function toggleNodeState(page: Page, action: "minimize" | "expand") {
expect(await page.getByTestId("hide-node-content").count()).toBe(
expectedCount,
);
// Minimizing also hides the node's connection handles via the `.no-show`
// class; expanding removes it. (Migrated from the former minimize.spec.ts.)
if (action === "minimize") {
await expect(page.locator(".react-flow__handle.no-show")).not.toHaveCount(
0,
);
} else {
await expect(page.locator(".react-flow__handle.no-show")).toHaveCount(0);
}
}
test(

View File

@ -3,59 +3,6 @@ import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
import { TEXTS } from "../../utils/constants/texts";
import { renameFlow } from "../../utils/rename-flow";
test("user must be able to move flow from folder", async ({ page }) => {
/* This is the original, happy-path regression: the destination
* project is BRAND NEW and has never been observed by React Query,
* so its `useGetFolder` cache doesn't exist and the first visit
* after the drop triggers a fresh fetch. It exercises drag-and-drop
* plumbing but does not exercise stale cache invalidation. */
const randomName = Math.random().toString(36).substring(2, 15);
await awaitBootstrapTest(page);
await page.getByTestId("side_nav_options_all-templates").click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await page.waitForTimeout(2000);
await renameFlow(page, { flowName: randomName });
await page.waitForTimeout(2000);
await page.getByTestId("icon-ChevronLeft").click();
await page.waitForSelector('[data-testid="add-project-button"]', {
timeout: 3000,
});
await page.getByTestId("add-project-button").click();
//wait for the project to be created and changed to the new project
await page.waitForTimeout(2000);
await page.getByTestId("sidebar-nav-Starter Project").click();
await page.waitForTimeout(2000);
await page.getByText(randomName).hover();
await page
.getByTestId("list-card")
.first()
.dragTo(page.locator('//*[@id="sidebar-nav-New Project"]'));
//wait for the drag and drop to be completed
await page.waitForTimeout(2000);
await page.getByTestId("sidebar-nav-New Project").click();
await page.waitForSelector('[data-testid="list-card"]');
const flowNameCount = await page.getByText(randomName).count();
expect(flowNameCount).toBeGreaterThan(0);
});
test("moved flow must appear when destination project was visited while still empty", async ({
page,
}) => {

View File

@ -4,6 +4,7 @@ import { TEXTS } from "../../utils/constants/texts";
import { loadDotenvIfLocal } from "../../utils/env/load-dotenv";
import { skipIfMissing } from "../../utils/env/skip-if-missing";
import { openTemplatesModal } from "../../utils/flow/new-project-flow";
import { renameFlow } from "../../utils/rename-flow";
test(
"should be able to share a component on the store by clicking on the share button on the canvas (requires store API key)",
@ -101,3 +102,89 @@ test(
});
},
);
test("should share component with share button", async ({ page }) => {
skipIfMissing.storeApiKey();
loadDotenvIfLocal(__dirname);
await page.goto("/");
await page.waitForTimeout(1000);
await page.getByTestId("button-store").click();
await page.waitForTimeout(1000);
await page.getByTestId("api-key-button-store").click({
timeout: 200000,
});
await page
.getByPlaceholder(TEXTS.placeholderApiKey)
.fill(process.env.STORE_API_KEY ?? "");
await page.getByTestId("api-key-save-button-store").click();
await page.waitForTimeout(1000);
await expect(page.getByText(TEXTS.toastApiKeySaved)).toBeVisible();
await page.waitForSelector('[data-testid="sidebar-search-input"]', {
timeout: 100000,
});
await page.getByTestId("icon-ChevronLeft").first().click();
await awaitBootstrapTest(page, {
skipGoto: true,
});
await page.waitForTimeout(1000);
const randomName = Math.random().toString(36).substring(2);
await page.getByTestId("side_nav_options_all-templates").click();
await page
.getByRole("heading", { name: TEXTS.templateBasicPrompting })
.click();
await renameFlow(page, { flowName: randomName });
await page.waitForSelector('[data-testid="shared-button-flow"]', {
timeout: 100000,
});
await page.getByTestId("shared-button-flow").first().click();
await expect(page.getByText("Name:")).toBeVisible();
await expect(page.getByText("Description:")).toBeVisible();
await expect(page.getByText("Set workflow status to public")).toBeVisible();
await expect(
page.getByText(
"Attention: API keys in specified fields are automatically removed upon sharing.",
),
).toBeVisible();
await expect(page.getByText("Export").first()).toBeVisible();
await expect(page.getByText("Share Flow").first()).toBeVisible();
await page.waitForTimeout(3000);
await expect(page.getByText("Agent").first()).toBeVisible();
await expect(page.getByText("Memory").first()).toBeVisible();
await expect(page.getByText("Chain").first()).toBeVisible();
await expect(page.getByText("Vector Store").first()).toBeVisible();
await expect(page.getByText("Prompt").last()).toBeVisible();
await expect(page.getByTestId("public-checkbox")).toBeVisible();
const flowName = await page.getByTestId("input-flow-name").inputValue();
const flowDescription = await page
.getByPlaceholder("Flow description")
.inputValue();
await expect(page.getByText(flowName).last()).toBeVisible();
await expect(page.getByText(flowDescription).last()).toBeVisible();
await page.waitForTimeout(1000);
// Trigger the actual share before asserting the success toast; re-sharing an
// already-published flow surfaces a replace confirmation.
await page.getByTestId("share-modal-button-flow").click();
const replace = await page.getByTestId("replace-button").isVisible();
if (replace) {
await page.getByTestId("replace-button").click();
}
await expect(page.getByText("Flow shared successfully").last()).toBeVisible();
});