From 204ad50bf7774ae8b212e2b537f4a3ffaec01f78 Mon Sep 17 00:00:00 2001 From: Tiago Melo Date: Tue, 10 Sep 2019 14:44:47 +0000 Subject: [PATCH] mgr/dashboard: Enable waitforAngular in E2E Convert waitForTableData into a method, using it as a decorator was causing problems. Signed-off-by: Tiago Melo --- .../frontend/e2e/block/images.e2e-spec.ts | 2 +- .../frontend/e2e/block/mirroring.e2e-spec.ts | 2 +- .../e2e/cluster/configuration.e2e-spec.ts | 4 +- .../frontend/e2e/cluster/logs.e2e-spec.ts | 11 +++ .../dashboard/frontend/e2e/cluster/logs.po.ts | 4 +- .../dashboard/frontend/e2e/page-helper.po.ts | 38 -------- .../frontend/e2e/rgw/daemons.e2e-spec.ts | 2 +- .../frontend/e2e/rgw/users.e2e-spec.ts | 9 ++ .../dashboard/frontend/e2e/rgw/users.po.ts | 3 - .../frontend/e2e/ui/dashboard.e2e-spec.ts | 87 +++++++++++-------- .../mgr/dashboard/frontend/protractor.conf.js | 9 -- .../app/ceph/cluster/logs/logs.component.ts | 18 ++-- .../services/refresh-interval.service.spec.ts | 14 ++- .../services/refresh-interval.service.ts | 14 +-- 14 files changed, 110 insertions(+), 107 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 2b1fa1db2f3..943c7877d8f 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 @@ -34,7 +34,7 @@ describe('Images page', () => { }); }); - describe('create, edit & delete image test', async () => { + describe('create, edit & delete image test', () => { const poolName = 'e2e_images_pool'; const imageName = 'e2e_images_image'; const newImageName = 'e2e_images_image_new'; 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 b6201019d2c..3327cf161bb 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 @@ -34,7 +34,7 @@ describe('Mirroring page', () => { }); }); - describe('checks that edit mode functionality shows in the pools table', async () => { + describe('checks that edit mode functionality shows in the pools table', () => { const poolName = 'mirroring_test'; beforeAll(async () => { diff --git a/src/pybind/mgr/dashboard/frontend/e2e/cluster/configuration.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/e2e/cluster/configuration.e2e-spec.ts index 23da2a70233..bf2f125b999 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/cluster/configuration.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/cluster/configuration.e2e-spec.ts @@ -23,8 +23,8 @@ describe('Configuration page', () => { }); describe('fields check', () => { - beforeAll(() => { - configuration.navigateTo(); + beforeAll(async () => { + await configuration.navigateTo(); }); it('should verify that selected footer increases when an entry is clicked', async () => { diff --git a/src/pybind/mgr/dashboard/frontend/e2e/cluster/logs.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/e2e/cluster/logs.e2e-spec.ts index 47c95a39bdc..dca739c3c54 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/cluster/logs.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/cluster/logs.e2e-spec.ts @@ -69,6 +69,13 @@ describe('Logs page', () => { }); describe('audit logs respond to editing configuration setting test', () => { + let originalTimeout; + + beforeEach(() => { + originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; + jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000; + }); + it('should change config settings and check audit logs reacted', async () => { await configuration.navigateTo(); await configuration.edit(configname, ['global', '5']); @@ -79,5 +86,9 @@ describe('Logs page', () => { await configuration.navigateTo(); await configuration.configClear(configname); }); + + afterEach(function() { + jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; + }); }); }); diff --git a/src/pybind/mgr/dashboard/frontend/e2e/cluster/logs.po.ts b/src/pybind/mgr/dashboard/frontend/e2e/cluster/logs.po.ts index c9e3ba6ff4b..e25e76644ba 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/cluster/logs.po.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/cluster/logs.po.ts @@ -58,7 +58,7 @@ export class LogsPageHelper extends PageHelper { const audit_logs_tab = $('.tab-pane.active'); const audit_logs_body = audit_logs_tab.element(by.css('.card-body')); - const logs = audit_logs_body.all(by.cssContainingText('.ng-star-inserted', poolname)); + const logs = audit_logs_body.all(by.cssContainingText('.message', poolname)); await expect(logs.getText()).toMatch(poolname); await expect(logs.getText()).toMatch(`pool ${poolfunction}`); @@ -114,7 +114,7 @@ export class LogsPageHelper extends PageHelper { const audit_logs_tab = $('.tab-pane.active'); const audit_logs_body = audit_logs_tab.element(by.css('.card-body')); - const logs = audit_logs_body.all(by.cssContainingText('.ng-star-inserted', configname)); + const logs = audit_logs_body.all(by.cssContainingText('.message', configname)); await this.waitPresence(logs.first()); 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 1cacf01dff8..392e2c52511 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/page-helper.po.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/page-helper.po.ts @@ -63,42 +63,6 @@ export abstract class PageHelper { }; } - /** - * This is a decorator to be used on methods which change the current page once, like `navigateTo` - * and `navigateBack` in this class do. It ensures that, if the new page contains a table, its - * data has been fully loaded. If no table is detected, it will return instantly. - */ - static waitForTableData(): Function { - return (_target: any, _propertyKey: string, descriptor: PropertyDescriptor) => { - const fn: Function = descriptor.value; - descriptor.value = async function(...args) { - const result = fn.apply(this, args); - - // If a table is on the new page, wait until it has gotten its data. - const implicitWaitTimeout = (await browser.getProcessedConfig()).implicitWaitTimeout; - await browser - .manage() - .timeouts() - .implicitlyWait(1000); - - const tableCount = await element.all(by.css('cd-table')).count(); - if (tableCount > 0) { - const progressBars = element.all(by.css('cd-table datatable-progress')); - await progressBars.each(async (progressBar) => { - await browser.wait(EC.stalenessOf(progressBar), TIMEOUT); - }); - } - - await browser - .manage() - .timeouts() - .implicitlyWait(implicitWaitTimeout); - - return result; - }; - }; - } - /** * Get the active breadcrumb item. */ @@ -224,14 +188,12 @@ export abstract class PageHelper { } } - @PageHelper.waitForTableData() async navigateTo(page = null) { page = page || 'index'; const url = this.pages[page]; await browser.get(url); } - @PageHelper.waitForTableData() async navigateBack() { await browser.navigate().back(); } diff --git a/src/pybind/mgr/dashboard/frontend/e2e/rgw/daemons.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/e2e/rgw/daemons.e2e-spec.ts index 2e757b75a24..0a9e551db32 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/rgw/daemons.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/rgw/daemons.e2e-spec.ts @@ -33,7 +33,7 @@ describe('RGW daemons page', () => { }); }); - describe('details and performance counters table tests', async () => { + describe('details and performance counters table tests', () => { beforeAll(async () => { await daemons.navigateTo(); }); diff --git a/src/pybind/mgr/dashboard/frontend/e2e/rgw/users.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/e2e/rgw/users.e2e-spec.ts index 4caaa4df175..29f1c7e78ac 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/rgw/users.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/rgw/users.e2e-spec.ts @@ -43,7 +43,12 @@ describe('RGW users page', () => { }); describe('Invalid input test', () => { + let originalTimeout; + beforeAll(async () => { + originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; + jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000; + await users.navigateTo(); }); @@ -54,5 +59,9 @@ describe('RGW users page', () => { it('should put invalid input into user edit form and check fields are marked invalid', async () => { await users.invalidEdit(); }); + + afterAll(function() { + jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; + }); }); }); diff --git a/src/pybind/mgr/dashboard/frontend/e2e/rgw/users.po.ts b/src/pybind/mgr/dashboard/frontend/e2e/rgw/users.po.ts index 735f9cf8790..fc2a6d6c584 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/rgw/users.po.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/rgw/users.po.ts @@ -79,9 +79,6 @@ export class UsersPageHelper extends PageHelper { // Try to give user already taken name. Should make field invalid. await username_field.clear(); await username_field.sendKeys(uname); - await this.waitFn( - async () => !(await username_field.getAttribute('class')).includes('ng-pending') - ); await expect(username_field.getAttribute('class')).toContain('ng-invalid'); await element(by.id('display_name')).click(); // trigger validation check await expect(element(by.css('#uid + .invalid-feedback')).getText()).toMatch( diff --git a/src/pybind/mgr/dashboard/frontend/e2e/ui/dashboard.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/e2e/ui/dashboard.e2e-spec.ts index d15c358d525..3ec8719bc5e 100644 --- a/src/pybind/mgr/dashboard/frontend/e2e/ui/dashboard.e2e-spec.ts +++ b/src/pybind/mgr/dashboard/frontend/e2e/ui/dashboard.e2e-spec.ts @@ -94,47 +94,60 @@ describe('Dashboard Main Page', () => { }); }); - it('Should check that dashboard cards have correct information', async () => { - interface TestSpec { - cardName: string; - regexMatcher?: RegExp; - pageObject: PageHelper; - } + describe('Correct information', () => { + let originalTimeout; - 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 } - ]; + beforeEach(async () => { + originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; + jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000; + }); - for (let i = 0; i < testSpecs.length; i++) { - const spec = testSpecs[i]; - await dashboard.navigateTo(); - 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]); + 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 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 { - return Promise.reject( - `Regex ${spec.regexMatcher} did not find a match for card with name ` + - `${spec.cardName}` - ); + dashCount = Number(infoCardBodyText); } - } else { - dashCount = Number(infoCardBodyText); + await spec.pageObject.navigateTo(); + const tableCount = await spec.pageObject.getTableTotalCount(); + await expect(dashCount).toBe( + tableCount, + `Text of card "${spec.cardName}" and regex "${spec.regexMatcher}" resulted in ${dashCount} ` + + `but did not match table count ${tableCount}` + ); } - await spec.pageObject.navigateTo(); - const tableCount = await spec.pageObject.getTableTotalCount(); - await expect(dashCount).toBe( - tableCount, - `Text of card "${spec.cardName}" and regex "${spec.regexMatcher}" resulted in ${dashCount} ` + - `but did not match table count ${tableCount}` - ); - } + }); + + afterEach(function() { + jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; + }); }); }); diff --git a/src/pybind/mgr/dashboard/frontend/protractor.conf.js b/src/pybind/mgr/dashboard/frontend/protractor.conf.js index 956ffdd1e1c..143437a81f7 100644 --- a/src/pybind/mgr/dashboard/frontend/protractor.conf.js +++ b/src/pybind/mgr/dashboard/frontend/protractor.conf.js @@ -58,7 +58,6 @@ const config = { config.onPrepare = async () => { await browser.manage().timeouts().implicitlyWait(config.implicitWaitTimeout); - await browser.waitForAngularEnabled(false); require('ts-node').register({ project: 'e2e/tsconfig.e2e.json' @@ -73,14 +72,6 @@ config.onPrepare = async () => { await browser.driver.findElement(by.name('password')).sendKeys(browser.params.login.password); await browser.driver.findElement(by.css('input[type="submit"]')).click(); - - // Login takes some time, so wait until it's done. - // For the test app's login, we know it's done when it redirects to - // dashboard. - await browser.driver.wait(async () => { - const url = await browser.driver.getCurrentUrl(); - return /dashboard/.test(url); - }); }; exports.config = config; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.ts index ddb8a73a7f7..dc33c535634 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.ts @@ -1,5 +1,5 @@ import { DatePipe } from '@angular/common'; -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, NgZone, OnDestroy, OnInit } from '@angular/core'; import { LogsService } from '../../../shared/api/logs.service'; import { Icons } from '../../../shared/enum/icons.enum'; @@ -31,16 +31,24 @@ export class LogsComponent implements OnInit, OnDestroy { selectedDate: Date; startTime: Date = new Date(); endTime: Date = new Date(); - constructor(private logsService: LogsService, private datePipe: DatePipe) { + constructor( + private logsService: LogsService, + private datePipe: DatePipe, + private ngZone: NgZone + ) { this.startTime.setHours(0, 0); this.endTime.setHours(23, 59); } ngOnInit() { this.getInfo(); - this.interval = window.setInterval(() => { - this.getInfo(); - }, 5000); + this.ngZone.runOutsideAngular(() => { + this.interval = window.setInterval(() => { + this.ngZone.run(() => { + this.getInfo(); + }); + }, 5000); + }); } ngOnDestroy() { diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.spec.ts index da55b44fd60..4d34ce0a965 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.spec.ts @@ -1,12 +1,19 @@ -import { fakeAsync, tick } from '@angular/core/testing'; +import { NgZone } from '@angular/core'; +import { fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { configureTestBed } from '../../../testing/unit-test-helper'; import { RefreshIntervalService } from './refresh-interval.service'; describe('RefreshIntervalService', () => { let service: RefreshIntervalService; + configureTestBed({ + imports: [], + providers: [RefreshIntervalService] + }); + beforeEach(() => { - service = new RefreshIntervalService(); + service = TestBed.get(RefreshIntervalService); }); it('should be created', () => { @@ -15,7 +22,8 @@ describe('RefreshIntervalService', () => { it('should initial private interval time right', () => { sessionStorage.setItem('dashboard_interval', '10000'); - service = new RefreshIntervalService(); + const ngZone = TestBed.get(NgZone); + service = new RefreshIntervalService(ngZone); expect(service.getRefreshInterval()).toBe(10000); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.ts index 0627d78ecf0..03aa3b8a56a 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.ts @@ -1,4 +1,4 @@ -import { Injectable, OnDestroy } from '@angular/core'; +import { Injectable, NgZone, OnDestroy } from '@angular/core'; import { BehaviorSubject, interval, Subscription } from 'rxjs'; @@ -13,7 +13,7 @@ export class RefreshIntervalService implements OnDestroy { // Observable streams intervalData$ = this.intervalDataSource.asObservable(); - constructor() { + constructor(private ngZone: NgZone) { const initialInterval = parseInt(sessionStorage.getItem('dashboard_interval'), 10) || 5000; this.setRefreshInterval(initialInterval); } @@ -25,9 +25,13 @@ export class RefreshIntervalService implements OnDestroy { if (this.intervalSubscription) { this.intervalSubscription.unsubscribe(); } - this.intervalSubscription = interval(this.intervalTime).subscribe(() => - this.intervalDataSource.next(this.intervalTime) - ); + this.ngZone.runOutsideAngular(() => { + this.intervalSubscription = interval(this.intervalTime).subscribe(() => + this.ngZone.run(() => { + this.intervalDataSource.next(this.intervalTime); + }) + ); + }); } getRefreshInterval() { -- 2.39.5