diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4b04f0dbfa4ba199f3c2ea365287dd6cf0570caa..0324b9326ac8e91112a3eaec46748b4dfcd3764d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,6 +4,8 @@
 # Copyright (C) 2018 Research Group Biomedical Physics,
 # Max-Planck-Institute for Dynamics and Self-Organization Göttingen
 # Copyright (C) 2019 Henrik tom Wörden
+# Copyright (C) 2020 Timm Fitschen (t.fitschen@indiscale.com)
+# Copyright (C) 2020 IndiScale GmbH (info@indiscale.com)
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as
@@ -33,31 +35,35 @@ stages:
   - deploy
 
 # run pylint
-test:
+linting:
+  timeout: 15 minutes
   tags: [ docker ]
-  stage: test
+  stage: linting
   script:
     - make pylint
   allow_failure: true
 
-# run qunit tests 
+# run qunit tests
 test:
+  timeout: 15 minutes
   tags: [ docker ]
   stage: test
   artifacts:
+    when: always
     paths:
       - public/qunit.log
       - screenlog.*
+      - screenshot.*
   script:
     - make run-qunit
-  allow_failure: true
-          
+
 # Trigger building of server image and integration tests
 trigger_build:
+  timeout: 15 minutes
   tags: [ docker ]
   stage: deploy
   script:
-    - echo $TOKEN     
+    - echo $TOKEN
     - /usr/bin/curl -X POST
        -F token=$DEPLOY_TRIGGER_TOKEN
        -F "variables[WEBUI]=$CI_COMMIT_REF_NAME"
@@ -77,6 +83,6 @@ build-testenv:
     - docker pull $CI_REGISTRY_IMAGE:latest || true
     - docker build 
       --pull
-      --cache-from $CI_REGISTRY_IMAGE:latest 
+      --cache-from $CI_REGISTRY_IMAGE:latest
       -t $CI_REGISTRY_IMAGE:latest .
     - docker push $CI_REGISTRY_IMAGE:latest
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e5a4b171688b22f0295cff46dd407eaaa85a99be..f3fcda0ed89756011e24232c5c0009b1168b9c4c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 * in [entity.xsl](./src/core/xsl/entity.xsl): an emtpy property value (a `NULL` property) did not produce any `<span>` element with class `caosdb-property-text-value`. This caused the current implementation of `getPropertyFromElement` in [caosdb.js](./src/core/js/caosdb.js) to return the unit of the property as the value. The new implementation produces an empty `<span>` (no child text node) which is more appropriate and also fixes the buggy `getPropertyFromElement` without touching it.
 * in [webcaosdb.js](./src/core/js/webcaosdb.js), `markdown` module: The markdown module is very generell and small now. The logic for converting comments (aka CommentAnnotations) to markdown is implemented in the `annotation` module now (which uses the `markdown` module as back-end, tho).
+* updated QUnit test framework to 2.9.2
 
 ### Deprecated (for soon-to-be removed features) 
 
diff --git a/libs/qunit-2.6.2.zip b/libs/qunit-2.6.2.zip
deleted file mode 100644
index 9492fbbfbb95b7742b70fd970195d2703a4e5ab9..0000000000000000000000000000000000000000
Binary files a/libs/qunit-2.6.2.zip and /dev/null differ
diff --git a/libs/qunit-2.9.2.zip b/libs/qunit-2.9.2.zip
new file mode 100644
index 0000000000000000000000000000000000000000..a6f2b4fc8e3d9f85998accb335995b503c3313c6
Binary files /dev/null and b/libs/qunit-2.9.2.zip differ
diff --git a/makefile b/makefile
index 0b74a07e5a848879b244f9023d3ee8efc313921a..ef1751ab209889b158698ad9daa0b50838941b84 100644
--- a/makefile
+++ b/makefile
@@ -4,8 +4,8 @@
 #
 # Copyright (C) 2018 Research Group Biomedical Physics,
 # Max-Planck-Institute for Dynamics and Self-Organization Göttingen
-# Copyright (C) 2019 Timm Fitschen (t.fitschen@indiscale.com)
-# Copyright (C) 2019 IndiScale GmbH (info@indiscale.com)
+# Copyright (C) 2019-2020 Timm Fitschen (t.fitschen@indiscale.com)
+# Copyright (C) 2019-2020 IndiScale GmbH (info@indiscale.com)
 # Copyright (C) 2019 Daniel Hornung (d.hornung@indiscale.com)
 #
 # This program is free software: you can redistribute it and/or modify
@@ -83,39 +83,45 @@ build_properties:
 	@ln -s $(PUBLIC_DIR) $(PUBLIC_DIR)/webinterface
 
 PORT = 8000
-TIMEOUT = 500
-XVFB-RUN = xvfb-run -e xerr.log
+TIMEOUT = 60
 run-test-server: test
