From fb025001bd6be33953401cadef241b21e66f54b2 Mon Sep 17 00:00:00 2001 From: Rafael Quintero Date: Thu, 11 Jul 2019 14:19:44 -0400 Subject: [PATCH] mgr/dashboard: E2E Tests for Create, Edit, Delete, and Invalid Input on RGW Buckets Fixes: https://tracker.ceph.com/issues/40372 Fixes: https://tracker.ceph.com/issues/40484 Fixes: https://tracker.ceph.com/issues/40259 Signed-off-by: Adam King Signed-off-by: Rafael Quintero --- .../mgr/dashboard/frontend/e2e/helper.po.ts | 9 +- .../dashboard/frontend/e2e/page-helper.po.ts | 42 +++- .../frontend/e2e/rgw/buckets.e2e-spec.ts | 47 ++++- .../dashboard/frontend/e2e/rgw/buckets.po.ts | 185 +++++++++++++++++- 4 files changed, 273 insertions(+), 10 deletions(-) diff --git a/src/pybind/mgr/dashboard/frontend/e2e/helper.po.ts b/src/pybind/mgr/dashboard/frontend/e2e/helper.po.ts index 3e95ff8224e..1dcdf5862f3 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/helper.po.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/helper.po.ts @@ -1,8 +1,15 @@ import { browser } from 'protractor'; +import { BucketsPageHelper } from './rgw/buckets.po'; export class Helper { static EC = browser.ExpectedConditions; - static TIMEOUT = 10000; + static TIMEOUT = 30000; + + buckets: BucketsPageHelper; + + constructor() { + this.buckets = new BucketsPageHelper(); + } /** * Checks if there are any errors on the browser 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 7212d6b764b..e0d9353220d 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 } from 'protractor'; +import { $, $$, browser, by, element } from 'protractor'; interface Pages { index: string; @@ -17,11 +17,49 @@ export abstract class PageHelper { .getText(); } + static getTableCount() { + return $('.datatable-footer-inner.selected-count'); + } + + static getTitleText() { + let title; + return browser + .wait(() => { + title = $('.panel-title'); + return title.isPresent(); + }) + .then(() => title.getText()); + } + + static getTableCell(content) { + return element(by.cssContainingText('.datatable-body-cell-label', content)); + } + + static getTable() { + return element.all(by.css('.datatable-body')); + } + static getTabsCount() { return $$('.nav.nav-tabs li').count(); } + static getFirstTableCellWithText(content) { + return element.all(by.cssContainingText('.datatable-body-cell-label', content)).first(); + } + + // Used for instances where a modal container recieved the click rather than the + // desired element + static moveClick(object) { + return browser + .actions() + .mouseMove(object) + .click() + .perform(); + } + navigateTo(page = null) { - return browser.get(this.pages[page || 'index']); + page = page || 'index'; + const url = this.pages[page]; + return browser.get(url); } } diff --git a/src/pybind/mgr/dashboard/frontend/e2e/rgw/buckets.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/e2e/rgw/buckets.e2e-spec.ts index ee74217318b..d6685f4541c 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/rgw/buckets.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/rgw/buckets.e2e-spec.ts @@ -1,11 +1,11 @@ import { Helper } from '../helper.po'; -import { BucketsPage } from './buckets.po'; +import { PageHelper } from '../page-helper.po'; describe('RGW buckets page', () => { - let page: BucketsPage; + let buckets; beforeAll(() => { - page = new BucketsPage(); + buckets = new Helper().buckets; }); afterEach(() => { @@ -14,11 +14,48 @@ describe('RGW buckets page', () => { describe('breadcrumb test', () => { beforeAll(() => { - page.navigateTo(); + buckets.navigateTo(); }); it('should open and show breadcrumb', () => { - expect(BucketsPage.getBreadcrumbText()).toEqual('Buckets'); + expect(PageHelper.getBreadcrumbText()).toEqual('Buckets'); + }); + }); + + describe('create, edit & delete bucket test', () => { + beforeAll(() => { + buckets.navigateTo(); + }); + + it('should create bucket', () => { + buckets.create('000test', '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'); + expect(PageHelper.getTableCell('000test').isPresent()).toBe(true); + }); + + it('should edit bucket', () => { + buckets.edit('000test', 'dev'); + expect(PageHelper.getTable().getText()).toMatch('dev'); + }); + + it('should delete bucket', () => { + buckets.delete('000test'); + expect(PageHelper.getTableCell('000test').isPresent()).toBe(false); + }); + }); + + describe('Invalid Input in Create and Edit tests', () => { + beforeAll(() => { + buckets.navigateTo(); + }); + + it('should test invalid inputs in create fields', () => { + buckets.invalidCreate(); + }); + + it('should test invalid input in edit owner field', () => { + buckets.create('000rq', '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'); + buckets.invalidEdit('000rq'); + buckets.delete('000rq'); }); }); }); diff --git a/src/pybind/mgr/dashboard/frontend/e2e/rgw/buckets.po.ts b/src/pybind/mgr/dashboard/frontend/e2e/rgw/buckets.po.ts index 5341f7520c4..3506a019075 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/rgw/buckets.po.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/rgw/buckets.po.ts @@ -1,5 +1,186 @@ +import { $, browser, by, element } from 'protractor'; +import { Helper } from '../helper.po'; import { PageHelper } from '../page-helper.po'; -export class BucketsPage extends PageHelper { - pages = { index: '/#/rgw/bucket' }; +export class BucketsPageHelper extends PageHelper { + pages = { + index: '/#/rgw/bucket', + create: '/#/rgw/bucket/create' + }; + + create(name, owner) { + this.navigateTo('create'); + + // Enter in bucket name + element(by.id('bid')).sendKeys(name); + + // Select bucket owner + element(by.id('owner')).click(); + element(by.cssContainingText('select[name=owner] option', owner)).click(); + expect(element(by.id('owner')).getAttribute('class')).toContain('ng-valid'); + + // Click the create button and wait for bucket to be made + const createButton = element(by.cssContainingText('button', 'Create Bucket')); + createButton.click().then(() => { + browser.wait( + Helper.EC.presenceOf(PageHelper.getTableCell(name)), + Helper.TIMEOUT, + 'Timed out waiting for bucket creation' + ); + }); + } + + edit(name, new_owner) { + this.navigateTo(); + + browser.wait(Helper.EC.elementToBeClickable(PageHelper.getTableCell(name)), 10000); // wait for table to load + PageHelper.getTableCell(name).click(); // click on the bucket you want to edit in the table + element(by.cssContainingText('button', 'Edit')).click(); // click button to move to edit page + + expect(PageHelper.getBreadcrumbText()).toEqual('Edit'); + + const ownerDropDown = element(by.id('owner')); + ownerDropDown.click(); // click owner dropdown menu + + element(by.cssContainingText('select[name=owner] option', new_owner)).click(); // select the new user + const editbutton = element(by.cssContainingText('button', 'Edit Bucket')); + editbutton.click().then(() => { + // wait to be back on buckets page with table visible + browser.wait( + Helper.EC.elementToBeClickable(PageHelper.getTableCell(name)), + 10000, + 'Could not return to buckets page and load table after editing bucket' + ); + + // click on edited bucket and check its details table for edited owner field + PageHelper.getTableCell(name).click(); + const element_details_table = element + .all(by.css('.table.table-striped.table-bordered')) + .first(); + expect(element_details_table.getText()).toMatch(new_owner); + }); + } + + delete(name) { + this.navigateTo(); + + // wait for table to load + browser.wait(Helper.EC.elementToBeClickable(PageHelper.getTableCell(name)), 10000); + + PageHelper.getTableCell(name).click(); // click on the bucket you want to delete in the table + $('.table-actions button.dropdown-toggle').click(); // click toggle menu + $('li.delete a').click(); // click delete + // wait for pop-up to be visible (checks for title of pop-up) + browser.wait(Helper.EC.visibilityOf($('.modal-title.float-left')), 10000).then(() => { + browser.wait(Helper.EC.visibilityOf($('.custom-control-label')), 5000); + $('.custom-control-label').click(); + element(by.cssContainingText('button', 'Delete bucket')) + .click() + .then(() => { + this.navigateTo(); + browser.wait(Helper.EC.not(Helper.EC.presenceOf(PageHelper.getTableCell(name))), 10000); + }); + }); + } + + invalidCreate() { + this.navigateTo('create'); + expect(PageHelper.getBreadcrumbText()).toEqual('Create'); + + const nameInputField = element(by.id('bid')); // Grabs name box field + const ownerDropDown = element(by.id('owner')); // Grab owner field + + // Gives an invalid name (too short), then waits for dashboard to determine validity + nameInputField.sendKeys('rq'); + + PageHelper.moveClick(ownerDropDown); // To trigger a validation + + browser.wait( + function() { + // Waiting for website to decide if name is valid or not + return nameInputField.getAttribute('class').then(function(classValue) { + return classValue.indexOf('ng-pending') === -1; + }); + }, + 5000, + 'Timed out waiting for dashboard to decide bucket name validity' + ); + + // Check that name input field was marked invalid in the css + expect(nameInputField.getAttribute('class')).toContain('ng-invalid'); + + // Check that error message was printed under name input field + expect(element(by.css('#bid + .invalid-feedback')).getText()).toMatch( + 'The value is not valid.' + ); + + // Test invalid owner input + PageHelper.moveClick(ownerDropDown); // Clicks the Owner drop down on the Create Bucket page + // select some valid option. The owner drop down error message will not appear unless a valid user was selected at + // one point before the invalid placeholder user is selected. + element(by.cssContainingText('select[name=owner] option', 'dev')).click(); + + PageHelper.moveClick(ownerDropDown); // Clicks the Owner drop down on the Create Bucket page + // select the first option, which is invalid because it is a placeholder + element(by.cssContainingText('select[name=owner] option', 'Select a user')).click(); + + PageHelper.moveClick(nameInputField); // To trigger a validation + + // Check that owner drop down field was marked invalid in the css + expect(element(by.id('owner')).getAttribute('class')).toContain('ng-invalid'); + + // Check that error message was printed under owner drop down field + expect(element(by.css('#owner + .invalid-feedback')).getText()).toMatch( + 'This field is required.' + ); + + // Clicks the Create Bucket button but the page doesn't move. Done by testing + // for the breadcrumb + PageHelper.moveClick(element(by.cssContainingText('button', 'Create Bucket'))); // Clicks Create Bucket button + expect(PageHelper.getBreadcrumbText()).toEqual('Create'); + // content in fields seems to subsist through tests if not cleared, so it is cleared + nameInputField.clear().then(() => { + element(by.cssContainingText('button', 'Cancel')).click(); + }); + } + + invalidEdit(name) { + this.navigateTo(); + + browser.wait( + Helper.EC.elementToBeClickable(PageHelper.getTableCell(name)), + 10000, + 'Failed waiting for bucket to be present in table' + ); // wait for table to load + PageHelper.getTableCell(name).click(); // click on the bucket you want to edit in the table + element(by.cssContainingText('button', 'Edit')).click(); // click button to move to edit page + + expect(PageHelper.getBreadcrumbText()).toEqual('Edit'); + + // Chooses 'Select a user' rather than a valid owner on Edit Bucket page + // and checks if it's an invalid input + const ownerDropDown = element(by.id('owner')); + browser.wait(Helper.EC.elementToBeClickable(ownerDropDown), 5000); + + PageHelper.moveClick(ownerDropDown); // Clicks the Owner drop down on the Create Bucket page + // select the first option, which is invalid because it is a placeholder + element(by.cssContainingText('select[name=owner] option', 'Select a user')).click(); + + // Changes when updated to bootstrap 4 -> Error message takes a long time to appear unless another field + // is clicked on. For that reason, I'm having the test click on the nedit button before checking for errors + element(by.cssContainingText('button', 'Edit Bucket')).click(); + + // Check that owner drop down field was marked invalid in the css + expect(element(by.id('owner')).getAttribute('class')).toContain('ng-invalid'); + + // Check that error message was printed under owner drop down field + expect(element(by.css('#owner + .invalid-feedback')).getText()).toMatch( + 'This field is required.' + ); + + // Clicks the Edit Bucket button but the page doesn't move. Done by testing for + // breadcrumb + element(by.cssContainingText('button', 'Edit Bucket')).click(); // Gets the Edit button and clicks it + expect(PageHelper.getBreadcrumbText()).toEqual('Edit'); + } } -- 2.39.5