]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: Enable waitforAngular in E2E
authorTiago Melo <tmelo@suse.com>
Tue, 10 Sep 2019 14:44:47 +0000 (14:44 +0000)
committerTiago Melo <tmelo@suse.com>
Wed, 11 Sep 2019 11:13:10 +0000 (11:13 +0000)
Convert waitForTableData into a method,
using it as a decorator was causing problems.

Signed-off-by: Tiago Melo <tmelo@suse.com>
14 files changed:
src/pybind/mgr/dashboard/frontend/e2e/block/images.e2e-spec.ts
src/pybind/mgr/dashboard/frontend/e2e/block/mirroring.e2e-spec.ts
src/pybind/mgr/dashboard/frontend/e2e/cluster/configuration.e2e-spec.ts
src/pybind/mgr/dashboard/frontend/e2e/cluster/logs.e2e-spec.ts
src/pybind/mgr/dashboard/frontend/e2e/cluster/logs.po.ts
src/pybind/mgr/dashboard/frontend/e2e/page-helper.po.ts
src/pybind/mgr/dashboard/frontend/e2e/rgw/daemons.e2e-spec.ts
src/pybind/mgr/dashboard/frontend/e2e/rgw/users.e2e-spec.ts
src/pybind/mgr/dashboard/frontend/e2e/rgw/users.po.ts
src/pybind/mgr/dashboard/frontend/e2e/ui/dashboard.e2e-spec.ts
src/pybind/mgr/dashboard/frontend/protractor.conf.js
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/logs/logs.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/refresh-interval.service.ts

index 2b1fa1db2f3caeb418e493b4cd3e0d31f2522917..943c7877d8fdb75055c71024013f5e9a2d528e9b 100644 (file)
@@ -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';
index b6201019d2c801c3880f9a61b05ad948caf9e002..3327cf161bbb6d7688a12afea6b3b3d1c2266e55 100644 (file)
@@ -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 () => {
index 23da2a70233433b11b0553b8baa359feddb5a3df..bf2f125b9998b1042109f9f47c847535e7527ef1 100644 (file)
@@ -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 () => {
index 47c95a39bdcf70b058294ecbfa32576e8570e18d..dca739c3c5445de5ebab0b9fdf193ca00c53a8c9 100644 (file)
@@ -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;
+    });
   });
 });
index c9e3ba6ff4bc025090e864a2d91618878776ee10..e25e76644baa90184a739de5203a77ba7a43c0d7 100644 (file)
@@ -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());
 
index 1cacf01dff893d8762d4b55c5bda4b390f820a7a..392e2c52511e766bbcfda50af461c1b2011e8bad 100644 (file)
@@ -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();
   }
index 2e757b75a24808bd1eff47dd64db1c2f580f69fc..0a9e551db3288ca9b4875ec81e03225b82c7e901 100644 (file)
@@ -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();
     });
index 4caaa4df17587252cd2ba4092c0592baafef4652..29f1c7e78ac77047e1224130afc5214be35492b5 100644 (file)
@@ -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;
+    });
   });
 });
index 735f9cf8790d11b92ec2bb85cfd5e891e5d216ad..fc2a6d6c5840e37ae5fc13b4708daffad64c6eb4 100644 (file)
@@ -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(
index d15c358d52505e41bd3948436a186e387c2b2108..3ec8719bc5e6fae109b5024c6483bf7fe5757c56 100644 (file)
@@ -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;
+    });
   });
 });
index 956ffdd1e1cc96facc5785218a209b5e30a8d397..143437a81f7a4303e22bbff97ae578718ddc97b7 100644 (file)
@@ -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;
index ddb8a73a7f77a53caddb2f241ce6e656ae414e11..dc33c5356344c989ee53cffd189863745e5f5621 100644 (file)
@@ -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() {
index da55b44fd60ffbb38963732cba3550a3b4e52112..4d34ce0a9653c5776efb2eb205c18bf48524d677 100644 (file)
@@ -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);
   });
 
index 0627d78ecf028315fa9a205296d340d43736f96a..03aa3b8a56ad1f6cb86525f06c9df50a325c67f6 100644 (file)
@@ -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() {