Merge pull request #20943 from tspmelo/fix-e2e

mgr/dashboard: fix frontend e2e tests

Reviewed-by: Ricardo Dias <rdias@suse.com>
Reviewed-by: Ricardo Marques <rimarques@suse.com>
Reviewed-by: Stephan Müller <smueller@suse.com>
This commit is contained in:
Lenz Grimmer 2018-04-09 17:32:00 +02:00 committed by GitHub
commit 3ddff17b13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 228 additions and 38 deletions

View File

@ -72,8 +72,30 @@ Run ``npm run test`` to execute the unit tests via `Karma
Running End-to-End Tests
~~~~~~~~~~~~~~~~~~~~~~~~
Run ``npm run e2e`` to execute the end-to-end tests via
`Protractor <http://www.protractortest.org/>`__.
We use `Protractor <http://www.protractortest.org/>`__ to run our frontend e2e
tests.
Our ``run-frontend-e2e-tests.sh`` script will check if Chrome or Docker is
installed and run the tests if either is found.
Start all frontend e2e tests by running::
$ ./run-frontend-e2e-tests.sh
Device:
You can force the script to use a specific device with the ``-d`` flag::
$ ./run-frontend-e2e-tests.sh -d <chrome|docker>
Remote:
If you want to run the tests outside the ceph environment, you will need to
manually define the dashboard url using ``-r``::
$ ./run-frontend-e2e-tests.sh -r <DASHBOARD_URL>
Note:
When using docker, as your device, you might need to run the script with sudo
permissions.
Further Help
~~~~~~~~~~~~

View File

@ -1,14 +0,0 @@
import { AppPage } from './app.po';
describe('ceph-dashboard App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to oa!');
});
});

View File

@ -1,11 +0,0 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getParagraphText() {
return element(by.css('oa-root h1')).getText();
}
}

View File

@ -0,0 +1,19 @@
import { Helper } from '../helper.po';
import { MonitorsPage } from './monitors.po';
describe('Monitors page', () => {
let page: MonitorsPage;
beforeAll(() => {
page = new MonitorsPage();
});
afterEach(() => {
Helper.checkConsole();
});
it('should open and show breadcrumnb', () => {
page.navigateTo();
expect(Helper.getBreadcrumbText()).toEqual('Monitors');
});
});

View File

@ -0,0 +1,7 @@
import { browser } from 'protractor';
export class MonitorsPage {
navigateTo() {
return browser.get('/#/monitor');
}
}

View File

@ -0,0 +1,19 @@
import { Helper } from '../helper.po';
import { OSDsPage } from './osds.po';
describe('OSDs page', () => {
let page: OSDsPage;
beforeAll(() => {
page = new OSDsPage();
});
afterEach(() => {
Helper.checkConsole();
});
it('should open and show breadcrumnb', () => {
page.navigateTo();
expect(Helper.getBreadcrumbText()).toEqual('OSDs');
});
});

View File

@ -0,0 +1,7 @@
import { browser } from 'protractor';
export class OSDsPage {
navigateTo() {
return browser.get('/#/osd');
}
}

View File

@ -0,0 +1,34 @@
import { $, browser } from 'protractor';
export class Helper {
static EC = browser.ExpectedConditions;
static TIMEOUT = 10000;
/**
* Checks if there are any errors on the browser
*
* @static
* @memberof Helper
*/
static checkConsole() {
browser
.manage()
.logs()
.get('browser')
.then(function(browserLog) {
browserLog = browserLog.filter(log => {
return log.level.value > 900; // SEVERE level
});
if (browserLog.length > 0) {
console.log('\n log: ' + require('util').inspect(browserLog));
}
expect(browserLog.length).toEqual(0);
});
}
static getBreadcrumbText() {
return $('.breadcrumb-item.active').getText();
}
}

View File

@ -55,7 +55,7 @@
"karma-jasmine-html-reporter": "^0.2.2",
"karma-junit-reporter": "^1.2.0",
"karma-phantomjs-launcher": "^1.0.4",
"protractor": "~5.1.2",
"protractor": "~5.3.0",
"ts-node": "~3.2.0",
"tslint": "~5.9.1",
"tslint-eslint-rules": "^4.1.1",

View File

