From: Patrick Seidensal Date: Fri, 23 Aug 2019 15:58:12 +0000 (+0200) Subject: mgr/dashboard: refactoring E2E dashboard test X-Git-Tag: v15.1.0~1745^2~9 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=74cc1f8155edd4cd610f6e6e5d6bc5fc440c78ba;p=ceph-ci.git mgr/dashboard: refactoring E2E dashboard test Signed-off-by: Patrick Seidensal --- diff --git a/src/pybind/mgr/dashboard/frontend/e2e/cluster/hosts.po.ts b/src/pybind/mgr/dashboard/frontend/e2e/cluster/hosts.po.ts index 0d0b8f1090d..81037c279c7 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/cluster/hosts.po.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/cluster/hosts.po.ts @@ -7,8 +7,7 @@ export class HostsPageHelper extends PageHelper { async check_for_host() { await this.navigateTo(); - const hostcount = await this.getTableCount().getText(); - expect(await hostcount.includes('0 total')).toBe(false); + expect(await this.getTableTotalCount()).not.toBe(0); } // function that checks all services links work for first diff --git a/src/pybind/mgr/dashboard/frontend/e2e/dashboard.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/e2e/dashboard.e2e-spec.ts index 02e14ecbe89..df1d03abc74 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/dashboard.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/dashboard.e2e-spec.ts @@ -1,10 +1,11 @@ -import { $$, browser, by, element } from 'protractor'; +import { browser } from 'protractor'; import { IscsiPageHelper } from './block/iscsi.po'; import { HostsPageHelper } from './cluster/hosts.po'; import { MonitorsPageHelper } from './cluster/monitors.po'; import { OSDsPageHelper } from './cluster/osds.po'; import { DashboardPageHelper } from './dashboard.po'; import { Helper } from './helper.po'; +import { PageHelper } from './page-helper.po'; import { PoolPageHelper } from './pools/pools.po'; import { DaemonsPageHelper } from './rgw/daemons.po'; @@ -18,184 +19,124 @@ describe('Dashboard Main Page', () => { let iscsi: IscsiPageHelper; beforeAll(() => { - dashboard = new Helper().dashboard; - daemons = new Helper().daemons; - hosts = new Helper().hosts; - osds = new Helper().osds; - pools = new Helper().pools; - monitors = new Helper().monitors; - iscsi = new Helper().iscsi; + const h = new Helper(); + dashboard = h.dashboard; + daemons = h.daemons; + hosts = h.hosts; + osds = h.osds; + pools = h.pools; + monitors = h.monitors; + iscsi = h.iscsi; }); afterEach(async () => { await Helper.checkConsole(); }); - describe('Check that all hyperlinks on cells lead to the correct page and fields exist', () => { + describe('Check that all hyperlinks on info cards lead to the correct page and fields exist', () => { beforeEach(async () => { await dashboard.navigateTo(); }); - it('should check all linked cells lead to correct page', async () => { - // Grabs cell and then clicks the hyperlink, some cells require different - // methods as stated below - - // Monitors Cell - expect(await browser.getCurrentUrl()).toContain('/#/dashboard'); - await dashboard.cellLink('Monitors'); - expect(await dashboard.getBreadcrumbText()).toEqual('Monitors'); - - // OSDs Cell - // await browser.navigate().back(); - await dashboard.navigateBack(); - expect(await browser.getCurrentUrl()).toContain('/#/dashboard'); - await dashboard.cellLink('OSDs'); - expect(await dashboard.getBreadcrumbText()).toEqual('OSDs'); - - // Hosts Cell - // await browser.navigate().back(); - await dashboard.navigateBack(); - expect(await browser.getCurrentUrl()).toContain('/#/dashboard'); - await dashboard.cellLink('Hosts'); - expect(await dashboard.getBreadcrumbText()).toEqual('Hosts'); - - // Object Gateways Cell - // await browser.navigate().back(); - await dashboard.navigateBack(); - expect(await browser.getCurrentUrl()).toContain('/#/dashboard'); - await element - .all(by.partialLinkText('Object')) - .last() - .click(); // Since there is a space and there are 2 occurances of - expect(await dashboard.getBreadcrumbText()).toEqual('Daemons'); // 'Object Gateways', this method was used to grab the link - - // iSCSI Gateways Cell - // await browser.navigate().back(); - await dashboard.navigateBack(); - expect(await browser.getCurrentUrl()).toContain('/#/dashboard'); - await dashboard.partialCellLink('iSCSI'); // Since there is a space between iSCSI and Gateways this method was - expect(await dashboard.getBreadcrumbText()).toEqual('Overview'); // used to grab and click the link - - // Pools Cell - // await browser.navigate().back(); - await dashboard.navigateBack(); - expect(await browser.getCurrentUrl()).toContain('/#/dashboard'); - await dashboard.cellLink('Pools'); - expect(await dashboard.getBreadcrumbText()).toEqual('Pools'); + it('should ensure that all linked info cards lead to correct page', async () => { + const expectationMap = { + Monitors: 'Monitors', + OSDs: 'OSDs', + Hosts: 'Hosts', + 'Object Gateways': 'Daemons', + 'iSCSI Gateways': 'Overview', + Pools: 'Pools' + }; + + for (const [linkText, breadcrumbText] of Object.entries(expectationMap)) { + expect(await browser.getCurrentUrl()).toContain('/#/dashboard'); + await dashboard.clickInfoCardLink(linkText); + expect(await dashboard.getBreadcrumbText()).toEqual(breadcrumbText); + await dashboard.navigateBack(); + } }); - it('should verify that cells exist on dashboard in proper order', async () => { - // Ensures that info cards are all displayed on the dashboard tab while being - // in the proper order, checks for card title and position via indexing into - // a list of all info cards - await dashboard.navigateTo(); - expect(await browser.getCurrentUrl()).toContain('/#/dashboard'); - expect(await dashboard.infoCardText(0)).toContain('Cluster Status'); - expect(await dashboard.infoCardText(1)).toContain('Monitors'); - expect(await dashboard.infoCardText(2)).toContain('OSDs'); - expect(await dashboard.infoCardText(3)).toContain('Manager Daemons'); - expect(await dashboard.infoCardText(4)).toContain('Hosts'); - expect(await dashboard.infoCardText(5)).toContain('Object Gateways'); - expect(await dashboard.infoCardText(6)).toContain('Metadata Servers'); - expect(await dashboard.infoCardText(7)).toContain('iSCSI Gateways'); - expect(await dashboard.infoCardText(8)).toContain('Client IOPS'); - expect(await dashboard.infoCardText(9)).toContain('Client Throughput'); - expect(await dashboard.infoCardText(10)).toContain('Client Read/Write'); - expect(await dashboard.infoCardText(11)).toContain('Recovery Throughput'); - expect(await dashboard.infoCardText(12)).toContain('Scrub'); - expect(await dashboard.infoCardText(13)).toContain('Pools'); - expect(await dashboard.infoCardText(14)).toContain('Raw Capacity'); - expect(await dashboard.infoCardText(15)).toContain('Objects'); - expect(await dashboard.infoCardText(16)).toContain('PGs per OSD'); - expect(await dashboard.infoCardText(17)).toContain('PG Status'); + it('should verify that info cards exist on dashboard in proper order', async () => { + // Ensures that info cards are all displayed on the dashboard tab while being in the proper + // order, checks for card title and position via indexing into a list of all info cards. + const order = [ + 'Cluster Status', + 'Monitors', + 'OSDs', + 'Manager Daemons', + 'Hosts', + 'Object Gateways', + 'Metadata Servers', + 'iSCSI Gateways', + 'Client IOPS', + 'Client Throughput', + 'Client Read/Write', + 'Recovery Throughput', + 'Scrub', + 'Pools', + 'Raw Capacity', + 'Objects', + 'PGs per OSD', + 'PG Status' + ]; + + for (let i = 0; i < order.length; i++) { + expect((await dashboard.infoCard(i)).getText()).toContain( + order[i], + `Order of ${order[i]} seems to be wrong` + ); + } }); it('should verify that info card group titles are present and in the right order', async () => { - // Checks that the group titles on the dashboard are correct and in the right order - await dashboard.navigateTo(); expect(await browser.getCurrentUrl()).toContain('/#/dashboard'); - expect(await dashboard.checkGroupTitles(0, 'Status')); - expect(await dashboard.checkGroupTitles(1, 'Performance')); - expect(await dashboard.checkGroupTitles(2, 'Capacity')); + expect(await dashboard.infoGroupTitle(0)).toBe('Status'); + expect(await dashboard.infoGroupTitle(1)).toBe('Performance'); + expect(await dashboard.infoGroupTitle(2)).toBe('Capacity'); }); }); - describe('Should check that dashboard cells have correct information', () => { - beforeAll(async () => { - await dashboard.navigateTo(); - }); - - it('should verify that proper number of object gateway daemons on dashboard', async () => { - // Checks that dashboard id card for Object Gateway has the correct number of Daemons - // by checking the Daemons page and taking the count found at the bottom of the table + it('Should check that dashboard cards have correct information', async () => { + interface TestSpec { + cardName: string; + regexMatcher?: RegExp; + pageObject: PageHelper; + } + + const testSpecs: TestSpec[] = [ + { cardName: 'Object Gateways', regexMatcher: /(\d+)\s+total/, pageObject: daemons }, + { cardName: 'Monitors', regexMatcher: /(\d+)\s+\(quorum/, pageObject: monitors }, + { cardName: 'Hosts', regexMatcher: /(\d+)\s+total/, pageObject: hosts }, + { cardName: 'OSDs', regexMatcher: /(\d+)\s+total/, pageObject: osds }, + { cardName: 'Pools', pageObject: pools }, + { cardName: 'iSCSI Gateways', regexMatcher: /(\d+)\s+total/, pageObject: iscsi } + ]; + + for (let i = 0; i < testSpecs.length; i++) { + const spec = testSpecs[i]; await dashboard.navigateTo(); - const dashCount = await dashboard.cardNumb(5); // Grabs number of daemons from dashboard Object Gateway card - await daemons.navigateTo(); - // Grabs number of daemons from table footer - const tableCount = (await daemons.getTableCount().getText()).slice(13); - expect(dashCount).toContain(tableCount); - }); - - it('should verify that proper number of monitors on dashboard', async () => { - // Checks that dashboard id card for Monitors has the correct number of Monitors - // by checking the Monitors page and taking the count found at the bottom of the table - await dashboard.navigateTo(); - // Grabs number of monitors from dashboard Monitor card - const dashCount = await dashboard.cardNumb(1); - await monitors.navigateTo(); - // Grabs number of monitors from table footer - const tableCount = (await $$('.datatable-footer-inner') - .first() - .getText()).slice(0, -6); - expect(dashCount).toContain(tableCount); - }); - - it('should verify that proper number of hosts on dashboard', async () => { - // Checks that dashboard id card for Hosts has the correct number of hosts - // by checking the Hosts page and taking the count found at the bottom of the table - await dashboard.navigateTo(); - // Grabs number of hosts from dashboard Hosts card - const dashCount = await dashboard.cardNumb(4); - await hosts.navigateTo(); - // Grabs number of hosts from table footer - const tableCount = (await hosts.getTableCount().getText()).slice(13, -6); - expect(dashCount).toContain(tableCount); - }); - - it('should verify that proper number of osds on dashboard', async () => { - // Checks that dashboard id card for Hosts has the correct number of hosts - // by checking the Hosts page and taking the count found at the bottom of the table - await dashboard.navigateTo(); - // Grabs number of hosts from dashboard Hosts card - const dashCount = (await dashboard.cardNumb(2)).slice(0, -17); - await osds.navigateTo(); - // Grabs number of hosts from table footer - const tableCount = (await osds.getTableCount().getText()).slice(13, -6); - expect(dashCount).toContain(tableCount); - }); - - it('should verify that proper number of pools on dashboard', async () => { - await dashboard.navigateTo(); - // Grabs number of hosts from dashboard Pools card - const dashCount = (await dashboard.cardNumb(13)).slice(4); - await pools.navigateTo(); - // Grabs number of pools from table footer - const tableCount = (await pools.getTableCount().getText()).slice(13, -6); - expect(dashCount).toContain(tableCount); - }); - - it('should verify that proper number of iscsi gateways on dashboard', async () => { - // Checks that dashboard id card for iSCSI has the correct number of gateways - // by checking the iSCSI page and taking the count found at the bottom of the table (first) - await dashboard.navigateTo(); - // Grabs number of gateways from dashboard iSCSI card - const dashCount = await dashboard.cardNumb(7); - await iscsi.navigateTo(); - // Grabs number of monitors from table footer - const tableCount = (await $$('.datatable-footer-inner') - .first() - .getText()).slice(0, -6); - expect(dashCount).toContain(tableCount); - }); + const infoCardBodyText = await dashboard.infoCardBodyText(spec.cardName); + let dashCount = 0; + if (spec.regexMatcher) { + const match = infoCardBodyText.match(new RegExp(spec.regexMatcher)); + if (match && match.length > 1) { + dashCount = Number(match[1]); + } else { + return Promise.reject( + `Regex ${spec.regexMatcher} did not find a match for card with name ` + + `${spec.cardName}` + ); + } + } else { + dashCount = Number(infoCardBodyText); + } + await spec.pageObject.navigateTo(); + const tableCount = await spec.pageObject.getTableTotalCount(); + expect(dashCount).toBe( + tableCount, + `Text of card ${spec.cardName} and regex ${spec.regexMatcher} resulted in ${dashCount} ` + + `but did not match table count ${tableCount}` + ); + } }); }); diff --git a/src/pybind/mgr/dashboard/frontend/e2e/dashboard.po.ts b/src/pybind/mgr/dashboard/frontend/e2e/dashboard.po.ts index 724608de753..a4271d931a4 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/dashboard.po.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/dashboard.po.ts @@ -1,4 +1,4 @@ -import { $, by, element } from 'protractor'; +import { $, $$, by, ElementFinder } from 'protractor'; import { PageHelper } from './page-helper.po'; export class DashboardPageHelper extends PageHelper { @@ -6,45 +6,48 @@ export class DashboardPageHelper extends PageHelper { index: '/#/dashboard' }; - async checkGroupTitles(index, name) { - // Checks that the titles of all the groups on the dashboard are correct - const titles = element.all(by.className('info-group-title')); - const text = await titles.get(index).getText(); - expect(text).toBe(name); + async infoGroupTitle(index: number): Promise { + return $$('.info-group-title') + .get(index) + .getText(); } - cellFromGroup(cardName) { - // Grabs cell from dashboard page based off the title. Then returns the card - // element - return $(`cd-info-card[cardtitle=${cardName}]`); - } - - async cellLink(cardName) { - // Grabs the link from the correct card using the cellFromGroup function, - // then clicks the hyperlinked title - await this.navigateTo(); - await this.cellFromGroup(cardName) + async clickInfoCardLink(cardName: string): Promise { + await $(`cd-info-card[cardtitle="${cardName}"]`) .element(by.linkText(cardName)) .click(); } - async partialCellLink(partName) { - // Used for cases in which there was a space inbetween two words in the hyperlink, - // has the same functionality as cellLink - await element(by.partialLinkText(partName)).click(); - } - - async infoCardText(index) { - // Grabs a list of all info cards, then checks by index that the title text - // is equal to the desired title, thus checking the presence of the card - const cardList = element.all(by.tagName('cd-info-card')); - return cardList.get(index).getText(); + async infoCard(indexOrTitle: number | string): Promise { + let infoCards = $$('cd-info-card'); + if (typeof indexOrTitle === 'number') { + if ((await infoCards.count()) <= indexOrTitle) { + return Promise.reject( + `No element found for index ${indexOrTitle}. Elements array has ` + + `only ${await infoCards.count()} elements.` + ); + } + return infoCards.get(indexOrTitle); + } else if (typeof indexOrTitle === 'string') { + infoCards = infoCards.filter( + async (e) => (await e.$('.card-title').getText()) === indexOrTitle + ); + if ((await infoCards.count()) === 0) { + return Promise.reject(`No element found for title "${indexOrTitle}"`); + } + return infoCards.first(); + } } - async cardNumb(index) { - // Grabs a list of all info cards and returns the text on the card via - // the index of the card in the list - const cardList = element.all(by.tagName('cd-info-card')); - return cardList.get(index).getText(); + async infoCardBodyText( + infoCard: ElementFinder | Promise | string + ): Promise { + let _infoCard: ElementFinder; + if (typeof infoCard === 'string') { + _infoCard = await this.infoCard(infoCard); + } else { + _infoCard = typeof infoCard.then === 'function' ? await infoCard : infoCard; + } + return _infoCard.$('.card-text').getText(); } } 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 11bc0eb79d9..afe73a08b11 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/page-helper.po.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/page-helper.po.ts @@ -89,8 +89,13 @@ export abstract class PageHelper { .getText(); } - getTableCount() { - return $('.datatable-footer-inner.selected-count'); + async getTableTotalCount(): Promise { + return Number( + (await $$('.datatable-footer-inner .page-count span') + .filter(async (e) => (await e.getText()).includes('total')) + .first() + .getText()).match(/.*(\d+\s+)total.*/)[1] + ); } // getTitleText() {