From 3a83c29b04c570983623c66d5eee08b397ef15e8 Mon Sep 17 00:00:00 2001 From: Rafael Quintero Date: Mon, 29 Jul 2019 09:02:12 -0400 Subject: [PATCH] mgr/dashboard: E2E Test for RBD Mirroring and Images Fixes: https://tracker.ceph.com/issues/38701 Fixes: https://tracker.ceph.com/issues/40581 Fixes: https://tracker.ceph.com/issues/40956 Signed-off-by: Adam King Signed-off-by: Rafael Quintero --- .../frontend/e2e/block/images.e2e-spec.ts | 40 +++++++++ .../dashboard/frontend/e2e/block/images.po.ts | 89 ++++++++++++++++++- .../frontend/e2e/block/mirroring.e2e-spec.ts | 32 +++++++ .../frontend/e2e/block/mirroring.po.ts | 45 ++++++++++ .../dashboard/frontend/e2e/pools/pools.po.ts | 37 ++++++-- 5 files changed, 236 insertions(+), 7 deletions(-) diff --git a/src/pybind/mgr/dashboard/frontend/e2e/block/images.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/e2e/block/images.e2e-spec.ts index 44c97415e344d..290d78eaae3ae 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/block/images.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/block/images.e2e-spec.ts @@ -2,9 +2,11 @@ import { Helper } from '../helper.po'; describe('Images page', () => { let images: Helper['images']; + let pools: Helper['pools']; beforeAll(() => { images = new Helper().images; + pools = new Helper().pools; }); afterEach(() => { @@ -30,4 +32,42 @@ describe('Images page', () => { expect(images.getTabText(2)).toEqual('Overall Performance'); }); }); + + describe('create, edit & delete image test', () => { + const poolName = 'e2e_images_pool'; + const imageName = 'e2e_images_image'; + const newImageName = 'e2e_images_image_new'; + + beforeAll(() => { + pools.navigateTo('create'); // Need pool for image testing + pools.create(poolName, 8, 'rbd').then(() => { + pools.navigateTo(); + pools.exist(poolName, true); + }); + images.navigateTo(); + }); + + it('should create image', () => { + images.createImage(imageName, poolName, '1'); + expect(images.getTableCell(imageName).isPresent()).toBe(true); + }); + + it('should edit image', () => { + images.editImage(imageName, poolName, newImageName, '2'); + expect(images.getTableCell(newImageName).isPresent()).toBe(true); + }); + + it('should delete image', () => { + images.deleteImage(newImageName); + expect(images.getTableCell(newImageName).isPresent()).toBe(false); + }); + + afterAll(() => { + pools.navigateTo(); // Deletes images test pool + pools.delete(poolName).then(() => { + pools.navigateTo(); + pools.exist(poolName, false); + }); + }); + }); }); diff --git a/src/pybind/mgr/dashboard/frontend/e2e/block/images.po.ts b/src/pybind/mgr/dashboard/frontend/e2e/block/images.po.ts index ad659ed33accd..eec5642f48b2f 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/block/images.po.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/block/images.po.ts @@ -1,5 +1,92 @@ +import { $, $$, browser, by, element } from 'protractor'; +import { Helper } from '../helper.po'; import { PageHelper } from '../page-helper.po'; export class ImagesPageHelper extends PageHelper { - pages = { index: '/#/block/rbd' }; + pages = { + index: '/#/block/rbd', + create: '/#/block/rbd/create' + }; + + // Creates a block image and fills in the name, pool, and size fields. Then checks + // if the image is present in the Images table. + createImage(name, pool, size) { + this.navigateTo('create'); + + // Need the string '[value=""]' to find the pool in the dropdown menu + const getPoolName = `[value="${pool}"]`; + + element(by.id('name')).sendKeys(name); // Enter in image name + + // Select image pool + element(by.id('pool')).click(); + element(by.cssContainingText('select[name=pool] option', pool)).click(); + $(getPoolName).click(); + expect(element(by.id('pool')).getAttribute('class')).toContain('ng-valid'); // check if selected + + // Enter in the size of the image + element(by.id('size')).click(); + element(by.id('size')).sendKeys(size); + + // Click the create button and wait for image to be made + element(by.cssContainingText('button', 'Create RBD')) + .click() + .then(() => { + browser.wait(Helper.EC.presenceOf(this.getTableCell(name)), Helper.TIMEOUT); + }); + } + + editImage(name, pool, newName, newSize) { + const base_url = '/#/block/rbd/edit/'; + const editURL = base_url + .concat(pool) + .concat('/') + .concat(name); + browser.get(editURL); + + element(by.id('name')).click(); // click name box and send new name + element(by.id('name')).clear(); + element(by.id('name')).sendKeys(newName); + element(by.id('size')).click(); + element(by.id('size')).clear(); + element(by.id('size')).sendKeys(newSize); // click the size box and send new size + + element(by.cssContainingText('button', 'Edit RBD')) + .click() + .then(() => { + this.navigateTo(); + browser + .wait(Helper.EC.elementToBeClickable(this.getTableCell(newName)), Helper.TIMEOUT) + .then(() => { + this.getTableCell(newName).click(); + expect( + element + .all(by.css('.table.table-striped.table-bordered')) + .first() + .getText() + ).toMatch(newSize); + }); // click edit button and wait to make sure new owner is present in table + }); + } + + deleteImage(name) { + this.navigateTo(); + + // wait for table to load + browser.wait(Helper.EC.elementToBeClickable(this.getTableCell(name)), Helper.TIMEOUT); + this.getTableCell(name).click(); // click on the image you want to delete in the table + $$('.table-actions button.dropdown-toggle') + .first() + .click(); // click toggle menu + $('li.delete.ng-star-inserted').click(); // click delete + // wait for pop-up to be visible (checks for title of pop-up) + browser.wait(Helper.EC.visibilityOf($('.modal-body')), Helper.TIMEOUT).then(() => { + $('.custom-control-label').click(); // click confirmation checkbox + element(by.cssContainingText('button', 'Delete RBD')) + .click() + .then(() => { + browser.wait(Helper.EC.stalenessOf(this.getTableCell(name)), Helper.TIMEOUT); + }); + }); + } } diff --git a/src/pybind/mgr/dashboard/frontend/e2e/block/mirroring.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/e2e/block/mirroring.e2e-spec.ts index b0108f0cb5c4a..9b1695517dd7b 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/block/mirroring.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/block/mirroring.e2e-spec.ts @@ -2,9 +2,11 @@ import { Helper } from '../helper.po'; describe('Mirroring page', () => { let mirroring: Helper['mirroring']; + let pools: Helper['pools']; beforeAll(() => { mirroring = new Helper().mirroring; + pools = new Helper().pools; }); afterEach(() => { @@ -30,4 +32,34 @@ describe('Mirroring page', () => { expect(mirroring.getTabText(2)).toEqual('Ready'); }); }); + + describe('checks that edit mode functionality shows in the pools table', () => { + const poolName = 'mirrorpoolrq'; + + beforeAll(() => { + pools.navigateTo('create'); // Need pool for mirroring testing + pools.create(poolName, 8, 'rbd').then(() => { + pools.navigateTo(); + pools.exist(poolName, true); + }); + }); + + it('tests editing mode for pools', () => { + mirroring.navigateTo(); + expect(mirroring.editMirror(poolName, 'Pool')); + expect(mirroring.getFirstTableCellWithText('pool').isPresent()).toBe(true); + expect(mirroring.editMirror(poolName, 'Image')); + expect(mirroring.getFirstTableCellWithText('image').isPresent()).toBe(true); + expect(mirroring.editMirror(poolName, 'Disabled')); + expect(mirroring.getFirstTableCellWithText('disabled').isPresent()).toBe(true); + }); + + afterAll(() => { + pools.navigateTo(); // Deletes mirroring test pool + pools.delete(poolName).then(() => { + pools.navigateTo(); + pools.exist(poolName, false); + }); + }); + }); }); diff --git a/src/pybind/mgr/dashboard/frontend/e2e/block/mirroring.po.ts b/src/pybind/mgr/dashboard/frontend/e2e/block/mirroring.po.ts index f1d562ca4c4c1..b9d778044589f 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/block/mirroring.po.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/block/mirroring.po.ts @@ -1,5 +1,50 @@ +import { $, browser, by, element } from 'protractor'; +import { Helper } from '../helper.po'; import { PageHelper } from '../page-helper.po'; export class MirroringPageHelper extends PageHelper { pages = { index: '/#/block/mirroring' }; + + // Goes to the mirroring page and edits a pool in the Pool table. Clicks on the + // pool and chooses a option (either pool, image, or disabled) + editMirror(name, option) { + this.navigateTo(); + // Clicks the pool in the table + browser + .wait(Helper.EC.elementToBeClickable(this.getFirstTableCellWithText(name)), Helper.TIMEOUT) + .then(() => { + this.getFirstTableCellWithText(name).click(); + }); + + // Clicks the Edit Mode button + const editModeButton = element(by.cssContainingText('button', 'Edit Mode')); + browser.wait(Helper.EC.elementToBeClickable(editModeButton), Helper.TIMEOUT).then(() => { + editModeButton.click(); + }); + // Clicks the drop down in the edit pop-up, then clicks the Update button + browser.wait(Helper.EC.visibilityOf($('.modal-content')), Helper.TIMEOUT).then(() => { + const mirrorDrop = element(by.id('mirrorMode')); + this.moveClick(mirrorDrop); + element(by.cssContainingText('select[name=mirrorMode] option', option)).click(); + }); + // Clicks update button and checks if the mode has been changed + element(by.cssContainingText('button', 'Update')) + .click() + .then(() => { + browser + .wait( + Helper.EC.stalenessOf( + element(by.cssContainingText('.modal-dialog', 'Edit pool mirror mode')) + ), + Helper.TIMEOUT + ) + .then(() => { + const val = option.toLowerCase(); // used since entries in table are lower case + browser.wait( + Helper.EC.visibilityOf(this.getFirstTableCellWithText(val)), + Helper.TIMEOUT + ); + }); + }); + } } diff --git a/src/pybind/mgr/dashboard/frontend/e2e/pools/pools.po.ts b/src/pybind/mgr/dashboard/frontend/e2e/pools/pools.po.ts index 535542fcc841c..968bcde019f63 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/pools/pools.po.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/pools/pools.po.ts @@ -29,7 +29,7 @@ export class PoolPageHelper extends PageHelper { } @PageHelper.restrictTo(pages.create) - create(name: string, placement_groups: number): promise.Promise { + create(name: string, placement_groups: number, ...apps: string[]): promise.Promise { const nameInput = $('input[name=name]'); nameInput.clear(); if (!this.isPowerOf2(placement_groups)) { @@ -45,12 +45,33 @@ export class PoolPageHelper extends PageHelper { $('input[name=pgNum]') .sendKeys(protractor.Key.CONTROL, 'a', protractor.Key.NULL, placement_groups) .then(() => { + this.setApplications(apps); return element(by.css('cd-submit-button')).click(); }); }); }); } + private setApplications(apps: string[]) { + if (!apps || apps.length === 0) { + return; + } + element(by.css('.float-left.mr-2.select-menu-edit')) + .click() + .then(() => { + browser + .wait( + Helper.EC.visibilityOf(element(by.css('.popover-content.popover-body'))), + Helper.TIMEOUT + ) + .then(() => + apps.forEach((app) => + element(by.cssContainingText('.select-menu-item-content', app)).click() + ) + ); + }); + } + @PageHelper.restrictTo(pages.index) delete(name: string): promise.Promise { return this.getTableCellByContent(name).then((tableCell: ElementFinder) => { @@ -61,13 +82,17 @@ export class PoolPageHelper extends PageHelper { return $('li.delete a') // click on "delete" menu item .click() .then(() => { - const getConfirmationCheckbox = () => $('#confirmation'); browser - .wait(() => EC.visibilityOf(getConfirmationCheckbox()), Helper.TIMEOUT) + .wait(Helper.EC.visibilityOf($('.custom-control-label')), Helper.TIMEOUT) .then(() => { - this.moveClick(getConfirmationCheckbox()).then(() => { - return element(by.cssContainingText('button', 'Delete Pool')).click(); // Click Delete item - }); + const getConfirmationCheckbox = () => $('.custom-control-label'); + browser + .wait(Helper.EC.visibilityOf(getConfirmationCheckbox()), Helper.TIMEOUT) + .then(() => { + this.moveClick(getConfirmationCheckbox()).then(() => { + return element(by.cssContainingText('button', 'Delete Pool')).click(); // Click Delete item + }); + }); }); }); }); -- 2.39.5