diff --git a/src/pybind/mgr/dashboard/frontend/e2e/cluster/mgr-modules.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/e2e/cluster/mgr-modules.e2e-spec.ts index 2dc916b0ba4..0da02af6b39 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/cluster/mgr-modules.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/cluster/mgr-modules.e2e-spec.ts @@ -20,4 +20,44 @@ describe('Manager modules page', () => { expect(mgrmodules.getBreadcrumbText()).toEqual('Manager modules'); }); }); + + describe('verifies editing functionality for manager modules', () => { + beforeAll(() => { + mgrmodules.navigateTo(); + }); + + it('should test editing on ansible module', () => { + const ansibleArr = [['rq', 'ca_bundle'], ['colts', 'server_location']]; + mgrmodules.editMgrModule('ansible', ansibleArr); + }); + + it('should test editing on deepsea module', () => { + const deepseaArr = [ + ['rq', 'salt_api_eauth'], + ['alm', 'salt_api_password'], + ['bu', 'salt_api_url'], + ['sox', 'salt_api_username'] + ]; + mgrmodules.editMgrModule('deepsea', deepseaArr); + }); + + it('should test editing on diskprediction_local module', () => { + const diskpredLocalArr = [['11', 'predict_interval'], ['0122', 'sleep_interval']]; + mgrmodules.editMgrModule('diskprediction_local', diskpredLocalArr); + }); + + it('should test editing on balancer module', () => { + const balancerArr = [['rq', 'pool_ids']]; + mgrmodules.editMgrModule('balancer', balancerArr); + }); + + it('should test editing on dashboard module', () => { + const dashboardArr = [['rq', 'AUDIT_API_ENABLED'], ['rafa', 'GRAFANA_API_PASSWORD']]; + mgrmodules.editMgrModule('dashboard', dashboardArr); + }); + + it('should test editing on devicehealth module', () => { + mgrmodules.editDevicehealth('1987', 'sox', '1999', '2020', '456', '567'); + }); + }); }); diff --git a/src/pybind/mgr/dashboard/frontend/e2e/cluster/mgr-modules.po.ts b/src/pybind/mgr/dashboard/frontend/e2e/cluster/mgr-modules.po.ts index 63306c3e214..4d6bd91825e 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/cluster/mgr-modules.po.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/cluster/mgr-modules.po.ts @@ -1,5 +1,212 @@ +import { $$, browser, by, element } from 'protractor'; +import { Helper } from '../helper.po'; import { PageHelper } from '../page-helper.po'; export class ManagerModulesPageHelper extends PageHelper { - pages = { index: '/#/mgr-modules' }; + pages = { + index: '/#/mgr-modules' + }; + + // NOTABLE ISSUES: .clear() does not work on most text boxes, therefore using sendKeys + // a Ctrl + 'a' BACK_SPACE is used. + // The need to click the module repeatedly in the table is to ensure + // that the values in the details tab updated. This fixed a bug I experienced. + + editMgrModule(name: string, tuple: string[][]) { + // Selects the Manager Module and then fills in the desired fields. + // Doesn't check/uncheck boxes because it is not reflected in the details table. + // DOES NOT WORK FOR ALL MGR MODULES, for example, Device health + this.navigateTo(); + browser.wait(Helper.EC.elementToBeClickable(this.getTableCell(name)), Helper.TIMEOUT); + this.getTableCell(name).click(); + element(by.cssContainingText('button', 'Edit')).click(); + + for (const entry of tuple) { + // Clears fields and adds edits + this.inputClear(element(by.id(entry[1]))); + element(by.id(entry[1])).sendKeys(entry[0]); + } + + element(by.cssContainingText('button', 'Update')) + .click() + .then(() => { + // Checks if edits appear + this.navigateTo(); + browser.wait(Helper.EC.visibilityOf(this.getTableCell(name)), Helper.TIMEOUT).then(() => { + this.getTableCell(name) + .click() + .then(() => { + for (const entry of tuple) { + browser.wait( + Helper.EC.textToBePresentInElement($$('.datatable-body').last(), entry[0]), + Helper.TIMEOUT + ); + } + }); + }); + }); + + // Clear mgr module of all edits made to it + this.navigateTo(); + browser.wait(Helper.EC.elementToBeClickable(this.getTableCell(name)), Helper.TIMEOUT); + this.getTableCell(name).click(); + element(by.cssContainingText('button', 'Edit')).click(); + + // Clears the editable fields + for (const entry of tuple) { + this.inputClear(element(by.id(entry[1]))); + } + + // Checks that clearing represents in details tab of module + element(by.cssContainingText('button', 'Update')) + .click() + .then(() => { + this.navigateTo(); + browser.wait(Helper.EC.visibilityOf(this.getTableCell(name)), Helper.TIMEOUT).then(() => { + this.getTableCell(name) + .click() + .then(() => { + for (const entry of tuple) { + browser.wait( + Helper.EC.not( + Helper.EC.textToBePresentInElement($$('.datatable-body').last(), entry[0]) + ), + Helper.TIMEOUT + ); + } + }); + }); + }); + } + + editDevicehealth( + threshhold?: string, + pooln?: string, + retention?: string, + scrape?: string, + sleep?: string, + warn?: string + ) { + // Isn't called by editMgrModule since clearing doesn't work. + // Selects the Devicehealth manager module, then fills in the desired fields, including all fields except + // checkboxes. Clicking checkboxes has been a notable issue in Protractor, therefore they were omitted in this + // version of the tests. Could be added in a future PR. Then checks if these edits appear in the details table. + this.navigateTo(); + let devHealthArray: [string, string][]; + devHealthArray = [ + [threshhold, 'mark_out_threshold'], + [pooln, 'pool_name'], + [retention, 'retention_period'], + [scrape, 'scrape_frequency'], + [sleep, 'sleep_interval'], + [warn, 'warn_threshold'] + ]; + + browser.wait(Helper.EC.elementToBeClickable(this.getTableCell('devicehealth')), Helper.TIMEOUT); + this.getTableCell('devicehealth').click(); + element(by.cssContainingText('button', 'Edit')) + .click() + .then(() => { + for (let i = 0, devHealthTuple; (devHealthTuple = devHealthArray[i]); i++) { + if (devHealthTuple[0] !== undefined) { + // Clears and inputs edits + this.inputClear(element(by.id(devHealthTuple[1]))); + element(by.id(devHealthTuple[1])).sendKeys(devHealthTuple[0]); + } + } + }); + + element(by.cssContainingText('button', 'Update')) + .click() + .then(() => { + this.navigateTo(); + browser + .wait(Helper.EC.visibilityOf(this.getTableCell('devicehealth')), Helper.TIMEOUT) + .then(() => { + // Checks for visibility of devicehealth in table + this.getTableCell('devicehealth') + .click() + .then(() => { + for (let i = 0, devHealthTuple; (devHealthTuple = devHealthArray[i]); i++) { + if (devHealthTuple[0] !== undefined) { + browser.wait(function() { + // Repeatedly reclicks the module to check if edits has been done + element( + by.cssContainingText('.datatable-body-cell-label', 'devicehealth') + ).click(); + return Helper.EC.textToBePresentInElement( + $$('.datatable-body').last(), + devHealthTuple[0] + ); + }, Helper.TIMEOUT); + } + } + }); + }); + }); + + // Inputs old values into devicehealth fields. This manager module doesnt allow for updates + // to be made when the values are cleared. Therefore, I restored them to their original values + // (on my local run of ceph-dev, this is subject to change i would assume). I'd imagine there is a + // better way of doing this. + this.navigateTo(); + browser.wait(Helper.EC.elementToBeClickable(this.getTableCell('devicehealth')), Helper.TIMEOUT); // checks ansible + this.getTableCell('devicehealth').click(); + element(by.cssContainingText('button', 'Edit')) + .click() + .then(() => { + this.inputClear(element(by.id('mark_out_threshold'))); + element(by.id('mark_out_threshold')).sendKeys('2419200'); + + this.inputClear(element(by.id('pool_name'))); + element(by.id('pool_name')).sendKeys('device_health_metrics'); + + this.inputClear(element(by.id('retention_period'))); + element(by.id('retention_period')).sendKeys('15552000'); + + this.inputClear(element(by.id('scrape_frequency'))); + element(by.id('scrape_frequency')).sendKeys('86400'); + + this.inputClear(element(by.id('sleep_interval'))); + element(by.id('sleep_interval')).sendKeys('600'); + + this.inputClear(element(by.id('warn_threshold'))); + element(by.id('warn_threshold')).sendKeys('7257600'); + }); + + // Checks that clearing represents in details tab of ansible + browser + .wait(Helper.EC.elementToBeClickable(element(by.cssContainingText('button', 'Update')))) + .then(() => { + element(by.cssContainingText('button', 'Update')) + .click() + .then(() => { + this.navigateTo(); + browser + .wait(Helper.EC.visibilityOf(this.getTableCell('devicehealth')), Helper.TIMEOUT) + .then(() => { + this.getTableCell('devicehealth') + .click() + .then(() => { + for (let i = 0, devHealthTuple; (devHealthTuple = devHealthArray[i]); i++) { + if (devHealthTuple[0] !== undefined) { + browser.wait(function() { + // Repeatedly reclicks the module to check if clearing has been done + element( + by.cssContainingText('.datatable-body-cell-label', 'devicehealth') + ).click(); + return Helper.EC.not( + Helper.EC.textToBePresentInElement( + $$('.datatable-body').last(), + devHealthTuple[0] + ) + ); + }, Helper.TIMEOUT); + } + } + }); + }); + }); + }); + } } diff --git a/src/pybind/mgr/dashboard/frontend/e2e/page-helper.po.ts b/src/pybind/mgr/dashboard/frontend/e2e/page-helper.po.ts index 37dbc9668cd..3c4f417e22a 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/page-helper.po.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/page-helper.po.ts @@ -1,4 +1,4 @@ -import { $, $$, browser, by, element, ElementFinder, promise } from 'protractor'; +import { $, $$, browser, by, element, ElementFinder, promise, protractor } from 'protractor'; interface Pages { index: string; @@ -116,6 +116,12 @@ export abstract class PageHelper { }); } + // used when .clear() does not work on a text box, sends a Ctrl + a, BACKSPACE + inputClear(elem) { + elem.sendKeys(protractor.Key.chord(protractor.Key.CONTROL, 'a')); + elem.sendKeys(protractor.Key.BACK_SPACE); + } + navigateTo(page = null) { page = page || 'index'; const url = this.pages[page];