From ca7b05aba6319716215a20e69ebb3dbe0d9fcfe4 Mon Sep 17 00:00:00 2001 From: Shyam Panjiyar Date: Tue, 17 Dec 2024 09:50:29 +0530 Subject: [PATCH] test: service account API access automation (#5258) ## Summary by CodeRabbit - **Bug Fixes** - Updated the locator for the management menu item to enhance navigation reliability. - Streamlined the control flow by removing unnecessary wait times in the management navigation process. - **New Features** - Added methods for managing service accounts, including adding, validating, and deleting service accounts. - Introduced a new test suite for validating service account functionality, covering error handling and successful operations during creation. - Added a new method for automatic login based on visibility of the login option. - **Improvements** - Updated the login method to ensure it waits for the correct login page URL before proceeding. - Simplified the login process in UI tests by removing unnecessary parameters and wait times. - Enhanced test scripts with improved structure and clarity, including comments for better understanding. --- .github/workflows/playwright.yml | 1 + tests/ui-testing/pages/iamPage.js | 203 ++++++++++++++++-- tests/ui-testing/pages/loginPage.js | 2 +- tests/ui-testing/pages/managementPage .js | 12 +- .../playwright-tests/aServiceAccount.spec.js | 42 ++++ .../ui-testing/playwright-tests/join.spec.js | 2 +- .../playwright-tests/reportsUpdated.spec.js | 2 +- .../secondsPrecisionAdded.spec.js | 10 +- .../playwright-tests/serviceAccount.spec.js | 166 ++++++++++++++ 9 files changed, 411 insertions(+), 29 deletions(-) create mode 100644 tests/ui-testing/playwright-tests/aServiceAccount.spec.js create mode 100644 tests/ui-testing/playwright-tests/serviceAccount.spec.js diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 9452b70dc0..1ce7a302ed 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -112,6 +112,7 @@ jobs: "reportsUpdated.spec.js", "join.spec.js", "changeOrg.spec.js", + "serviceAccount.spec.js", ] diff --git a/tests/ui-testing/pages/iamPage.js b/tests/ui-testing/pages/iamPage.js index 13ef41da5b..ea1100e222 100644 --- a/tests/ui-testing/pages/iamPage.js +++ b/tests/ui-testing/pages/iamPage.js @@ -7,41 +7,50 @@ export class IamPage { this.page = page; this.iamPageMenu = page.locator('[data-test="menu-link-\\/iam-item"]'); + this.serviceAccountsLink = "//a[contains(@data-test,'service-accounts')]"; + + this.addServiceAccountButton = "//span[text()='Add Service Account']"; + + this.emailInput = "//input[@aria-label='Email *']"; + + this.firstNameInput = "//input[@aria-label='First Name']"; + + this.submitButton = "//button[@type='submit']"; + + this.closeDialogButton = "//div[@class='q-card']/div[1]/button/span/i[text()='close']"; + + // this.deleteButton = emailName => `//td[contains(text(),'${emailName}')]/following-sibling::td/button[@title='Delete Service Account']`; + this.deleteButton = emailName => `//td[contains(text(),'${emailName}')]/following-sibling::td/button[@title='Delete Service Account']`; + + this.confirmOkButton = "//div[@class='q-card']/div[2]/button/span[text()='OK']"; + this.cancelButton = "//div[@class='q-card']/div[2]/button/span[text()='Cancel']"; + + this.refreshButton = emailName => `//td[contains(text(),'${emailName}')]/following-sibling::td/button[@title='Refresh Service Token']`; + + this.updateButton = emailName => `//td[contains(text(),'${emailName}')]/following-sibling::td/button[@title='Update Service Account']`; + + this.alertMessage = this.page.getByRole('alert').first(); + + } + async gotoIamPage() { - await this.iamPageMenu.click(); - - } async iamPageDefaultOrg() { - await this.page.locator('[data-test="navbar-organizations-select"]').getByText('arrow_drop_down').click(); await this.page.getByText('default', { exact: true }).click(); - - } async iamPageDefaultMultiOrg() { - - - await this.page.locator('[data-test="navbar-organizations-select"]').getByText('arrow_drop_down').click(); - - await this.page.getByRole('option', { name: 'defaulttestmulti' }).locator('div').nth(2).click(); - - - - } async iamPageURLValidation() { - await expect(this.page).toHaveURL(/defaulttestmulti/); - } async iamURLValidation() { @@ -50,4 +59,164 @@ export class IamPage { } + + async iamPageServiceAccountsTab() { + + await this.page.locator('[data-test="iam-service-accounts-tab"]').click(); + + } + + async iamPageAddServiceAccount() { + + await this.page.getByRole('button', { name: 'Add Service Account' }).click(); + + } + + + async iamPageAddServiceAccountEmailValidation() { + + await this.page.getByRole('button', { name: 'Save' }).click(); + + await expect(this.page.getByRole('alert').nth(2)).toContainText('Please enter a valid email address'); + + + } + + async enterEmailServiceAccount(emailName) { + + await this.page.getByLabel('Email *').click(); + await this.page.getByLabel('Email *').fill(emailName); + + } + + async enterSameEmailServiceAccount() { + + await this.page.getByLabel('Email *').click(); + await this.page.getByLabel('Email *').fill(process.env["ZO_ROOT_USER_EMAIL"]); + + } + + async enterFirstLastNameServiceAccount() { + + + await this.page.getByLabel('First Name').click(); + await this.page.getByLabel('First Name').fill('Test'); + await this.page.getByLabel('Last Name').click(); + await this.page.getByLabel('Last Name').fill('Auto'); + + } + + async clickCancelServiceAccount() { + + await this.page.getByRole('button', { name: 'Cancel' }).click(); + + + } + + async clickSaveServiceAccount() { + await this.page.getByRole('button', { name: 'Save' }).click(); + } + + + async verifySuccessMessage(expectedMessage) { + await expect(this.page.locator('role=alert').first()).toBeVisible(); + await expect(this.alertMessage).toContainText(expectedMessage); + + } + + async validateServiceAccountToken() { + + await expect(this.page.getByRole('dialog')).toBeVisible(); + await expect(this.page.getByRole('dialog')).toContainText('Service Account Token'); + + } + + async waitResEmailServiceAccount(emailName) { + await this.page.waitForResponse( + (response) => + response.url().includes("/api/default/service_accounts/${emailName}") && response.status() === 200 + ); + + } + + async clickCopyToken() { + + await this.page.locator('#q-portal--dialog--2').getByRole('button', { name: 'Copy Token' }).click(); + // await this.page.locator('#q-portal--dialog--3').getByRole('button', { name: 'Copy Token' }).click(); + + } + + + + async clickDownloadToken() { + + const downloadPromise = this.page.waitForEvent('download'); + + await this.page.getByRole('button', { name: 'Download Token' }).click(); + + } + + async clickServiceAccountPopUpClosed() { + + await this.page.locator('button').filter({ hasText: 'close' }).click(); + + } + + async reloadServiceAccountPage(emailName) { + + await this.page.reload(); // Optional, if necessary + // await this.page.locator('[data-test="iam-page"]').getByText('arrow_drop_down').click({ force: true }); + // await this.page.getByText('100').click({ force: true }); + + } + + async deletedServiceAccount(emailName) { + const deleteButtonLocator = this.page.locator(this.deleteButton(emailName)); + // Wait for the delete button to be visible + await deleteButtonLocator.waitFor({ state: 'visible', timeout: 30000 }); + // Click the delete button + await deleteButtonLocator.click({ force: true }); + + } + + async requestServiceAccountOk(emailName) { + // Wait for the confirmation button to be visible and click it + await this.page.locator(this.confirmOkButton).waitFor({ state: 'visible', timeout: 10000 }); + await this.page.locator(this.confirmOkButton).click({ force: true }); + // Add some buffer wait, if necessary + await this.page.waitForTimeout(2000); + } + + + + async requestServiceAccountCancel() { + // Wait for the cancel confirmation button to be visible and click it + await this.page.locator(this.cancelButton).waitFor({ state: 'visible', timeout: 10000 }); + await this.page.locator(this.cancelButton).click({ force: true }); + + // Add some buffer wait, if necessary + await this.page.waitForTimeout(2000); + + + } + + async updatedServiceAccount(emailName) { + const updateButtonLocator = this.page.locator(this.updateButton(emailName)); + // Wait for the update button to be visible + await updateButtonLocator.waitFor({ state: 'visible', timeout: 30000 }); + // Click the update button + await updateButtonLocator.click({ force: true }); + await expect(this.page.getByRole('dialog')).toContainText('Update Service Account'); + } + + async refreshServiceAccount(emailName) { + const refreshButtonLocator = this.page.locator(this.refreshButton(emailName)); + // Wait for the refresh button to be visible + await refreshButtonLocator.waitFor({ state: 'visible', timeout: 30000 }); + // Click the refresh button + await refreshButtonLocator.click({ force: true }); + } + + + } diff --git a/tests/ui-testing/pages/loginPage.js b/tests/ui-testing/pages/loginPage.js index a63c677f08..a7aa04e5b3 100644 --- a/tests/ui-testing/pages/loginPage.js +++ b/tests/ui-testing/pages/loginPage.js @@ -36,4 +36,4 @@ export class LoginPage { } -} +} \ No newline at end of file diff --git a/tests/ui-testing/pages/managementPage .js b/tests/ui-testing/pages/managementPage .js index 6756e0711a..f99cc4b10e 100644 --- a/tests/ui-testing/pages/managementPage .js +++ b/tests/ui-testing/pages/managementPage .js @@ -8,7 +8,12 @@ export this.page = page; this.homeIcon = page.locator("[name ='home']"); - this.managementMenuItem = page.locator('[data-test="menu-link-settings-item"]'); + + //this.managementMenuItem = page.locator('[data-test="menu-link-/settings/-item"]'); + + this.managementMenuItem = page.locator('[data-test="menu-link-settings-item"]'); + + this.submitButton = page.locator('[data-test="dashboard-add-submit"]'); // Add appropriate data-test attribute this.customLogoText = page.locator("[aria-label ='Custom Logo Text']"); this.saveButton = page.locator('[data-test="settings_ent_logo_custom_text_save_btn"]'); @@ -25,7 +30,10 @@ export } async goToManagement() { - await this.page.waitForSelector('[data-test="menu-link-settings-item"]'); + + // await this.page.locator('[data-test="menu-link-settings-item"]').click(); + + // await this.page.waitForSelector('[data-test="menu-link-/settings/-item"]'); await this.managementMenuItem.click({ force: true }); await expect(this.page.getByRole('main')).toContainText('Management'); } diff --git a/tests/ui-testing/playwright-tests/aServiceAccount.spec.js b/tests/ui-testing/playwright-tests/aServiceAccount.spec.js new file mode 100644 index 0000000000..2a57ddf5ab --- /dev/null +++ b/tests/ui-testing/playwright-tests/aServiceAccount.spec.js @@ -0,0 +1,42 @@ +import { test, expect } from "./baseFixtures.js"; +import { LoginPage } from '../pages/loginPage.js'; +import { IamPage } from "../pages/iamPage.js"; + + + +test.describe("Service Account for API access", () => { + let loginPage, iamPage; + + test.beforeEach(async ({ page }) => { + loginPage = new LoginPage(page); + iamPage = new IamPage(page); + await loginPage.gotoLoginPage(); + await loginPage.loginAsInternalUser(); + await loginPage.login(); + }); + + + test("Error Message displayed if Email Blank", async ({ page }) => { + + await iamPage.gotoIamPage(); + await iamPage.iamURLValidation(); + await iamPage.iamPageServiceAccountsTab(); + await iamPage.iamPageAddServiceAccount(); + await iamPage.iamPageAddServiceAccountEmailValidation(); + + }); + + test("Service Account created", async ({ page }) => { + + await iamPage.gotoIamPage(); + //await iamPage.iamURLValidation(); + await iamPage.iamPageServiceAccountsTab(); + await iamPage.iamPageAddServiceAccount(); + await iamPage.iamPageAddServiceAccountEmailValidation(); + + }); + + + + +}); diff --git a/tests/ui-testing/playwright-tests/join.spec.js b/tests/ui-testing/playwright-tests/join.spec.js index 0334e5ef1c..562596efa8 100644 --- a/tests/ui-testing/playwright-tests/join.spec.js +++ b/tests/ui-testing/playwright-tests/join.spec.js @@ -12,7 +12,7 @@ test.describe("Join for logs", () => { ingestionPage = new IngestionPage(page); logsPage = new LogsPage(page); await loginPage.gotoLoginPage(); - await loginPage.login(); + await loginPage.login(); // Login as root user await ingestionPage.ingestion(); await ingestionPage.ingestionJoin(); }); diff --git a/tests/ui-testing/playwright-tests/reportsUpdated.spec.js b/tests/ui-testing/playwright-tests/reportsUpdated.spec.js index 7806a8a5c7..79c05d386c 100644 --- a/tests/ui-testing/playwright-tests/reportsUpdated.spec.js +++ b/tests/ui-testing/playwright-tests/reportsUpdated.spec.js @@ -30,4 +30,4 @@ test.describe("Report test cases Updated", () => { await dashboardPage.navigateToDashboards(); await dashboardPage.deleteDashboard(); }); -}); +}); \ No newline at end of file diff --git a/tests/ui-testing/playwright-tests/secondsPrecisionAdded.spec.js b/tests/ui-testing/playwright-tests/secondsPrecisionAdded.spec.js index 938128a3ca..715f602e37 100644 --- a/tests/ui-testing/playwright-tests/secondsPrecisionAdded.spec.js +++ b/tests/ui-testing/playwright-tests/secondsPrecisionAdded.spec.js @@ -11,16 +11,12 @@ test('Relative Seconds on Logs page', async ({ page }) => { const loginPage = new LoginPage(page); const logsPage = new LogsPage(page); // Step 1: Navigate to the application and login - await page.goto(process.env["ZO_BASE_URL"]); - // console.log ('URL Opened') await loginPage.gotoLoginPage(); - await loginPage.login(process.env["ZO_ROOT_USER_EMAIL"], process.env["ZO_ROOT_USER_PASSWORD"]); + await loginPage.login(); // Step 2: Navigate to Logs Page - await page.waitForTimeout(4000); // Wait for login process + // await page.waitForTimeout(4000); // Wait for login process await logsPage.navigateToLogs(); - // Step 3: Select Index and Stream - await page.waitForTimeout(3000); // Wait for logs page to load - // Step 4: Set the time to past 30 seconds and verify + // Step 3: Set the time to past 30 seconds and verify await logsPage.setTimeToPast30Seconds(); await logsPage.verifyTimeSetTo30Seconds(); await logsPage.signOut(); diff --git a/tests/ui-testing/playwright-tests/serviceAccount.spec.js b/tests/ui-testing/playwright-tests/serviceAccount.spec.js new file mode 100644 index 0000000000..25085661e8 --- /dev/null +++ b/tests/ui-testing/playwright-tests/serviceAccount.spec.js @@ -0,0 +1,166 @@ +import { test, expect } from "./baseFixtures.js"; +import { LoginPage } from '../pages/loginPage.js'; +import { IamPage } from "../pages/iamPage.js"; + + + +test.describe("Service Account for API access", () => { + let loginPage, iamPage; + const emailName = `email${Date.now()}@gmail.com`; + + test.beforeEach(async ({ page }) => { + loginPage = new LoginPage(page); + iamPage = new IamPage(page); + await loginPage.gotoLoginPage(); + await loginPage.login(); + + }); + + + test("Error Message displayed if Email Blank", async ({ page }) => { + + await iamPage.gotoIamPage(); + await iamPage.iamURLValidation(); + await iamPage.iamPageServiceAccountsTab(); + await iamPage.iamPageAddServiceAccount(); + await iamPage.iamPageAddServiceAccountEmailValidation(); + + }); + + test("Service Account created", async ({ page }) => { + + await iamPage.gotoIamPage(); + await iamPage.iamPageServiceAccountsTab(); + await iamPage.iamPageAddServiceAccount(); + await iamPage.enterEmailServiceAccount(emailName); + await iamPage.clickSaveServiceAccount(); + await iamPage.verifySuccessMessage('Service Account created successfully.'); + + }); + + test("Service Account not created if Email already exists", async ({ page }) => { + + await iamPage.gotoIamPage(); + await iamPage.iamPageServiceAccountsTab(); + await iamPage.iamPageAddServiceAccount(); + await iamPage.enterSameEmailServiceAccount(); + await iamPage.clickSaveServiceAccount(); + await iamPage.verifySuccessMessage('User already exists'); + + }); + + test("Service Account not created if Cancel clicked", async ({ page }) => { + + await iamPage.gotoIamPage(); + await iamPage.iamPageServiceAccountsTab(); + await iamPage.iamPageAddServiceAccount(); + await iamPage.enterEmailServiceAccount(emailName); + await iamPage.enterFirstLastNameServiceAccount(); + await iamPage.clickCancelServiceAccount(); + + }); + + test("Service Account Token copied", async ({ page }) => { + + await iamPage.gotoIamPage(); + await iamPage.iamPageServiceAccountsTab(); + await iamPage.iamPageAddServiceAccount(); + await iamPage.enterEmailServiceAccount(emailName); + await iamPage.clickSaveServiceAccount(); + await iamPage.verifySuccessMessage('Service Account created successfully.'); + await iamPage.clickCopyToken(); + + + }); + + test("Service Account Download Token", async ({ page }) => { + + await iamPage.gotoIamPage(); + await iamPage.iamPageServiceAccountsTab(); + await iamPage.iamPageAddServiceAccount(); + await iamPage.enterEmailServiceAccount(emailName); + await iamPage.clickSaveServiceAccount(); + await iamPage.verifySuccessMessage('Service Account created successfully.'); + await iamPage.clickDownloadToken(); + + + }); + test("Service Account Token Pop Up Closed", async ({ page }) => { + + await iamPage.gotoIamPage(); + await iamPage.iamPageServiceAccountsTab(); + await iamPage.iamPageAddServiceAccount(); + await iamPage.enterEmailServiceAccount(emailName); + await iamPage.clickSaveServiceAccount(); + await iamPage.verifySuccessMessage('Service Account created successfully.'); + await iamPage.clickServiceAccountPopUpClosed(); + + }); + + test("Service Account Created and deleted", async ({ page }) => { + + await iamPage.gotoIamPage(); + await iamPage.iamPageServiceAccountsTab(); + await iamPage.iamPageAddServiceAccount(); + await iamPage.enterEmailServiceAccount(emailName); + await iamPage.clickSaveServiceAccount(); + await iamPage.verifySuccessMessage('Service Account created successfully.'); + await iamPage.clickServiceAccountPopUpClosed(); + await iamPage.reloadServiceAccountPage(); + await iamPage.deletedServiceAccount(emailName); + await iamPage.requestServiceAccountOk(); + await iamPage.verifySuccessMessage('Service Account deleted successfully.'); + + }); + + test("Service Account Created and not deleted if cancel clicked", async ({ page }) => { + + await iamPage.gotoIamPage(); + await iamPage.iamPageServiceAccountsTab(); + await iamPage.iamPageAddServiceAccount(); + await iamPage.enterEmailServiceAccount(emailName); + await iamPage.clickSaveServiceAccount(); + await iamPage.verifySuccessMessage('Service Account created successfully.'); + await iamPage.clickServiceAccountPopUpClosed(); + await iamPage.reloadServiceAccountPage(); + await iamPage.deletedServiceAccount(emailName); + await iamPage.requestServiceAccountCancel(); + + }); + + test("Service Account Created and updated details", async ({ page }) => { + + await iamPage.gotoIamPage(); + await iamPage.iamPageServiceAccountsTab(); + await iamPage.iamPageAddServiceAccount(); + await iamPage.enterEmailServiceAccount(emailName); + await iamPage.clickSaveServiceAccount(); + await iamPage.verifySuccessMessage('Service Account created successfully.'); + await iamPage.clickServiceAccountPopUpClosed(); + await iamPage.reloadServiceAccountPage(); + await iamPage.updatedServiceAccount(emailName); + await iamPage.enterFirstLastNameServiceAccount(); + await iamPage.clickSaveServiceAccount(); + await iamPage.verifySuccessMessage('Service Account updated successfully.'); + + }); + + test("Service Account Created and refresh token", async ({ page }) => { + + await iamPage.gotoIamPage(); + await iamPage.iamPageServiceAccountsTab(); + await iamPage.iamPageAddServiceAccount(); + await iamPage.enterEmailServiceAccount(emailName); + await iamPage.clickSaveServiceAccount(); + await iamPage.verifySuccessMessage('Service Account created successfully.'); + await iamPage.clickServiceAccountPopUpClosed(); + await iamPage.reloadServiceAccountPage(); + await iamPage.refreshServiceAccount(emailName); + await iamPage.requestServiceAccountOk(); + await iamPage.verifySuccessMessage('Service token refreshed successfully.'); + + }); + + + +});