mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-03 16:10:21 +00:00
test: Folder Structure changes for Alerts, updated yml file for running multiple spec files instead of one (#7212)
### **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** 📝 <table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Bug fix</strong></td><td><details><summary>2 files</summary><table> <tr> <td><strong>alertDestinationsPage.js</strong><dd><code>fix import path for common actions</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/7212/files#diff-57e5c53b4ef998b3d8708149620ff94dcc46807927f1a3a9e9f28ed3ebe25b0a">+1/-1</a> </td> </tr> <tr> <td><strong>changeOrg.spec.js</strong><dd><code>update AlertsPage import path reference</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/7212/files#diff-607c77ca637716a5b8d37ec25dbaf7dc91cddd7d981b72f9c460f54498d14351">+1/-1</a> </td> </tr> </table></details></td></tr><tr><td><strong>Enhancement</strong></td><td><details><summary>2 files</summary><table> <tr> <td><strong>alertTemplatesPage.js</strong><dd><code>add template import locators and methods</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/7212/files#diff-f4ebd248b2140051e74dbbbd1aabe344f01c80704ce5857e9c28a04350e018f3">+71/-0</a> </td> </tr> <tr> <td><strong>alertsPage.js</strong><dd><code>improve locators and add timeouts</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/7212/files#diff-79ee01d6d7f145f19207b93b87143b545a1188cfc6a82bc8c45fecfbf68f8a1d">+8/-6</a> </td> </tr> </table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>5 files</summary><table> <tr> <td><strong>alerts-e2e-flow.spec.js</strong><dd><code>add end-to-end alerts flow tests</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/7212/files#diff-32972e38c1fe0e8bb887f9f254b3130916a36f96ea924f9987493d44efefeab9">+162/-0</a> </td> </tr> <tr> <td><strong>alerts-import.spec.js</strong><dd><code>add import/export alerts functionality tests</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/7212/files#diff-62b6ec4818685eacc0a934d68aaadfd73b512ef4f4be25fcd5135b97c2844912">+129/-0</a> </td> </tr> <tr> <td><strong>alerts-ui-operations.spec.js</strong><dd><code>add alerts UI operations validation tests</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/7212/files#diff-c9b523b5f5dea834b69abdf20f4acef361bdf37b1585c450e10238b1d1984f2e">+255/-0</a> </td> </tr> <tr> <td><strong>emailTemplateImport.json</strong><dd><code>add email template import fixture</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/7212/files#diff-2f3913b2a88452f900baf8bec360cca87b30e26819c98483028ec98dc09a23d2">+7/-0</a> </td> </tr> <tr> <td><strong>webhookTemplateImport.json</strong><dd><code>add webhook template import fixture</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/7212/files#diff-7b0a644740e335227bc785d900c70972619e669ec2e0669f521d50de9d0425ba">+7/-0</a> </td> </tr> </table></details></td></tr><tr><td><strong>Miscellaneous</strong></td><td><details><summary>1 files</summary><table> <tr> <td><strong>alerts.spec.js</strong><dd><code>comment out legacy alerts spec file</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/7212/files#diff-a51e6bab2ebb7ff9069a707fd7da5bb3e0948db8013765b4a934ba594c1bcf57">+426/-354</a></td> </tr> </table></details></td></tr><tr><td><strong>Configuration changes</strong></td><td><details><summary>1 files</summary><table> <tr> <td><strong>playwright.yml</strong><dd><code>include new alerts spec files in CI</code> </dd></td> <td><a href="https://github.com/openobserve/openobserve/pull/7212/files#diff-7afcd2d8f7b49bda74843f209eefb7b2da45f7e7803bf2e4bd636699b76aa2d3">+3/-1</a> </td> </tr> </table></details></td></tr></tr></tbody></table> ___ > <details> <summary> Need help?</summary><li>Type <code>/help how to ...</code> in the comments thread for any questions about PR-Agent usage.</li><li>Check out the <a href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a> for more information.</li></details>
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
+71
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-6
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
+20
-109
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "",
|
||||
"url": "",
|
||||
"method": "post",
|
||||
"skip_tls_verify": false,
|
||||
"template": "email",
|
||||
"emails": [
|
||||
"shnath@openobserve.ai"
|
||||
],
|
||||
"type": "email"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "",
|
||||
"url": "DEMO",
|
||||
"method": "post",
|
||||
"skip_tls_verify": false,
|
||||
"headers": {},
|
||||
"template": "",
|
||||
"emails": [],
|
||||
"type": "http"
|
||||
}
|
||||
@@ -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": ""
|
||||
}
|
||||
Reference in New Issue
Block a user