From 505bd69aa3740269c4e57bdc3d59ff626d0efd47 Mon Sep 17 00:00:00 2001 From: Neha P <144308844+neha00290@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:13:55 +0530 Subject: [PATCH] test: e2e-enrichment-bulkapi-pytest (#5371) ## Summary by CodeRabbit - **New Features** - Introduced end-to-end testing for bulk ingestion with comprehensive coverage. - Enhanced functionality for managing enrichment tables, including adding, uploading, and deleting entries. - **Bug Fixes** - Improved error handling and user interaction checks in the enrichment table upload process. - **Tests** - Added multiple new test cases for enrichment table functionality, including upload and error handling scenarios. - Updated existing test cases for consistency and improved logic. - **Chores** - Standardized import statements and improved overall test structure for clarity. --- tests/api-testing/tests/test_bulk.py | 110 ++++++ tests/test-data/append.csv | 2 + tests/test-data/protocols.csv | 14 + tests/ui-testing/pages/pipelinePage.js | 75 +++- .../playwright-tests/logsqueries.spec.js | 10 +- .../playwright-tests/pipelines.spec.js | 333 +++++++++++++----- 6 files changed, 442 insertions(+), 102 deletions(-) create mode 100644 tests/api-testing/tests/test_bulk.py create mode 100644 tests/test-data/append.csv create mode 100644 tests/test-data/protocols.csv diff --git a/tests/api-testing/tests/test_bulk.py b/tests/api-testing/tests/test_bulk.py new file mode 100644 index 0000000000..cbdec1a6fe --- /dev/null +++ b/tests/api-testing/tests/test_bulk.py @@ -0,0 +1,110 @@ +import json + +def test_e2e_bulk_ingest(create_session, base_url): + """ + Running an E2E test for bulk ingestion using the _bulk API. + This includes both positive (valid payload) and negative (invalid payload) cases. + """ + session = create_session + org_id = "pytests" + url = f"{base_url}api/{org_id}/_bulk" + headers = {"Content-Type": "application/json"} + + # **Positive Test Case: Valid Bulk Payload** + valid_bulk_payload = ( + '{ "index" : { "_index" : "pytest1", "_id" : "1" } }\n' + '{ "field1" : "value1" }\n' + '{ "index" : { "_index" : "pytest1", "_id" : "2" } }\n' + '{ "field1" : "value2" }' + ) + + resp_valid_bulk = session.post(url, data=valid_bulk_payload, headers=headers) + print("Positive Test Response:", resp_valid_bulk.content) + + # Assert positive case + assert resp_valid_bulk.status_code == 200, ( + f"Expected status code 200 for valid bulk request, but got {resp_valid_bulk.status_code}. " + f"Response: {resp_valid_bulk.content}" + ) + assert not resp_valid_bulk.json().get("errors"), ( + f"Expected no errors in the valid bulk request response, but got {resp_valid_bulk.json()}" + ) + + # **Negative Test Case 1: Invalid Bulk Payload (Missing Document Body)** + invalid_bulk_payload = ( + '{ "index" : { "_index" : "pytest10222", "_id" : "1" } }\n' + '{ "field1" : "value1" }' # Add a document body after the index action + ) + + resp_invalid_bulk_1 = session.post(url, data=invalid_bulk_payload, headers=headers) + + # Print the raw response and content for debugging + print("Negative Test Response (Raw Content):", resp_invalid_bulk_1.content) + + try: + response_json = resp_invalid_bulk_1.json() # Attempt to parse the JSON response + print("Negative Test Response (JSON):", response_json) + except Exception as e: + print("Error parsing JSON response:", str(e)) + + # Assert negative case for the first invalid payload + # Expecting a 200 response even for invalid data, but check that there's no error field + assert resp_invalid_bulk_1.status_code == 200, ( + f"Expected status code 200 for invalid bulk request, but got {resp_invalid_bulk_1.status_code}. " + f"Response: {resp_invalid_bulk_1.content}" + ) + assert not resp_invalid_bulk_1.json().get("errors"), ( + f"Expected no errors in the invalid bulk request response, but got {resp_invalid_bulk_1.json()}" + ) + + # **Negative Test Case 2: Invalid Bulk Payload (Empty Index)** + invalid_bulk_payload2 = [ + { "index" : { "_index" : "" } }, # Empty index + { "field1" : "value1" } + ] + + # Convert the payload to JSON + invalid_bulk_payload2_json = '\n'.join([json.dumps(item) for item in invalid_bulk_payload2]) + + # Posting the payload to the API + resp_invalid_bulk_2 = session.post(url, data=invalid_bulk_payload2_json, headers=headers) + + # Print the raw response and content for debugging + print("Negative Test Response 2 (Raw Content):", resp_invalid_bulk_2.content) + + # Try to parse and print the JSON response + try: + response_json = resp_invalid_bulk_2.json() # Attempt to parse the JSON response + print("Negative Test Response 2 (JSON):", response_json) + except Exception as e: + print("Error parsing JSON response:", str(e)) + + # Assert that we get a 200 status code (as observed in Postman behavior) + assert resp_invalid_bulk_2.status_code == 200, ( + f"Expected status code 200 for invalid bulk request, but got {resp_invalid_bulk_2.status_code}. " + f"Response: {resp_invalid_bulk_2.content}" + ) + + # Assert no errors in the response, as expected from Postman behavior + assert not resp_invalid_bulk_2.json().get("errors"), ( + f"Expected no errors in the invalid bulk request response, but got {resp_invalid_bulk_2.json()}" + ) + + + # **Negative Test Case: Invalid index name** + invalid_bulk_payload3 = ( + '{ "index" : { "_index" : "****", "_id" : "1" } }\n' + '{ "field1" : "value1" }\n' + '{ "index" : { "_index" : "****", "_id" : "2" } }\n' + '{ "field1" : "value2" }' + ) + + resp_valid_bulk = session.post(url, data=valid_bulk_payload, headers=headers) + print("Negative Test Response:", resp_valid_bulk.content) + + # Assert positive case + assert resp_valid_bulk.status_code == 200, ( + f"Expected status code 200 for valid bulk request, but got {resp_valid_bulk.status_code}. " + f"Response: {resp_valid_bulk.content}" + ) + diff --git a/tests/test-data/append.csv b/tests/test-data/append.csv new file mode 100644 index 0000000000..b16fc7e183 --- /dev/null +++ b/tests/test-data/append.csv @@ -0,0 +1,2 @@ +protocol_number,keyword,protocol_description +0,HOPOPT,IPv6 Hop-by-Hop Option diff --git a/tests/test-data/protocols.csv b/tests/test-data/protocols.csv new file mode 100644 index 0000000000..396207d6a7 --- /dev/null +++ b/tests/test-data/protocols.csv @@ -0,0 +1,14 @@ +protocol_number,keyword,protocol_description +0,HOPOPT,IPv6 Hop-by-Hop Option +1,ICMP,Internet Control Message +2,IGMP,Internet Group Management +3,GGP,Gateway-to-Gateway +4,IPv4,IPv4 encapsulation +5,ST,Stream +6,TCP,Transmission Control +7,CBT,CBT +8,EGP,Exterior Gateway Protocol +9,IGP,any private interior gateway (used by Cisco for their IGRP) +10,BBN-RCC-MON,BBN RCC Monitoring +11,NVP-II,Network Voice Protocol +12,PUP,PUP diff --git a/tests/ui-testing/pages/pipelinePage.js b/tests/ui-testing/pages/pipelinePage.js index e372dd03b0..90456bc227 100644 --- a/tests/ui-testing/pages/pipelinePage.js +++ b/tests/ui-testing/pages/pipelinePage.js @@ -62,9 +62,14 @@ class PipelinePage { this.columnInput = page.getByPlaceholder('Column'); // Locator for the "Column" input field this.columnOption = page.getByRole('option', { name: 'kubernetes_container_name' }) this.fieldRequiredError = page.getByText('Field is required!') - -this.functionNameInput = page.locator('[data-test="add-function-node-routing-section"] input[aria-label="Name"]'); -this.addConditionSaveButton = page.locator('[data-test="add-condition-save-btn"]'); + this.tableRowsLocator = page.locator("tbody tr"); + this.confirmButton = page.locator('[data-test="confirm-button"]'); + this.functionNameInput = page.locator('[data-test="add-function-node-routing-section"] input[aria-label="Name"]'); + this.addConditionSaveButton = page.locator('[data-test="add-condition-save-btn"]'); + this.pipelineMenu = '[data-test="menu-link-\\/pipeline-item"]'; + this.enrichmentTableTab = + '[data-test="function-enrichment-table-tab"] > .q-tab__content > .q-tab__label'; + this.addEnrichmentTableButton = 'text=Add Enrichment Table'; @@ -268,6 +273,68 @@ async verifyFieldRequiredError() { await this.fieldRequiredError.click(); } +async navigateToAddEnrichmentTable() { + await this.page.locator(this.pipelineMenu).click(); + await this.page.click(this.enrichmentTableTab, { force: true }); + await this.page.getByText("Add Enrichment Table").click(); } -export default PipelinePage; +async uploadEnrichmentTable(fileName, fileContentPath) { + // Set the file to be uploaded + const inputFile = await this.page.locator(this.fileInput); + await inputFile.setInputFiles(fileContentPath); + + // Enter the file name + await this.page.fill(this.fileNameInput, fileName); + + // Click on 'Save' + await this.page.getByText(this.saveButton).click({ force: true }); + + // Wait for the process to complete + await this.page.reload(); + await this.page.waitForTimeout(3000); + + // Search for the uploaded file + await this.page.getByPlaceholder(this.searchPlaceholder).fill(fileName); + await this.page.waitForTimeout(3000); +} +async deleteEnrichmentTableByName(fileName) { + const rows = await this.tableRowsLocator; + let fileFound = false; + + for (let i = 0; i < (await rows.count()); i++) { + const row = rows.nth(i); + const functionName = await row + .locator("td.text-left") + .nth(1) + .textContent(); + + if (functionName?.trim() === fileName) { + fileFound = true; + console.log("Uploaded file found:", functionName); + + // Click the 'Delete Function' button + await row.locator('[title="Delete Function"]').click(); + + // Confirm the deletion + await this.confirmButton.click(); + break; + } + } + + if (!fileFound) { + throw new Error( + `Uploaded file "${fileName}" not found in the enrichment table.` + ); + } + +} + +async navigateToEnrichmentTableTab() { + await this.pipelineMenuLink.click(); + await this.enrichmentTableTab.click(); +} + +} + +export default PipelinePage; \ No newline at end of file diff --git a/tests/ui-testing/playwright-tests/logsqueries.spec.js b/tests/ui-testing/playwright-tests/logsqueries.spec.js index 39e708846d..4b61363758 100644 --- a/tests/ui-testing/playwright-tests/logsqueries.spec.js +++ b/tests/ui-testing/playwright-tests/logsqueries.spec.js @@ -131,7 +131,7 @@ test.describe("Logs Queries testcases", () => { await page.locator('[data-test="logs-search-saved-views-btn"]').getByLabel('Expand').click(); await page.locator('button').filter({ hasText: 'savesaved_search' }).click(); await page.locator('[data-test="add-alert-name-input"]').click(); - await page.locator('[data-test="add-alert-name-input"]').fill('streamslogsnavigate'); + await page.locator('[data-test="add-alert-name-input"]').fill('streamslognave'); await page.locator('[data-test="saved-view-dialog-save-btn"]').click({ force: true }); await page.waitForTimeout(5000); await page.locator('[data-test="menu-link-\\/streams-item"]').click({ force: true }); @@ -142,13 +142,13 @@ test.describe("Logs Queries testcases", () => { await page.waitForSelector('[data-test="logs-search-saved-views-btn"]') await page.locator('[data-test="logs-search-saved-views-btn"]').getByLabel('Expand').click(); await page.locator('[data-test="log-search-saved-view-field-search-input"]').click({ force: true }); - await page.locator('[data-test="log-search-saved-view-field-search-input"]').fill('streamslogsnavigate'); + await page.locator('[data-test="log-search-saved-view-field-search-input"]').fill('streamslognave'); await page.waitForTimeout(3000); - await page.waitForSelector(':text("streamslogsnavigate")'); - await page.click(':text("streamslogsnavigate")'); + await page.waitForSelector(':text("streamslognav")'); + await page.click(':text("streamslognave")'); await page.locator('[data-test="logs-search-saved-views-btn"]').getByLabel('Expand').click(); await page.locator('[data-test="log-search-saved-view-field-search-input"]').click(); - await page.getByTitle('streamslogsnavigate').click(); + await page.getByTitle('streamslognave').click(); await page.getByText('delete').click(); await page.locator('[data-test="confirm-button"]').click(); diff --git a/tests/ui-testing/playwright-tests/pipelines.spec.js b/tests/ui-testing/playwright-tests/pipelines.spec.js index 96398924ca..335302ee90 100644 --- a/tests/ui-testing/playwright-tests/pipelines.spec.js +++ b/tests/ui-testing/playwright-tests/pipelines.spec.js @@ -4,8 +4,8 @@ import { log } from "console"; import logsdata from "../../test-data/logs_data.json"; import PipelinePage from "../pages/pipelinePage"; import { pipeline } from "stream"; -import fs from 'fs'; -import { v4 as uuidv4 } from 'uuid'; +import fs from "fs"; +import { v4 as uuidv4 } from "uuid"; test.describe.configure({ mode: "parallel" }); @@ -79,16 +79,11 @@ test.describe("Pipeline testcases", () => { await expect.poll(async () => (await search).status()).toBe(200); // await search.hits.FIXME_should("be.an", "array"); } - // tebefore(async function () { - // // logData("log"); - // // const data = page; - // // logData = data; - - // console.log("--logData--", logData); - // }); + test.beforeEach(async ({ page }) => { await login(page); await page.waitForTimeout(5000); + // ("ingests logs via API", () => { const orgId = process.env["ORGNAME"]; @@ -192,7 +187,7 @@ test.describe("Pipeline testcases", () => { await page.waitForTimeout(1000); await pipelinePage.selectStreamOption(); await pipelinePage.saveInputNodeStream(); - await page.locator('button').filter({ hasText: 'delete' }).nth(1).click(); + await page.locator("button").filter({ hasText: "delete" }).nth(1).click(); await page.locator('[data-test="confirm-button"]').click(); // Generate a random pipeline name and save @@ -224,7 +219,7 @@ test.describe("Pipeline testcases", () => { await page.waitForTimeout(2000); await pipelinePage.selectStreamOption(); await pipelinePage.saveInputNodeStream(); - await page.locator('button').filter({ hasText: 'delete' }).nth(1).click(); + await page.locator("button").filter({ hasText: "delete" }).nth(1).click(); await page.locator('[data-test="confirm-button"]').click(); // Delete the pipeline and confirm @@ -251,10 +246,10 @@ test.describe("Pipeline testcases", () => { await pipelinePage.selectStreamOption(); await pipelinePage.saveInputNodeStream(); await page.waitForTimeout(3000); - await page.locator('button').filter({ hasText: 'delete' }).nth(1).click(); + 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.locator("button").filter({ hasText: "edit" }).hover(); + await page.getByRole("img", { name: "Output Stream" }).click(); await pipelinePage.toggleCreateStream(); await page.getByLabel("Name *").click(); await page.getByLabel("Name *").fill("destination-node"); @@ -352,10 +347,10 @@ test.describe("Pipeline testcases", () => { await pipelinePage.saveInputNodeStream(); // await pipelinePage.selectAndDragFunction(); // Function drag 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 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(".view-lines").click(); @@ -376,8 +371,8 @@ test.describe("Pipeline testcases", () => { 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.getByRole("button", { name: randomFunctionName }).hover(); + await page.getByRole("img", { name: "Output Stream" }).click(); await pipelinePage.toggleCreateStream(); await page.getByLabel("Name *").click(); await page.getByLabel("Name *").fill("destination-node"); @@ -407,7 +402,6 @@ test.describe("Pipeline testcases", () => { await pipelinePage.verifyPipelineDeleted(); }); - test("should display error when function name is not added", async ({ page, }) => { @@ -421,8 +415,7 @@ test.describe("Pipeline testcases", () => { await pipelinePage.toggleCreateFunction(); await page.waitForTimeout(1000); await pipelinePage.saveFunction(); - await pipelinePage. assertFunctionNameRequiredErrorVisible() - + await pipelinePage.assertFunctionNameRequiredErrorVisible(); }); test("should display error when function name added but function is not added", async ({ @@ -439,11 +432,12 @@ test.describe("Pipeline testcases", () => { await page.waitForTimeout(1000); await pipelinePage.enterFunctionName(randomFunctionName); await pipelinePage.saveFunction(); - await pipelinePage.assertFunctionRequiredErrorVisible() + await pipelinePage.assertFunctionRequiredErrorVisible(); }); - - test.skip("should display error if query added without sql", async ({ page }) => { + test.skip("should display error if query added without sql", async ({ + page, + }) => { const pipelinePage = new PipelinePage(page); // Open the pipeline menu and wait for a moment @@ -459,12 +453,9 @@ test.describe("Pipeline testcases", () => { await pipelinePage.saveQuery(); await page.waitForTimeout(2000); // TODO: Change the locator to the correct one, once fixed - await page.getByText('Invalid SQL Query').click() - + await page.getByText("Invalid SQL Query").click(); }); - - test("should display error when save clicked directly while adding destination", async ({ page, }) => { @@ -476,7 +467,7 @@ test.describe("Pipeline testcases", () => { await pipelinePage.selectAndDragSecondStream(); await page.waitForTimeout(2000); await pipelinePage.saveStream(); - await pipelinePage.assertStreamSelectionErrorVisible() + await pipelinePage.assertStreamSelectionErrorVisible(); }); test("should add source, condition & destination node and then delete the pipeline", async ({ @@ -498,21 +489,29 @@ test.describe("Pipeline testcases", () => { await pipelinePage.selectStreamOption(); await pipelinePage.saveInputNodeStream(); await page.waitForTimeout(2000); - await page.locator('button').filter({ hasText: 'delete' }).nth(1).click(); + 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.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 + .getByRole("button", { name: "kubernetes_container_name" }) + .hover(); + await page.getByRole("img", { name: "Output Stream" }).click(); await pipelinePage.toggleCreateStream(); await page.getByLabel("Name *").click(); await page.getByLabel("Name *").fill("destination-node"); @@ -543,7 +542,6 @@ test.describe("Pipeline testcases", () => { await pipelinePage.verifyPipelineDeleted(); }); - test("should display error when function is not selected under select function", async ({ page, }) => { @@ -558,7 +556,6 @@ test.describe("Pipeline testcases", () => { await pipelinePage.verifyFieldRequiredError(); }); - test("should display error when condition is not added but user clicks save", async ({ page, }) => { @@ -566,13 +563,12 @@ test.describe("Pipeline testcases", () => { await pipelinePage.openPipelineMenu(); await page.waitForTimeout(1000); await pipelinePage.addPipeline(); - await pipelinePage.selectAndDragCondition() // Function drag; + await pipelinePage.selectAndDragCondition(); // Function drag; await page.waitForTimeout(1000); - await pipelinePage.saveCondition() + await pipelinePage.saveCondition(); await pipelinePage.verifyFieldRequiredError(); }); - test("should add source & destination node without connection and error to be displayed", async ({ page, }) => { @@ -592,7 +588,7 @@ test.describe("Pipeline testcases", () => { await pipelinePage.selectStreamOption(); await pipelinePage.saveInputNodeStream(); await page.waitForTimeout(1000); - await page.locator('button').filter({ hasText: 'delete' }).nth(1).click(); + await page.locator("button").filter({ hasText: "delete" }).nth(1).click(); await page.locator('[data-test="confirm-button"]').click(); await pipelinePage.dragStreamToTarget(pipelinePage.streamButton); // First stream drag @@ -617,60 +613,211 @@ test.describe("Pipeline testcases", () => { const pipelineName = `pipeline-${Math.random().toString(36).substring(7)}`; await pipelinePage.enterPipelineName(pipelineName); await pipelinePage.savePipeline(); - await page.getByText('Please connect all nodes').click(); - + await page.getByText("Please connect all nodes").click(); }); - test('should upload an enrichment table under functions', async ({ page }) => { - await page.locator('[data-test="menu-link-\\/pipeline-item"]').click(); - - // Click the enrichment table tab - await page.click('[data-test="function-enrichment-table-tab"] > .q-tab__content > .q-tab__label', { force: true }); - - // Click on 'Add Enrichment Table' - await page.getByText('Add Enrichment Table').click(); - - // Generate a unique file name - const fileName = `enrichment_info_${uuidv4()}.csv`; - const fileContent = '../test-data/enrichment_info.csv'; + test("should upload an enrichment table under functions", async ({ + page, + }) => { + const pipelinePage = new PipelinePage(page); + await pipelinePage.navigateToAddEnrichmentTable(); + // Generate a unique file name and replace hyphens with underscores + let fileName = `enrichment_info_${uuidv4()}_csv`.replace(/-/g, "_"); + console.log("Generated File Name:", fileName); + + // Set the file to be uploaded + const fileContentPath = "../test-data/enrichment_info.csv"; const inputFile = await page.locator('input[type="file"]'); - await inputFile.setInputFiles(fileContent); - - // Wait for the upload to process - await page.waitForTimeout(5000); - + await inputFile.setInputFiles(fileContentPath); + // Enter the file name - await page.fill('.q-input > .q-field__inner > .q-field__control', fileName); - + await page.fill(".q-input > .q-field__inner > .q-field__control", fileName); + // Click on 'Save' - await page.getByText('Save').click({ force: true }); - await page.waitForTimeout(5000); - await page.getByRole('button', { name: 'Explore' }).nth(1).click(); + await page.getByText("Save").click({ force: true }); + + // Wait for the process to complete + await page.reload(); + await page.waitForTimeout(3000); + await page.getByPlaceholder("Search Enrichment Table").fill(fileName); + await page.waitForTimeout(3000); + // Explore the uploaded table + await page.getByRole("button", { name: "Explore" }).click(); await page.locator('[data-test="log-table-column-0-_timestamp"]').click(); await page.locator('[data-test="close-dialog"]').click(); - await page.waitForTimeout(5000); + await page.waitForTimeout(3000); + + // Return to the pipeline menu and verify the uploaded table await page.locator('[data-test="menu-link-\\/pipeline-item"]').click(); await page.locator('[data-test="function-enrichment-table-tab"]').click(); - - // Locate rows in the table - const rows = await page.locator('tbody tr'); - - // Iterate through each row to find and delete the function - for (let i = 0; i < await rows.count(); i++) { - const row = rows.nth(i); - const functionName = await row.locator('td.text-left').nth(1).textContent(); - - if (functionName?.includes('enrichment_info')) { - // Click the 'Delete Function' button - await row.locator('[title="Delete Function"]').click(); - - // Handle the confirmation dialog - await page.locator('[data-test="confirm-button"]').click(); - } - } + // Verify and delete enrichment tables + await pipelinePage.deleteEnrichmentTableByName(fileName) }); -}); + test.skip("should upload an enrichment table under functions with VRL", async ({ + page, + }) => { + const pipelinePage = new PipelinePage(page); + await pipelinePage.navigateToAddEnrichmentTable(); + // Generate a unique file name + let fileName = `protocols_${uuidv4()}_csv`; + console.log("Generated File Name:", fileName); + fileName = fileName.replace(/-/g, "_"); + + // Set the file to be uploaded + const fileContentPath = "../test-data/protocols.csv"; + const inputFile = await page.locator('input[type="file"]'); + await inputFile.setInputFiles(fileContentPath); + + // Enter the file name + await page.fill(".q-input > .q-field__inner > .q-field__control", fileName); + + // Click on 'Save' + await page.getByText("Save").click({ force: true }); + + // Reload the page to ensure table is updated + await page.reload(); + await page.waitForTimeout(3000); + + // Search for the uploaded file name + await page.getByPlaceholder("Search Enrichment Table").fill(fileName); + await page.waitForTimeout(3000); + + // Verify the file name in the enrichment table + const rows = await page.locator("tbody tr"); + let fileFound = false; + + for (let i = 0; i < (await rows.count()); i++) { + const row = rows.nth(i); + const displayedName = await row + .locator("td.text-left") + .nth(1) + .textContent(); + if (displayedName?.trim() === fileName) { + fileFound = true; + console.log("File found in table:", displayedName); + break; + } + } + + if (!fileFound) { + throw new Error(`File name "${fileName}" not found in the table.`); + } + + // Explore the file + await page.getByRole("button", { name: "Explore" }).click(); + await page.locator("#fnEditor").getByLabel("Editor content;Press Alt+F1") + .fill(` +abc, err = get_enrichment_table_record("${fileName}", { + "protocol_number": to_string!(.protocol_number) +}) +.protocol_keyword = abc.keyword +`); + + // Wait briefly to allow input processing + await page.waitForTimeout(1000); + + // Apply the query + await applyQueryButton(page); + + // Verify that no warning is shown for query execution + const warningElement = page.locator("text=warning Query execution"); + await expect(warningElement).toBeHidden(); + + // Expand the first row in the logs table + await page.waitForTimeout(3000); + await page + .locator( + '[data-test="log-table-column-0-_timestamp"] [data-test="table-row-expand-menu"]' + ) + .click(); + await page.waitForTimeout(1000); + await page + .locator('[data-test="log-expand-detail-key-protocol_keyword-text"]') + .click(); + // Return to the pipeline menu and delete the uploaded table + await page.locator('[data-test="menu-link-\\/pipeline-item"]').click(); + await page.locator('[data-test="function-enrichment-table-tab"]').click(); + await pipelinePage.deleteEnrichmentTableByName(fileName) + + }); + + + test("should display error when CSV not added in enrichment table", async ({ + page, + }) => { + const pipelinePage = new PipelinePage(page); + await pipelinePage.navigateToAddEnrichmentTable(); + await page.getByLabel('Name').fill('test') + + await page.getByRole('button', { name: 'Save' }).click(); + await page.getByText('CSV File is required!').click(); + }) + + + test("should append an enrichment table under functions", async ({ page }) => { + const pipelinePage = new PipelinePage(page); + await pipelinePage.navigateToAddEnrichmentTable(); + + // Generate a unique file name and replace hyphens with underscores + let fileName = `append_${uuidv4()}_csv`.replace(/-/g, "_"); + console.log("Generated File Name:", fileName); + + // First upload step + const fileContentPath = "../test-data/append.csv"; + const inputFile = await page.locator('input[type="file"]'); + await inputFile.setInputFiles(fileContentPath); + + // Enter the file name + await page.fill(".q-input > .q-field__inner > .q-field__control", fileName); + + // Click on 'Save' + await page.getByText("Save").click({ force: true }); + + // Wait for the process to complete + await page.reload(); + await page.waitForTimeout(3000); + await page.getByPlaceholder("Search Enrichment Table").fill(fileName); + await page.waitForTimeout(3000); + + // Explore the uploaded table + await page.getByRole("button", { name: "Explore" }).click(); + await page.locator('[data-test="log-table-column-0-_timestamp"]').click(); + await page.locator('[data-test="close-dialog"]').click(); + await page.waitForTimeout(3000); + + // Navigate to append data to the existing enrichment table + await page.locator('[data-test="menu-link-\\/pipeline-item"]').click(); + await page.locator('[data-test="function-enrichment-table-tab"]').click(); + await page.getByPlaceholder("Search Enrichment Table").fill(fileName); + await page.getByRole("button", { name: "Enrichment Tables" }).click(); + + // Select the append toggle + await page.getByLabel("Append data to existing").locator("div").first().click(); + + + // Upload the CSV again for appending + await inputFile.setInputFiles(fileContentPath); + await page.waitForTimeout(3000); + await page.getByRole('button', { name: 'Save' }).click(); + await page.waitForTimeout(3000); + await page.getByPlaceholder("Search Enrichment Table").fill(fileName); + await page.waitForTimeout(3000); + + // Explore the uploaded table + await page.getByRole("button", { name: "Explore" }).click(); + + const showingText = page.getByText('Showing 1 to 2 out of 2'); + await expect(showingText).toBeVisible() + + await page.locator('[data-test="menu-link-\\/pipeline-item"]').click(); + await page.locator('[data-test="function-enrichment-table-tab"]').click(); + await page.getByPlaceholder("Search Enrichment Table").fill(fileName); + await pipelinePage.deleteEnrichmentTableByName(fileName) + + }); + +});