-	cd $(PUBLIC_DIR); $(MISC_DIR)/unit_test_http_server.py $(PORT) $(TIMEOUT) False; echo $$? > $(ROOT_DIR)/.server_done
+	$(MISC_DIR)/unit_test_http_server.py $(PORT) $(TIMEOUT) False $(PUBLIC_DIR)
 
 keep-test-server: test
-	cd $(PUBLIC_DIR); $(MISC_DIR)/unit_test_http_server.py $(PORT) $(TIMEOUT) True; echo $$? > $(ROOT_DIR)/.server_done
+	$(MISC_DIR)/unit_test_http_server.py $(PORT) $(TIMEOUT) True $(PUBLIC_DIR)
 
-run-qunit:
-	$(foreach exec, firefox screen xvfb-run,\
+run-qunit: test
+	$(foreach exec, firefox Xvfb xwd,\
 	    $(if $(shell which $(exec)),echo "found $(exec)",$(error "No $(exec) in PATH")))
 
-	# start server
-	screen -L -S caosdb-webui-test -t server -d -m -A make run-test-server
+	# start server in background
+	$(MISC_DIR)/unit_test_http_server.py $(PORT) $(TIMEOUT) False $(PUBLIC_DIR) &
 
-	# start firefox
-	screen -S caosdb-webui-test -X screen -t firefox $(XVFB-RUN) firefox \
-		"http://localhost:$(PORT)/?loggerPort=$(PORT)"
+	# start firefox with virtual xserver
+	Xvfb :1 -screen 0 1024x768x24 &
+	DISPLAY=:1 firefox "http://localhost:$(PORT)/?hidepassed" &
 
-	# wait until server stops
 	while [ 1 -eq 1 ]; do \
-		[ ! -e .server_done ] || break; \
+		sleep 5 ; \
+		[ ! -e $(PUBLIC_DIR)/.server_done ] || break; \
 	done; \
-	true
+
+	# screen shot of firefox
+	DISPLAY=:1 xwd -root -out screenshot.xwd
 
 	# stop firefox
 	pkill -u $(shell whoami) firefox || true
 
 	# print stuff
-	cat screenlog.* || true
 	cat $(PUBLIC_DIR)/qunit.log || true
-	cat .server_done || true
-	[ $$(cat .server_done) -eq 0 ]
+	cat $(PUBLIC_DIR)/.server_done || true
+	#
+	#
+	#
+	@if [ ! $$(cat $(PUBLIC_DIR)/.server_done) -eq 0 ] ; then \
+		echo "THE TESTS FAILED. PLEASE REVIEW screenshot.xwd FOR DETAILS." ; \
+		exit 1; \
+	fi
 
 cp-ext:
 	# TODO FIXME Base path for not-XSL-expanded files
@@ -205,10 +211,10 @@ $(LIBS_DIR)/js/state-machine.js: unzip $(LIBS_DIR)/js
 	ln -s $(LIBS_DIR)/javascript-state-machine-master/dist/state-machine.min.js $@
 
 $(LIBS_DIR)/css/qunit.css: unzip $(LIBS_DIR)/css
-	ln -s $(LIBS_DIR)/qunit-2.6.2/qunit-2.6.2.css $@
+	ln -s $(LIBS_DIR)/qunit-2.9.2/qunit-2.9.2.css $@
 
 $(LIBS_DIR)/js/qunit.js: unzip $(LIBS_DIR)/js
-	ln -s $(LIBS_DIR)/qunit-2.6.2/qunit-2.6.2.js $@
+	ln -s $(LIBS_DIR)/qunit-2.9.2/qunit-2.9.2.js $@
 
 $(LIBS_DIR)/js/dropzone.js: unzip $(LIBS_DIR)/js
 	ln -s $(LIBS_DIR)/dropzone-5.5.0/dist/dropzone.js $@
@@ -255,7 +261,6 @@ clean:
 	$(RM) -r $(PUBLIC_DIR)
 	for f in $(LIBS_SUBDIRS); do unlink $$f || $(RM) -r $$f || true; done
 	for f in $(patsubst %.zip,%/,$(LIBS_ZIP)); do $(RM) -r $$f; done
-	$(RM) screenlog.*
 	$(RM) .server_done
 
 .PHONY: unzip
diff --git a/misc/unit_test_http_server.py b/misc/unit_test_http_server.py
index a83d762ae69940d0eb43083ad9eff892e54f182b..c52f84e4ced6da2289dc853c6b65c3c9dc6ed202 100755
--- a/misc/unit_test_http_server.py
+++ b/misc/unit_test_http_server.py
@@ -6,6 +6,8 @@
 #
 # Copyright (C) 2018 Research Group Biomedical Physics,
 # Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+# Copyright (C) 2019-2020 Timm Fitschen (t.fitschen@indiscale.com)
+# Copyright (C) 2019-2020 IndiScale GmbH (info@indiscale.com)
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as
@@ -28,8 +30,10 @@ For running and logging the unit test suite of webcaosdb.
 """
 import sys
 import os
+from datetime import datetime
 from http.server import SimpleHTTPRequestHandler, HTTPServer
 
+os.chdir(sys.argv[4])
 counter = 0
 class UnitTestsHandler(SimpleHTTPRequestHandler):
     """UnitTestsHandler
@@ -124,7 +128,9 @@ class UnitTestHTTPServer(HTTPServer):
 
     def _shutdown(self, message, code):
         self._keep_running = False
-        self._exit_message = message
+        self._exit_message = "[{}] {}".format(datetime.now(), message)
+        with open(".server_done", "w") as logfile:
+            logfile.write("{}".format(code))
         self._exit_code = code
 
     def start(self):
diff --git a/test/core/js/modules/ext_xsl_download.js.js b/test/core/js/modules/ext_xls_download.js.js
similarity index 96%
rename from test/core/js/modules/ext_xsl_download.js.js
rename to test/core/js/modules/ext_xls_download.js.js
index 12ba18b9da3bf393d8249cb5456c7016f13456d6..360b389725eec2bc6a4e4115e9cc46cd91e33d69 100644
--- a/test/core/js/modules/ext_xsl_download.js.js
+++ b/test/core/js/modules/ext_xls_download.js.js
@@ -37,7 +37,7 @@ QUnit.module("ext_xls_download");
         return str2xml('<response><script code="0" /><stdout>bla</stdout></response>');
     }
 
