From 7fe850326aa36b82c314c3e10fdc4f7643ca9bd2 Mon Sep 17 00:00:00 2001 From: Shrinath Rao Date: Thu, 19 Jun 2025 11:15:59 +0530 Subject: [PATCH] test: Folder Structure changes for Alerts, updated yml file for running multiple spec files instead of one (#7212) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### **PR Type** Tests, Enhancement ___ ### **Description** - Fix import paths in Alerts page classes - Add template import and creation methods - Introduce new Alerts E2E, import, UI tests - Update CI workflow with new test specs ___ ### **Changes walkthrough** 📝
Relevant files
Bug fix
2 files
alertDestinationsPage.js
fix import path for common actions                                             
+1/-1     
changeOrg.spec.js
update AlertsPage import path reference                                   
+1/-1     
Enhancement
2 files
alertTemplatesPage.js
add template import locators and methods                                 
+71/-0   
alertsPage.js
improve locators and add timeouts                                               
+8/-6     
Tests
5 files
alerts-e2e-flow.spec.js
add end-to-end alerts flow tests                                                 
+162/-0 
alerts-import.spec.js
add import/export alerts functionality tests                         
+129/-0 
alerts-ui-operations.spec.js
add alerts UI operations validation tests                               
+255/-0 
emailTemplateImport.json
add email template import fixture                                               
+7/-0     
webhookTemplateImport.json
add webhook template import fixture                                           
+7/-0     
Miscellaneous
1 files
alerts.spec.js
comment out legacy alerts spec file                                           
+426/-354
Configuration changes
1 files
playwright.yml
include new alerts spec files in CI                                           
+3/-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 | 4 +- .../ui-testing/pages/alertDestinationsPage.js | 105 ------- .../alertsPages/alertDestinationsPage.js | 186 +++++++++++ .../{ => alertsPages}/alertTemplatesPage.js | 71 +++++ .../pages/{ => alertsPages}/alertsPage.js | 14 +- tests/ui-testing/pages/commonActions.js | 4 +- tests/ui-testing/pages/dashboardPage.js | 23 ++ .../Alerts/alerts-e2e-flow.spec.js | 178 +++++++++++ .../Alerts/alerts-import.spec.js | 288 ++++++++++++++++++ .../alerts-ui-operations.spec.js} | 129 ++------ .../playwright-tests/changeOrg.spec.js | 2 +- .../dashboards/dashboard-folder.spec.js | 38 ++- .../utils/emailDestinationImport.json | 11 + .../ui-testing/utils/emailTemplateImport.json | 7 + .../utils/webhookDestinationImport.json | 10 + .../utils/webhookTemplateImport.json | 7 + 16 files changed, 849 insertions(+), 228 deletions(-) delete mode 100644 tests/ui-testing/pages/alertDestinationsPage.js create mode 100644 tests/ui-testing/pages/alertsPages/alertDestinationsPage.js rename tests/ui-testing/pages/{ => alertsPages}/alertTemplatesPage.js (76%) rename tests/ui-testing/pages/{ => alertsPages}/alertsPage.js (98%) create mode 100644 tests/ui-testing/playwright-tests/Alerts/alerts-e2e-flow.spec.js create mode 100644 tests/ui-testing/playwright-tests/Alerts/alerts-import.spec.js rename tests/ui-testing/playwright-tests/{alerts.spec.js => Alerts/alerts-ui-operations.spec.js} (67%) create mode 100644 tests/ui-testing/utils/emailDestinationImport.json create mode 100644 tests/ui-testing/utils/emailTemplateImport.json create mode 100644 tests/ui-testing/utils/webhookDestinationImport.json create mode 100644 tests/ui-testing/utils/webhookTemplateImport.json diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 77868c15f2..2a998b95df 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -100,7 +100,9 @@ jobs: matrix: browser: [chrome] testfilename: [ - "alerts.spec.js", + "alerts/alerts-e2e-flow.spec.js", + "alerts/alerts-ui-operations.spec.js", + "alerts/alerts-import.spec.js", "changeOrg.spec.js", "dashboards/custom-charts.spec.js", "dashboards/dashboard.spec.js", diff --git a/tests/ui-testing/pages/alertDestinationsPage.js b/tests/ui-testing/pages/alertDestinationsPage.js deleted file mode 100644 index bf398b8443..0000000000 --- a/tests/ui-testing/pages/alertDestinationsPage.js +++ /dev/null @@ -1,105 +0,0 @@ -import { expect, test } from '@playwright/test'; -import { CommonActions } from './commonActions'; - -export class AlertDestinationsPage { - constructor(page) { - this.page = page; - this.commonActions = new CommonActions(page); - - // Navigation locators - this.settingsMenuItem = '[data-test="menu-link-settings-item"]'; - this.destinationsTab = '[data-test="alert-destinations-tab"]'; - - // Destination creation locators - this.addDestinationButton = '[data-test="alert-destination-list-add-alert-btn"]'; - this.destinationNameInput = '[data-test="add-destination-name-input"]'; - this.templateSelect = '[data-test="add-destination-template-select"]'; - this.urlInput = '[data-test="add-destination-url-input"]'; - this.submitButton = '[data-test="add-destination-submit-btn"]'; - this.successMessage = 'Destination saved'; - } - - async navigateToDestinations() { - await this.page.locator(this.settingsMenuItem).click(); - await this.page.locator(this.destinationsTab).click(); - await this.page.waitForTimeout(2000); // Wait for page to load - } - - /** @param {string} destinationName @param {string} url @param {string} templateName */ - async createDestination(destinationName, url, templateName) { - await this.navigateToDestinations(); - await this.page.waitForTimeout(2000); // Wait for page to load - - await this.page.locator(this.addDestinationButton).click(); - await this.page.locator(this.destinationNameInput).click(); - await this.page.locator(this.destinationNameInput).fill(destinationName); - await this.page.waitForTimeout(1000); - - // Handle template selection with scrolling - await this.page.locator(this.templateSelect).click(); - await this.page.waitForTimeout(2000); // Wait for template options to load - - // Use the common scroll function - await this.commonActions.scrollAndFindOption(templateName, 'template'); - - await this.page.waitForTimeout(1000); - - await this.page.locator(this.urlInput).click(); - await this.page.locator(this.urlInput).fill(url); - await this.page.waitForTimeout(1000); - - await this.page.locator(this.submitButton).click(); - await expect(this.page.getByText(this.successMessage)).toBeVisible(); - - // Verify the destination exists by checking all pages - await this.verifyDestinationExists(destinationName); - } - - async findDestinationAcrossPages(destinationName) { - let destinationFound = false; - let isLastPage = false; - - while (!destinationFound && !isLastPage) { - try { - await this.page.getByRole('cell', { name: destinationName }).waitFor({ timeout: 2000 }); - destinationFound = true; - console.log('Found destination:', destinationName); - } catch (error) { - // Check if there's a next page button and if it's enabled - const nextPageButton = this.page.getByRole('button').filter({ hasText: 'chevron_right' }).first(); - if (await nextPageButton.isVisible() && await nextPageButton.isEnabled()) { - await nextPageButton.click(); - await this.page.waitForTimeout(2000); - } else { - isLastPage = true; - } - } - } - return destinationFound; - } - - async verifyDestinationExists(destinationName) { - const found = await this.findDestinationAcrossPages(destinationName); - if (!found) { - throw new Error(`Destination ${destinationName} not found after checking all pages`); - } - } - - async ensureDestinationExists(destinationName, url, templateName) { - await this.navigateToDestinations(); - await this.page.waitForTimeout(2000); - - // Check if destination exists by searching through all pages - const destinationFound = await this.findDestinationAcrossPages(destinationName); - - if (!destinationFound) { - // Destination not found, create new one - await this.createDestination(destinationName, url, templateName); - console.log('Created new destination:', destinationName); - } else { - console.log('Found existing destination:', destinationName); - } - - return destinationName; - } -} \ No newline at end of file diff --git a/tests/ui-testing/pages/alertsPages/alertDestinationsPage.js b/tests/ui-testing/pages/alertsPages/alertDestinationsPage.js new file mode 100644 index 0000000000..27888e0019 --- /dev/null +++ b/tests/ui-testing/pages/alertsPages/alertDestinationsPage.js @@ -0,0 +1,186 @@ +import { expect, test } from '@playwright/test'; +import { CommonActions } from '../commonActions'; + +export class AlertDestinationsPage { + constructor(page) { + this.page = page; + this.commonActions = new CommonActions(page); + + // Navigation locators + this.settingsMenuItem = '[data-test="menu-link-settings-item"]'; + this.destinationsTab = '[data-test="alert-destinations-tab"]'; + this.destinationsListTitle = '[data-test="alert-destinations-list-title"]'; + + // Destination creation locators + this.addDestinationButton = '[data-test="alert-destination-list-add-alert-btn"]'; + this.destinationNameInput = '[data-test="add-destination-name-input"]'; + this.templateSelect = '[data-test="add-destination-template-select"]'; + this.urlInput = '[data-test="add-destination-url-input"]'; + this.submitButton = '[data-test="add-destination-submit-btn"]'; + this.successMessage = 'Destination saved'; + + // Import locators + this.destinationImportButton = '[data-test="destination-import"]'; + this.importJsonUrlTab = '[data-test="tab-import_json_url"]'; + this.destinationImportUrlInput = '[data-test="destination-import-url-input"]'; + this.destinationImportJsonBtn = '[data-test="destination-import-json-btn"]'; + this.destinationImportNameError = '[data-test="destination-import-name-error"]'; + this.destinationImportTemplateInput = '[data-test="destination-import-template-input"]'; + this.destinationImportNameInput = '[data-test="destination-import-name-input"]'; + this.destinationImportCancelBtn = '[data-test="destination-import-cancel-btn"]'; + this.destinationListSearchInput = '[data-test="destination-list-search-input"]'; + this.confirmButton = '[data-test="confirm-button"]'; + this.deleteDestinationButton = '[data-test="alert-destination-list-{destinationName}-delete-destination"]'; + this.importJsonFileTab = '[data-test="tab-import_json_file"]'; + this.destinationImportFileInput = '[data-test="destination-import-file-input"]'; + } + + async navigateToDestinations() { + await this.page.locator(this.settingsMenuItem).click(); + await this.page.locator(this.destinationsTab).click(); + await expect(this.page.locator(this.destinationsListTitle)).toBeVisible(); + } + + /** @param {string} destinationName @param {string} url @param {string} templateName */ + async createDestination(destinationName, url, templateName) { + await this.navigateToDestinations(); + await this.page.waitForTimeout(2000); // Wait for page to load + + await this.page.locator(this.addDestinationButton).click(); + await this.page.locator(this.destinationNameInput).click(); + await this.page.locator(this.destinationNameInput).fill(destinationName); + await this.page.waitForTimeout(1000); + + // Handle template selection with scrolling + await this.page.locator(this.templateSelect).click(); + await this.page.waitForTimeout(2000); // Wait for template options to load + + // Use the common scroll function + await this.commonActions.scrollAndFindOption(templateName, 'template'); + + await this.page.waitForTimeout(1000); + + await this.page.locator(this.urlInput).click(); + await this.page.locator(this.urlInput).fill(url); + await this.page.waitForTimeout(1000); + + await this.page.locator(this.submitButton).click(); + await expect(this.page.getByText(this.successMessage)).toBeVisible(); + + // Verify the destination exists by checking all pages + await this.verifyDestinationExists(destinationName); + } + + async findDestinationAcrossPages(destinationName) { + let destinationFound = false; + let isLastPage = false; + + while (!destinationFound && !isLastPage) { + try { + await this.page.getByRole('cell', { name: destinationName }).waitFor({ timeout: 2000 }); + destinationFound = true; + console.log('Found destination:', destinationName); + } catch (error) { + // Check if there's a next page button and if it's enabled + const nextPageButton = this.page.getByRole('button').filter({ hasText: 'chevron_right' }).first(); + if (await nextPageButton.isVisible() && await nextPageButton.isEnabled()) { + await nextPageButton.click(); + await this.page.waitForTimeout(2000); + } else { + isLastPage = true; + } + } + } + return destinationFound; + } + + async verifyDestinationExists(destinationName) { + const found = await this.findDestinationAcrossPages(destinationName); + if (!found) { + throw new Error(`Destination ${destinationName} not found after checking all pages`); + } + } + + async ensureDestinationExists(destinationName, url, templateName) { + await this.navigateToDestinations(); + await this.page.waitForTimeout(2000); + + // Check if destination exists by searching through all pages + const destinationFound = await this.findDestinationAcrossPages(destinationName); + + if (!destinationFound) { + // Destination not found, create new one + await this.createDestination(destinationName, url, templateName); + console.log('Created new destination:', destinationName); + } else { + console.log('Found existing destination:', destinationName); + } + + return destinationName; + } + + /** + * Import destination from URL + * @param {string} url - URL of the destination JSON + * @param {string} templateName - Name of the template to use + * @param {string} destinationName - Name for the destination + */ + async importDestinationFromUrl(url, templateName, destinationName) { + await this.page.locator(this.destinationImportButton).click(); + await this.page.locator(this.importJsonUrlTab).click(); + await this.page.locator(this.destinationImportUrlInput).click(); + await this.page.locator(this.destinationImportUrlInput).fill(url); + await this.page.waitForTimeout(2000); // Wait for JSON to load + await this.page.locator(this.destinationImportJsonBtn).click(); + await expect(this.page.locator(this.destinationImportNameError)).toBeVisible(); + await this.page.locator(this.destinationImportTemplateInput).click(); + await this.commonActions.scrollAndFindOption(templateName, 'template'); + + await this.page.locator(this.destinationImportNameInput).click(); + await this.page.locator(this.destinationImportNameInput).fill(destinationName); + await this.page.locator(this.destinationImportJsonBtn).click(); + } + + /** + * Delete destination using search + * @param {string} destinationName - Name of the destination to delete + */ + async deleteDestinationWithSearch(destinationName) { + await this.page.locator(this.destinationListSearchInput).click(); + await this.page.locator(this.destinationListSearchInput).fill(destinationName); + await this.page.waitForTimeout(1000); // Wait for search results + await this.page.locator(this.deleteDestinationButton.replace('{destinationName}', destinationName)).click(); + await this.page.locator(this.confirmButton).click(); + await this.page.waitForTimeout(1000); // Wait for deletion to complete + await expect(this.page.getByText('No data available')).toBeVisible(); + } + + /** + * Cancel destination import + */ + async cancelDestinationImport() { + await this.page.locator(this.destinationImportCancelBtn).click(); + await expect(this.page.locator(this.destinationsListTitle)).toBeVisible(); + } + + /** + * Import destination from file + * @param {string} filePath - Path to the destination JSON file + * @param {string} templateName - Name of the template to use + * @param {string} destinationName - Name for the destination + */ + async importDestinationFromFile(filePath, templateName, destinationName) { + await this.page.locator(this.destinationImportButton).click(); + await this.page.locator(this.importJsonFileTab).click(); + await this.page.locator(this.destinationImportFileInput).setInputFiles(filePath); + await this.page.waitForTimeout(2000); // Wait for JSON to load + await this.page.locator(this.destinationImportJsonBtn).click(); + await this.page.waitForTimeout(1000); // Wait for error message + await this.page.locator(this.destinationImportTemplateInput).click(); + await this.commonActions.scrollAndFindOption(templateName, 'template'); + + await this.page.locator(this.destinationImportNameInput).click(); + await this.page.locator(this.destinationImportNameInput).fill(destinationName); + await this.page.locator(this.destinationImportJsonBtn).click(); + } +} \ No newline at end of file diff --git a/tests/ui-testing/pages/alertTemplatesPage.js b/tests/ui-testing/pages/alertsPages/alertTemplatesPage.js similarity index 76% rename from tests/ui-testing/pages/alertTemplatesPage.js rename to tests/ui-testing/pages/alertsPages/alertTemplatesPage.js index 5e178ccce7..fa50f1ac21 100644 --- a/tests/ui-testing/pages/alertTemplatesPage.js +++ b/tests/ui-testing/pages/alertsPages/alertTemplatesPage.js @@ -1,5 +1,6 @@ import { expect, test } from '@playwright/test'; import { test as base } from '@playwright/test'; +import fs from 'fs'; export class AlertTemplatesPage { constructor(page) { @@ -24,6 +25,16 @@ export class AlertTemplatesPage { this.confirmButton = '[data-test="confirm-button"]'; this.templateDeletedMessage = 'Template %s deleted successfully'; this.templateInUseMessage = 'Template is in use for destination {destinationName}'; + + // Template import locators + this.templateImportButton = '[data-test="template-import"]'; + this.importUrlTab = '[data-test="tab-import_json_url"]'; + this.importUrlInput = '[data-test="template-import-url-input"]'; + this.importJsonButton = '[data-test="template-import-json-btn"]'; + this.importNameInput = '[data-test="template-import-name-input"]'; + this.importFileInput = '[data-test="template-import-json-file-input"]'; + this.templateImportSuccessMessage = 'Successfully imported'; + this.templateImportErrorText = 'Template - 1: "email template" creation failed --> Reason: Template name cannot contain \':\', \'#\', \'?\', \'&\', \'%\', \'/\', quotes and space characters'; } async navigateToTemplates(retryCount = 0) { @@ -252,4 +263,64 @@ export class AlertTemplatesPage { throw new Error(`Template ${templateName} not found in the list`); } } + + /** + * Import template from URL + * @param {string} url - URL to import template from + * @param {string} templateName - Name for the imported template + * @param {string} importType - Type of import: 'valid' or 'invalid' + */ + async importTemplateFromUrl(url, templateName, importType = 'valid') { + await this.navigateToTemplates(); + await this.page.waitForTimeout(2000); + + await this.page.locator(this.templateImportButton).click(); + await this.page.locator(this.importUrlTab).click(); + await this.page.locator(this.importUrlInput).click(); + await this.page.locator(this.importUrlInput).fill(url); + await this.page.waitForTimeout(1000); // Small delay after filling URL + await this.page.locator(this.importJsonButton).click(); + await expect(this.page.getByText('Template - 1: The "name"')).toBeVisible(); + await this.page.locator(this.importNameInput).click(); + await this.page.locator(this.importNameInput).fill(templateName); + await this.page.locator(this.importJsonButton).click(); + + if (importType === 'invalid') { + await expect(this.page.locator('pre')).toContainText(this.templateImportErrorText); + } else { + await expect(this.page.getByText(this.templateImportSuccessMessage)).toBeVisible(); + } + } + + /** + * Verify imported template exists + * @param {string} templateName - Name of the imported template + */ + async verifyImportedTemplateExists(templateName) { + await this.page.locator(this.templateSearchInput).click(); + await this.page.locator(this.templateSearchInput).fill(templateName); + await expect(this.page.getByRole('cell', { name: templateName })).toBeVisible(); + } + + /** + * Create template from JSON file + * @param {string} filePath - Path to the JSON file + * @param {string} templateName - Name for the template (will override the name in file) + * @param {string} importType - Type of import: 'valid' or 'invalid' + */ + async createTemplateFromFile(filePath, templateName, importType = 'valid') { + await this.navigateToTemplates(); + await this.page.locator(this.templateImportButton).click(); + await this.page.locator(this.importFileInput).setInputFiles(filePath); + await this.page.locator(this.importJsonButton).click(); + await expect(this.page.getByText('Template - 1: The "name"')).toBeVisible(); + await this.page.locator(this.importNameInput).fill(templateName); + await this.page.locator(this.importJsonButton).click(); + + if (importType === 'invalid') { + await expect(this.page.locator('pre')).toContainText(this.templateImportErrorText); + } else { + await expect(this.page.getByText(this.templateImportSuccessMessage)).toBeVisible(); + } + } } \ No newline at end of file diff --git a/tests/ui-testing/pages/alertsPage.js b/tests/ui-testing/pages/alertsPages/alertsPage.js similarity index 98% rename from tests/ui-testing/pages/alertsPage.js rename to tests/ui-testing/pages/alertsPages/alertsPage.js index c298ff23d7..0b1d86ef5c 100644 --- a/tests/ui-testing/pages/alertsPage.js +++ b/tests/ui-testing/pages/alertsPages/alertsPage.js @@ -1,6 +1,6 @@ import { expect, test } from '@playwright/test'; import fs from 'fs'; -import { CommonActions } from './commonActions'; +import { CommonActions } from '../commonActions'; export class AlertsPage { constructor(page) { @@ -121,7 +121,7 @@ export class AlertsPage { * @param {string} folderName - Name of the folder to navigate to */ async navigateToFolder(folderName) { - await this.page.getByText(folderName).click(); + await this.page.getByText(folderName).first().click(); // Wait for the folder content to load by checking for either the table or no data message await Promise.race([ this.page.locator('table').waitFor({ state: 'visible', timeout: 30000 }), @@ -202,13 +202,14 @@ export class AlertsPage { /** @param {string} alertName */ async verifyAlertCreated(alertName) { const nameToVerify = alertName || this.currentAlertName; // Use provided name or stored name - await expect(this.page.getByRole('cell', { name: nameToVerify })).toBeVisible(); - await expect(this.page.locator(this.pauseStartAlert.replace('{alertName}', nameToVerify))).toBeVisible(); + await expect(this.page.getByRole('cell', { name: nameToVerify }).first()).toBeVisible(); + await expect(this.page.locator(this.pauseStartAlert.replace('{alertName}', nameToVerify)).first()).toBeVisible(); } /** @param {string} alertName */ async updateAlert(alertName) { - await this.page.locator(this.alertUpdateButton.replace('{alertName}', alertName)).click(); + // Use first() to get the first instance of the alert + await this.page.locator(this.alertUpdateButton.replace('{alertName}', alertName)).first().click(); await expect(this.page.getByText(this.alertSetupText)).toBeVisible(); await this.page.waitForTimeout(1000); // Wait for form to load @@ -460,7 +461,8 @@ export class AlertsPage { const downloadPromise = this.page.waitForEvent('download'); await this.page.locator('[data-test="alert-list-export-alerts-btn"]').click(); const download = await downloadPromise; - await expect(this.page.getByText('Successfully exported 2 alerts')).toBeVisible(); + await this.page.waitForTimeout(2000); // Wait for the export process to complete + await expect(this.page.getByText('Successfully exported')).toBeVisible({ timeout: 10000 }); return download; } diff --git a/tests/ui-testing/pages/commonActions.js b/tests/ui-testing/pages/commonActions.js index c886664baa..26bc0b1c25 100644 --- a/tests/ui-testing/pages/commonActions.js +++ b/tests/ui-testing/pages/commonActions.js @@ -37,8 +37,8 @@ export class CommonActions { async scrollAndFindOption(optionName, optionType) { const dropdown = this.page.locator('.q-menu'); let optionFound = false; - let maxScrolls = 20; - let scrollAmount = 300; + let maxScrolls = 50; + let scrollAmount = 1000; let totalScrolled = 0; while (!optionFound && maxScrolls > 0) { diff --git a/tests/ui-testing/pages/dashboardPage.js b/tests/ui-testing/pages/dashboardPage.js index d322dbdd7b..e9cab5c6ac 100644 --- a/tests/ui-testing/pages/dashboardPage.js +++ b/tests/ui-testing/pages/dashboardPage.js @@ -73,6 +73,22 @@ export class DashboardPage { await this.page.locator('[data-test="confirm-button"]:visible').click(); await expect(this.page.getByRole('alert')).toContainText('Dashboard deleted successfully.'); } + + async deleteSearchedDashboard(dashboardName) { + // First search for the dashboard + await this.page.locator('[data-test="dashboard-search"]').click(); + await this.page.locator('[data-test="dashboard-search"]').fill(dashboardName); + await this.page.waitForTimeout(1000); + + // Find the dashboard row and click the delete button + const dashboardRow = this.page.getByRole("row", { name: new RegExp(`.*${dashboardName}`) }); + await dashboardRow.locator('[data-test="dashboard-delete"]').click(); + + // Confirm deletion + await this.page.locator('[data-test="confirm-button"]').click(); + await expect(this.page.getByRole('alert')).toContainText('Dashboard deleted successfully.'); + } + async setTimeToPast30Seconds() { // Set the time filter to the last 30 seconds await this.page.locator(this.dateTimeButton).click(); @@ -82,6 +98,13 @@ export class DashboardPage { // Verify that the time filter displays "Past 30 Seconds" await expect(this.page.locator(this.dateTimeButton)).toContainText(Past30SecondsValue); } + async verifyShareDashboardLink(randomDashboardName){ + await this.page.locator('[data-test="dashboard-share-btn"]').click(); + await expect(this.page.getByText('Link copied successfully')).toBeVisible(); + const copiedUrl = await this.page.evaluate(() => navigator.clipboard.readText()); + await this.page.goto(copiedUrl); + await expect(this.page.getByText(randomDashboardName)).toBeVisible(); + } async setDateTime() { await expect(this.page.locator(this.dateTimeButton)).toBeVisible(); await this.page.locator(this.dateTimeButton).click(); diff --git a/tests/ui-testing/playwright-tests/Alerts/alerts-e2e-flow.spec.js b/tests/ui-testing/playwright-tests/Alerts/alerts-e2e-flow.spec.js new file mode 100644 index 0000000000..b2709427d7 --- /dev/null +++ b/tests/ui-testing/playwright-tests/Alerts/alerts-e2e-flow.spec.js @@ -0,0 +1,178 @@ +import { test, expect } from "../baseFixtures"; +import logData from "../../cypress/fixtures/log.json"; +import { AlertsPage } from '../../pages/alertsPages/alertsPage.js'; +import { AlertTemplatesPage } from '../../pages/alertsPages/alertTemplatesPage.js'; +import { AlertDestinationsPage } from '../../pages/alertsPages/alertDestinationsPage.js'; +import { CommonActions } from '../../pages/commonActions.js'; +import DashboardFolder from '../../pages/dashboardPages/dashboard-folder.js'; + +/** + * Helper function for login + * Handles the initial login process and navigation + */ +async function login(page) { + await page.goto(process.env["ZO_BASE_URL"]); + await page.waitForTimeout(1000); + if (await page.getByText('Login as internal user').isVisible()) { + await page.getByText('Login as internal user').click(); + } + await page + .locator('[data-cy="login-user-id"]') + .fill(process.env["ZO_ROOT_USER_EMAIL"]); + await page + .locator('[data-cy="login-password"]') + .fill(process.env["ZO_ROOT_USER_PASSWORD"]); + await page.locator('[data-cy="login-sign-in"]').click(); + await page.waitForTimeout(4000); + await page.goto(process.env["ZO_BASE_URL"]); +} + +test.describe("Alerts E2E Flow", () => { + // Shared test variables + let alertsPage; + let templatesPage; + let destinationsPage; + let commonActions; + let dashboardFolders; + let createdTemplateName; + let createdDestinationName; + let sharedRandomValue; + + /** + * Setup for each test + * - Logs in + * - Generates shared random value + * - Ingests test data (except for scheduled alert test) + * - Navigates to alerts page + */ + test.beforeEach(async ({ page }, testInfo) => { + await login(page); + alertsPage = new AlertsPage(page); + templatesPage = new AlertTemplatesPage(page); + destinationsPage = new AlertDestinationsPage(page); + commonActions = new CommonActions(page); + dashboardFolders = new DashboardFolder(page); + await page.waitForTimeout(5000); + + // Generate shared random value if not already generated + if (!sharedRandomValue) { + sharedRandomValue = alertsPage.generateRandomString(); + console.log('Generated shared random value for this run:', sharedRandomValue); + } + + // Skip data ingestion for scheduled alert test + if (!test.info().title.includes('Scheduled Alert')) { + // Ingest test data using common actions + const streamName = 'auto_playwright_stream'; + await commonActions.ingestTestData(streamName); + } + + // Navigate to alerts page + await page.goto( + `${logData.alertUrl}?org_identifier=${process.env["ORGNAME"]}` + ); + }); + + /** + * Test: Complete E2E flow for alerts + * Tests all major alert operations in sequence + */ + test('Alerts E2E Flow - Create, Update, Move, Clone, Delete, Pause, Resume', { + tag: ['@e2eAlerts', '@all', '@alerts'] + }, async ({ page }) => { + // Initialize page objects + alertsPage = new AlertsPage(page); + templatesPage = new AlertTemplatesPage(page); + destinationsPage = new AlertDestinationsPage(page); + commonActions = new CommonActions(page); + dashboardFolders = new DashboardFolder(page); + + // Test data setup + const streamName = 'auto_playwright_stream'; + const column = 'job'; + const value = 'test'; + + // Ensure prerequisites exist + createdTemplateName = 'auto_playwright_template_' + sharedRandomValue; + await templatesPage.ensureTemplateExists(createdTemplateName); + createdDestinationName = 'auto_playwright_destination_' + sharedRandomValue; + const slackUrl = "DEMO"; + await destinationsPage.ensureDestinationExists(createdDestinationName, slackUrl, createdTemplateName); + + // Navigate to alerts tab + await commonActions.navigateToAlerts(); + await page.waitForTimeout(2000); + + // ===== First Iteration: Initial Alert Creation and Management ===== + // Create and verify folder + const folderName = 'auto_' + sharedRandomValue; + await alertsPage.createFolder(folderName, 'Test Automation Folder'); + await alertsPage.verifyFolderCreated(folderName); + console.log('Successfully created folder:', folderName); + + // Navigate to folder and create first alert + await alertsPage.navigateToFolder(folderName); + const alertName = await alertsPage.createAlert(streamName, column, value, createdDestinationName, sharedRandomValue); + await alertsPage.verifyAlertCreated(alertName); + console.log('Successfully created alert:', alertName); + + // Clone first alert + await alertsPage.cloneAlert(alertName, 'logs', streamName); + console.log('Successfully cloned alert:', alertName); + + // Delete all instances of the cloned alert + await alertsPage.deleteImportedAlert(alertName); + console.log('Successfully deleted all instances of alert:', alertName); + + // ===== Second Iteration: New Alert Creation and Management ===== + // Navigate back to folder and create new alert + await alertsPage.navigateToFolder(folderName); + await page.waitForTimeout(2000); + + const newAlertName = await alertsPage.createAlert(streamName, column, value, createdDestinationName, sharedRandomValue); + await alertsPage.verifyAlertCreated(newAlertName); + console.log('Successfully created new alert:', newAlertName); + + // Update the new alert's operator + await alertsPage.updateAlert(newAlertName); + console.log('Successfully updated new alert:', newAlertName); + + // Clone the new alert + await alertsPage.cloneAlert(newAlertName, 'logs', streamName); + console.log('Successfully cloned new alert:', newAlertName); + + // ===== Alert Pause and Resume ===== + // Pause the new alert + await alertsPage.pauseAlert(newAlertName); + console.log('Successfully paused alert:', newAlertName); + + // Resume the new alert + await alertsPage.resumeAlert(newAlertName); + console.log('Successfully resumed alert:', newAlertName); + + // ===== Alert Movement and Cleanup ===== + // Move alerts to target folder + const targetFolderName = 'testfoldermove'; + await alertsPage.ensureFolderExists(targetFolderName, 'Test Folder for Moving Alerts'); + await alertsPage.moveAllAlertsToFolder(targetFolderName); + await page.waitForTimeout(2000); + + await dashboardFolders.searchFolder(folderName); + await expect(page.locator(`text=${folderName}`)).toBeVisible(); + await dashboardFolders.deleteFolder(folderName); + + // Verify alerts in target folder + await page.waitForTimeout(2000); + await dashboardFolders.searchFolder(targetFolderName); + await expect(page.locator(`text=${targetFolderName}`)).toBeVisible(); + await alertsPage.navigateToFolder(targetFolderName); + await page.waitForTimeout(2000); + + // Search and verify alert instance + await alertsPage.searchAlert(newAlertName); + await alertsPage.verifySearchResults(2); + + // Delete the alert + await alertsPage.deleteAlertByRow(newAlertName); + }); +}); \ No newline at end of file diff --git a/tests/ui-testing/playwright-tests/Alerts/alerts-import.spec.js b/tests/ui-testing/playwright-tests/Alerts/alerts-import.spec.js new file mode 100644 index 0000000000..b7a66f0af4 --- /dev/null +++ b/tests/ui-testing/playwright-tests/Alerts/alerts-import.spec.js @@ -0,0 +1,288 @@ +import { test, expect } from "../baseFixtures"; +import logData from "../../cypress/fixtures/log.json"; +import { AlertsPage } from '../../pages/alertsPages/alertsPage.js'; +import { AlertTemplatesPage } from '../../pages/alertsPages/alertTemplatesPage.js'; +import { AlertDestinationsPage } from '../../pages/alertsPages/alertDestinationsPage.js'; +import { CommonActions } from '../../pages/commonActions.js'; +import DashboardFolder from '../../pages/dashboardPages/dashboard-folder.js'; + +/** + * Helper function for login + * Handles the initial login process and navigation + */ +async function login(page) { + await page.goto(process.env["ZO_BASE_URL"]); + await page.waitForTimeout(1000); + if (await page.getByText('Login as internal user').isVisible()) { + await page.getByText('Login as internal user').click(); + } + await page + .locator('[data-cy="login-user-id"]') + .fill(process.env["ZO_ROOT_USER_EMAIL"]); + await page + .locator('[data-cy="login-password"]') + .fill(process.env["ZO_ROOT_USER_PASSWORD"]); + await page.locator('[data-cy="login-sign-in"]').click(); + await page.waitForTimeout(4000); + await page.goto(process.env["ZO_BASE_URL"]); +} + +test.describe("Alerts Import/Export", () => { + // Shared test variables + let alertsPage; + let templatesPage; + let destinationsPage; + let commonActions; + let sharedRandomValue; + let dashboardFolders; + + /** + * Setup for each test + * - Logs in + * - Initializes page objects + * - Generates shared random value + * - Ingests test data + * - Navigates to alerts page + */ + test.beforeEach(async ({ page }, testInfo) => { + await login(page); + alertsPage = new AlertsPage(page); + templatesPage = new AlertTemplatesPage(page); + destinationsPage = new AlertDestinationsPage(page); + commonActions = new CommonActions(page); + await page.waitForTimeout(5000); + + // Generate shared random value if not already generated + if (!sharedRandomValue) { + sharedRandomValue = alertsPage.generateRandomString(); + console.log('Generated shared random value for this run:', sharedRandomValue); + } + + // Ingest test data using common actions + const streamName = 'auto_playwright_stream'; + await commonActions.ingestTestData(streamName); + + // Navigate to alerts page + await page.goto( + `${logData.alertUrl}?org_identifier=${process.env["ORGNAME"]}` + ); + }); + + /** + * Test: Import/Export Alert Functionality + * Tests importing and exporting alerts + */ + test('Import/Export Alert Functionality', { + tag: ['@all', '@alerts', '@alertsImportExport'] + }, async ({ page }) => { + // Initialize page objects + alertsPage = new AlertsPage(page); + templatesPage = new AlertTemplatesPage(page); + destinationsPage = new AlertDestinationsPage(page); + commonActions = new CommonActions(page); + dashboardFolders = new DashboardFolder(page); + + // Create template + const templateName = 'auto_playwright_template_' + sharedRandomValue; + await templatesPage.createTemplate(templateName); + console.log('Created template:', templateName); + + // Create destination + const destinationName = 'auto_playwright_destination_' + sharedRandomValue; + const slackUrl = "DEMO"; + await destinationsPage.ensureDestinationExists(destinationName, slackUrl, templateName); + console.log('Created destination:', destinationName); + + // Navigate to alerts page + await commonActions.navigateToAlerts(); + + // Create folder + const folderName = 'auto_' + sharedRandomValue; + await alertsPage.createFolder(folderName, 'Test Automation Folder'); + console.log('Created folder:', folderName); + + // Create alert + const streamName = 'auto_playwright_stream'; + const column = 'job'; + const value = 'test'; + await alertsPage.navigateToFolder(folderName); + await page.waitForTimeout(2000); + const alertName = await alertsPage.createAlert(streamName, column, value, destinationName, sharedRandomValue); + await alertsPage.verifyAlertCreated(alertName); + console.log('Successfully created alert:', alertName); + await page.waitForTimeout(2000); + + // Export alerts + const download = await alertsPage.exportAlerts(); + const downloadPath = `./alerts-${new Date().toISOString().split('T')[0]}-${streamName}.json`; + await download.saveAs(downloadPath); + + // Test invalid import + await alertsPage.importInvalidFile('utils/td150.json'); + + // Import valid file + await alertsPage.importValidFile(downloadPath); + + // Clean up imported alert + await alertsPage.deleteImportedAlert(alertName); + + await dashboardFolders.searchFolder(folderName); + await expect(page.locator(`text=${folderName}`)).toBeVisible(); + await dashboardFolders.deleteFolder(folderName); + + await alertsPage.cleanupDownloadedFile(downloadPath); + }); + + /** + * Test: Template Import from URL and Direct Creation + * Tests importing templates from URL and creating templates directly from files + */ + test('Template Import from URL and Direct Template Creation', { + tag: ['@templateImport', '@all', '@alerts'] + }, async ({ page }) => { + // Initialize page objects + alertsPage = new AlertsPage(page); + templatesPage = new AlertTemplatesPage(page); + destinationsPage = new AlertDestinationsPage(page); + commonActions = new CommonActions(page); + dashboardFolders = new DashboardFolder(page); + + // Test URLs + const webhookTemplateUrl = 'https://raw.githubusercontent.com/openobserve/alert_tests/refs/heads/main/Webhook_Template_Import.json'; + const emailTemplateUrl = 'https://raw.githubusercontent.com/openobserve/alert_tests/refs/heads/main/Email_Template_Import.json'; + + // Test 1: Valid template import from URL (Webhook template) + const urlWebhookTemplateName = 'auto_url_webhook_template_' + sharedRandomValue; + await templatesPage.importTemplateFromUrl(webhookTemplateUrl, urlWebhookTemplateName, 'valid'); + console.log('WEBHOOK URL template IMPORTED:', urlWebhookTemplateName); + + // Verify imported webhook template exists + await templatesPage.verifyImportedTemplateExists(urlWebhookTemplateName); + console.log('Successfully verified imported webhook template exists:', urlWebhookTemplateName); + + // Delete URL imported webhook template + await templatesPage.deleteTemplateAndVerify(urlWebhookTemplateName); + console.log('WEBHOOK URL template DELETED:', urlWebhookTemplateName); + + // Test 2: Valid template import from URL (Email template) + const urlEmailTemplateName = 'auto_url_email_template_' + sharedRandomValue; + await templatesPage.importTemplateFromUrl(emailTemplateUrl, urlEmailTemplateName, 'valid'); + console.log('EMAIL URL template IMPORTED:', urlEmailTemplateName); + + // Verify imported email template exists + await templatesPage.verifyImportedTemplateExists(urlEmailTemplateName); + console.log('Successfully verified imported email template exists:', urlEmailTemplateName); + + // Delete URL imported email template + await templatesPage.deleteTemplateAndVerify(urlEmailTemplateName); + console.log('EMAIL URL template DELETED:', urlEmailTemplateName); + + // Test 3: Valid direct webhook template creation + const directWebhookTemplateName = 'auto_direct_webhook_template_' + sharedRandomValue; + await templatesPage.createTemplateFromFile('utils/webhookTemplateImport.json', directWebhookTemplateName, 'valid'); + console.log('WEBHOOK Direct template IMPORTED:', directWebhookTemplateName); + + // Verify direct webhook template exists + await templatesPage.verifyImportedTemplateExists(directWebhookTemplateName); + console.log('Successfully verified direct webhook template exists:', directWebhookTemplateName); + + // Delete direct webhook template + await templatesPage.deleteTemplateAndVerify(directWebhookTemplateName); + console.log('WEBHOOK Direct template DELETED:', directWebhookTemplateName); + + // Test 4: Valid direct email template creation + const directEmailTemplateName = 'auto_direct_email_template_' + sharedRandomValue; + await templatesPage.createTemplateFromFile('utils/emailTemplateImport.json', directEmailTemplateName, 'valid'); + console.log('EMAIL Direct template IMPORTED:', directEmailTemplateName); + + // Verify direct email template exists + await templatesPage.verifyImportedTemplateExists(directEmailTemplateName); + console.log('Successfully verified direct email template exists:', directEmailTemplateName); + + // Delete direct email template + await templatesPage.deleteTemplateAndVerify(directEmailTemplateName); + console.log('EMAIL Direct template DELETED:', directEmailTemplateName); + + console.log('All template import tests completed successfully with cleanup'); + }); + + /** + * Test: Destination Import from URL and File + * Tests importing destinations from both URL and local files + */ + test('Destination Import from URL and File', { + tag: ['@destinationImport', '@all', '@alerts'] + }, async ({ page }) => { + // Initialize page objects + alertsPage = new AlertsPage(page); + templatesPage = new AlertTemplatesPage(page); + destinationsPage = new AlertDestinationsPage(page); + commonActions = new CommonActions(page); + dashboardFolders = new DashboardFolder(page); + + // Create webhook template for webhook destination + const webhookTemplateName = 'auto_webhook_template_' + sharedRandomValue; + await templatesPage.createTemplate(webhookTemplateName); + console.log('Created webhook template:', webhookTemplateName); + + // Import email template for email destination + const emailTemplateUrl = 'https://raw.githubusercontent.com/openobserve/alert_tests/refs/heads/main/Email_Template_Import.json'; + const emailTemplateName = 'auto_email_template_' + sharedRandomValue; + await templatesPage.importTemplateFromUrl(emailTemplateUrl, emailTemplateName, 'valid'); + console.log('Imported email template:', emailTemplateName); + await templatesPage.verifyImportedTemplateExists(emailTemplateName); + console.log('Successfully verified imported email template exists:', emailTemplateName); + + // Navigate to destinations page + await destinationsPage.navigateToDestinations(); + console.log('Navigated to destinations page'); + + // Test 1: Import webhook destination from URL + const webhookDestinationUrl = 'https://raw.githubusercontent.com/openobserve/alert_tests/refs/heads/main/Webhook_Destination_Import.json'; + const webhookDestinationName = 'auto_dest_name_' + sharedRandomValue; + await destinationsPage.importDestinationFromUrl(webhookDestinationUrl, webhookTemplateName, webhookDestinationName); + await expect(page.getByText('Successfully imported')).toBeVisible(); + console.log('Imported webhook destination from URL:', webhookDestinationName); + await destinationsPage.verifyDestinationExists(webhookDestinationName); + console.log('Successfully verified webhook destination exists'); + await destinationsPage.deleteDestinationWithSearch(webhookDestinationName); + console.log('Successfully deleted webhook destination'); + + // Test 2: Import webhook destination from file + await destinationsPage.importDestinationFromFile('utils/webhookDestinationImport.json', webhookTemplateName, webhookDestinationName); + console.log('Imported webhook destination from file:', webhookDestinationName); + await expect(page.getByText('Successfully imported')).toBeVisible(); + await destinationsPage.verifyDestinationExists(webhookDestinationName); + console.log('Successfully verified webhook destination exists'); + await destinationsPage.deleteDestinationWithSearch(webhookDestinationName); + console.log('Successfully deleted webhook destination'); + + // Test 3: Import email destination from URL + const emailDestinationUrl = 'https://raw.githubusercontent.com/openobserve/alert_tests/refs/heads/main/Email_Destination_Import.json'; + const emailDestinationName = 'auto_email_dest_' + sharedRandomValue; + await destinationsPage.importDestinationFromUrl(emailDestinationUrl, emailTemplateName, emailDestinationName); + console.log('Imported email destination from URL:', emailDestinationName); + await expect(page.getByText('Destination - 1: "')).toBeVisible(); + await destinationsPage.cancelDestinationImport(); + console.log('Successfully cancelled email destination import'); + + // Test 4: Import email destination from file + await destinationsPage.importDestinationFromFile('utils/emailDestinationImport.json', emailTemplateName, emailDestinationName); + console.log('Imported email destination from file:', emailDestinationName); + await expect(page.getByText('Destination - 1: "')).toBeVisible(); + await destinationsPage.cancelDestinationImport(); + console.log('Successfully cancelled email destination import'); + + // Navigate to templates page for cleanup + await templatesPage.navigateToTemplates(); + console.log('Navigated to templates page for cleanup'); + + // Clean up templates + await templatesPage.deleteTemplateAndVerify(webhookTemplateName); + console.log('Webhook template deleted:', webhookTemplateName); + await templatesPage.deleteTemplateAndVerify(emailTemplateName); + console.log('Email template deleted:', emailTemplateName); + + console.log('All destination import tests completed successfully with cleanup'); + }); +}); \ No newline at end of file diff --git a/tests/ui-testing/playwright-tests/alerts.spec.js b/tests/ui-testing/playwright-tests/Alerts/alerts-ui-operations.spec.js similarity index 67% rename from tests/ui-testing/playwright-tests/alerts.spec.js rename to tests/ui-testing/playwright-tests/Alerts/alerts-ui-operations.spec.js index 7efba51410..fba084af8b 100644 --- a/tests/ui-testing/playwright-tests/alerts.spec.js +++ b/tests/ui-testing/playwright-tests/Alerts/alerts-ui-operations.spec.js @@ -1,9 +1,10 @@ -import { test, expect } from "./baseFixtures"; -import logData from "../../ui-testing/cypress/fixtures/log.json"; -import { AlertsPage } from '../../ui-testing/pages/alertsPage.js'; -import { AlertTemplatesPage } from '../../ui-testing/pages/alertTemplatesPage.js'; -import { AlertDestinationsPage } from '../../ui-testing/pages/alertDestinationsPage.js'; -import { CommonActions } from '../../ui-testing/pages/commonActions.js'; +import { test, expect } from "../baseFixtures"; +import logData from "../../cypress/fixtures/log.json"; +import { AlertsPage } from '../../pages/alertsPages/alertsPage.js'; +import { AlertTemplatesPage } from '../../pages/alertsPages/alertTemplatesPage.js'; +import { AlertDestinationsPage } from '../../pages/alertsPages/alertDestinationsPage.js'; +import { CommonActions } from '../../pages/commonActions.js'; +import DashboardFolder from '../../pages/dashboardPages/dashboard-folder.js'; /** * Helper function for login @@ -26,7 +27,7 @@ async function login(page) { await page.goto(process.env["ZO_BASE_URL"]); } -test.describe("Alerts Module testcases", () => { +test.describe("Alerts UI Operations", () => { // Shared test variables let alertsPage; let templatesPage; @@ -35,6 +36,7 @@ test.describe("Alerts Module testcases", () => { let createdTemplateName; let createdDestinationName; let sharedRandomValue; + let dashboardFolders; /** * Setup for each test @@ -91,107 +93,6 @@ test.describe("Alerts Module testcases", () => { console.log('Created destination:', createdDestinationName); }); - /** - * Test: Complete E2E flow for alerts - * Tests all major alert operations in sequence - */ - test('Alerts E2E Flow - Create, Update, Move, Clone, Delete, Pause, Resume', { - tag: ['@e2eAlerts', '@all', '@alerts'] - }, async ({ page }) => { - // Test data setup - const streamName = 'auto_playwright_stream'; - const column = 'job'; - const value = 'test'; - - // Ensure prerequisites exist - createdTemplateName = 'auto_playwright_template_' + sharedRandomValue; - await templatesPage.ensureTemplateExists(createdTemplateName); - createdDestinationName = 'auto_playwright_destination_' + sharedRandomValue; - const slackUrl = "DEMO"; - await destinationsPage.ensureDestinationExists(createdDestinationName, slackUrl, createdTemplateName); - - // Navigate to alerts tab - await commonActions.navigateToAlerts(); - await page.waitForTimeout(2000); - - // ===== First Iteration: Initial Alert Creation and Management ===== - // Create and verify folder - const folderName = 'auto_' + sharedRandomValue; - await alertsPage.createFolder(folderName, 'Test Automation Folder'); - await alertsPage.verifyFolderCreated(folderName); - console.log('Successfully created folder:', folderName); - - // Navigate to folder and create first alert - await alertsPage.navigateToFolder(folderName); - const alertName = await alertsPage.createAlert(streamName, column, value, createdDestinationName, sharedRandomValue); - await alertsPage.verifyAlertCreated(alertName); - console.log('Successfully created alert:', alertName); - - // Clone first alert - await alertsPage.cloneAlert(alertName, 'logs', streamName); - console.log('Successfully cloned alert:', alertName); - - // ===== Import/Export Flow ===== - // Export alerts - const download = await alertsPage.exportAlerts(); - const downloadPath = `./alerts-${new Date().toISOString().split('T')[0]}-${streamName}.json`; - await download.saveAs(downloadPath); - - // Test invalid import - await alertsPage.importInvalidFile('utils/td150.json'); - - // Import valid file - await alertsPage.importValidFile(downloadPath); - - // Clean up imported alert - await alertsPage.deleteImportedAlert(alertName); - await alertsPage.cleanupDownloadedFile(downloadPath); - - // ===== Second Iteration: New Alert Creation and Management ===== - // Navigate back to folder and create new alert - await alertsPage.navigateToFolder(folderName); - await page.waitForTimeout(2000); - - const newAlertName = await alertsPage.createAlert(streamName, column, value, createdDestinationName, sharedRandomValue); - await alertsPage.verifyAlertCreated(newAlertName); - console.log('Successfully created new alert:', newAlertName); - - // Update the new alert's operator - await alertsPage.updateAlert(newAlertName); - console.log('Successfully updated new alert:', newAlertName); - - // Clone the new alert - await alertsPage.cloneAlert(newAlertName, 'logs', streamName); - console.log('Successfully cloned new alert:', newAlertName); - - // ===== Alert Pause and Resume ===== - // Pause the new alert - await alertsPage.pauseAlert(newAlertName); - console.log('Successfully paused alert:', newAlertName); - - // Resume the new alert - await alertsPage.resumeAlert(newAlertName); - console.log('Successfully resumed alert:', newAlertName); - - // ===== Alert Movement and Cleanup ===== - // Move alerts to target folder - const targetFolderName = 'testfoldermove'; - await alertsPage.ensureFolderExists(targetFolderName, 'Test Folder for Moving Alerts'); - await alertsPage.moveAllAlertsToFolder(targetFolderName); - - // Verify alerts in target folder - await page.waitForTimeout(2000); - await alertsPage.navigateToFolder(targetFolderName); - await page.waitForTimeout(2000); - - // Search and verify alert instance - await alertsPage.searchAlert(newAlertName); - await alertsPage.verifySearchResults(2); - - // Delete the alert - await alertsPage.deleteAlertByRow(newAlertName); - }); - /** * Test: Delete alert template functionality * Verifies template deletion and in-use scenarios @@ -218,6 +119,7 @@ test.describe("Alerts Module testcases", () => { test('Create and Delete Scheduled Alert with SQL Query', { tag: ['@scheduledAlerts', '@all', '@alerts'] }, async ({ page }) => { + dashboardFolders = new DashboardFolder(page); const streamName = 'auto_playwright_stream'; // Ensure prerequisites exist @@ -248,6 +150,9 @@ test.describe("Alerts Module testcases", () => { // Clean up await alertsPage.deleteAlertByRow(alertName); + await dashboardFolders.searchFolder(folderName); + await expect(page.locator(`text=${folderName}`)).toBeVisible(); + await dashboardFolders.deleteFolder(folderName); }); /** @@ -262,6 +167,7 @@ test.describe("Alerts Module testcases", () => { templatesPage = new AlertTemplatesPage(page); destinationsPage = new AlertDestinationsPage(page); commonActions = new CommonActions(page); + dashboardFolders = new DashboardFolder(page); // Create template const templateName = 'auto_playwright_template_' + sharedRandomValue; @@ -341,8 +247,13 @@ test.describe("Alerts Module testcases", () => { await alertsPage.ensureFolderExists(targetFolderName, 'Test Folder for Moving Alerts'); await alertsPage.moveAllAlertsToFolder(targetFolderName); + await dashboardFolders.searchFolder(folderName); + await expect(page.locator(`text=${folderName}`)).toBeVisible(); + await dashboardFolders.deleteFolder(folderName); + // Verify alerts in target folder await page.waitForTimeout(2000); + await dashboardFolders.searchFolder(targetFolderName); await alertsPage.navigateToFolder(targetFolderName); await page.waitForTimeout(2000); @@ -353,4 +264,4 @@ test.describe("Alerts Module testcases", () => { // Delete the alert await alertsPage.deleteAlertByRow(alertName); }); -}); +}); \ No newline at end of file diff --git a/tests/ui-testing/playwright-tests/changeOrg.spec.js b/tests/ui-testing/playwright-tests/changeOrg.spec.js index 255382961f..05e330b9ed 100644 --- a/tests/ui-testing/playwright-tests/changeOrg.spec.js +++ b/tests/ui-testing/playwright-tests/changeOrg.spec.js @@ -10,7 +10,7 @@ import { PipelinesPage } from "../pages/pipelinesPage.js"; import { DashboardPage } from "../pages/dashboardPage.js"; import { StreamsPage } from "../pages/streamsPage.js"; import { ReportsPage } from "../pages/reportsPage.js"; -import { AlertsPage } from "../pages/alertsPage.js"; +import { AlertsPage } from "../pages/alertsPages/alertsPage.js"; import { DataPage } from "../pages/dataPage.js"; import { IamPage } from "../pages/iamPage.js"; import { ManagementPage } from "../pages/managementPage.js"; diff --git a/tests/ui-testing/playwright-tests/dashboards/dashboard-folder.spec.js b/tests/ui-testing/playwright-tests/dashboards/dashboard-folder.spec.js index 532cc73cb1..a50e77db27 100644 --- a/tests/ui-testing/playwright-tests/dashboards/dashboard-folder.spec.js +++ b/tests/ui-testing/playwright-tests/dashboards/dashboard-folder.spec.js @@ -1,10 +1,27 @@ -import { test, expect } from "../baseFixtures.js"; +import { test as base, expect } from "../baseFixtures.js"; import logData from "../../cypress/fixtures/log.json"; import { login } from "../utils/dashLogin.js"; import { ingestion } from "../utils/dashIngestion.js"; import { waitForDashboardPage } from "../utils/dashCreation.js"; import DashboardListPage from "../../pages/dashboardPages/dashboard-list.js"; import DashboardFolder from "../../pages/dashboardPages/dashboard-folder.js"; +import DashboardCreate from "../../pages/dashboardPages/dashboard-create.js"; +import { DashboardPage } from "../../pages/dashboardPage.js"; +import { AlertsPage } from '../../pages/alertsPages/alertsPage.js'; + +const randomDashboardName = + "Dashboard_" + Math.random().toString(36).substr(2, 9); + +// Extend Playwright test to grant clipboard permissions +export const test = base.extend({ + context: async ({ browser }, use) => { + const context = await browser.newContext({ + permissions: ["clipboard-read", "clipboard-write"], + }); + await use(context); + await context.close(); + }, +}); test.describe.configure({ mode: "parallel" }); @@ -22,11 +39,14 @@ test.describe("dashboard folder testcases", () => { await orgNavigation; }); - test("should create and delete a unique folder, and verify it's deleted", async ({ + test.skip("Should create and delete a unique folder, and verify it's deleted" ,async ({ page, }) => { const dashboardPage = new DashboardListPage(page); const dashboardFolders = new DashboardFolder(page); + const dashboardCreate = new DashboardCreate(page); + const dashboardPageActions = new DashboardPage(page); + const alertsPage = new AlertsPage(page); await dashboardPage.menuItem("dashboards-item"); await waitForDashboardPage(page); @@ -36,11 +56,21 @@ test.describe("dashboard folder testcases", () => { // Create the folder await dashboardFolders.createFolder(folderName); - // Search to confirm it exists await dashboardFolders.searchFolder(folderName); + await alertsPage.verifyFolderCreated(folderName); + await alertsPage.navigateToFolder(folderName); + await page.waitForTimeout(2000); - await expect(page.locator(`text=${folderName}`)).toBeVisible(); + + await dashboardCreate.createDashboard(randomDashboardName); + await page.waitForTimeout(1000); + await dashboardPageActions.verifyShareDashboardLink(randomDashboardName); + await page.waitForTimeout(1000); + // Click on folder name in breadcrumbs to go back to folder view + await page.getByText(folderName).click(); + await page.waitForTimeout(1000); + await dashboardPageActions.deleteSearchedDashboard(randomDashboardName); // Delete folder await dashboardFolders.deleteFolder(folderName); diff --git a/tests/ui-testing/utils/emailDestinationImport.json b/tests/ui-testing/utils/emailDestinationImport.json new file mode 100644 index 0000000000..02e13bccca --- /dev/null +++ b/tests/ui-testing/utils/emailDestinationImport.json @@ -0,0 +1,11 @@ +{ + "name": "", + "url": "", + "method": "post", + "skip_tls_verify": false, + "template": "email", + "emails": [ + "shnath@openobserve.ai" + ], + "type": "email" +} \ No newline at end of file diff --git a/tests/ui-testing/utils/emailTemplateImport.json b/tests/ui-testing/utils/emailTemplateImport.json new file mode 100644 index 0000000000..b84fbcfa86 --- /dev/null +++ b/tests/ui-testing/utils/emailTemplateImport.json @@ -0,0 +1,7 @@ +{ + "name": "", + "body": "{\n \"text\": \"{alert_name} is active. This is the alert url - {alert_url}. This is an email template generated by playwright automation code\"\n}", + "isDefault": true, + "type": "email", + "title": "Email Template for Automation" +} \ No newline at end of file diff --git a/tests/ui-testing/utils/webhookDestinationImport.json b/tests/ui-testing/utils/webhookDestinationImport.json new file mode 100644 index 0000000000..b642c8769d --- /dev/null +++ b/tests/ui-testing/utils/webhookDestinationImport.json @@ -0,0 +1,10 @@ +{ + "name": "", + "url": "DEMO", + "method": "post", + "skip_tls_verify": false, + "headers": {}, + "template": "", + "emails": [], + "type": "http" +} \ No newline at end of file diff --git a/tests/ui-testing/utils/webhookTemplateImport.json b/tests/ui-testing/utils/webhookTemplateImport.json new file mode 100644 index 0000000000..4dcbdc5b27 --- /dev/null +++ b/tests/ui-testing/utils/webhookTemplateImport.json @@ -0,0 +1,7 @@ +{ + "name": "", + "body": "{\r\n \"text\": \"{alert_name} is active. This is the alert url {alert_url}. This alert template has been created using a playwright automation script. This template has been uploaded as a part of import feature.\"\r\n }", + "isDefault": true, + "type": "http", + "title": "" +} \ No newline at end of file