diff --git a/.github/workflows/typescript_test.yml b/.github/workflows/typescript_test.yml index 8b91001ca..07f86602a 100644 --- a/.github/workflows/typescript_test.yml +++ b/.github/workflows/typescript_test.yml @@ -1,6 +1,12 @@ name: Run Frontend Tests on: + workflow_dispatch: + inputs: + branch: + description: "(Optional) Branch to checkout" + required: false + type: string pull_request: merge_group: @@ -19,14 +25,18 @@ jobs: strategy: fail-fast: false matrix: - shardIndex: [1, 2, 3, 4, 5, 6, 7, 8] - shardTotal: [8] + shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + shardTotal: [10] env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} STORE_API_KEY: ${{ secrets.STORE_API_KEY }} steps: - name: Checkout code uses: actions/checkout@v4 + with: + # If branch is passed as input, checkout that branch + # else checkout the default branch + ref: ${{ github.event.inputs.branch || github.ref }} - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/src/frontend/src/modals/IOModal/components/SessionView/index.tsx b/src/frontend/src/modals/IOModal/components/SessionView/index.tsx index 70174ee72..21b64cfb1 100644 --- a/src/frontend/src/modals/IOModal/components/SessionView/index.tsx +++ b/src/frontend/src/modals/IOModal/components/SessionView/index.tsx @@ -52,7 +52,6 @@ export default function SessionView({ rows }: { rows: Array }) { ]} overlayNoRowsTemplate="No data available" onSelectionChanged={(event: SelectionChangedEvent) => { - console.log(event.api.getSelectedRows()); setSelectedRows(event.api.getSelectedRows().map((row) => row.id)); }} rowSelection="multiple" diff --git a/src/frontend/src/pages/SettingsPage/pages/messagesPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/messagesPage/index.tsx index 3be8122de..b2e654cef 100644 --- a/src/frontend/src/pages/SettingsPage/pages/messagesPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/messagesPage/index.tsx @@ -67,9 +67,7 @@ export default function MessagesPage() { ]} overlayNoRowsTemplate="No data available" onSelectionChanged={(event: SelectionChangedEvent) => { - setSelectedRows( - event.api.getSelectedRows().map((row) => row.index), - ); + setSelectedRows(event.api.getSelectedRows().map((row) => row.id)); }} rowSelection="multiple" suppressRowClickSelection={true} diff --git a/src/frontend/tests/end-to-end/Basic Prompting.ts b/src/frontend/tests/end-to-end/Basic Prompting.spec.ts similarity index 92% rename from src/frontend/tests/end-to-end/Basic Prompting.ts rename to src/frontend/tests/end-to-end/Basic Prompting.spec.ts index 0fdb56e41..aff45928c 100644 --- a/src/frontend/tests/end-to-end/Basic Prompting.ts +++ b/src/frontend/tests/end-to-end/Basic Prompting.spec.ts @@ -1,5 +1,4 @@ import { expect, test } from "@playwright/test"; -import path from "path"; test("Basic Prompting (Hello, World)", async ({ page }) => { if (!process?.env?.OPENAI_API_KEY) { @@ -67,10 +66,18 @@ test("Basic Prompting (Hello, World)", async ({ page }) => { .getByTestId("input-chat-playground") .last() .fill("Say hello as a pirate"); - await page.getByTestId("icon-LucideSend").last().click(); - await page.waitForTimeout(3000); - await page.getByText("Ahoy").last().isVisible(); + await page.waitForSelector('[data-testid="icon-LucideSend"]', { + timeout: 100000, + }); + + await page.getByTestId("icon-LucideSend").last().click(); + + await page.waitForSelector("text=matey", { + timeout: 100000, + }); + + await page.getByText("matey").last().isVisible(); await page.getByText("Default Session").last().click(); await page.getByText("timestamp", { exact: true }).last().isVisible(); diff --git a/src/frontend/tests/end-to-end/chatInputOutputUser-shard-0.spec.ts b/src/frontend/tests/end-to-end/chatInputOutputUser-shard-0.spec.ts index 690a3b805..72c451c10 100644 --- a/src/frontend/tests/end-to-end/chatInputOutputUser-shard-0.spec.ts +++ b/src/frontend/tests/end-to-end/chatInputOutputUser-shard-0.spec.ts @@ -80,6 +80,10 @@ test("user must be able to send an image on chat", async ({ page }) => { { fileContent }, ); + await page.waitForSelector('[data-testid="input-chat-playground"]', { + timeout: 100000, + }); + // Locate the target element const element = await page.getByTestId("input-chat-playground"); diff --git a/src/frontend/tests/end-to-end/generalBugs.spec.ts b/src/frontend/tests/end-to-end/generalBugs-shard-0.spec.ts similarity index 100% rename from src/frontend/tests/end-to-end/generalBugs.spec.ts rename to src/frontend/tests/end-to-end/generalBugs-shard-0.spec.ts diff --git a/src/frontend/tests/end-to-end/generalBugs-shard-1.spec.ts b/src/frontend/tests/end-to-end/generalBugs-shard-1.spec.ts new file mode 100644 index 000000000..bd4c72e28 --- /dev/null +++ b/src/frontend/tests/end-to-end/generalBugs-shard-1.spec.ts @@ -0,0 +1,89 @@ +import { expect, test } from "@playwright/test"; +import * as dotenv from "dotenv"; +import path from "path"; + +test("should delete rows from table message", async ({ page }) => { + if (!process?.env?.OPENAI_API_KEY) { + //You must set the OPENAI_API_KEY on .env file to run this test + expect(false).toBe(true); + } + + await page.goto("/"); + await page.waitForTimeout(2000); + + let modalCount = 0; + try { + const modalTitleElement = await page?.getByTestId("modal-title"); + if (modalTitleElement) { + modalCount = await modalTitleElement.count(); + } + } catch (error) { + modalCount = 0; + } + + while (modalCount === 0) { + await page.getByText("New Project", { exact: true }).click(); + await page.waitForTimeout(5000); + modalCount = await page.getByTestId("modal-title")?.count(); + } + + await page.getByRole("heading", { name: "Basic Prompting" }).click(); + + await page.waitForSelector('[title="fit view"]', { + timeout: 100000, + }); + + await page.getByTitle("fit view").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + + await page + .getByTestId("popover-anchor-input-openai_api_key") + .fill(process.env.OPENAI_API_KEY ?? ""); + + await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("gpt-4o-0-option").click(); + + await page.waitForTimeout(2000); + + await page.getByTestId("button_run_chat output").click(); + await page.waitForSelector("text=built successfully", { timeout: 30000 }); + + await page.getByText("built successfully").last().click({ + timeout: 15000, + }); + + await page.getByText("Playground", { exact: true }).click(); + await page + .getByText("No input message provided.", { exact: true }) + .last() + .isVisible(); + + await page.waitForSelector('[data-testid="input-chat-playground"]', { + timeout: 100000, + }); + + await page + .getByTestId("input-chat-playground") + .last() + .fill("Say hello as a pirate"); + await page.getByTestId("icon-LucideSend").last().click(); + + await page.waitForSelector("text=matey", { + timeout: 100000, + }); + + await page.getByText("Close").last().click(); + await page.getByTestId("user-profile-settings").last().click(); + await page.getByText("Settings").last().click(); + await page.getByText("Messages").last().click(); + + const label = "Press Space to toggle all rows selection (unchecked)"; + await page.getByLabel(label).first().click(); + + await page.getByTestId("icon-Trash2").first().click(); + + await page.waitForSelector("text=No Data Available", { timeout: 30000 }); + await page.getByText("No Data Available").isVisible(); +}); diff --git a/src/frontend/tests/end-to-end/generalBugs-shard-2.spec.ts b/src/frontend/tests/end-to-end/generalBugs-shard-2.spec.ts new file mode 100644 index 000000000..fb5f5129b --- /dev/null +++ b/src/frontend/tests/end-to-end/generalBugs-shard-2.spec.ts @@ -0,0 +1,72 @@ +import { expect, test } from "@playwright/test"; + +test("should use webhook component on API", async ({ page }) => { + if (!process?.env?.OPENAI_API_KEY) { + //You must set the OPENAI_API_KEY on .env file to run this test + expect(false).toBe(true); + } + + await page.goto("/"); + await page.waitForTimeout(2000); + + let modalCount = 0; + try { + const modalTitleElement = await page?.getByTestId("modal-title"); + if (modalTitleElement) { + modalCount = await modalTitleElement.count(); + } + } catch (error) { + modalCount = 0; + } + + while (modalCount === 0) { + await page.getByText("New Project", { exact: true }).click(); + await page.waitForTimeout(5000); + modalCount = await page.getByTestId("modal-title")?.count(); + } + + await page.getByTestId("blank-flow").click(); + await page.waitForSelector('[data-testid="extended-disclosure"]', { + timeout: 30000, + }); + + await page.getByTestId("extended-disclosure").click(); + await page.getByPlaceholder("Search").click(); + await page.getByPlaceholder("Search").fill("webhook"); + + await page.waitForTimeout(1000); + + await page + .getByTestId("dataWebhook Input") + .dragTo(page.locator('//*[@id="react-flow-id"]')); + await page.mouse.up(); + await page.mouse.down(); + + await page.waitForSelector('[title="fit view"]', { + timeout: 100000, + }); + + await page.getByTitle("fit view").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + + await page.waitForTimeout(2000); + await page.getByText("API", { exact: true }).click(); + + await page.getByText("Webhook cURL", { exact: true }).click(); + await page.getByRole("tab", { name: "Webhook cURL" }).click(); + + await page.getByTestId("icon-Copy").last().click(); + + const handle = await page.evaluateHandle(() => + navigator.clipboard.readText(), + ); + const clipboardContent = await handle.jsonValue(); + expect(clipboardContent.length).toBeGreaterThan(0); + expect(clipboardContent).toContain("curl -X POST"); + expect(clipboardContent).toContain("webhook"); + await page.getByRole("tab", { name: "Tweaks" }).click(); + // await page.getByText("Webhook Input").isVisible(); + // await page.getByText("Webhook Input").click(); +}); diff --git a/src/frontend/tests/end-to-end/generalBugs-shard-3.spec.ts b/src/frontend/tests/end-to-end/generalBugs-shard-3.spec.ts new file mode 100644 index 000000000..445c870d3 --- /dev/null +++ b/src/frontend/tests/end-to-end/generalBugs-shard-3.spec.ts @@ -0,0 +1,181 @@ +import { expect, test } from "@playwright/test"; + +test("should copy code from playground modal", async ({ page }) => { + await page.goto("/"); + await page.locator("span").filter({ hasText: "My Collection" }).isVisible(); + await page.waitForTimeout(2000); + + let modalCount = 0; + try { + const modalTitleElement = await page?.getByTestId("modal-title"); + if (modalTitleElement) { + modalCount = await modalTitleElement.count(); + } + } catch (error) { + modalCount = 0; + } + + while (modalCount === 0) { + await page.getByText("New Project", { exact: true }).click(); + await page.waitForTimeout(5000); + modalCount = await page.getByTestId("modal-title")?.count(); + } + + await page.waitForSelector('[data-testid="blank-flow"]', { + timeout: 30000, + }); + + await page.getByTestId("blank-flow").click(); + await page.waitForSelector('[data-testid="extended-disclosure"]', { + timeout: 30000, + }); + await page.getByTestId("extended-disclosure").click(); + await page.getByPlaceholder("Search").click(); + await page.getByPlaceholder("Search").fill("chat output"); + await page.waitForTimeout(1000); + + await page + .getByTestId("outputsChat Output") + .dragTo(page.locator('//*[@id="react-flow-id"]')); + await page.mouse.up(); + await page.mouse.down(); + + await page.getByTitle("fit view").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + + await page.getByPlaceholder("Search").click(); + await page.getByPlaceholder("Search").fill("chat input"); + await page.waitForTimeout(1000); + + await page + .getByTestId("inputsChat Input") + .dragTo(page.locator('//*[@id="react-flow-id"]')); + await page.mouse.up(); + await page.mouse.down(); + + await page.getByPlaceholder("Search").click(); + await page.getByPlaceholder("Search").fill("openai"); + await page.waitForTimeout(1000); + + await page.getByTitle("fit view").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + + await page + .getByTestId("modelsOpenAI") + .dragTo(page.locator('//*[@id="react-flow-id"]')); + await page.mouse.up(); + await page.mouse.down(); + + await page.waitForSelector('[title="fit view"]', { + timeout: 100000, + }); + + await page.getByTitle("fit view").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + + if (!process?.env?.OPENAI_API_KEY) { + //You must set the OPENAI_API_KEY on .env file to run this test + expect(false).toBe(true); + } + + await page + .getByTestId("popover-anchor-input-openai_api_key") + .fill(process.env.OPENAI_API_KEY ?? ""); + + const elementsChatInput = await page + .locator('[data-testid="handle-chatinput-shownode-message-right"]') + .all(); + + let visibleElementHandle; + + for (const element of elementsChatInput) { + if (await element.isVisible()) { + visibleElementHandle = element; + break; + } + } + + // Click and hold on the first element + await visibleElementHandle.hover(); + await page.mouse.down(); + + const elementsOpenAiInput = await page + .locator('[data-testid="handle-openaimodel-shownode-input-left"]') + .all(); + + for (const element of elementsOpenAiInput) { + if (await element.isVisible()) { + visibleElementHandle = element; + break; + } + } + + await visibleElementHandle.hover(); + await page.mouse.up(); + + const elementsOpenAiOutput = await page + .locator('[data-testid="handle-openaimodel-shownode-text-right"]') + .all(); + + for (const element of elementsOpenAiOutput) { + if (await element.isVisible()) { + visibleElementHandle = element; + break; + } + } + + // Click and hold on the first element + await visibleElementHandle.hover(); + await page.mouse.down(); + + // Move to the second element + const elementsChatOutput = await page + .getByTestId("handle-chatoutput-shownode-text-left") + .all(); + + for (const element of elementsChatOutput) { + if (await element.isVisible()) { + visibleElementHandle = element; + break; + } + } + + await visibleElementHandle.hover(); + await page.mouse.up(); + + await page.getByLabel("fit view").click(); + await page.getByText("Playground", { exact: true }).click(); + await page.waitForSelector('[data-testid="input-chat-playground"]', { + timeout: 100000, + }); + await page.getByTestId("input-chat-playground").click(); + await page + .getByTestId("input-chat-playground") + .fill("Could you provide a Python example for a 'Hello, World!' program?"); + + await page.waitForSelector('[data-testid="icon-LucideSend"]', { + timeout: 100000, + }); + + await page.getByTestId("icon-LucideSend").click(); + + await page.getByRole("tab", { name: "python" }).isVisible({ + timeout: 100000, + }); + + await page.getByTestId("icon-Copy").first().click(); + + const handle = await page.evaluateHandle(() => + navigator.clipboard.readText(), + ); + const clipboardContent = await handle.jsonValue(); + expect(clipboardContent.length).toBeGreaterThan(0); + expect(clipboardContent).toContain("Hello"); +}); diff --git a/src/frontend/tests/end-to-end/textInputOutput.spec.ts b/src/frontend/tests/end-to-end/textInputOutput.spec.ts index 7264a3af1..dc7e8c2d5 100644 --- a/src/frontend/tests/end-to-end/textInputOutput.spec.ts +++ b/src/frontend/tests/end-to-end/textInputOutput.spec.ts @@ -91,10 +91,6 @@ test("TextInputOutputComponent", async ({ page }) => { await visibleElementHandle.hover(); await page.mouse.down(); - const elementsOpenAiInput = await page.getByTestId( - "handle-openaimodel-shownode-input-left", - ); - for (const element of elementsTextInputOutput) { if (await element.isVisible()) { visibleElementHandle = element; diff --git a/src/frontend/tsconfig.json b/src/frontend/tsconfig.json index d713c77f7..28cdcd18b 100644 --- a/src/frontend/tsconfig.json +++ b/src/frontend/tsconfig.json @@ -34,7 +34,7 @@ "tests/end-to-end/floatComponent.spec.ts", "tests/end-to-end/flowPage.spec.ts", "tests/end-to-end/flowSettings.spec.ts", - "tests/end-to-end/generalBugs.spec.ts", + "tests/end-to-end/generalBugs-shard-0.spec.ts", "tests/end-to-end/globalVariables.spec.ts", "tests/end-to-end/group.spec.ts", "tests/end-to-end/folders.spec.ts",