-    var _go_to_script_results = function(xls_link, filename) {
+    _go_to_script_results = function(xls_link, filename) {
         xls_link.setAttribute(
             "href",
             location.protocol + "//" +location.host + "/Shared/" + filename);
diff --git a/test/core/js/setup.js b/test/core/js/setup.js
index f2bca4e928b8d5ef01b19ef6cd2d4425bae0ee24..9894827988999e89a6388206f10bdd815098e81e 100644
--- a/test/core/js/setup.js
+++ b/test/core/js/setup.js
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 Timm Fitschen (t.fitschen@indiscale.com)
+ * Copyright (C) 2020 IndiScale GmbH (info@indiscale.com)
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -22,51 +24,25 @@
  */
 "use strict";
 
-function getQueryValue(key) {
-    var match = RegExp('[?&]' + key + '=([^&]*)').exec(window.location.search);
-    return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
+function _reset_env() {
+  if(connection && typeof connection._init === "function") {
+    connection._init()
+  }
 }
 
-{
-    let loggerPort = getQueryValue("loggerPort");
-    console.log("found loggerPort: " + loggerPort);
-    if (loggerPort) {
-        console.log("logging QUnit results to http://127.0.0.1:" + loggerPort);
+QUnit.config.testTimeout = 10000;
 
-        QUnit.done(function( details ) {
-            console.log("done");
-            let report = (details.failed === 0 ? "SUCCESS\n" : "FAILURE\n") + "Total: " + details.total + "\nFailed: " + details.failed + "\nPassed: " + details.passed + "\nRuntime: " + details.runtime + "\n";
-            console.log(report);
-            return $.post("http://127.0.0.1:" + loggerPort + "/done", report);
-        });
+QUnit.moduleStart(function(module) {
+    localStorage.clear();
+    sessionStorage.clear();
+});
 
-        QUnit.config.testTimeout = 30000;
+QUnit.moduleDone(function(module) {
+    _reset_env();
+});
 
-        QUnit.log(function(obj) {
-            if(!obj.result) {
-                var failed_assertion = JSON.stringify(obj, null, 2);
-                $.post("http://127.0.0.1:" + loggerPort + "/log", "FAILURE\n" + failed_assertion);
-            }
-        });
+QUnit.done(function( details ) {
+    var report = (details.failed === 0 ? "SUCCESS\n" : "FAILURE\n") + JSON.stringify(details, null, 2);
+    $.post("/done", report);
+});
 
-        QUnit.testDone( function( details ) {
-          var result = {
-            "Module name": details.module,
-            "Test name": details.name,
-            "Assertions": {
-              "Total": details.total,
-              "Passed": details.passed,
-              "Failed": details.failed
-            },
-            "Skipped": details.skipped,
-            "Todo": details.todo,
-            "Runtime": details.runtime
-          };
-
-          return $.post("http://127.0.0.1:" + loggerPort + "/log", JSON.stringify( result, null, 2 ));
-
-        });
-
-
-    }
-}
diff --git a/test/docker/Dockerfile b/test/docker/Dockerfile
index 43a45e7ad35368db45b6c13fb54b3e252b9a121c..a19843fb3de11bbedf1c01f71619470bcc99f75c 100644
--- a/test/docker/Dockerfile
+++ b/test/docker/Dockerfile
@@ -1,7 +1,7 @@
 FROM debian:latest
 RUN apt-get update && \
     apt-get install firefox-esr gettext-base pylint3 python3-pip \
-    python3-httpbin git curl screen xvfb unzip -y
+    python3-httpbin git curl x11-apps xvfb unzip -y
 RUN git clone -b dev https://gitlab.com/caosdb/caosdb-pylib.git && \
     cd caosdb-pylib && pip3 install .