test: e2e-enrichment-bulkapi-pytest (#5371)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## 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.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Neha P
2024-12-16 17:13:55 +05:30
committed by GitHub
parent 6ee5a54d0b
commit 505bd69aa3
6 changed files with 442 additions and 102 deletions
+110
View File
@@ -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}"
)
+2
View File
@@ -0,0 +1,2 @@
protocol_number,keyword,protocol_description
0,HOPOPT,IPv6 Hop-by-Hop Option
1 protocol_number keyword protocol_description
2 0 HOPOPT IPv6 Hop-by-Hop Option
+14
View File
@@ -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
1 protocol_number keyword protocol_description
2 0 HOPOPT IPv6 Hop-by-Hop Option
3 1 ICMP Internet Control Message
4 2 IGMP Internet Group Management
5 3 GGP Gateway-to-Gateway
6 4 IPv4 IPv4 encapsulation
7 5 ST Stream
8 6 TCP Transmission Control
9 7 CBT CBT
10 8 EGP Exterior Gateway Protocol
11 9 IGP any private interior gateway (used by Cisco for their IGRP)
12 10 BBN-RCC-MON BBN RCC Monitoring
13 11 NVP-II Network Voice Protocol
14 12 PUP PUP
+71 -4
View File
@@ -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;
@@ -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();
@@ -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)
});
});