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 = {
+				'"' : '&quot;',
+				'\'': '&apos;',
+				'<' : '&lt;',
+				'>' : '&gt;',
+				'&' : '&amp;'
+			};
+
+			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