Skip to content

Commit

Permalink
add an e2e-tests subproject
Browse files Browse the repository at this point in the history
gesellix committed Jan 23, 2015
1 parent 09cbbc7 commit 677b287
Showing 10 changed files with 455 additions and 1 deletion.
1 change: 1 addition & 0 deletions application-version.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DUMMY
4 changes: 4 additions & 0 deletions e2e-tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.gz
*.log
node_modules
report
160 changes: 160 additions & 0 deletions e2e-tests/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import java.util.concurrent.CountDownLatch

buildscript {
dependencies {
classpath 'com.moowork.gradle:gradle-node-plugin:0.8'
classpath 'de.undercouch:gradle-download-task:1.2'
}
}

apply plugin: 'com.moowork.node'
apply plugin: 'de.undercouch.download'
apply plugin: 'com.github.ben-manes.versions'

ext {
applicationVersion = 'UNDEFINED'

seleniumServer = null
nodeServer = null
appFrontend = null
appBackend = null
}

task readVersionFile << {
def applicationVersionFile = new File('application-version.txt')
if (!applicationVersionFile.exists()) {
throw new RuntimeException("No application version file found")
}
applicationVersion = applicationVersionFile.text.trim()
logger.warn "Using application version from file '${applicationVersionFile.name}': ${applicationVersion}"
}

task copyJars(dependsOn: [readVersionFile, ':frontend:build', ':backend:build']) {
doLast {
File explodedDist = mkdir("$buildDir/")
rootProject.subprojects.each { project ->
project.tasks.withType(Jar).each { archiveTask ->
copy {
from archiveTask.archivePath
into explodedDist
}
}
}
// download {
// src createNexusDownloadUrl('backend', applicationVersion, 'jar')
// dest "$buildDir/backend.jar"
// }
// download {
// src createNexusDownloadUrl('frontend', applicationVersion, 'jar')
// dest "$buildDir/frontend.jar"
// }
}
}

//def createNexusDownloadUrl(def artifactName, def artifactVersion, def type) {
// "${project["nexus.public.url"]}/${project["nexus.groupId"]}/$artifactName/$artifactVersion/$artifactName-${artifactVersion}.$type"
//}

def defaultEnvironment() {
def environment = ["PATH=${System.env.PATH}"]
environment += "HOME=${System.env.HOME}"
environment += "http_proxy=${System.env.http_proxy}"
environment += "https_proxy=${System.env.https_proxy}"
return environment
}

def execAsync(command, printStdOutput, environment, dir, output) {
logger.lifecycle "Starting async command $command"

final CountDownLatch condition = new CountDownLatch(1)

def commandEnvironment = defaultEnvironment() + environment as List
logger.lifecycle "environment: $commandEnvironment"

def proc = command.execute(commandEnvironment, new File(dir as String))
Thread.start {
try {
proc.in.eachLine { line ->
if (printStdOutput) {
println "$line"
}
if (output != null && line?.contains(output)) {
condition.countDown()
}
}
}
catch (ignored) {}
}
Thread.start {
try {
proc.err.eachLine { line ->
if (printStdOutput) {
println line
}
}
}
catch (ignored) {}
}
return [proc, output != null ? condition : null]
}

def execWithLogs(command, environment, dir, failBuild) {
logger.lifecycle "Execute command $command"

def commandEnvironment = defaultEnvironment() + environment as List
logger.lifecycle "environment: $commandEnvironment"

def proc = command.execute(commandEnvironment, new File(dir as String))
Thread.start {
try {
proc.in.eachLine { line -> println "$line" }
}
catch (ignored) {}
}
Thread.start {
try {
proc.err.eachLine { line -> println line }
}
catch (ignored) {}
}
def exitCode = proc.waitFor()
if (failBuild && exitCode != 0) {
println "Error: Exit-Code $exitCode for command $command"
throw new RuntimeException("Exit-Code $exitCode for command $command")
}
}

node {
version = '0.10.32'
download = true
}

task startSeleniumServer(dependsOn: npmInstall) {
doLast {
def seleniumStandaloneFiles = fileTree("$projectDir/node_modules/selenium-server-standalone-jar/jar")
def seleniumServerStandaloneLocation = seleniumStandaloneFiles.first().absolutePath
(seleniumServer) = execAsync("java -jar $seleniumServerStandaloneLocation -port 4449", false, [], "$projectDir", null)
}
}

task e2eTests(dependsOn: [copyJars, startSeleniumServer]) {
doLast {
(nodeServer) = execAsync("node e2e-reverse-proxy.js", true, [], "$projectDir", null)

def condBackend, condFrontend
(appBackend, condBackend) = execAsync(["java", "-jar", "$buildDir/backend.jar", "--spring.profiles.active=dev-stage"], true, [], "$projectDir", "Started BackendApplication")
(appFrontend, condFrontend) = execAsync(["java", "-jar", "$buildDir/frontend.jar", "--spring.profiles.active=dev-stage"], true, [], "$projectDir", "Started FrontendApplication")

condBackend.await()
condFrontend.await()
execWithLogs("node_modules/.bin/protractor ./protractor-conf-teamcity.js", [], "$projectDir", true)
}
}

