From c8bdada9da9db92ee2e71aa24fb9fcac979f2fb1 Mon Sep 17 00:00:00 2001
From: Neha P <144308844+neha00290@users.noreply.github.com>
Date: Tue, 1 Jul 2025 18:15:48 +0530
Subject: [PATCH] test:pipeline-core-tests (#7404)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
### **PR Type**
Tests, Enhancement
___
### **Description**
- Introduce core pipeline Playwright test suite
- Skip redundant pipeline tests in pipelines.spec.js
- Increase CI workers count in config
- Add helper functions for login and ingestion
___
### **Changes diagram**
```mermaid
flowchart LR
CT["pipeline-core.spec.js"]
PT["pipelines.spec.js"]
PC["playwright.config.js"]
CI["CI pipeline"]
CT -- "adds core tests" --> CI
PT -- "skip redundant tests" --> CI
PC -- "increase CI workers" --> CI
```
___
### **Changes walkthrough** 📝
| Relevant
files |
|---|
| Tests |
pipeline-core.spec.jsAdd core
pipeline UI tests
tests/ui-testing/playwright-tests/pipeline-core.spec.js
Add new Playwright tests for core pipeline flows Implement
helper functions: login, ingestion, exploration Test
source-destination, function, and condition nodes Configure
tests to run in parallel mode
|
+269/-0
|
pipelines.spec.jsSkip redundant
pipeline tests
tests/ui-testing/playwright-tests/pipelines.spec.js
Mark three pipeline tests as skipped Update tests: source &
destination, function, condition flows Retain old tests but
disable execution
|
+3/-3
|
|
| Configuration
changes |
playwright.config.jsIncrease CI
workers count
tests/ui-testing/playwright.config.js
- Increase CI `workers` from 1 to 2 for parallel runs
|
+1/-1
|
|
___
> Need help?
Type /help how to
... in the comments thread for any questions about PR-Agent
usage.Check out the documentation
for more information.
---
.github/workflows/playwright.yml | 3 +-
.../playwright-tests/pipeline-core.spec.js | 268 ++++++++++++++++++
.../playwright-tests/pipelines.spec.js | 6 +-
tests/ui-testing/playwright.config.js | 2 +-
4 files changed, 274 insertions(+), 5 deletions(-)
create mode 100644 tests/ui-testing/playwright-tests/pipeline-core.spec.js
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
index 096b631d5d..9a84d9ff53 100644
--- a/.github/workflows/playwright.yml
+++ b/.github/workflows/playwright.yml
@@ -143,7 +143,8 @@ jobs:
"usersOrg.spec.js",
"websocket.spec.js",
"pipeline-dynamic.spec.js",
- "searchpartition.spec.js"
+ "searchpartition.spec.js",
+ "pipeline-core.spec.js"
# "remotepipeline.spec.js",
]
diff --git a/tests/ui-testing/playwright-tests/pipeline-core.spec.js b/tests/ui-testing/playwright-tests/pipeline-core.spec.js
new file mode 100644
index 0000000000..0f126196a5
--- /dev/null
+++ b/tests/ui-testing/playwright-tests/pipeline-core.spec.js
@@ -0,0 +1,268 @@
+import { test, expect } from "./baseFixtures";
+import logData from "../../ui-testing/cypress/fixtures/log.json";
+import logsdata from "../../test-data/logs_data.json";
+import PipelinePage from "../pages/pipelinePage";
+import { LogsPage } from '../pages/logsPage.js';
+
+test.describe.configure({ mode: "parallel" });
+
+
+const randomFunctionName = `Pipeline${Math.floor(Math.random() * 1000)}`;
+
+async function login(page) {
+ await page.goto(process.env["ZO_BASE_URL"]);
+ if (await page.getByText('Login as internal user').isVisible()) {
+ await page.getByText('Login as internal user').click();
+ }
+ await page.waitForTimeout(1000);
+ await page
+ .locator('[data-cy="login-user-id"]')
+ .fill(process.env["ZO_ROOT_USER_EMAIL"]);
+ await page.locator("label").filter({ hasText: "Password *" }).click();
+ await page
+ .locator('[data-cy="login-password"]')
+ .fill(process.env["ZO_ROOT_USER_PASSWORD"]);
+ await page.locator('[data-cy="login-sign-in"]').click();
+}
+
+async function ingestion(page) {
+ const orgId = process.env["ORGNAME"];
+ const streamNames = ["e2e_automate", "e2e_automate1", "e2e_automate2", "e2e_automate3"];
+ const basicAuthCredentials = Buffer.from(
+ `${process.env["ZO_ROOT_USER_EMAIL"]}:${process.env["ZO_ROOT_USER_PASSWORD"]}`
+ ).toString('base64');
+
+ const headers = {
+ "Authorization": `Basic ${basicAuthCredentials}`,
+ "Content-Type": "application/json",
+ };
+ for (const streamName of streamNames) {
+ const response = await page.evaluate(async ({ url, headers, orgId, streamName, logsdata }) => {
+ const fetchResponse = await fetch(`${url}/api/${orgId}/${streamName}/_json`, {
+ method: 'POST',
+ headers: headers,
+ body: JSON.stringify(logsdata)
+ });
+ return await fetchResponse.json();
+ }, {
+ url: process.env.INGESTION_URL,
+ headers: headers,
+ orgId: orgId,
+ streamName: streamName,
+ logsdata: logsdata
+ });
+ console.log(response);
+ }
+}
+
+async function exploreStreamAndNavigateToPipeline(page, streamName) {
+ await page.locator('[data-test="menu-link-\\/streams-item"]').click();
+ await page.waitForTimeout(1000);
+ await page.locator('[data-test="log-stream-refresh-stats-btn"]').click();
+ await page.waitForTimeout(1000);
+ await page.getByPlaceholder('Search Stream').click();
+ await page.getByPlaceholder('Search Stream').fill(streamName);
+ await page.waitForTimeout(1000);
+ await page.getByRole('button', { name: 'Explore' }).first().click();
+ await page.locator('[data-test="log-table-column-1-_timestamp"] [data-test="table-row-expand-menu"]').click();
+ await page.locator('[data-test="menu-link-\\/pipeline-item"]').click();
+}
+
+async function exploreStreamAndInteractWithLogDetails(page, streamName) {
+ await page.locator('[data-test="menu-link-\\/streams-item"]').click();
+ await page.getByPlaceholder('Search Stream').click();
+ await page.getByPlaceholder('Search Stream').fill(streamName);
+ await page.waitForTimeout(1000);
+ await page.getByRole('button', { name: 'Explore' }).first().click();
+ await page.locator('[data-test="log-table-column-1-_timestamp"] [data-test="table-row-expand-menu"]').click();
+ await page.locator('[data-test="log-expand-detail-key-a-text"]').click();
+ await page.locator('[data-test="menu-link-\\/pipeline-item"]').click();
+}
+
+async function applyQueryButton(page) {
+ const search = page.waitForResponse(logData.applyQuery);
+ await page.waitForTimeout(3000);
+ await page.locator("[data-test='logs-search-bar-refresh-btn']").click({
+ force: true,
+ });
+ await expect.poll(async () => (await search).status()).toBe(200);
+}
+
+test.describe("Core Pipeline Tests", () => {
+ let logsPage;
+
+ test.beforeEach(async ({ page }) => {
+ await login(page);
+ logsPage = new LogsPage(page);
+ await page.waitForTimeout(5000);
+
+ const orgId = process.env["ORGNAME"];
+ const streamName = "e2e_automate";
+ const basicAuthCredentials = Buffer.from(
+ `${process.env["ZO_ROOT_USER_EMAIL"]}:${process.env["ZO_ROOT_USER_PASSWORD"]}`
+ ).toString("base64");
+
+ const headers = {
+ Authorization: `Basic ${basicAuthCredentials}`,
+ "Content-Type": "application/json",
+ };
+
+ await ingestion(page);
+ await page.goto(
+ `${logData.logsUrl}?org_identifier=${process.env["ORGNAME"]}`
+ );
+ await logsPage.selectStreamAndStreamTypeForLogs("e2e_automate");
+ await applyQueryButton(page);
+ });
+
+ test("should add source & destination node and then delete the pipeline", async ({ page }) => {
+ const pipelinePage = new PipelinePage(page);
+
+ await pipelinePage.openPipelineMenu();
+ await page.waitForTimeout(1000);
+ await pipelinePage.addPipeline();
+ await pipelinePage.selectStream();
+ await pipelinePage.dragStreamToTarget(pipelinePage.streamButton);
+ await pipelinePage.selectLogs();
+
+ await pipelinePage.enterStreamName("e2e");
+ await pipelinePage.enterStreamName("e2e_automate3");
+ await page.waitForTimeout(2000);
+ await page.getByRole("option", { name: "e2e_automate3", exact: true }).click();
+ await pipelinePage.saveInputNodeStream();
+ await page.waitForTimeout(3000);
+ await page.locator("button").filter({ hasText: "delete" }).nth(1).click();
+ await page.locator('[data-test="confirm-button"]').click();
+ await page.locator("button").filter({ hasText: "edit" }).hover();
+ await page.getByRole("img", { name: "Output Stream" }).click();
+ await page.getByLabel("Stream Name *").click();
+ await page.getByLabel("Stream Name *").fill("destination-node");
+ await page.waitForTimeout(1000);
+ await pipelinePage.clickInputNodeStreamSave();
+ const pipelineName = `pipeline-${Math.random().toString(36).substring(7)}`;
+ await pipelinePage.enterPipelineName(pipelineName);
+ await pipelinePage.savePipeline();
+ await ingestion(page);
+
+ await exploreStreamAndNavigateToPipeline(page, 'destination_node');
+ await pipelinePage.searchPipeline(pipelineName);
+ await page.waitForTimeout(1000);
+ const deletePipelineButton = page.locator(
+ `[data-test="pipeline-list-${pipelineName}-delete-pipeline"]`
+ );
+ await deletePipelineButton.waitFor({ state: "visible" });
+ await deletePipelineButton.click();
+ await pipelinePage.confirmDeletePipeline();
+ await pipelinePage.verifyPipelineDeleted();
+ });
+
+ test("should add source, function, destination and then delete pipeline", async ({ page }) => {
+ const pipelinePage = new PipelinePage(page);
+
+ await pipelinePage.openPipelineMenu();
+ await page.waitForTimeout(1000);
+ await pipelinePage.addPipeline();
+ await pipelinePage.selectStream();
+ await pipelinePage.dragStreamToTarget(pipelinePage.streamButton);
+ await pipelinePage.selectLogs();
+ await pipelinePage.enterStreamName("e2e");
+ await pipelinePage.enterStreamName("e2e_automate1");
+ await page.waitForTimeout(2000);
+ await page.getByRole("option", { name: "e2e_automate1", exact: true }).click();
+ await pipelinePage.saveInputNodeStream();
+ await page.waitForTimeout(2000);
+ await page.locator("button").filter({ hasText: "delete" }).nth(1).click();
+ await page.locator('[data-test="confirm-button"]').click();
+ await page.locator("button").filter({ hasText: "edit" }).hover();
+ await page.getByRole("img", { name: "Function", exact: true }).click();
+ await pipelinePage.toggleCreateFunction();
+ await pipelinePage.enterFunctionName(randomFunctionName);
+ await page.locator('[data-test="logs-vrl-function-editor"]').locator(".view-lines").click();
+ await page.keyboard.type(".a=41", { delay: 100 });
+ await page.keyboard.press("Enter");
+ await page.keyboard.type(".", { delay: 100 });
+ await page.getByText("Note: The function will be").click();
+ await page.getByText(".a=41 .");
+ await page.waitForTimeout(1000);
+ await pipelinePage.saveNewFunction();
+ await page.waitForTimeout(3000);
+ await pipelinePage.saveFunction();
+ await page.waitForTimeout(3000);
+ await page.getByRole("button", { name: randomFunctionName }).hover();
+ await page.getByRole("img", { name: "Output Stream" }).click();
+ await page.getByLabel("Stream Name *").click();
+ await page.getByLabel("Stream Name *").fill("destination-node");
+ await page.waitForTimeout(100);
+ await pipelinePage.clickInputNodeStreamSave();
+ const pipelineName = `pipeline-${Math.random().toString(36).substring(7)}`;
+ await pipelinePage.enterPipelineName(pipelineName);
+ await pipelinePage.savePipeline();
+ await ingestion(page);
+
+ await exploreStreamAndInteractWithLogDetails(page, 'destination_node');
+ await pipelinePage.searchPipeline(pipelineName);
+ await page.waitForTimeout(1000);
+ const deletePipelineButton = page.locator(
+ `[data-test="pipeline-list-${pipelineName}-delete-pipeline"]`
+ );
+ await deletePipelineButton.waitFor({ state: "visible" });
+ await deletePipelineButton.click();
+ await pipelinePage.confirmDeletePipeline();
+ await pipelinePage.verifyPipelineDeleted();
+ });
+
+ test("should add source, condition & destination node and then delete the pipeline", async ({ page }) => {
+ const pipelinePage = new PipelinePage(page);
+
+ await pipelinePage.openPipelineMenu();
+ await page.waitForTimeout(1000);
+ await pipelinePage.addPipeline();
+ await pipelinePage.selectStream();
+ await pipelinePage.dragStreamToTarget(pipelinePage.streamButton);
+ await pipelinePage.selectLogs();
+
+ await pipelinePage.enterStreamName("e2e");
+ await pipelinePage.enterStreamName("e2e_automate2");
+ await page.waitForTimeout(2000);
+ await page.getByRole("option", { name: "e2e_automate2", exact: true }).click();
+ await pipelinePage.saveInputNodeStream();
+ await page.waitForTimeout(2000);
+ await page.locator("button").filter({ hasText: "delete" }).nth(1).click();
+ await page.locator('[data-test="confirm-button"]').click();
+ await page.locator("button").filter({ hasText: "edit" }).hover();
+ await page.getByRole("img", { name: "Stream", exact: true }).click();
+ await page.getByPlaceholder("Column").click();
+ await page.getByPlaceholder("Column").fill("container_name");
+ await page.getByRole("option", { name: "kubernetes_container_name" }).click();
+ await page.locator(
+ "div:nth-child(2) > div:nth-child(2) > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > .q-field__native"
+ ).click();
+ await page.getByText("Contains", { exact: true }).click();
+ await page.getByPlaceholder("Value").click();
+ await page.getByPlaceholder("Value").fill("prometheus");
+ await pipelinePage.saveCondition();
+ await page.waitForTimeout(2000);
+ await page.getByRole("button", { name: "kubernetes_container_name" }).hover();
+ await page.getByRole("img", { name: "Output Stream" }).click();
+ await page.getByLabel("Stream Name *").click();
+ await page.getByLabel("Stream Name *").fill("destination-node");
+ await page.waitForTimeout(1000);
+ await pipelinePage.clickInputNodeStreamSave();
+ const pipelineName = `pipeline-${Math.random().toString(36).substring(7)}`;
+ await pipelinePage.enterPipelineName(pipelineName);
+ await pipelinePage.savePipeline();
+ await ingestion(page);
+
+ await exploreStreamAndNavigateToPipeline(page, 'destination_node');
+ await page.waitForTimeout(1000);
+ await pipelinePage.searchPipeline(pipelineName);
+ await page.waitForTimeout(1000);
+ const deletePipelineButton = page.locator(
+ `[data-test="pipeline-list-${pipelineName}-delete-pipeline"]`
+ );
+ await deletePipelineButton.waitFor({ state: "visible" });
+ await deletePipelineButton.click();
+ await pipelinePage.confirmDeletePipeline();
+ await pipelinePage.verifyPipelineDeleted();
+ });
+});
\ No newline at end of file
diff --git a/tests/ui-testing/playwright-tests/pipelines.spec.js b/tests/ui-testing/playwright-tests/pipelines.spec.js
index d9375a9ae6..d7f5e2755a 100644
--- a/tests/ui-testing/playwright-tests/pipelines.spec.js
+++ b/tests/ui-testing/playwright-tests/pipelines.spec.js
@@ -270,7 +270,7 @@ test.describe("Pipeline testcases", () => {
await pipelinePage.confirmDelete();
});
- test("should add source & destination node and then delete the pipeline", async ({
+ test.skip("should add source & destination node and then delete the pipeline", async ({
page,
}) => {
const pipelinePage = new PipelinePage(page);
@@ -378,7 +378,7 @@ test.describe("Pipeline testcases", () => {
await pipelinePage.confirmDelete();
});
- test("should add source, function,destination and then delete pipeline", async ({
+ test.skip("should add source, function,destination and then delete pipeline", async ({
page,
}) => {
const pipelinePage = new PipelinePage(page);
@@ -525,7 +525,7 @@ test.describe("Pipeline testcases", () => {
await pipelinePage.assertStreamSelectionErrorVisible();
});
- test("should add source, condition & destination node and then delete the pipeline", async ({
+ test.skip("should add source, condition & destination node and then delete the pipeline", async ({
page,
}) => {
const pipelinePage = new PipelinePage(page);
diff --git a/tests/ui-testing/playwright.config.js b/tests/ui-testing/playwright.config.js
index 5e0227e16e..ea1e7e2f52 100644
--- a/tests/ui-testing/playwright.config.js
+++ b/tests/ui-testing/playwright.config.js
@@ -21,7 +21,7 @@ module.exports = defineConfig({
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
- workers: process.env.CI ? 1 : undefined,
+ workers: process.env.CI ? 2 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */