mirror of
https://github.com/ceph/ceph
synced 2024-12-29 15:03:33 +00:00
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:
commit
3ddff17b13
@ -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
|
||||
~~~~~~~~~~~~
|
||||
|
@ -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!');
|
||||
});
|
||||
});
|
@ -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();
|
||||
}
|
||||
}
|
@ -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');
|
||||
});
|
||||
});
|
@ -0,0 +1,7 @@
|
||||
import { browser } from 'protractor';
|
||||
|
||||
export class MonitorsPage {
|
||||
navigateTo() {
|
||||
return browser.get('/#/monitor');
|
||||
}
|
||||
}
|
@ -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');
|
||||
});
|
||||
});
|
7
src/pybind/mgr/dashboard/frontend/e2e/cluster/osds.po.ts
Normal file
7
src/pybind/mgr/dashboard/frontend/e2e/cluster/osds.po.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { browser } from 'protractor';
|
||||
|
||||
export class OSDsPage {
|
||||
navigateTo() {
|
||||
return browser.get('/#/osd');
|
||||
}
|
||||
}
|
34
src/pybind/mgr/dashboard/frontend/e2e/helper.po.ts
Normal file
34
src/pybind/mgr/dashboard/frontend/e2e/helper.po.ts
Normal 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();
|
||||
}
|
||||
}
|
@ -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",
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
66
src/pybind/mgr/dashboard/run-frontend-e2e-tests.sh
Executable file
66
src/pybind/mgr/dashboard/run-frontend-e2e-tests.sh
Executable 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
|
Loading…
Reference in New Issue
Block a user