task stopProcesses << {
appBackend?.destroy()
appFrontend?.destroy()
seleniumServer?.destroy()
nodeServer?.destroy()
}
e2eTests.finalizedBy stopProcesses
39 changes: 39 additions & 0 deletions e2e-tests/e2e-reverse-proxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict';

var domain = require('domain');
var d = domain.create();

var frontendPort = '8080';
var backendPort = '8090';

d.on('error', function (e) {
console.log('error: ', e.message);
});

d.run(function () {
var express = require('express');
var app = express();

var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({});

app.use(express.cookieParser());

app.all(/(\/[^\/]+)?\/(example-backend)\/(.*)/, function (req, res) {
proxy.web(req, res, {
target: 'http://localhost:' + backendPort
});
});

app.all(/(\/[^\/]+)?\/(example)\/(.*)/, function (req, res) {
proxy.web(req, res, {
target: 'http://localhost:' + frontendPort
});
});

var listenPort = 3010;
app.listen(listenPort);
console.log("e2e test proxy server running on port " + listenPort);
console.log("frontend is expected on port " + frontendPort);
console.log("backend is expected on port " + backendPort);
});
12 changes: 12 additions & 0 deletions e2e-tests/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "e2e-test-infra",
"version": "1.0.0",
"devDependencies": {
"http-proxy": "^1.0.3",
"express": "^3.5.1",
"phantomjs": "^1.9.7",
"protractor": "1.3.1",
"selenium-server-standalone-jar": "2.43.1",
"jasmine-reporters": "^0.4.1"
}
}
37 changes: 37 additions & 0 deletions e2e-tests/protractor-conf-defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use strict";

var disableNgAnimate = function () {
angular.module('disableNgAnimate', []).run(function ($animate) {
$animate.enabled(false);
});
};

var e2eDefaults = function () {
var defaultConfig = {};

var defaultBaseUri = 'http://localhost:' + (process.env.HTTP_PORT || '3010');
var contextPath = "/example/";
defaultConfig.baseUrl = defaultBaseUri + contextPath;

var defaultSpecsPattern = 'tests/*.js';
var specsPattern = (process.env.e2eTest) ? 'tests/' + process.env.e2eTest + '.js' : defaultSpecsPattern;
defaultConfig.specs = [specsPattern];

defaultConfig.jasmineNodeOpts = {
showColors: true,
defaultTimeoutInterval: 30000
};
defaultConfig.onPrepare = function () {
// provide our own util functions via window.browser
// see https://github.com/angular/protractor/issues/441
require('./protractorUtils');

// Disable animations so e2e tests run more quickly
browser.addMockModule('disableNgAnimate', disableNgAnimate);
};

console.log('using defaults: ' + JSON.stringify(defaultConfig, undefined, 2));
return defaultConfig;
};
exports.e2eDefaults = e2eDefaults;
exports.disableNgAnimate = disableNgAnimate;
23 changes: 23 additions & 0 deletions e2e-tests/protractor-conf-teamcity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use strict";
var defaultConfig = require('./protractor-conf-defaults');
var localConfig = defaultConfig.e2eDefaults();

localConfig.seleniumAddress = 'http://localhost:4449/wd/hub';
localConfig.capabilities = {
'phantomjs.binary.path': 'node_modules/.bin/phantomjs',
'browserName': 'phantomjs',
'count': 1,
'sharedTestFiles': false,
'maxInstances': 1
};
localConfig.onPrepare = function () {
// provide our own util functions via window.browser
// see https://github.com/angular/protractor/issues/441
require('./protractorUtils');
require('jasmine-reporters');
jasmine.getEnv().addReporter(new jasmine.TeamcityReporter());

// Disable animations so e2e tests run more quickly
browser.addMockModule('disableNgAnimate', defaultConfig.disableNgAnimate);
};
exports.config = localConfig;
158 changes: 158 additions & 0 deletions e2e-tests/protractorUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
"use strict";

var fs = require('fs'),
path = require('path');

var reportDir = path.resolve(__dirname + '/report/'),
screenshotsDir = path.resolve(reportDir + '/screenshots/');

if (!fs.existsSync(reportDir)) {
fs.mkdirSync(reportDir);
}

console.log('screenshotsDir = [' + screenshotsDir + ']');
if (!fs.existsSync(screenshotsDir)) {
fs.mkdirSync(screenshotsDir);
}

var errorCallback = function (err) {
console.log(err);
};

var getDateStr = function () {
var d = (new Date() + '').replace(new RegExp(':', 'g'), '-').split(' ');
// "2013-Sep-03-21:58:03"
return [d[3], d[1], d[2], d[4]].join('-');
};

