Enable Protractor end-to-end testing.
This enables end-to-end testing with Protractor, along with some basic route and page load testing for the home and project pages. PhantomJS is used to execute tests, and API requests are mocked using `protractor-http-mock` (see `test/e2e/mocks/`). A new gulp `e2e` task is also added to prepare and execute the tests, along with a shortcut, `npm run protractor`. The default `npm run test` is also amended to include E2E tests, with unit tests moved to the new shortcut `npm run unit`. Change-Id: Idb2eef2d851035c715e23553db56fc80deeab8e7
This commit is contained in:
parent
a90ac92bed
commit
6e3f74a847
|
@ -7,11 +7,9 @@ var browserSync = require('browser-sync');
|
|||
|
||||
gulp.task('dev-resources', function() {
|
||||
|
||||
if (!global.isProd) {
|
||||
return gulp.src(config.devResources.src)
|
||||
.pipe(changed(config.devResources.dest))
|
||||
.pipe(gulp.dest(config.devResources.dest))
|
||||
.pipe(browserSync.reload({ stream: true, once: true }));
|
||||
}
|
||||
return gulp.src(config.devResources.src)
|
||||
.pipe(changed(config.devResources.dest))
|
||||
.pipe(gulp.dest(config.devResources.dest))
|
||||
.pipe(browserSync.reload({ stream: true, once: true }));
|
||||
|
||||
});
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
var gulp = require('gulp');
|
||||
var runSequence = require('run-sequence');
|
||||
|
||||
gulp.task('e2e', function(callback) {
|
||||
|
||||
callback = callback || function() {};
|
||||
|
||||
runSequence(
|
||||
'prod',
|
||||
'dev-resources',
|
||||
'protractor',
|
||||
callback);
|
||||
|
||||
});
|
|
@ -18,6 +18,10 @@ gulp.task('protractor', ['webdriver-update', 'webdriver', 'server'], function()
|
|||
.on('error', function(err) {
|
||||
// Make sure failed tests cause gulp to exit non-zero
|
||||
throw err;
|
||||
})
|
||||
.on('end', function() {
|
||||
// server task will wait for user to quit, so force it to end here
|
||||
process.exit();
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
"phantomjs": "1.9.17",
|
||||
"pretty-hrtime": "^1.0.0",
|
||||
"protractor": "^2.2.0",
|
||||
"protractor-http-mock": "^0.1.18",
|
||||
"run-sequence": "^1.1.2",
|
||||
"tiny-lr": "^0.1.6",
|
||||
"uglifyify": "^3.0.1",
|
||||
|
@ -70,9 +71,9 @@
|
|||
},
|
||||
"scripts": {
|
||||
"pretest": "npm install",
|
||||
"test": "gulp unit",
|
||||
"preprotractor": "npm run update-webdriver",
|
||||
"protractor": "npm run protractor test/protractor.conf.js",
|
||||
"unit": "gulp unit",
|
||||
"protractor": "gulp e2e",
|
||||
"test": "gulp unit && gulp e2e",
|
||||
"lint": "eslint ./"
|
||||
},
|
||||
"dependencies": {},
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/*global browser, by */
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('E2E: Example', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser.get('/');
|
||||
browser.waitForAngular();
|
||||
});
|
||||
|
||||
it('should route correctly', function() {
|
||||
expect(browser.getLocationAbsUrl()).toMatch('/');
|
||||
});
|
||||
|
||||
it('should show the number defined in the controller', function() {
|
||||
var element = browser.findElement(by.css('.number-example'));
|
||||
expect(element.getText()).toEqual('1234');
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
module.exports = {
|
||||
request: {
|
||||
method: 'GET',
|
||||
path: 'config.json'
|
||||
},
|
||||
response: {
|
||||
data: {
|
||||
"apiRoot": "http://localhost:5000"
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,401 @@
|
|||
module.exports = {
|
||||
request: {
|
||||
method: 'JSONP',
|
||||
path: '/runs/group_by/project'
|
||||
},
|
||||
response: {
|
||||
data: {
|
||||
"runs": {
|
||||
"2015-10-20T20:00:00": {
|
||||
"openstack/keystone": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1154,
|
||||
"skip": 120
|
||||
}
|
||||
],
|
||||
"openstack/requirements": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1154,
|
||||
"skip": 120
|
||||
}
|
||||
]
|
||||
},
|
||||
"2015-10-20T21:00:00": {
|
||||
"openstack/ceilometer": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 58,
|
||||
"skip": 34
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 55,
|
||||
"skip": 38
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1164,
|
||||
"skip": 116
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1164,
|
||||
"skip": 116
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 58,
|
||||
"skip": 34
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1164,
|
||||
"skip": 116
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1444,
|
||||
"skip": 82
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 55,
|
||||
"skip": 38
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1164,
|
||||
"skip": 116
|
||||
}
|
||||
],
|
||||
"openstack/keystone": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 3,
|
||||
"skip": 0
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1154,
|
||||
"skip": 120
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 58,
|
||||
"skip": 34
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1154,
|
||||
"skip": 120
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 58,
|
||||
"skip": 34
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 3,
|
||||
"skip": 0
|
||||
}
|
||||
],
|
||||
"openstack/neutron": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 3,
|
||||
"skip": 0
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 3,
|
||||
"skip": 0
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 108,
|
||||
"skip": 23
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 112,
|
||||
"skip": 18
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 112,
|
||||
"skip": 18
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 108,
|
||||
"skip": 23
|
||||
}
|
||||
],
|
||||
"openstack/python-novaclient": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
}
|
||||
],
|
||||
"openstack/requirements": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1154,
|
||||
"skip": 120
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 58,
|
||||
"skip": 34
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1154,
|
||||
"skip": 120
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 3,
|
||||
"skip": 0
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 58,
|
||||
"skip": 34
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 58,
|
||||
"skip": 34
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 58,
|
||||
"skip": 34
|
||||
}
|
||||
],
|
||||
"openstack/taskflow": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
},
|
||||
{
|
||||
"fail": 1,
|
||||
"pass": 1433,
|
||||
"skip": 86
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
}
|
||||
]
|
||||
},
|
||||
"2015-10-20T22:00:00": {
|
||||
"openstack-infra/devstack-gate": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 3,
|
||||
"skip": 0
|
||||
}
|
||||
],
|
||||
"openstack/ceilometer": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1444,
|
||||
"skip": 82
|
||||
}
|
||||
],
|
||||
"openstack/diskimage-builder": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 71,
|
||||
"skip": 0
|
||||
}
|
||||
],
|
||||
"openstack/keystone": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
}
|
||||
],
|
||||
"openstack/neutron": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 3,
|
||||
"skip": 0
|
||||
}
|
||||
],
|
||||
"openstack/requirements": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1154,
|
||||
"skip": 120
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
}
|
||||
],
|
||||
"openstack/taskflow": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
}
|
||||
],
|
||||
"openstack/trove": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 6,
|
||||
"skip": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"2015-10-20T23:00:00": {
|
||||
"openstack-dev/pbr": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 0,
|
||||
"skip": 0
|
||||
}
|
||||
],
|
||||
"openstack-infra/devstack-gate": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 58,
|
||||
"skip": 34
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1154,
|
||||
"skip": 120
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1154,
|
||||
"skip": 120
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 3,
|
||||
"skip": 0
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 58,
|
||||
"skip": 34
|
||||
}
|
||||
],
|
||||
"openstack/keystone": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1154,
|
||||
"skip": 120
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 3,
|
||||
"skip": 0
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1154,
|
||||
"skip": 120
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
}
|
||||
],
|
||||
"openstack/neutron": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 112,
|
||||
"skip": 18
|
||||
},
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 112,
|
||||
"skip": 18
|
||||
}
|
||||
],
|
||||
"openstack/python-neutronclient": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
}
|
||||
],
|
||||
"openstack/python-novaclient": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 1434,
|
||||
"skip": 86
|
||||
}
|
||||
],
|
||||
"openstack/trove": [
|
||||
{
|
||||
"fail": 0,
|
||||
"pass": 6,
|
||||
"skip": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,34 @@
|
|||
module.exports = {
|
||||
request: {
|
||||
method: 'JSONP',
|
||||
path: '/projects/openstack/taskflow/runs'
|
||||
},
|
||||
response: {
|
||||
data: {
|
||||
"timedelta": [
|
||||
{
|
||||
"datetime": "2015-10-23T20:00:00",
|
||||
"job_data": [
|
||||
{
|
||||
"fail": 0,
|
||||
"job_name": "gate-tempest-dsvm-neutron-src-taskflow",
|
||||
"mean_run_time": 4859.3,
|
||||
"pass": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"datetime": "2015-11-10T23:00:00",
|
||||
"job_data": [
|
||||
{
|
||||
"fail": 0,
|
||||
"job_name": "gate-tempest-dsvm-neutron-src-taskflow",
|
||||
"mean_run_time": 6231.47,
|
||||
"pass": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
|
@ -2,11 +2,55 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
var mock = require('protractor-http-mock');
|
||||
|
||||
describe('E2E: Routes', function() {
|
||||
|
||||
it('should have a working home route', function() {
|
||||
mock(['config', 'home_project']);
|
||||
|
||||
browser.get('#/');
|
||||
|
||||
// route should be defined (will redirect to / if not)
|
||||
expect(browser.getLocationAbsUrl()).toMatch('/');
|
||||
|
||||
// data should actually be requested (no request if error)
|
||||
expect(mock.requestsMade()).toContain(jasmine.objectContaining({
|
||||
url: 'http://localhost:5000/runs/group_by/project',
|
||||
method: 'JSONP'
|
||||
}));
|
||||
|
||||
// should have a link to the next page
|
||||
var selector = 'a[href="#/project/openstack%252Ftaskflow"]';
|
||||
expect(element(by.css(selector)).isPresent()).toBe(true);
|
||||
});
|
||||
|
||||
it('should have a working project route', function() {
|
||||
mock(['config', 'project_taskflow']);
|
||||
|
||||
browser.get('#/project/openstack%252Ftaskflow');
|
||||
|
||||
// route should be defined (will redirect to / if not)
|
||||
browser.getLocationAbsUrl().then(function(url) {
|
||||
// note: phantomjs converts the octal escape to '/' for getLocationAbsUrl
|
||||
// for browsers that don't do this (chrome, firefox, etc), escape it
|
||||
// manually to make sure the expectation works correctly
|
||||
expect(url.replace('%252F', '/')).toMatch('/project/openstack/taskflow');
|
||||
});
|
||||
|
||||
// data should actually be requested (no request if error)
|
||||
expect(mock.requestsMade()).toContain(jasmine.objectContaining({
|
||||
url: 'http://localhost:5000/projects/openstack/taskflow/runs',
|
||||
method: 'JSONP'
|
||||
}));
|
||||
|
||||
// should have a link to the next page
|
||||
var selector = 'a[href="#/job/gate-tempest-dsvm-neutron-src-taskflow"]';
|
||||
expect(element(by.css(selector)).isPresent()).toBe(true);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
mock.teardown();
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
var phantomjs = require('phantomjs');
|
||||
|
||||
var gulpConfig = require('../gulp/config');
|
||||
|
||||
exports.config = {
|
||||
|
@ -8,12 +10,9 @@ exports.config = {
|
|||
|
||||
baseUrl: 'http://localhost:' + gulpConfig.serverPort + '/',
|
||||
|
||||
directConnect: true,
|
||||
|
||||
capabilities: {
|
||||
browserName: 'chrome',
|
||||
version: '',
|
||||
platform: 'ANY'
|
||||
browserName: 'phantomjs',
|
||||
'phantomjs.binary.path': phantomjs.path
|
||||
},
|
||||
|
||||
framework: 'jasmine',
|
||||
|
@ -27,6 +26,16 @@ exports.config = {
|
|||
|
||||
specs: [
|
||||
'e2e/**/*.js'
|
||||
]
|
||||
],
|
||||
|
||||
mocks: {
|
||||
dir: 'e2e/mocks'
|
||||
},
|
||||
|
||||
onPrepare: function() {
|
||||
require('protractor-http-mock').config = {
|
||||
rootDirectory: __dirname
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue