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>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7212/files#diff-57e5c53b4ef998b3d8708149620ff94dcc46807927f1a3a9e9f28ed3ebe25b0a">+1/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>changeOrg.spec.js</strong><dd><code>update AlertsPage import
path reference</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7212/files#diff-607c77ca637716a5b8d37ec25dbaf7dc91cddd7d981b72f9c460f54498d14351">+1/-1</a>&nbsp;
&nbsp; &nbsp; </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>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7212/files#diff-f4ebd248b2140051e74dbbbd1aabe344f01c80704ce5857e9c28a04350e018f3">+71/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>alertsPage.js</strong><dd><code>improve locators and add
timeouts</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7212/files#diff-79ee01d6d7f145f19207b93b87143b545a1188cfc6a82bc8c45fecfbf68f8a1d">+8/-6</a>&nbsp;
&nbsp; &nbsp; </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>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7212/files#diff-32972e38c1fe0e8bb887f9f254b3130916a36f96ea924f9987493d44efefeab9">+162/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>alerts-import.spec.js</strong><dd><code>add import/export
alerts functionality tests</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7212/files#diff-62b6ec4818685eacc0a934d68aaadfd73b512ef4f4be25fcd5135b97c2844912">+129/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>alerts-ui-operations.spec.js</strong><dd><code>add alerts UI
operations validation tests</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7212/files#diff-c9b523b5f5dea834b69abdf20f4acef361bdf37b1585c450e10238b1d1984f2e">+255/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>emailTemplateImport.json</strong><dd><code>add email
template import fixture</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7212/files#diff-2f3913b2a88452f900baf8bec360cca87b30e26819c98483028ec98dc09a23d2">+7/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>webhookTemplateImport.json</strong><dd><code>add webhook
template import fixture</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7212/files#diff-7b0a644740e335227bc785d900c70972619e669ec2e0669f521d50de9d0425ba">+7/-0</a>&nbsp;
&nbsp; &nbsp; </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>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </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>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7212/files#diff-7afcd2d8f7b49bda74843f209eefb7b2da45f7e7803bf2e4bd636699b76aa2d3">+3/-1</a>&nbsp;
&nbsp; &nbsp; </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:
Shrinath Rao
2025-06-19 11:15:59 +05:30
committed by GitHub
parent 34e9285270
commit 7fe850326a
16 changed files with 849 additions and 228 deletions
+3 -1
View File
@@ -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();
}
}
@@ -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();
}
}
}
@@ -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;
}
+2 -2
View File
@@ -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) {
+23
View File
@@ -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');
});
});
@@ -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": ""
}