@ -9,7 +9,10 @@ exports.config = {
'./e2e/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
'browserName': 'chrome',
chromeOptions: {
args: ['--no-sandbox', '--headless', '--window-size=1920x1080']
}
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
@ -19,10 +22,34 @@ exports.config = {
defaultTimeoutInterval: 30000,
print: function() {}
},
params: {
login: {
user: 'admin',
password: 'admin'
}
},
onPrepare() {
require('ts-node').register({
project: 'e2e/tsconfig.e2e.json'
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
browser.get('/#/login');
browser.driver.findElement(by.name('username')).clear();
browser.driver.findElement(by.name('username')).sendKeys(browser.params.login.user);
browser.driver.findElement(by.name('password')).clear();
browser.driver.findElement(by.name('password')).sendKeys(browser.params.login.password);
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.
return browser.driver.wait(function() {
return browser.driver.getCurrentUrl().then(function(url) {
return /dashboard/.test(url);
});
});
}
};

View File

@ -3,6 +3,7 @@ import {
Component,
EventEmitter,
Input,
NgZone,
OnChanges,
OnDestroy,
OnInit,
@ -113,7 +114,7 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O
// table columns after the browser window has been resized.
private currentWidth: number;
constructor() {}
constructor(private ngZone: NgZone) {}
ngOnInit() {
this._addTemplates();
@ -136,12 +137,17 @@ export class TableComponent implements AfterContentChecked, OnInit, OnChanges, O
return c;
});
this.tableColumns = this.columns.filter(c => !c.isHidden);
if (this.autoReload) { // Also if nothing is bound to fetchData nothing will be triggered
if (this.autoReload) {
// Also if nothing is bound to fetchData nothing will be triggered
// Force showing the loading indicator because it has been set to False in
// useData() when this method was triggered by ngOnChanges().
this.loadingIndicator = true;
this.subscriber = Observable.timer(0, this.autoReload).subscribe(x => {
return this.reloadData();
this.ngZone.runOutsideAngular(() => {
this.subscriber = Observable.timer(0, this.autoReload).subscribe(x => {
this.ngZone.run(() => {
return this.reloadData();
});
});
});
}
}

View File

@ -1,5 +1,5 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Injectable, NgZone } from '@angular/core';
import { Subject } from 'rxjs/Subject';
@ -13,7 +13,11 @@ export class SummaryService {
// Observable streams
summaryData$ = this.summaryDataSource.asObservable();
constructor(private http: HttpClient, private authStorageService: AuthStorageService) {
constructor(
private http: HttpClient,
private authStorageService: AuthStorageService,
private ngZone: NgZone
) {
this.refresh();
}
@ -24,8 +28,12 @@ export class SummaryService {
});
}
setTimeout(() => {
this.refresh();
}, 5000);
this.ngZone.runOutsideAngular(() => {
setTimeout(() => {
this.ngZone.run(() => {
this.refresh();
});
}, 5000);
});
}
}

View File

@ -0,0 +1,66 @@
#!/usr/bin/env bash
set -e
stop() {
if [ "$REMOTE" == "false" ]; then
cd $BUILD_DIR
../src/stop.sh
fi
exit
}
BASE_URL=''
DEVICE=''
REMOTE='false'
while getopts 'd:r:' flag; do
case "${flag}" in
d) DEVICE=$OPTARG;;
r) REMOTE='true'
BASE_URL=$OPTARG;;
esac
done
if [ "$DEVICE" == "" ]; then
if [ -x "$(command -v google-chrome)" ] || [ -x "$(command -v google-chrome-stable)" ]; then
DEVICE="chrome"
elif [ -x "$(command -v docker)" ]; then
DEVICE="docker"
else
echo "ERROR: Chrome and Docker not found. You need to install one of \
them to run the e2e frontend tests."
stop
fi
fi
DASH_DIR=`pwd`
cd ../../../../build
BUILD_DIR=`pwd`
if [ "$BASE_URL" == "" ]; then
MGR=2 RGW=1 ../src/vstart.sh -n -d
sleep 10
BASE_URL=`./bin/ceph mgr services | jq .dashboard`
BASE_URL=${BASE_URL//\"}
fi
cd $DASH_DIR/frontend
. $BUILD_DIR/src/pybind/mgr/dashboard/node-env/bin/activate
npm install
npm run build -- --prod
if [ $DEVICE == "chrome" ]; then
npm run e2e -- --serve=false --base-href $BASE_URL || stop
elif [ $DEVICE == "docker" ]; then
docker run -d -v $(pwd):/workdir --net=host --name angular-e2e-container rogargon/angular-e2e || stop
docker exec angular-e2e-container npm run e2e -- --serve=false --base-href $BASE_URL
docker stop angular-e2e-container
docker rm angular-e2e-container
else
echo "ERROR: Device not recognized. Valid devices are 'chrome' and 'docker'."
fi
stop