From ff30fa334d58ffeda2ed39479d3092da3a59b6c0 Mon Sep 17 00:00:00 2001 From: pkupczyk <pkupczyk> Date: Tue, 14 May 2013 07:44:58 +0000 Subject: [PATCH] SP-617 / BIS-366 : openbis.js - integrate automated tests with CI server - follow-up - generate junit report from qunit tests SVN: 29116 --- .../webapps/common-test/html/common-test.js | 5 + .../common-test/html/qunit-reporter-junit.js | 317 ++++++++++++++++++ .../openbis-screening-test/html/index.html | 2 + .../1/as/webapps/openbis-test/html/index.html | 2 + .../jstest/page/OpenbisJsCommonWebapp.java | 7 + .../suite/common/JsTestCommonSelenium.java | 30 +- 6 files changed, 353 insertions(+), 10 deletions(-) create mode 100644 js-test/servers/common/core-plugins/tests/1/as/webapps/common-test/html/qunit-reporter-junit.js diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/common-test/html/common-test.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/common-test/html/common-test.js index 15497eab2de..06a1f8a450a 100644 --- a/js-test/servers/common/core-plugins/tests/1/as/webapps/common-test/html/common-test.js +++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/common-test/html/common-test.js @@ -1,3 +1,8 @@ +QUnit.jUnitReport = function(report) { + $("#qunit-junit-report").text(report.xml); + console.log(report.xml); +} + var createFacade = function(action, url, timeoutOrNull){ stop(); diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/common-test/html/qunit-reporter-junit.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/common-test/html/qunit-reporter-junit.js new file mode 100644 index 00000000000..2f4b4e20a12 --- /dev/null +++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/common-test/html/qunit-reporter-junit.js @@ -0,0 +1,317 @@ +/** + * JUnit reporter for QUnit v1.0.2pre + * + * https://github.com/jquery/qunit-reporter-junit + * + * Copyright 2013 jQuery Foundation and other contributors + * Released under the MIT license. + * https://jquery.org/license/ + */ +(function() { + + 'use strict'; + + var currentRun, currentModule, currentTest, assertCount; + + // Gets called when a report is generated. + QUnit.jUnitReport = function(/* data */) { + // Override me! + }; + + QUnit.begin(function() { + currentRun = { + modules: [], + total: 0, + passed: 0, + failed: 0, + start: new Date(), + time: 0 + }; + }); + + QUnit.moduleStart(function(data) { + currentModule = { + name: data.name, + tests: [], + total: 0, + passed: 0, + failed: 0, + start: new Date(), + time: 0, + stdout: [], + stderr: [] + }; + + currentRun.modules.push(currentModule); + }); + + QUnit.testStart(function(data) { + // Setup default module if no module was specified + if (!currentModule) { + currentModule = { + name: data.module || 'default', + tests: [], + total: 0, + passed: 0, + failed: 0, + start: new Date(), + time: 0, + stdout: [], + stderr: [] + }; + + currentRun.modules.push(currentModule); + } + + // Reset the assertion count + assertCount = 0; + + currentTest = { + name: data.name, + failedAssertions: [], + total: 0, + passed: 0, + failed: 0, + start: new Date(), + time: 0 + }; + + currentModule.tests.push(currentTest); + }); + + QUnit.log(function(data) { + assertCount++; + + // Ignore passing assertions + if (!data.result) { + currentTest.failedAssertions.push(data); + + // Add log message of failure to make it easier to find in Jenkins CI + currentModule.stdout.push('[' + currentModule.name + ', ' + currentTest.name + ', ' + assertCount + '] ' + data.message); + } + }); + + QUnit.testDone(function(data) { + currentTest.time = (new Date()).getTime() - currentTest.start.getTime(); // ms + currentTest.total = data.total; + currentTest.passed = data.passed; + currentTest.failed = data.failed; + + currentTest = null; + }); + + QUnit.moduleDone(function(data) { + currentModule.time = (new Date()).getTime() - currentModule.start.getTime(); // ms + currentModule.total = data.total; + currentModule.passed = data.passed; + currentModule.failed = data.failed; + + currentModule = null; + }); + + QUnit.done(function(data) { + currentRun.time = data.runtime || ((new Date()).getTime() - currentRun.start.getTime()); // ms + currentRun.total = data.total; + currentRun.passed = data.passed; + currentRun.failed = data.failed; + + generateReport(data, currentRun); + }); + + var generateReport = function(results, run) { + var pad = function(n) { + return n < 10 ? '0' + n : n; + }; + + var toISODateString = function(d) { + return d.getUTCFullYear() + '-' + + pad(d.getUTCMonth() + 1)+'-' + + pad(d.getUTCDate()) + 'T' + + pad(d.getUTCHours()) + ':' + + pad(d.getUTCMinutes()) + ':' + + pad(d.getUTCSeconds()) + 'Z'; + }; + + var convertMillisToSeconds = function(ms) { + return Math.round(ms * 1000) / 1000000; + }; + + var xmlEncode = function(text) { + var baseEntities = { + '"' : '"', + '\'': ''', + '<' : '<', + '>' : '>', + '&' : '&' + }; + + return ('' + text).replace(/[<>&\"\']/g, function(chr) { + return baseEntities[chr] || chr; + }); + }; + + var XmlWriter = function(settings) { + settings = settings || {}; + + var data = [], stack = [], lineBreakAt; + + var addLineBreak = function(name) { + if (lineBreakAt[name] && data[data.length - 1] !== '\n') { + data.push('\n'); + } + }; + + lineBreakAt = (function(items) { + var i, map = {}; + items = items || []; + + i = items.length; + while (i--) { + map[items[i]] = {}; + } + return map; + })(settings.linebreak_at); + + this.start = function(name, attrs, empty) { + if (!empty) { + stack.push(name); + } + + data.push('<' + name); + + for (var aname in attrs) { + data.push(' ' + xmlEncode(aname) + '="' + xmlEncode(attrs[aname]) + '"'); + } + + data.push(empty ? ' />' : '>'); + addLineBreak(name); + }; + + this.end = function() { + var name = stack.pop(); + addLineBreak(name); + data.push('</' + name + '>'); + addLineBreak(name); + }; + + this.text = function(text) { + data.push(xmlEncode(text)); + }; + + this.cdata = function(text) { + data.push('<![CDATA[' + text + ']]>'); + }; + + this.comment = function(text) { + data.push('<!--' + text + '-->'); + }; + this.pi = function(name, text) { + data.push('<?' + name + (text ? ' ' + text : '') + '?>\n'); + }; + + this.doctype = function(text) { + data.push('<!DOCTYPE' + text + '>\n'); + }; + + this.getString = function() { + while (stack.length) { + this.end(); // internally calls `stack.pop();` + } + return data.join('').replace(/\n$/, ''); + }; + + this.reset = function() { + data.length = 0; + stack.length = 0; + }; + + // Start by writing the XML declaration + this.pi(settings.xmldecl || 'xml version="1.0" encoding="UTF-8"'); + }; + + + // Generate JUnit XML report! + var m, mLen, module, t, tLen, test, a, aLen, assertion, isEmptyElement, + xmlWriter = new XmlWriter({ + linebreak_at: ['testsuites', 'testsuite', 'testcase', 'failure', 'system-out', 'system-err'] + }); + + xmlWriter.start('testsuites', { + name: (window && window.location && window.location.href) || (run.modules.length === 1 && run.modules[0].name) || null, + hostname: 'localhost', + tests: run.total, + failures: run.failed, + errors: 0, + time: convertMillisToSeconds(run.time), // ms → sec + timestamp: toISODateString(run.start) + }); + + for (m = 0, mLen = run.modules.length; m < mLen; m++) { + module = run.modules[m]; + + xmlWriter.start('testsuite', { + id: m, + name: module.name, + hostname: 'localhost', + tests: module.total, + failures: module.failed, + errors: 0, + time: convertMillisToSeconds(module.time), // ms → sec + timestamp: toISODateString(module.start) + }); + + for (t = 0, tLen = module.tests.length; t < tLen; t++) { + test = module.tests[t]; + + xmlWriter.start('testcase', { + name: test.name, + tests: test.total, + failures: test.failed, + errors: 0, + time: convertMillisToSeconds(test.time), // ms → sec + timestamp: toISODateString(test.start) + }); + + for (a = 0, aLen = test.failedAssertions.length; a < aLen; a++) { + assertion = test.failedAssertions[a]; + + isEmptyElement = assertion && !(assertion.actual && assertion.expected); + xmlWriter.start('failure', { type: 'AssertionFailedError', message: assertion.message }, isEmptyElement); + if (!isEmptyElement) { + xmlWriter.start('actual', { value: assertion.actual }, true); + xmlWriter.start('expected', { value: assertion.expected }, true); + xmlWriter.end(); //'failure' + } + } + + xmlWriter.end(); //'testcase' + } + + // Per-module stdout + if (module.stdout && module.stdout.length) { + xmlWriter.start('system-out'); + xmlWriter.cdata('\n' + module.stdout.join('\n') + '\n'); + xmlWriter.end(); //'system-out' + } + + // Per-module stderr + if (module.stderr && module.stderr.length) { + xmlWriter.start('system-err'); + xmlWriter.cdata('\n' + module.stderr.join('\n') + '\n'); + xmlWriter.end(); //'system-err' + } + + xmlWriter.end(); //'testsuite' + } + + xmlWriter.end(); //'testsuites' + + + // Invoke the user-defined callback + QUnit.jUnitReport({ + results: results, + xml: xmlWriter.getString() + }); + }; + +})(); diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-screening-test/html/index.html b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-screening-test/html/index.html index 0debbe6edd1..a79d55c0e91 100644 --- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-screening-test/html/index.html +++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-screening-test/html/index.html @@ -9,6 +9,7 @@ <script src="/openbis/resources/js/openbis.js"></script> <script src="/openbis/resources/js/openbis-screening.js"></script> <script src="../common-test/qunit.js"></script> + <script src="../common-test/qunit-reporter-junit.js"></script> <script src="../common-test/common-test.js"></script> <script src="openbis-screening-test.js"></script> @@ -16,5 +17,6 @@ <body> <div id="qunit"></div> <div id="qunit-fixture"></div> + <div id="qunit-junit-report"></div> </body> </html> diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-test/html/index.html b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-test/html/index.html index e981d873cf3..d42fee86669 100644 --- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-test/html/index.html +++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-test/html/index.html @@ -8,6 +8,7 @@ <script src="/openbis/resources/js/jquery.js"></script> <script src="/openbis/resources/js/openbis.js"></script> <script src="../common-test/qunit.js"></script> + <script src="../common-test/qunit-reporter-junit.js"></script> <script src="../common-test/common-test.js"></script> <script src="openbis-test.js"></script> @@ -15,5 +16,6 @@ <body> <div id="qunit"></div> <div id="qunit-fixture"></div> + <div id="qunit-junit-report"></div> </body> </html> diff --git a/js-test/source/java/ch/systemsx/cisd/openbis/jstest/page/OpenbisJsCommonWebapp.java b/js-test/source/java/ch/systemsx/cisd/openbis/jstest/page/OpenbisJsCommonWebapp.java index 93cf861c63d..1738342f617 100644 --- a/js-test/source/java/ch/systemsx/cisd/openbis/jstest/page/OpenbisJsCommonWebapp.java +++ b/js-test/source/java/ch/systemsx/cisd/openbis/jstest/page/OpenbisJsCommonWebapp.java @@ -33,6 +33,9 @@ public class OpenbisJsCommonWebapp @Locate("qunit-testresult") private WebElement infoBox; + @Locate("qunit-junit-report") + private WebElement junitReport; + public int getFailedCount() { SeleniumTest.setImplicitWait(300, TimeUnit.SECONDS); @@ -46,4 +49,8 @@ public class OpenbisJsCommonWebapp } } + public String getJunitReport() + { + return junitReport.getText(); + } } diff --git a/js-test/source/java/ch/systemsx/cisd/openbis/jstest/suite/common/JsTestCommonSelenium.java b/js-test/source/java/ch/systemsx/cisd/openbis/jstest/suite/common/JsTestCommonSelenium.java index a6492c68958..4c39ecbbf7e 100644 --- a/js-test/source/java/ch/systemsx/cisd/openbis/jstest/suite/common/JsTestCommonSelenium.java +++ b/js-test/source/java/ch/systemsx/cisd/openbis/jstest/suite/common/JsTestCommonSelenium.java @@ -16,15 +16,20 @@ package ch.systemsx.cisd.openbis.jstest.suite.common; +import java.io.File; + import junit.framework.Assert; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; +import ch.systemsx.cisd.common.filesystem.FileUtilities; import ch.systemsx.cisd.openbis.jstest.layout.OpenbisJsWebappLocation; import ch.systemsx.cisd.openbis.jstest.layout.OpenbisScreeningJsWebappLocation; +import ch.systemsx.cisd.openbis.jstest.page.OpenbisJsCommonWebapp; import ch.systemsx.cisd.openbis.uitest.dsl.SeleniumTest; +import ch.systemsx.cisd.openbis.uitest.layout.Location; /** * @author pkupczyk @@ -73,26 +78,31 @@ public class JsTestCommonSelenium extends SeleniumTest @Test public void runOpenbisJsTests() { - try - { - Assert.assertEquals(0, browser().goTo(new OpenbisJsWebappLocation()).getFailedCount()); - } finally - { - SeleniumTest.driver.switchTo().defaultContent(); - } + runTests("runOpenbisJsTests", new OpenbisJsWebappLocation()); } @Test public void runOpenbisScreeningJsTests() + { + runTests("runOpenbisScreeningJsTests", new OpenbisScreeningJsWebappLocation()); + } + + private void runTests(String method, Location<OpenbisJsCommonWebapp> location) { try { - Assert.assertEquals(0, browser().goTo(new OpenbisScreeningJsWebappLocation()) - .getFailedCount()); + OpenbisJsCommonWebapp webapp = browser().goTo(location); + int failedCount = webapp.getFailedCount(); + + File report = + new File("targets/dist/" + this.getClass().getSimpleName() + "/" + method + + "/report.xml"); + FileUtilities.writeToFile(report, webapp.getJunitReport()); + + Assert.assertEquals(0, failedCount); } finally { SeleniumTest.driver.switchTo().defaultContent(); } } - } -- GitLab