var timestampToDate = function (unix_timestamp) {
var date = new Date(unix_timestamp);
// hours part from the timestamp
var hours = date.getHours();
// minutes part from the timestamp
var minutes = date.getMinutes();
// seconds part from the timestamp
var seconds = date.getSeconds();

var timeValues = [hours, minutes, seconds];
timeValues.forEach(function (val) {
if (val.length < 2) {
// padding
val = '0' + val;
}
});
// will display time in 10:30:23 format
return hours + ':' + minutes + ':' + seconds;
};

var writeScreenshotToFile = function (pngFileName) {
browser.takeScreenshot().then(function (png) {
console.log('Writing file ' + pngFileName);
fs.writeFileSync(pngFileName, png, {encoding: 'base64'}, function (err) {
console.log(err);
});
}, errorCallback);
};

var writeLogentriesToConsole = function (logsEntries) {
var len = logsEntries.length;

for (var i = 0; i < len; ++i) {
var logEntry = logsEntries[i];
var msg = "Browser Console: " + timestampToDate(logEntry.timestamp) + ' ' + logEntry.type + ' ' + logEntry.message;
console.log(msg);
}
};

var writeLogs = function () {
var logs = browser.driver.manage().logs(),
logType = 'browser';

logs.getAvailableLogTypes().then(function (logTypes) {
if (logTypes.indexOf(logType) > -1) {
browser.driver.manage().logs().get(logType).then(function (logsEntries) {
writeLogentriesToConsole(logsEntries);
}, errorCallback);
}
});
};

var expectNoBeforeunload = function () {
expect(browser.driver.executeScript("var result = (window.onbeforeunload != undefined) ? window.onbeforeunload():undefined; window.onbeforeunload = function(){}; return result;")).toBeFalsy();
};

var reloadVorgang = function () {
var deferred = protractor.promise.defer();

writeLogs();
var vorgangsnummerElement = element(by.binding('vorgang.vorgangsnummer'));
vorgangsnummerElement.getText().then(function (vorgangsnummer) {
expectNoBeforeunload();
browser.get(browser.baseUrl + '?vorgangsnummer=' + vorgangsnummer);
deferred.fulfill(vorgangsnummer);
}, deferred.reject);

return deferred.promise;
};

browser.reloadVorgangAfterDebounce = function () {
var waitForDebounce = function () {
var debounceTimeout = 150;
return browser.driver.sleep(debounceTimeout);
};

var deferred = protractor.promise.defer();
waitForDebounce().then(function () {
deferred.fulfill(reloadVorgang());
}, deferred.reject);
return deferred.promise;
};

browser.reloadVorgang = reloadVorgang;

// taken from http://eitanp461.blogspot.de/2014/01/advanced-protractor-features.html
var dumpConsoleLog = function () {

var passed = jasmine.getEnv().currentSpec.results().passed();

// normalize file names
var specName = jasmine.getEnv().currentSpec.description.replace(new RegExp(' ', 'g'), '-'),
baseFileName = specName + '-' + getDateStr();

if (!passed) {
var pngFileName = path.resolve(screenshotsDir + '/' + baseFileName + '.png');
writeScreenshotToFile(pngFileName);
}
writeLogs();
};

var globalSetup = function (setWindowSize) {
beforeEach(function () {
browser.get(browser.baseUrl);
if (setWindowSize) {
browser.driver.manage().window().setSize(1440, 900);
}

// disable transitions to avoid flaky tests
browser.executeScript("$('<style id=\"test\" type=\"text/css\">" +
"* {" +
"-webkit-transition: none !important;" +
"-moz-transition: none !important;" +
"-o-transition: none !important;" +
"transition: none !important;" +
"}" +
"</style>').appendTo(document.head);");
});

afterEach(function () {
browser.dumpConsoleLog();
expectNoBeforeunload();
});
};

browser.writeLogs = writeLogs;
browser.dumpConsoleLog = dumpConsoleLog;

browser.globalSetup = globalSetup;


19 changes: 19 additions & 0 deletions e2e-tests/tests/example-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use strict";
describe('example app', function () {

browser.globalSetup();

it('should be available', function () {
expect(element(by.id('example')).isPresent()).toBe(true);
});

it('should show a welcome message', function () {
var welcomeTextElement = element(by.binding('welcomeText'));
expect(welcomeTextElement.getText()).toEqual("hello world");

//browser.reloadVorgang().then(function (vorgangsnummer) {
// var vorgangsnummerElement = element(by.binding('vorgang.vorgangsnummer'));
// expect(vorgangsnummerElement.getText()).toEqual(vorgangsnummer);
//});
});
});
3 changes: 2 additions & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
rootProject.name = 'gradle-with-docker'
include 'backend',
'common',
'frontend'
'frontend',
'e2e-tests'

0 comments on commit 677b287

Please sign in to comment.