test:pipeline-core-tests (#7404)

### **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** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Tests</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>pipeline-core.spec.js</strong><dd><code>Add core
pipeline UI tests</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

tests/ui-testing/playwright-tests/pipeline-core.spec.js

<li>Add new Playwright tests for core pipeline flows<br> <li> Implement
helper functions: login, ingestion, exploration<br> <li> Test
source-destination, function, and condition nodes<br> <li> Configure
tests to run in parallel mode


</details>


  </td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7404/files#diff-e6249b045437e7f087b825dc0ee2d8ccebed0894d10c84b7bc9b12dca7a25754">+269/-0</a>&nbsp;
</td>

</tr>

<tr>
  <td>
    <details>
<summary><strong>pipelines.spec.js</strong><dd><code>Skip redundant
pipeline tests</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></summary>
<hr>

tests/ui-testing/playwright-tests/pipelines.spec.js

<li>Mark three pipeline tests as skipped<br> <li> Update tests: source &
destination, function, condition flows<br> <li> Retain old tests but
disable execution


</details>


  </td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7404/files#diff-9b48f5edc53f2413789549b189d4a80f134a320dd5beebf96d738069eb43033a">+3/-3</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Configuration
changes</strong></td><td><table>
<tr>
  <td>
    <details>
<summary><strong>playwright.config.js</strong><dd><code>Increase CI
workers count</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

tests/ui-testing/playwright.config.js

- Increase CI `workers` from 1 to 2 for parallel runs


</details>


  </td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7404/files#diff-91cb9615068914b2e4f5d66fcbed50d3e9771ea2d882f8e8048bbe54eab3c414">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
This commit is contained in:
Neha P
2025-07-01 18:15:48 +05:30
committed by GitHub
parent 5efc5e7a3a
commit c8bdada9da
4 changed files with 274 additions and 5 deletions
+2 -1
View File
@@ -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",
]
@@ -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();
});
});
@@ -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);
+1 -1
View File
@@ -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. */