You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by bo...@apache.org on 2021/01/12 18:58:35 UTC

[myfaces-tobago] 01/02: improve jasmine test tools

This is an automated email from the ASF dual-hosted git repository.

bommel pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit 3db6f3708a01da704f8e3141690a73b62b5f61c0
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Mon Jan 11 21:24:43 2021 +0100

    improve jasmine test tools
    
    * better setup method (with eventType/element)
    * hide "private" methods
    * better logging
    * remove outdated tobago test tools
    * adjust tests
---
 .../080-sheet/10-sort/Sheet_Sorting.test.js        |  96 ++---
 .../080-sheet/30-event/Sheet_Event.test.js         |  67 ++-
 .../080-sheet/70-tree/Sheet_Tree.test.js           |   3 +-
 .../090-tree/04-listbox/Tree_Listbox.test.js       |  24 +-
 .../06-validation/00/Content_Validation.test.js    |  78 ++--
 .../30-concept/06-validation/01/JSR_303.test.js    |  25 +-
 .../06-validation/30-messages/Messages.test.js     |   4 +-
 .../08-form/10-required/Required.test.js           |  32 +-
 .../30-concept/08-form/20-ajax/Ajax.test.js        |  48 +--
 .../webapp/content/30-concept/08-form/Form.test.js |  48 ++-
 .../30-concept/30-behavior/Behavior.test.js        |  69 ++--
 .../30-concept/51-for-each/For_Each.test.js        |  12 +-
 .../00-collapsible-box/Collapsible_Box.test.js     |  52 +--
 .../20-collapsible-panel/Collapsible_Panel.test.js | 292 ++++++-------
 .../Collapsible_Section.test.js                    | 292 ++++++-------
 .../40000-style/100-headings/Headings.test.js      |   5 +-
 .../Ajax_Special_Character.test.js                 |   3 +-
 .../resources/tobago/test/tobago-test-tool.js      | 454 ++++++++-------------
 18 files changed, 743 insertions(+), 861 deletions(-)

diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/10-sort/Sheet_Sorting.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/10-sort/Sheet_Sorting.test.js
index 921b36a..f3877c0 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/10-sort/Sheet_Sorting.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/10-sort/Sheet_Sorting.test.js
@@ -26,13 +26,11 @@ it("Basics: Name", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => colNameFn().classList.contains("tobago-sheet-header-markup-ascending"),
-      () => colNameFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", colNameFn);
   test.setup(
       () => rowsFn()[0].querySelector("tobago-out").textContent === "Earth",
-      () => {
-        leftPagingFn().value = "22";
-        leftPagingFn().dispatchEvent(new Event("blur", {bubbles: true}));
-      });
+      () => leftPagingFn().value = "22",
+      "blur", leftPagingFn);
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-sortable")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-ascending")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-descending")).not.toBe(true));
@@ -67,13 +65,11 @@ it("Basics: Period", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => colPeriodFn().classList.contains("tobago-sheet-header-markup-ascending"),
-      () => colPeriodFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", colPeriodFn);
   test.setup(
       () => rowsFn()[0].querySelector("tobago-out").textContent === "Rosalind",
-      () => {
-        leftPagingFn().value = "29";
-        leftPagingFn().dispatchEvent(new Event("blur", {bubbles: true}));
-      });
+      () => leftPagingFn().value = "29",
+      "blur", leftPagingFn);
   test.do(() => expect(colPeriodFn().classList.contains("tobago-sheet-header-markup-sortable")).toBe(true));
   test.do(() => expect(colPeriodFn().classList.contains("tobago-sheet-header-markup-ascending")).toBe(true));
   test.do(() => expect(colPeriodFn().classList.contains("tobago-sheet-header-markup-descending")).not.toBe(true));
@@ -108,13 +104,11 @@ it("Basics: Year", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => colYearFn().classList.contains("tobago-sheet-header-markup-ascending"),
-      () => colYearFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", colYearFn);
   test.setup(
       () => rowsFn()[0].querySelector("tobago-out").textContent === "Amalthea",
-      () => {
-        leftPagingFn().value = "22";
-        leftPagingFn().dispatchEvent(new Event("blur", {bubbles: true}));
-      });
+      () => leftPagingFn().value = "22",
+      "blur", leftPagingFn);
   test.do(() => expect(colYearFn().classList.contains("tobago-sheet-header-markup-sortable")).toBe(true));
   test.do(() => expect(colYearFn().classList.contains("tobago-sheet-header-markup-ascending")).toBe(true));
   test.do(() => expect(colYearFn().classList.contains("tobago-sheet-header-markup-descending")).not.toBe(true));
@@ -145,13 +139,11 @@ it("Basics: left paging", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => colNameFn().classList.contains("tobago-sheet-header-markup-ascending"),
-      () => colNameFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", colNameFn);
   test.setup(
       () => rowsFn()[0].querySelector("tobago-out").textContent === "1986U10",
-      () => {
-        leftPagingFn().value = "1";
-        leftPagingFn().dispatchEvent(new Event("blur", {bubbles: true}));
-      });
+      () => leftPagingFn().value = "1",
+      "blur", leftPagingFn);
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-sortable")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-ascending")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-descending")).not.toBe(true));
@@ -196,13 +188,11 @@ it("Basics: center paging", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => colNameFn().classList.contains("tobago-sheet-header-markup-ascending"),
-      () => colNameFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", colNameFn);
   test.setup(
       () => rowsFn()[0].querySelector("tobago-out").textContent === "1986U10",
-      () => {
-        leftPagingFn().value = "1";
-        leftPagingFn().dispatchEvent(new Event("blur", {bubbles: true}));
-      });
+      () => leftPagingFn().value = "1",
+      "blur", leftPagingFn);
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-sortable")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-ascending")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-descending")).not.toBe(true));
@@ -259,13 +249,11 @@ it("Basics: right paging", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => colNameFn().classList.contains("tobago-sheet-header-markup-ascending"),
-      () => colNameFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", colNameFn);
   test.setup(
       () => rowsFn()[0].querySelector("tobago-out").textContent === "Earth",
-      () => {
-        leftPagingFn().value = "22";
-        leftPagingFn().dispatchEvent(new Event("blur", {bubbles: true}));
-      });
+      () => leftPagingFn().value = "22",
+      "blur", leftPagingFn);
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-sortable")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-ascending")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-descending")).not.toBe(true));
@@ -331,13 +319,11 @@ it("Custom Sorting: Name", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => colNameFn().classList.contains("tobago-sheet-header-markup-ascending"),
-      () => colNameFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", colNameFn);
   test.setup(
       () => rowsFn()[0].querySelector("tobago-out").textContent === "Earth",
-      () => {
-        leftPagingFn().value = "22";
-        leftPagingFn().dispatchEvent(new Event("blur", {bubbles: true}));
-      });
+      () => leftPagingFn().value = "22",
+      "blur", leftPagingFn);
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-sortable")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-ascending")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-descending")).not.toBe(true));
@@ -372,13 +358,11 @@ it("Custom Sorting: Period", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => colPeriodFn().classList.contains("tobago-sheet-header-markup-ascending"),
-      () => colPeriodFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", colPeriodFn);
   test.setup(
       () => rowsFn()[0].querySelector("tobago-out").textContent === "Mimas",
-      () => {
-        leftPagingFn().value = "29";
-        leftPagingFn().dispatchEvent(new Event("blur", {bubbles: true}));
-      });
+      () => leftPagingFn().value = "29",
+      "blur", leftPagingFn);
   test.do(() => expect(colPeriodFn().classList.contains("tobago-sheet-header-markup-sortable")).toBe(true));
   test.do(() => expect(colPeriodFn().classList.contains("tobago-sheet-header-markup-ascending")).toBe(true));
   test.do(() => expect(colPeriodFn().classList.contains("tobago-sheet-header-markup-descending")).not.toBe(true));
@@ -413,13 +397,11 @@ it("Custom Sorting: Year", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => colYearFn().classList.contains("tobago-sheet-header-markup-ascending"),
-      () => colYearFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", colYearFn);
   test.setup(
       () => rowsFn()[0].querySelectorAll("tobago-out")[2].textContent === "1789",
-      () => {
-        leftPagingFn().value = "22";
-        leftPagingFn().dispatchEvent(new Event("blur", {bubbles: true}));
-      });
+      () => leftPagingFn().value = "22",
+      "blur", leftPagingFn);
   test.do(() => expect(colYearFn().classList.contains("tobago-sheet-header-markup-sortable")).toBe(true));
   test.do(() => expect(colYearFn().classList.contains("tobago-sheet-header-markup-ascending")).toBe(true));
   test.do(() => expect(colYearFn().classList.contains("tobago-sheet-header-markup-descending")).not.toBe(true));
@@ -450,13 +432,11 @@ it("Custom Sorting: left paging", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => colNameFn().classList.contains("tobago-sheet-header-markup-ascending"),
-      () => colNameFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", colNameFn);
   test.setup(
       () => rowsFn()[0].querySelector("tobago-out").textContent === "1986U10",
-      () => {
-        leftPagingFn().value = "1";
-        leftPagingFn().dispatchEvent(new Event("blur", {bubbles: true}));
-      });
+      () => leftPagingFn().value = "1",
+      "blur", leftPagingFn);
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-sortable")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-ascending")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-descending")).not.toBe(true));
@@ -501,13 +481,11 @@ it("Custom Sorting: center paging", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => colNameFn().classList.contains("tobago-sheet-header-markup-ascending"),
-      () => colNameFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", colNameFn);
   test.setup(
       () => rowsFn()[0].querySelector("tobago-out").textContent === "1986U10",
-      () => {
-        leftPagingFn().value = "1";
-        leftPagingFn().dispatchEvent(new Event("blur", {bubbles: true}));
-      });
+      () => leftPagingFn().value = "1",
+      "blur", leftPagingFn);
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-sortable")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-ascending")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-descending")).not.toBe(true));
@@ -564,13 +542,11 @@ it("Custom Sorting: right paging", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => colNameFn().classList.contains("tobago-sheet-header-markup-ascending"),
-      () => colNameFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", colNameFn);
   test.setup(
       () => rowsFn()[0].querySelector("tobago-out").textContent === "Earth",
-      () => {
-        leftPagingFn().value = "22";
-        leftPagingFn().dispatchEvent(new Event("blur", {bubbles: true}));
-      });
+      () => leftPagingFn().value = "22",
+      "blur", leftPagingFn);
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-sortable")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-ascending")).toBe(true));
   test.do(() => expect(colNameFn().classList.contains("tobago-sheet-header-markup-descending")).not.toBe(true));
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/30-event/Sheet_Event.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/30-event/Sheet_Event.test.js
index e18c477..79f59fc 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/30-event/Sheet_Event.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/30-event/Sheet_Event.test.js
@@ -18,7 +18,13 @@
 import {querySelectorFn} from "/script/tobago-test.js";
 import {JasmineTestTool} from "/tobago/test/tobago-test-tool.js";
 
-it("On click with ajax", function (done) {
+it("must be fixed first", function (done) {
+  let test = new JasmineTestTool(done);
+  test.do(() => fail("must be fixed first"));
+  test.start();
+});
+
+/*it("On click with ajax", function (done) {
   let oneClickAjaxFn = querySelectorFn("#page\\:mainForm\\:changeExample\\:\\:0");
   let venusFn = querySelectorFn("#page\\:mainForm\\:s1\\:2\\:sample0");
   let jupiterFn = querySelectorFn("#page\\:mainForm\\:s1\\:5\\:sample0");
@@ -27,19 +33,15 @@ it("On click with ajax", function (done) {
 
   let test = new JasmineTestTool(done);
   test.do(() => oneClickAjaxFn().checked = true);
-  test.do(() => oneClickAjaxFn().dispatchEvent(new Event("change", {bubbles: true})));
-  test.wait(() => venusFn());
+  test.event("change", oneClickAjaxFn, () => venusFn());
   test.do(() => expect(venusFn() != null).toBe(true));
   test.do(() => expect(jupiterFn() != null).toBe(true));
   test.do(() => expect(saturnFn() != null).toBe(true));
-  test.do(() => venusFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => namefieldFn() && namefieldFn().value === "Venus");
+  test.event("click", venusFn, () => namefieldFn() && namefieldFn().value === "Venus");
   test.do(() => expect(namefieldFn().value).toBe("Venus"));
-  test.do(() => jupiterFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => namefieldFn() && namefieldFn().value === "Jupiter");
+  test.event("click", jupiterFn, () => namefieldFn() && namefieldFn().value === "Jupiter");
   test.do(() => expect(namefieldFn().value).toBe("Jupiter"));
-  test.do(() => saturnFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => namefieldFn() && namefieldFn().value === "Saturn");
+  test.event("click", saturnFn, () => namefieldFn() && namefieldFn().value === "Saturn");
   test.do(() => expect(namefieldFn().value).toBe("Saturn"));
   test.start();
 });
@@ -53,19 +55,15 @@ it("On click with full request", function (done) {
 
   let test = new JasmineTestTool(done);
   test.do(() => oneClickFullRequestFn().checked = true);
-  test.do(() => oneClickFullRequestFn().dispatchEvent(new Event("change", {bubbles: true})));
-  test.wait(() => venusFn());
+  test.event("change", oneClickFullRequestFn, () => venusFn());
   test.do(() => expect(venusFn() != null).toBe(true));
   test.do(() => expect(jupiterFn() != null).toBe(true));
   test.do(() => expect(saturnFn() != null).toBe(true));
-  test.do(() => venusFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => namefieldFn() && namefieldFn().value === "Venus");
+  test.event("click", venusFn, () => namefieldFn() && namefieldFn().value === "Venus");
   test.do(() => expect(namefieldFn().value).toBe("Venus"));
-  test.do(() => jupiterFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => namefieldFn() && namefieldFn().value === "Jupiter");
+  test.event("click", jupiterFn, () => namefieldFn() && namefieldFn().value === "Jupiter");
   test.do(() => expect(namefieldFn().value).toBe("Jupiter"));
-  test.do(() => saturnFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => namefieldFn() && namefieldFn().value === "Saturn");
+  test.event("click", saturnFn, () => namefieldFn() && namefieldFn().value === "Saturn");
   test.do(() => expect(namefieldFn().value).toBe("Saturn"));
   test.start();
 });
@@ -79,19 +77,15 @@ it("On double click with full request", function (done) {
 
   let test = new JasmineTestTool(done);
   test.do(() => doubleClickFullRequestFn().checked = true);
-  test.do(() => doubleClickFullRequestFn().dispatchEvent(new Event("change", {bubbles: true})));
-  test.wait(() => venusFn());
+  test.event("change", doubleClickFullRequestFn, () => venusFn());
   test.do(() => expect(venusFn() != null).toBe(true));
   test.do(() => expect(jupiterFn() != null).toBe(true));
   test.do(() => expect(saturnFn() != null).toBe(true));
-  test.do(() => venusFn().dispatchEvent(new Event("dblclick", {bubbles: true})));
-  test.wait(() => namefieldFn() && namefieldFn().value === "Venus");
+  test.event("dblclick", venusFn, () => namefieldFn() && namefieldFn().value === "Venus");
   test.do(() => expect(namefieldFn().value).toBe("Venus"));
-  test.do(() => jupiterFn().dispatchEvent(new Event("dblclick", {bubbles: true})));
-  test.wait(() => namefieldFn() && namefieldFn().value === "Jupiter");
+  test.event("dblclick", jupiterFn, () => namefieldFn() && namefieldFn().value === "Jupiter");
   test.do(() => expect(namefieldFn().value).toBe("Jupiter"));
-  test.do(() => saturnFn().dispatchEvent(new Event("dblclick", {bubbles: true})));
-  test.wait(() => namefieldFn() && namefieldFn().value === "Saturn");
+  test.event("dblclick", saturnFn, () => namefieldFn() && namefieldFn().value === "Saturn");
   test.do(() => expect(namefieldFn().value).toBe("Saturn"));
   test.start();
 });
@@ -107,31 +101,24 @@ it("Open popup on click with ajax", function (done) {
 
   let test = new JasmineTestTool(done);
   test.do(() => radioButtonFn().checked = true);
-  test.do(() => radioButtonFn().dispatchEvent(new Event("change", {bubbles: true})));
-  test.wait(() => venusFn());
+  test.event("change", radioButtonFn, () => venusFn());
   test.do(() => expect(venusFn() != null).toBe(true));
   test.do(() => expect(jupiterFn() != null).toBe(true));
   test.do(() => expect(saturnFn() != null).toBe(true));
-  test.do(() => venusFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => popupFn() && popupFn().classList.contains("show") === true);
+  test.event("click", venusFn, () => popupFn() && popupFn().classList.contains("show") === true);
   test.do(() => expect(popupFn().classList.contains("show")).toBe(true));
   test.do(() => expect(nameFn().value).toBe("Venus"));
-  test.do(() => cancelFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => popupFn() && popupFn().classList.contains("show") !== true);
+  test.event("click", cancelFn, () => popupFn() && popupFn().classList.contains("show") !== true);
   test.do(() => expect(popupFn().classList.contains("show")).not.toBe(true));
-  test.do(() => jupiterFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => popupFn() && popupFn().classList.contains("show") === true);
+  test.event("click", jupiterFn, () => popupFn() && popupFn().classList.contains("show") === true);
   test.do(() => expect(popupFn().classList.contains("show")).toBe(true));
   test.do(() => expect(nameFn().value).toBe("Jupiter"));
-  test.do(() => cancelFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => popupFn() && popupFn().classList.contains("show") !== true);
+  test.event("click", cancelFn, () => popupFn() && popupFn().classList.contains("show") !== true);
   test.do(() => expect(popupFn().classList.contains("show")).not.toBe(true));
-  test.do(() => saturnFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => popupFn() && popupFn().classList.contains("show") === true);
+  test.event("click", saturnFn, () => popupFn() && popupFn().classList.contains("show") === true);
   test.do(() => expect(popupFn().classList.contains("show")).toBe(true));
   test.do(() => expect(nameFn().value).toBe("Saturn"));
-  test.do(() => cancelFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => popupFn() && popupFn().classList.contains("show") !== true);
+  test.event("click", cancelFn, () => popupFn() && popupFn().classList.contains("show") !== true);
   test.do(() => expect(popupFn().classList.contains("show")).not.toBe(true));
   test.start();
-});
+});*/
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/70-tree/Sheet_Tree.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/70-tree/Sheet_Tree.test.js
index def1b1c..c8b3d20 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/70-tree/Sheet_Tree.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/70-tree/Sheet_Tree.test.js
@@ -49,8 +49,7 @@ it("Collapse tree", function (done) {
   let sheetRow = row1yearFn().parentElement.parentElement;
   test.do(() => expect(sheetRow.classList.contains("tobago-sheet-row")).toBe(true));
   test.do(() => expect(getComputedStyle(sheetRow).display).not.toBe("none"));
-  test.do(() => rootTreeButtonFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => row0nameFn() && row0nameFn().textContent === "Sun");
+  test.event("click", rootTreeButtonFn, () => row0nameFn() && row0nameFn().textContent === "Sun");
   test.do(() => expect(row0nameFn().textContent).toBe("Sun"));
   test.do(() => expect(row0centralBodyFn().textContent).toBe("-"));
   test.do(() => expect(row0distanceFn().textContent).toBe("0"));
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/04-listbox/Tree_Listbox.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/04-listbox/Tree_Listbox.test.js
index f318fbd..3dacdb5 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/04-listbox/Tree_Listbox.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/090-tree/04-listbox/Tree_Listbox.test.js
@@ -29,12 +29,10 @@ it("Select 2,2,0 and submit", function (done) {
 
   const test = new JasmineTestTool(done);
   test.setup(() => isLevelSelectVisible(2, 1),
-      () => {
-        node1().selected = true;
-        node1().dispatchEvent(new Event("change", {bubbles: true}));
-      });
+      () => node1().selected = true,
+      "change", node1);
   test.setup(() => output().textContent !== "[[2, 2, 0]]",
-      () => submit().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", submit);
 
   test.do(() => node3().selected = true);
   test.event("change", node3,
@@ -96,12 +94,10 @@ it("Select 3 and submit", function (done) {
 
   const test = new JasmineTestTool(done);
   test.setup(() => isLevelSelectVisible(2, 1),
-      () => {
-        node1().selected = true;
-        node1().dispatchEvent(new Event("change", {bubbles: true}));
-      });
+      () => node1().selected = true,
+      "change", node1);
   test.setup(() => output().textContent !== "[[3]]",
-      () => submit().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", submit);
 
   test.do(() => node9().selected = true);
   test.event("change", node9,
@@ -136,12 +132,10 @@ it("Select 4,2,1,1 and submit", function (done) {
 
   const test = new JasmineTestTool(done);
   test.setup(() => isLevelSelectVisible(2, 1),
-      () => {
-        node1().selected = true;
-        node1().dispatchEvent(new Event("change", {bubbles: true}));
-      });
+      () => node1().selected = true,
+      "change", node1);
   test.setup(() => output().textContent !== "[[4, 2, 1, 1]]",
-      () => submit().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", submit);
 
   test.do(() => node10().selected = true);
   test.event("change", node10,
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/06-validation/00/Content_Validation.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/06-validation/00/Content_Validation.test.js
index 462a9da..86c87e9 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/06-validation/00/Content_Validation.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/06-validation/00/Content_Validation.test.js
@@ -25,9 +25,11 @@ it("Required: Submit without content.", function (done) {
   let textareaValue = textareaFn().value;
 
   let test = new JasmineTestTool(done);
+  test.setup(() => messagesFn() && messagesFn().length === 0,
+      () => textareaFn().value = "Alice",
+      "click", submitFn);
   test.do(() => textareaFn().value = "");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 1);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 1);
   test.do(() => expect(messagesFn().length).toBe(1));
   test.do(() => expect(textareaFn().value).toBe(textareaValue));
   test.start();
@@ -39,9 +41,11 @@ it("Required: Submit with content.", function (done) {
   let submitFn = querySelectorFn("#page\\:mainForm\\:required\\:submit_r");
 
   let test = new JasmineTestTool(done);
+  test.setup(() => messagesFn() && messagesFn().length === 1,
+      () => textareaFn().value = "",
+      "click", submitFn);
   test.do(() => textareaFn().value = "some content");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 0
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 0
       && textareaFn().value && textareaFn().value === "some content");
   test.do(() => expect(messagesFn().length).toBe(0));
   test.do(() => expect(textareaFn().value).toBe("some content"));
@@ -54,9 +58,11 @@ it("Validate Length: Submit single character.", function (done) {
   let submitFn = querySelectorFn("#page\\:mainForm\\:validateLength\\:submit_vl");
 
   let test = new JasmineTestTool(done);
+  test.setup(() => messagesFn() && messagesFn().length === 0,
+      () => inFn().value = "Bob",
+      "click", submitFn);
   test.do(() => inFn().value = "a");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 1);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 1);
   test.do(() => expect(messagesFn().length).toBe(1));
   test.start();
 });
@@ -67,9 +73,11 @@ it("Validate Length: Submit two character.", function (done) {
   let submitFn = querySelectorFn("#page\\:mainForm\\:validateLength\\:submit_vl");
 
   let test = new JasmineTestTool(done);
+  test.setup(() => messagesFn() && messagesFn().length === 1,
+      () => inFn().value = "",
+      "click", submitFn);
   test.do(() => inFn().value = "ab");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 0);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
 });
@@ -80,9 +88,11 @@ it("Validate Range: Submit no number.", function (done) {
   let submitFn = querySelectorFn("#page\\:mainForm\\:validateRange\\:submit_vr");
 
   let test = new JasmineTestTool(done);
+  test.setup(() => messagesFn() && messagesFn().length === 0,
+      () => inFn().value = "42",
+      "click", submitFn);
   test.do(() => inFn().value = "no number");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 1);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 1);
   test.do(() => expect(messagesFn().length).toBe(1));
   test.start();
 });
@@ -93,9 +103,11 @@ it("Validate Range: Submit number '2' which is out of range.", function (done) {
   let submitFn = querySelectorFn("#page\\:mainForm\\:validateRange\\:submit_vr");
 
   let test = new JasmineTestTool(done);
+  test.setup(() => messagesFn() && messagesFn().length === 0,
+      () => inFn().value = "42",
+      "click", submitFn);
   test.do(() => inFn().value = "2");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 1);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 1);
   test.do(() => expect(messagesFn().length).toBe(1));
   test.start();
 });
@@ -106,9 +118,11 @@ it("Validate Range: Submit number '78' which is out of range.", function (done)
   let submitFn = querySelectorFn("#page\\:mainForm\\:validateRange\\:submit_vr");
 
   let test = new JasmineTestTool(done);
+  test.setup(() => messagesFn() && messagesFn().length === 0,
+      () => inFn().value = "42",
+      "click", submitFn);
   test.do(() => inFn().value = "78");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 1);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 1);
   test.do(() => expect(messagesFn().length).toBe(1));
   test.start();
 });
@@ -119,9 +133,11 @@ it("Validate Range: Submit number '64' which is within the range.", function (do
   let submitFn = querySelectorFn("#page\\:mainForm\\:validateRange\\:submit_vr");
 
   let test = new JasmineTestTool(done);
+  test.setup(() => messagesFn() && messagesFn().length === 1,
+      () => inFn().value = "1000",
+      "click", submitFn);
   test.do(() => inFn().value = "64");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 0);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
 });
@@ -132,9 +148,11 @@ it("Regex Validation: Submit 'T' which violates the pattern.", function (done) {
   let submitFn = querySelectorFn("#page\\:mainForm\\:regexValidation\\:submit_rv");
 
   let test = new JasmineTestTool(done);
+  test.setup(() => messagesFn() && messagesFn().length === 0,
+      () => inFn().value = "T3",
+      "click", submitFn);
   test.do(() => inFn().value = "T");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 1);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 1);
   test.do(() => expect(messagesFn().length).toBe(1));
   test.start();
 });
@@ -145,9 +163,11 @@ it("Regex Validation: Submit '3' which violates the pattern.", function (done) {
   let submitFn = querySelectorFn("#page\\:mainForm\\:regexValidation\\:submit_rv");
 
   let test = new JasmineTestTool(done);
+  test.setup(() => messagesFn() && messagesFn().length === 0,
+      () => inFn().value = "T3",
+      "click", submitFn);
   test.do(() => inFn().value = "3");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 1);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 1);
   test.do(() => expect(messagesFn().length).toBe(1));
   test.start();
 });
@@ -158,9 +178,11 @@ it("Regex Validation: Submit 'T3' which is accepted.", function (done) {
   let submitFn = querySelectorFn("#page\\:mainForm\\:regexValidation\\:submit_rv");
 
   let test = new JasmineTestTool(done);
+  test.setup(() => messagesFn() && messagesFn().length === 1,
+      () => inFn().value = "Charlie",
+      "click", submitFn);
   test.do(() => inFn().value = "T3");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 0);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
 });
@@ -171,9 +193,11 @@ it("Custom Validator: Submit rejected string.", function (done) {
   let submitFn = querySelectorFn("#page\\:mainForm\\:customValidator\\:submit_cv");
 
   let test = new JasmineTestTool(done);
+  test.setup(() => messagesFn() && messagesFn().length === 0,
+      () => inFn().value = "tobago",
+      "click", submitFn);
   test.do(() => inFn().value = "java");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 1);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 1);
   test.do(() => expect(messagesFn().length).toBe(1));
   test.start();
 });
@@ -184,9 +208,11 @@ it("Custom Validator: Submit accepted string.", function (done) {
   let submitFn = querySelectorFn("#page\\:mainForm\\:customValidator\\:submit_cv");
 
   let test = new JasmineTestTool(done);
+  test.setup(() => messagesFn() && messagesFn().length === 1,
+      () => inFn().value = "Dave",
+      "click", submitFn);
   test.do(() => inFn().value = "tobago");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 0);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
 });
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/06-validation/01/JSR_303.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/06-validation/01/JSR_303.test.js
index bbb9188..6053795 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/06-validation/01/JSR_303.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/06-validation/01/JSR_303.test.js
@@ -18,15 +18,20 @@
 import {querySelectorAllFn, querySelectorFn} from "/script/tobago-test.js";
 import {JasmineTestTool} from "/tobago/test/tobago-test-tool.js";
 
-it("Required: Submit without content.", function (done) {
+it("must be fixed first", function (done) {
+  let test = new JasmineTestTool(done);
+  test.do(() => fail("must be fixed first"));
+  test.start();
+});
+
+/*it("Required: Submit without content.", function (done) {
   let messagesFn = querySelectorAllFn("#page\\:messages.tobago-messages div");
   let inFn = querySelectorFn("#page\\:mainForm\\:required\\:in1\\:\\:field");
   let submitFn = querySelectorFn("#page\\:mainForm\\:required\\:submit1");
 
   let test = new JasmineTestTool(done);
   test.do(() => inFn().value = "");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 1);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 1);
   test.do(() => expect(messagesFn().length).toBe(1));
   test.start();
 });
@@ -38,8 +43,7 @@ it("Required: Submit with content.", function (done) {
 
   let test = new JasmineTestTool(done);
   test.do(() => inFn().value = "some content");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 0);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
 });
@@ -51,8 +55,7 @@ it("Length: Submit single character.", function (done) {
 
   let test = new JasmineTestTool(done);
   test.do(() => inFn().value = "a");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 1);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 1);
   test.do(() => expect(messagesFn().length).toBe(1));
   test.start();
 });
@@ -64,8 +67,7 @@ it("Length: Submit three characters.", function (done) {
 
   let test = new JasmineTestTool(done);
   test.do(() => inFn().value = "abc");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 0);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
 });
@@ -77,8 +79,7 @@ it("Length: Submit five characters.", function (done) {
 
   let test = new JasmineTestTool(done);
   test.do(() => inFn().value = "abcde");
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => messagesFn() && messagesFn().length === 1);
+  test.event("click", submitFn, () => messagesFn() && messagesFn().length === 1);
   test.do(() => expect(messagesFn().length).toBe(1));
   test.start();
-});
+});*/
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/06-validation/30-messages/Messages.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/06-validation/30-messages/Messages.test.js
index 603e24d..1620405 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/06-validation/30-messages/Messages.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/06-validation/30-messages/Messages.test.js
@@ -28,10 +28,10 @@ it("Press '7 Messages' Button and close the first, the last and the fourth", fun
   let test = new JasmineTestTool(done);
   test.setup(
       () => tab().classList.contains("active"),
-      () => tab().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", tab);
   test.setup(
       () => alerts().length === 7,
-      () => messagesButton().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", messagesButton);
   test.do(() => expect(alertLabels()[0].textContent).toBe("First Message - Info"));
   test.do(() => expect(alertLabels()[1].textContent).toBe("Second Message - Fatal"));
   test.do(() => expect(alertLabels()[2].textContent).toBe("Third Message - Warn"));
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/08-form/10-required/Required.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/08-form/10-required/Required.test.js
index 8843562..a645d2a 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/08-form/10-required/Required.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/08-form/10-required/Required.test.js
@@ -26,10 +26,8 @@ it("submit inner form 1 without violations", function (done) {
 
   let test = new JasmineTestTool(done);
   test.setup(() => form1OutputFn().textContent !== "Alice",
-      () => {
-        form1InputFn().value = "Bob";
-        form1SubmitFn().dispatchEvent(new Event("click", {bubbles: true}));
-      });
+      () => form1InputFn().value = "Bob",
+      "click", form1SubmitFn);
   test.do(() => form1InputFn().value = "Alice");
   test.event("click", form1SubmitFn, () => form1OutputFn().textContent === "Alice")
   test.do(() => expect(form1InputFn().value).toBe("Alice"));
@@ -50,9 +48,9 @@ it("submit inner form 2, violate required field", function (done) {
   test.setup(() => form2AlertFn() === null,
       () => {
         form2InputFn().value = "Bob";
-        form2SubmitFn().dispatchEvent(new Event("click", {bubbles: true}));
         form2OutputValue = "Bob";
-      });
+      },
+      "click", form2SubmitFn);
   test.do(() => form2InputFn().value = "");
   test.do(() => expect(form2InputFn().value).toBe(""));
   test.event("click", form2SubmitFn, () => form2AlertFn() !== null)
@@ -72,10 +70,8 @@ it("submit inner form 2 without violations", function (done) {
 
   let test = new JasmineTestTool(done);
   test.setup(() => form2OutputFn().textContent !== "Bob",
-      () => {
-        form2InputFn().value = "Charlie";
-        form2SubmitFn().dispatchEvent(new Event("click", {bubbles: true}));
-      });
+      () => form2InputFn().value = "Charlie",
+      "click", form2SubmitFn);
   test.do(() => form2InputFn().value = "Bob");
   test.event("click", form2SubmitFn, () => form2OutputFn().textContent === "Bob");
   test.do(() => expect(form2InputFn().value).toBe("Bob"));
@@ -102,10 +98,10 @@ it("submit outer form, violate both required fields", function (done) {
       () => {
         form2InputFn().value = "Charlie";
         outerFormInputFn().value = "Dave";
-        outerFormSubmitFn().dispatchEvent(new Event("click", {bubbles: true}));
         form2OutputValue = "Charlie";
         outerFormOutputValue = "Dave"
-      });
+      },
+      "click", outerFormSubmitFn);
   test.do(() => form2InputFn().value = "");
   test.do(() => outerFormInputFn().value = "");
   test.event("click", outerFormSubmitFn, () => form2AlertFn() !== null && outerFormAlertFn() !== null);
@@ -136,9 +132,9 @@ it("submit outer form, violate required field in form 2", function (done) {
   test.setup(() => form2AlertFn() === null,
       () => {
         form2InputFn().value = "Dave";
-        form2SubmitFn().dispatchEvent(new Event("click", {bubbles: true}));
         form2OutputValue = "Dave";
-      });
+      },
+      "click", form2SubmitFn);
   test.do(() => form2InputFn().value = "");
   test.do(() => outerFormInputFn().value = "Eve");
   test.event("click", outerFormSubmitFn, () => form2AlertFn() !== null && outerFormAlertFn() === null);
@@ -169,10 +165,10 @@ it("submit outer form, violate required field in outer form", function (done) {
       () => {
         form2InputFn().value = "Frank"
         outerFormInputFn().value = "Eve";
-        outerFormSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
         form2OutputValue = "Frank"
         outerFormOutputValue = "Eve";
-      });
+      },
+      "click", outerFormSubmitFn);
   test.do(() => form2InputFn().value = "Frank");
   test.do(() => outerFormInputFn().value = "");
   test.event("click", outerFormSubmitFn, () => outerFormAlertFn() !== null);
@@ -206,8 +202,8 @@ it("submit outer form without violations", function (done) {
         form1InputFn().value = "Alice";
         form2InputFn().value = "Bob";
         outerFormInputFn().value = "Charlie";
-        outerFormSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
+      },
+      "click", outerFormSubmitFn);
   test.do(() => form1InputFn().value = "Frank");
   test.do(() => form2InputFn().value = "Eve");
   test.do(() => outerFormInputFn().value = "Grace");
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/08-form/20-ajax/Ajax.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/08-form/20-ajax/Ajax.test.js
index 6851ce1..b6296b5 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/08-form/20-ajax/Ajax.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/08-form/20-ajax/Ajax.test.js
@@ -26,10 +26,8 @@ it("submit inner form 1 without violations", function (done) {
 
   let test = new JasmineTestTool(done);
   test.setup(() => form1OutputFn().textContent !== "Alice",
-      () => {
-        form1InputFn().value = "Bob";
-        form1SubmitFn().dispatchEvent(new Event("click", {bubbles: true}));
-      });
+      () => form1InputFn().value = "Bob",
+      "click", form1SubmitFn);
   test.do(() => form1InputFn().value = "Alice");
   test.event("click", form1SubmitFn, () => form1OutputFn().textContent === "Alice");
   test.do(() => expect(form1InputFn().value).toBe("Alice"));
@@ -50,9 +48,9 @@ it("submit inner form 2, violate required field", function (done) {
   test.setup(() => form2AlertFn() === null,
       () => {
         form2InputFn().value = "Bob";
-        form2SubmitFn().dispatchEvent(new Event("click", {bubbles: true}));
         form2OutputValue = "Bob";
-      });
+      },
+      "click", form2SubmitFn);
   test.do(() => form2InputFn().value = "");
   test.event("click", form2SubmitFn, () => form2AlertFn() !== null);
   test.do(() => expect(form2InputFn().value).toBe(""));
@@ -71,10 +69,8 @@ it("submit inner form 2 without violations", function (done) {
 
   let test = new JasmineTestTool(done);
   test.setup(() => form2OutputFn().textContent !== "Bob",
-      () => {
-        form2InputFn().value = "Alice";
-        form2SubmitFn().dispatchEvent(new Event("click", {bubbles: true}));
-      });
+      () => form2InputFn().value = "Alice",
+      "click", form2SubmitFn);
   test.do(() => form2InputFn().value = "Bob");
   test.event("click", form2SubmitFn, () => form2OutputFn().textContent === "Bob");
   test.do(() => expect(form2InputFn().value).toBe("Bob"));
@@ -106,11 +102,11 @@ it("submit outer form, violate both required fields", function (done) {
         form1InputFn().value = "Alice";
         form2InputFn().value = "Bob";
         outerFormInputFn().value = "Charlie";
-        outerFormSubmitFn().dispatchEvent(new Event("click", {bubbles: true}));
         form1OutputValue = "Alice"
         form2OutputValue = "Bob";
         outerFormOutputValue = "Charlie"
-      });
+      },
+      "click", outerFormSubmitFn);
   test.do(() => form1InputFn().value = "Dave");
   test.do(() => form2InputFn().value = "");
   test.do(() => outerFormInputFn().value = "");
@@ -148,11 +144,11 @@ it("submit outer form, violate required field in form 2", function (done) {
         form1InputFn().value = "Alice";
         form2InputFn().value = "Bob";
         outerFormInputFn().value = "Charlie";
-        outerFormSubmitFn().dispatchEvent(new Event("click", {bubbles: true}));
         form1OutputValue = "Alice"
         form2OutputValue = "Bob";
         outerFormOutputValue = "Charlie"
-      });
+      },
+      "click", outerFormSubmitFn);
   test.do(() => form1InputFn().value = "Charlie");
   test.do(() => form2InputFn().value = "");
   test.do(() => outerFormInputFn().value = "Dave");
@@ -190,11 +186,11 @@ it("submit outer form, violate required field in outer form", function (done) {
         form1InputFn().value = "Dave"
         form2InputFn().value = "Eve"
         outerFormInputFn().value = "Frank";
-        outerFormSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
         form1OutputValue = "Dave"
         form2OutputValue = "Eve"
         outerFormOutputValue = "Frank";
-      });
+      },
+      "click", outerFormSubmitFn);
   test.do(() => form1InputFn().value = "Eve");
   test.do(() => form2InputFn().value = "Frank");
   test.do(() => outerFormInputFn().value = "");
@@ -231,8 +227,8 @@ it("submit outer form without violations", function (done) {
         form1InputFn().value = "Eve";
         form2InputFn().value = "Frank";
         outerFormInputFn().value = "Grace";
-        outerFormSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
+      },
+      "click", outerFormSubmitFn);
   test.do(() => form1InputFn().value = "Hank");
   test.do(() => form2InputFn().value = "Irene");
   test.do(() => outerFormInputFn().value = "John");
@@ -272,11 +268,11 @@ it("submit inner forms, violate required field in form 2", function (done) {
         form1InputFn().value = "Alice";
         form2InputFn().value = "Bob";
         outerFormInputFn().value = "Charlie"
-        outerFormSubmitFn().dispatchEvent(new Event("click", {bubbles: true}));
         form1OutputValue = "Alice"
         form2OutputValue = "Bob";
         outerFormOutputValue = "Charlie";
-      });
+      },
+      "click", outerFormSubmitFn);
   test.do(() => form1InputFn().value = "Kate");
   test.do(() => form2InputFn().value = "");
   test.do(() => outerFormInputFn().value = "Leonard");
@@ -314,9 +310,9 @@ it("submit inner forms without violations", function (done) {
         form1InputFn().value = "Kate";
         form2InputFn().value = "Mike";
         outerFormInputFn().value = "Leonard";
-        outerFormSubmitFn().dispatchEvent(new Event("click", {bubbles: true}));
         outerFormOutputValue = "Leonard";
-      });
+      },
+      "click", outerFormSubmitFn);
   test.do(() => form1InputFn().value = "Mike");
   test.do(() => form2InputFn().value = "Neil");
   test.do(() => outerFormInputFn().value = "");
@@ -356,11 +352,11 @@ it("submit outer value, violate required field", function (done) {
         form1InputFn().value = "Leonard";
         form2InputFn().value = "Mike";
         outerFormInputFn().value = "Neil";
-        outerFormSubmitFn().dispatchEvent(new Event("click", {bubbles: true}));
         form1OutputValue = "Leonard";
         form2OutputValue = "Mike";
         outerFormOutputValue = "Neil";
-      });
+      },
+      "click", outerFormSubmitFn);
   test.do(() => form1InputFn().value = "Oscar");
   test.do(() => form2InputFn().value = "Penny");
   test.do(() => outerFormInputFn().value = "");
@@ -399,10 +395,10 @@ it("submit outer value without violations", function (done) {
         form1InputFn().value = "Neil";
         form2InputFn().value = "Oscar";
         outerFormInputFn().value = "Penny";
-        outerFormSubmitFn().dispatchEvent(new Event("click", {bubbles: true}));
         form1OutputValue = "Neil";
         form2OutputValue = "Oscar";
-      });
+      },
+      "click", outerFormSubmitFn);
   test.do(() => form1InputFn().value = "Quin");
   test.do(() => form2InputFn().value = "Sue");
   test.do(() => outerFormInputFn().value = "Ted");
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/08-form/Form.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/08-form/Form.test.js
index ac67482..243865b 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/08-form/Form.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/08-form/Form.test.js
@@ -21,39 +21,43 @@ import {JasmineTestTool} from "/tobago/test/tobago-test-tool.js";
 it("submit form 1", function (done) {
   let form1InputFieldFn = querySelectorFn("#page\\:mainForm\\:form1\\:in1\\:\\:field");
   let form2InputFieldFn = querySelectorFn("#page\\:mainForm\\:form2\\:in2\\:\\:field");
-  let form1OutputFieldFn = querySelectorFn("#page\\:mainForm\\:form1\\:out1 span");
-  let form2OutputFieldFn = querySelectorFn("#page\\:mainForm\\:form2\\:out2 span");
+  let form1OutputFieldFn = querySelectorFn("#page\\:mainForm\\:form1\\:out1 tobago-out");
+  let form2OutputFieldFn = querySelectorFn("#page\\:mainForm\\:form2\\:out2 tobago-out");
   let form1SubmitButtonFn = querySelectorFn("#page\\:mainForm\\:form1\\:submit1");
-  let $form2OutputFieldValue = form2OutputFieldFn().textContent;
+  let form2OutputFieldValue = form2OutputFieldFn().textContent;
 
   let test = new JasmineTestTool(done);
-  test.do(() => form1InputFieldFn().value = "Oliver");
-  test.do(() => form2InputFieldFn().value = "Peter");
-  test.do(() => form1SubmitButtonFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => form1InputFieldFn() && form1InputFieldFn().value === "Oliver");
-  test.do(() => expect(form1InputFieldFn().value).toBe("Oliver"));
-  test.do(() => expect(form1OutputFieldFn().textContent).toBe("Oliver"));
-  test.do(() => expect(form2InputFieldFn().value).toBe("Peter"));
-  test.do(() => expect(form2OutputFieldFn().textContent).toBe($form2OutputFieldValue));
+  test.setup(() => form1OutputFieldFn().textContent !== "Alice",
+      () => form1InputFieldFn().value = "Eve",
+      "click", form1SubmitButtonFn);
+  test.do(() => form1InputFieldFn().value = "Alice");
+  test.do(() => form2InputFieldFn().value = "Bob");
+  test.event("click", form1SubmitButtonFn, () => form1OutputFieldFn().textContent === "Alice");
+  test.do(() => expect(form1InputFieldFn().value).toBe("Alice"));
+  test.do(() => expect(form1OutputFieldFn().textContent).toBe("Alice"));
+  test.do(() => expect(form2InputFieldFn().value).toBe("Bob"));
+  test.do(() => expect(form2OutputFieldFn().textContent).toBe(form2OutputFieldValue));
   test.start();
 });
 
 it("submit form 2", function (done) {
   let form1InputFieldFn = querySelectorFn("#page\\:mainForm\\:form1\\:in1\\:\\:field");
   let form2InputFieldFn = querySelectorFn("#page\\:mainForm\\:form2\\:in2\\:\\:field");
-  let form1OutputFieldFn = querySelectorFn("#page\\:mainForm\\:form1\\:out1 span");
-  let form2OutputFieldFn = querySelectorFn("#page\\:mainForm\\:form2\\:out2 span");
+  let form1OutputFieldFn = querySelectorFn("#page\\:mainForm\\:form1\\:out1 tobago-out");
+  let form2OutputFieldFn = querySelectorFn("#page\\:mainForm\\:form2\\:out2 tobago-out");
   let form2SubmitButtonFn = querySelectorFn("#page\\:mainForm\\:form2\\:submit2");
-  let $form1OutputFieldValue = form1OutputFieldFn().textContent;
+  let form1OutputFieldValue = form1OutputFieldFn().textContent;
 
   let test = new JasmineTestTool(done);
-  test.do(() => form1InputFieldFn().value = "Oliver");
-  test.do(() => form2InputFieldFn().value = "Peter");
-  test.do(() => form2SubmitButtonFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => form1InputFieldFn() && form1InputFieldFn().value === "Oliver");
-  test.do(() => expect(form1InputFieldFn().value).toBe("Oliver"));
-  test.do(() => expect(form1OutputFieldFn().textContent).toBe($form1OutputFieldValue));
-  test.do(() => expect(form2InputFieldFn().value).toBe("Peter"));
-  test.do(() => expect(form2OutputFieldFn().textContent).toBe("Peter"));
+  test.setup(() => form2OutputFieldFn().textContent !== "Dave",
+      () => form2InputFieldFn().value = "Frank",
+      "click", form2SubmitButtonFn);
+  test.do(() => form1InputFieldFn().value = "Charlie");
+  test.do(() => form2InputFieldFn().value = "Dave");
+  test.event("click", form2SubmitButtonFn, () => form2OutputFieldFn().textContent === "Dave");
+  test.do(() => expect(form1InputFieldFn().value).toBe("Charlie"));
+  test.do(() => expect(form1OutputFieldFn().textContent).toBe(form1OutputFieldValue));
+  test.do(() => expect(form2InputFieldFn().value).toBe("Dave"));
+  test.do(() => expect(form2OutputFieldFn().textContent).toBe("Dave"));
   test.start();
 });
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/30-behavior/Behavior.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/30-behavior/Behavior.test.js
index 089c6d1..96f3cd9 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/30-behavior/Behavior.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/30-behavior/Behavior.test.js
@@ -15,29 +15,32 @@
  * limitations under the License.
  */
 
-import {querySelectorFn} from "/script/tobago-test.js";
 import {JasmineTestTool} from "/tobago/test/tobago-test-tool.js";
 
-it("Ajax Input", function (done) {
+it("must be fixed first", function (done) {
+  let test = new JasmineTestTool(done);
+  test.do(() => fail("must be fixed first"));
+  test.start();
+});
+
+/*it("Ajax Input", function (done) {
   let ajaxInputFn = querySelectorFn("#page\\:mainForm\\:j_id_2g\\:\\:field");
-  let ajaxOutputFn = querySelectorFn("#page\\:mainForm\\:outAjax span");
+  let ajaxOutputFn = querySelectorFn("#page\\:mainForm\\:outAjax tobago-out");
 
   let test = new JasmineTestTool(done);
   test.do(() => ajaxInputFn().value = "Alice");
-  test.do(() => ajaxInputFn().dispatchEvent(new Event("change", {bubbles: true})));
-  test.wait(() => ajaxOutputFn() && ajaxOutputFn().textContent === "Alice");
+  test.event("change", ajaxInputFn, () => ajaxOutputFn() && ajaxOutputFn().textContent === "Alice");
   test.do(() => expect(ajaxOutputFn().textContent).toBe(ajaxInputFn().value));
   test.start();
 });
 
 it("Event Input", function (done) {
   let eventInputFn = querySelectorFn("#page\\:mainForm\\:j_id_2k\\:\\:field");
-  let eventOutputFn = querySelectorFn("#page\\:mainForm\\:j_id_2m span");
+  let eventOutputFn = querySelectorFn("#page\\:mainForm\\:j_id_2m tobago-out");
 
   let test = new JasmineTestTool(done);
   test.do(() => eventInputFn().value = "Alice");
-  test.do(() => eventInputFn().dispatchEvent(new Event("change", {bubbles: true})));
-  test.wait(() => eventOutputFn() && eventOutputFn().textContent === "Alice");
+  test.event("change", eventInputFn, () => eventOutputFn() && eventOutputFn().textContent === "Alice");
   test.do(() => expect(eventOutputFn().textContent).toBe(eventInputFn().value));
   test.start();
 });
@@ -45,47 +48,35 @@ it("Event Input", function (done) {
 it("change the event name", function (done) {
   let ajaxFn = querySelectorFn("#page\\:mainForm\\:j_id_2p");
   let eventFn = querySelectorFn("#page\\:mainForm\\:j_id_2q");
-  let outCounterFn = querySelectorFn("#page\\:mainForm\\:outCounter span");
+  let outCounterFn = querySelectorFn("#page\\:mainForm\\:outCounter tobago-out");
   let counter = Number(outCounterFn().textContent);
 
   let test = new JasmineTestTool(done);
-  test.do(() => ajaxFn().dispatchEvent(new Event("dblclick", {bubbles: true})));
-  test.wait(() => outCounterFn() && Number(outCounterFn().textContent) === counter+1);
-  test.do(() => expect(Number(outCounterFn().textContent)).toBe(counter+1));
-  test.do(() => eventFn().dispatchEvent(new Event("dblclick", {bubbles: true})));
-  test.wait(() => outCounterFn() && Number(outCounterFn().textContent) === counter+2);
-  test.do(() => expect(Number(outCounterFn().textContent)).toBe(counter+2));
-  test.do(() => ajaxFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => outCounterFn() && Number(outCounterFn().textContent) === counter+2);
-  test.do(() => expect(Number(outCounterFn().textContent)).toBe(counter+2));
-  test.do(() => eventFn().dispatchEvent(new Event("dblclick", {bubbles: true})));
-  test.wait(() => outCounterFn() && Number(outCounterFn().textContent) === counter+3);
-  test.do(() => expect(Number(outCounterFn().textContent)).toBe(counter+3));
-  test.do(() => eventFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => outCounterFn() && Number(outCounterFn().textContent) === counter+3);
-  test.do(() => expect(Number(outCounterFn().textContent)).toBe(counter+3));
-  test.do(() => ajaxFn().dispatchEvent(new Event("dblclick", {bubbles: true})));
-  test.wait(() => outCounterFn() && Number(outCounterFn().textContent) === counter+4);
-  test.do(() => expect(Number(outCounterFn().textContent)).toBe(counter+4));
+  test.event("dblclick", ajaxFn, () => outCounterFn() && Number(outCounterFn().textContent) === counter + 1);
+  test.do(() => expect(Number(outCounterFn().textContent)).toBe(counter + 1));
+  test.event("dblclick", eventFn, () => outCounterFn() && Number(outCounterFn().textContent) === counter + 2);
+  test.do(() => expect(Number(outCounterFn().textContent)).toBe(counter + 2));
+  test.event("click", ajaxFn, () => outCounterFn() && Number(outCounterFn().textContent) === counter + 2);
+  test.do(() => expect(Number(outCounterFn().textContent)).toBe(counter + 2));
+  test.event("dblclick", eventFn, () => outCounterFn() && Number(outCounterFn().textContent) === counter + 3);
+  test.do(() => expect(Number(outCounterFn().textContent)).toBe(counter + 3));
+  test.event("click", eventFn, () => outCounterFn() && Number(outCounterFn().textContent) === counter + 3);
+  test.do(() => expect(Number(outCounterFn().textContent)).toBe(counter + 3));
+  test.event("dblclick", ajaxFn, () => outCounterFn() && Number(outCounterFn().textContent) === counter + 4);
+  test.do(() => expect(Number(outCounterFn().textContent)).toBe(counter + 4));
   test.start();
 });
 
 it("f:ajax and tc:event", function (done) {
   let submitFn = querySelectorFn("#page\\:mainForm\\:btnAjaxEvent");
-  let outFn = querySelectorFn("#page\\:mainForm\\:out span");
+  let outFn = querySelectorFn("#page\\:mainForm\\:out tobago-out");
 
   let test = new JasmineTestTool(done);
-  test.setup(
-      () => outFn().textContent === "Ajax",
-      () => submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  );
-  test.do(() => submitFn().dispatchEvent(new Event("dblclick", {bubbles: true})));
-  test.wait(() => outFn() && outFn().textContent === "Event");
+  test.setup(() => outFn().textContent === "Ajax",
+      null, "click", submitFn);
+  test.event("dblclick", submitFn, () => outFn() && outFn().textContent === "Event");
   test.do(() => expect(outFn().textContent).toBe("Event"));
-  test.do(() => submitFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => outFn() && outFn().textContent === "Ajax");
+  test.event("click", submitFn, () => outFn() && outFn().textContent === "Ajax");
   test.do(() => expect(outFn().textContent).toBe("Ajax"));
   test.start();
-});
-
-
+});*/
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/51-for-each/For_Each.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/51-for-each/For_Each.test.js
index 9b31157..98f96d3 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/51-for-each/For_Each.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/51-for-each/For_Each.test.js
@@ -29,21 +29,17 @@ it("Add a river and reset.", function (done) {
   let uiRepeatSectionsFn = querySelectorAllFn("#page\\:mainForm\\:uiRepeat tobago-section");
 
   let test = new JasmineTestTool(done);
-  test.do(() => resetFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => forEachBoxesFn() && forEachBoxesFn().length !== 0);
+  test.setup(() => forEachBoxesFn().length === 3,
+      null, "click", resetFn);
   test.do(() => expect(forEachBoxesFn().length).toBe(3));
   test.do(() => expect(uiRepeatSectionsFn().length).toBe(3));
   test.do(() => nameFn().value = "Mississippi");
   test.do(() => lengthFn().value = "6275");
   test.do(() => dischargeFn().value = "16200");
-  test.do(() => addFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => forEachBoxesFn() && forEachBoxesFn().length === 4
-      && uiRepeatSectionsFn() && uiRepeatSectionsFn().length === 4);
+  test.event("click", addFn, () => forEachBoxesFn().length === 4 && uiRepeatSectionsFn().length === 4);
   test.do(() => expect(forEachBoxesFn().length).toBe(4));
   test.do(() => expect(uiRepeatSectionsFn().length).toBe(4));
-  test.do(() => resetFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => forEachBoxesFn() && forEachBoxesFn().length === 3
-      && uiRepeatSectionsFn() && uiRepeatSectionsFn().length === 3);
+  test.event("click", resetFn, () => forEachBoxesFn().length === 3 && uiRepeatSectionsFn().length === 3);
   test.do(() => expect(forEachBoxesFn().length).toBe(3));
   test.do(() => expect(uiRepeatSectionsFn().length).toBe(3));
   test.start();
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/53-collapsible/00-collapsible-box/Collapsible_Box.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/53-collapsible/00-collapsible-box/Collapsible_Box.test.js
index 47a7864..c29fc9d 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/53-collapsible/00-collapsible-box/Collapsible_Box.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/53-collapsible/00-collapsible-box/Collapsible_Box.test.js
@@ -26,7 +26,7 @@ it("Simple Collapsible Box: show -> hide transition", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => contentFn(),
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", showFn);
   test.do(() => expect(contentFn() !== null).toBe(true));
   test.event("click", hideFn, () => !contentFn());
   test.do(() => expect(contentFn() !== null).toBe(false));
@@ -41,7 +41,7 @@ it("Simple Collapsible Box: hide -> show transition", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => !contentFn(),
-      () => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", hideFn);
   test.do(() => expect(contentFn() !== null).toBe(false));
   test.event("click", showFn, () => contentFn());
   test.do(() => expect(contentFn() !== null).toBe(true));
@@ -59,10 +59,10 @@ it("Full Server Request: open both boxes", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => !content1Fn(),
-      () => hide1Fn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", hide1Fn);
   test.setup(
       () => !content2Fn(),
-      () => hide2Fn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", hide2Fn);
   test.do(() => expect(content1Fn() !== null).toBe(false));
   test.do(() => expect(content2Fn() !== null).toBe(false));
   test.event("click", show1Fn, () => content1Fn());
@@ -85,10 +85,10 @@ it("Full Server Request: open box 1, close box 2", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => !content1Fn(),
-      () => hide1Fn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", hide1Fn);
   test.setup(
       () => content2Fn(),
-      () => show2Fn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", show2Fn);
   test.do(() => expect(content1Fn() !== null).toBe(false));
   test.do(() => expect(content2Fn() !== null).toBe(true));
   test.event("click", show1Fn, () => content1Fn());
@@ -111,10 +111,10 @@ it("Full Server Request: close box 1, open box 2", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => content1Fn(),
-      () => show1Fn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", show1Fn);
   test.setup(
       () => !content2Fn(),
-      () => hide2Fn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", hide2Fn);
   test.do(() => expect(content1Fn() !== null).toBe(true));
   test.do(() => expect(content2Fn() !== null).toBe(false));
   test.event("click", hide1Fn, () => !content1Fn());
@@ -137,10 +137,10 @@ it("Full Server Request: close both boxes", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => content1Fn(),
-      () => show1Fn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", show1Fn);
   test.setup(
       () => content2Fn(),
-      () => show2Fn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", show2Fn);
   test.do(() => expect(content1Fn() !== null).toBe(true));
   test.do(() => expect(content2Fn() !== null).toBe(true));
   test.event("click", hide1Fn, () => !content1Fn());
@@ -158,9 +158,10 @@ it("Client Side: show -> hide transition", function (done) {
   let boxFn = querySelectorFn("#page\\:mainForm\\:client\\:noRequestBox");
 
   let test = new JasmineTestTool(done);
-  test.do(() => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(() => !boxFn().classList.contains("tobago-collapsed"),
+      null, "click", showFn);
   test.do(() => expect(boxFn().classList.contains("tobago-collapsed")).toBe(false));
-  test.do(() => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.event("click", hideFn, () => boxFn().classList.contains("tobago-collapsed"));
   test.do(() => expect(boxFn().classList.contains("tobago-collapsed")).toBe(true));
   test.start();
 });
@@ -171,28 +172,29 @@ it("Client Side: hide -> show transition", function (done) {
   let boxFn = querySelectorFn("#page\\:mainForm\\:client\\:noRequestBox");
 
   let test = new JasmineTestTool(done);
-  test.do(() => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(() => boxFn().classList.contains("tobago-collapsed"),
+      null, "click", hideFn);
   test.do(() => expect(boxFn().classList.contains("tobago-collapsed")).toBe(true));
-  test.do(() => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.event("click", showFn, () => !boxFn().classList.contains("tobago-collapsed"));
   test.do(() => expect(boxFn().classList.contains("tobago-collapsed")).toBe(false));
   test.start();
 });
 
 it("Client Side: hide content and submit empty string", function (done) {
   let messagesFn = querySelectorAllFn("#page\\:messages.tobago-messages div");
+  let showFn = querySelectorFn("#page\\:mainForm\\:client\\:showNoRequestBox");
   let hideFn = querySelectorFn("#page\\:mainForm\\:client\\:hideNoRequestBox");
   let boxFn = querySelectorFn("#page\\:mainForm\\:client\\:noRequestBox");
   let inFn = querySelectorFn("#page\\:mainForm\\:client\\:inNoRequestBox\\:\\:field");
   let submitFn = querySelectorFn("#page\\:mainForm\\:client\\:submitNoRequestBox");
 
   let test = new JasmineTestTool(done);
-  test.setup(
-      () => messagesFn() && messagesFn().length === 0,
-      () => {
-        inFn().value = "some content";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}));
-      });
-  test.do(() => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(() => messagesFn() && messagesFn().length === 0,
+      () => inFn().value = "some content",
+      "click", submitFn);
+  test.setup(() => !boxFn().classList.contains("tobago-collapsed"),
+      null, "click", showFn);
+  test.event("click", hideFn, () => boxFn().classList.contains("tobago-collapsed"));
   test.do(() => expect(boxFn().classList.contains("tobago-collapsed")).toBe(true));
   test.do(() => inFn().value = "");
   test.event("click", submitFn, () => messagesFn() && messagesFn().length === 1);
@@ -208,7 +210,7 @@ it("Ajax: show -> hide transition", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => inFn(),
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", showFn);
   test.do(() => expect(inFn() !== null).toBe(true));
   test.event("click", hideFn, () => !inFn());
   test.do(() => expect(inFn() !== null).toBe(false));
@@ -223,7 +225,7 @@ it("Ajax: hide -> show transition", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => !inFn(),
-      () => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", hideFn);
   test.do(() => expect(inFn() !== null).toBe(false));
   test.event("click", showFn, () => inFn());
   test.do(() => expect(inFn() !== null).toBe(true));
@@ -241,10 +243,10 @@ it("Ajax: submit empty string with shown and hidden content", function (done) {
   let test = new JasmineTestTool(done);
   test.setup(
       () => inFn(),
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", showFn);
   test.setup(
       () => messagesFn() && messagesFn().length === 0,
-      () => hide1FullReqFn().dispatchEvent(new Event("click", {bubbles: true})));
+      null, "click", hide1FullReqFn);
   test.do(() => expect(inFn() !== null).toBe(true));
   test.do(() => inFn().value = "");
   test.event("click", submitFn, () => messagesFn() && messagesFn().length === 1);
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/53-collapsible/20-collapsible-panel/Collapsible_Panel.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/53-collapsible/20-collapsible-panel/Collapsible_Panel.test.js
index f29c4f1..619859a 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/53-collapsible/20-collapsible-panel/Collapsible_Panel.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/53-collapsible/20-collapsible-panel/Collapsible_Panel.test.js
@@ -26,8 +26,9 @@ it("Simple Panel: show -> hide transition", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:simple\\:inSimple\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => panelCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.event("click", hideFn, () => panelCollapsedFn().value === "true" && inFn() === null);
@@ -45,8 +46,9 @@ it("Simple Panel: hide -> show transition", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:simple\\:inSimple\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "true" && inFn() === null,
-      () => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => panelCollapsedFn().value === "true" && inFn() === null,
+      null, "click", hideFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
   test.event("click", showFn, () => panelCollapsedFn().value === "false" && inFn() !== null);
@@ -64,13 +66,13 @@ it("Simple Panel: collapsed = false; submit valid input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:simple\\:inSimple\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length > 0,
-      () => {
-        inFn().value = "";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
+  test.setup(
+      () => panelCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
+  test.setup(
+      () => messagesFn().length > 0,
+      () => inFn().value = "",
+      "click", submitFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Alice");
@@ -87,13 +89,13 @@ it("Simple Panel: collapsed = false; submit empty input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:simple\\:inSimple\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length === 0,
-      () => {
-        inFn().value = "Bob";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
+  test.setup(
+      () => panelCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
+  test.setup(
+      () => messagesFn().length === 0,
+      () => inFn().value = "Bob",
+      "click", submitFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => expect(messagesFn().length).toBe(0));
@@ -114,18 +116,19 @@ it("Simple Panel: valid input; show -> hide transition; submit", function (done)
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => panelCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Charlie");
   test.event("click", hideFn, () => panelCollapsedFn().value === "true" && inFn() === null);
   test.do(() => expect(panelCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
-  test.setup(() => messagesFn().length > 0, () => {
-    clientInFn().value = "";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => messagesFn().length > 0,
+      () => clientInFn().value = "",
+      "click", clientSubmitFn);
   test.event("click", submitFn, () => messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
@@ -142,18 +145,19 @@ it("Simple Panel: empty input; show -> hide transition; submit", function (done)
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => panelCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "");
   test.event("click", hideFn, () => panelCollapsedFn().value === "true" && inFn() === null);
   test.do(() => expect(panelCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
-  test.setup(() => messagesFn().length > 0, () => {
-    clientInFn().value = "";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => messagesFn().length > 0,
+      () => clientInFn().value = "",
+      "click", clientSubmitFn);
   test.event("click", submitFn, () => messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
@@ -167,8 +171,9 @@ it("Full Server Request: show -> hide transition", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:server\\:inServer\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => panelCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.event("click", hideFn, () => panelCollapsedFn().value === "true" && inFn() === null);
@@ -186,8 +191,9 @@ it("Full Server Request: hide -> show transition", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:server\\:inServer\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "true" && inFn() === null,
-      () => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => panelCollapsedFn().value === "true" && inFn() === null,
+      null, "click", hideFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
   test.event("click", showFn, () => panelCollapsedFn().value === "false" && inFn() !== null);
@@ -205,13 +211,13 @@ it("Full Server Request: collapsed = false; submit valid input", function (done)
   let inFn = querySelectorFn("#page\\:mainForm\\:server\\:inServer\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length > 0,
-      () => {
-        inFn().value = "";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
+  test.setup(
+      () => panelCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
+  test.setup(
+      () => messagesFn().length > 0,
+      () => inFn().value = "",
+      "click", submitFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Alice");
@@ -228,13 +234,13 @@ it("Full Server Request: collapsed = false; submit empty input", function (done)
   let inFn = querySelectorFn("#page\\:mainForm\\:server\\:inServer\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length === 0,
-      () => {
-        inFn().value = "Bob";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
+  test.setup(
+      () => panelCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
+  test.setup(
+      () => messagesFn().length === 0,
+      () => inFn().value = "Bob",
+      "click", submitFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => expect(messagesFn().length).toBe(0));
@@ -255,18 +261,19 @@ it("Full Server Request: valid input; show -> hide transition; submit", function
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => panelCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Charlie");
   test.event("click", hideFn, () => panelCollapsedFn().value === "true" && inFn() === null);
   test.do(() => expect(panelCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
-  test.setup(() => messagesFn().length > 0, () => {
-    clientInFn().value = "";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => messagesFn().length > 0,
+      () => clientInFn().value = "",
+      "click", clientSubmitFn);
   test.event("click", submitFn, () => messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
@@ -283,18 +290,19 @@ it("Full Server Request: empty input; show -> hide transition; submit", function
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => panelCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "");
   test.event("click", hideFn, () => panelCollapsedFn().value === "true" && inFn() === null);
   test.do(() => expect(panelCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
-  test.setup(() => messagesFn().length > 0, () => {
-    clientInFn().value = "";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => messagesFn().length > 0,
+      () => clientInFn().value = "",
+      "click", clientSubmitFn);
   test.event("click", submitFn, () => messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
@@ -309,13 +317,13 @@ it("Client Sided: show -> hide transition", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => messagesFn().length === 0,
-      () => {
-        inFn().value = "Alice";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
-  test.setup(() => panelCollapsedFn().value === "false",
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => messagesFn().length === 0,
+      () => inFn().value = "Alice",
+      "click", submitFn);
+  test.setup(
+      () => panelCollapsedFn().value === "false",
+      null, "click", showFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.event("click", hideFn, () => panelCollapsedFn().value === "true");
@@ -334,13 +342,13 @@ it("Client Sided: hide -> show transition", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => messagesFn().length === 0,
-      () => {
-        inFn().value = "Bob";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
-  test.setup(() => panelCollapsedFn().value === "true",
-      () => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => messagesFn().length === 0,
+      () => inFn().value = "Bob",
+      "click", submitFn);
+  test.setup(
+      () => panelCollapsedFn().value === "true",
+      null, "click", hideFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.event("click", showFn, () => panelCollapsedFn().value === "false");
@@ -358,13 +366,13 @@ it("Client Sided: collapsed = false; submit valid input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => messagesFn().length > 0,
-      () => {
-        inFn().value = "";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
-  test.setup(() => panelCollapsedFn().value === "false",
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => messagesFn().length > 0,
+      () => inFn().value = "",
+      "click", submitFn);
+  test.setup(
+      () => panelCollapsedFn().value === "false",
+      null, "click", showFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Charlie");
@@ -381,13 +389,13 @@ it("Client Sided: collapsed = false; submit empty input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => messagesFn().length === 0,
-      () => {
-        inFn().value = "Bob";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
-  test.setup(() => panelCollapsedFn().value === "false",
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => messagesFn().length === 0,
+      () => inFn().value = "Bob",
+      "click", submitFn);
+  test.setup(
+      () => panelCollapsedFn().value === "false",
+      null, "click", showFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => expect(messagesFn().length).toBe(0));
@@ -405,13 +413,13 @@ it("Client Sided: collapsed = true; submit valid input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => messagesFn().length > 0,
-      () => {
-        inFn().value = "";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
-  test.setup(() => panelCollapsedFn().value === "true",
-      () => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => messagesFn().length > 0,
+      () => inFn().value = "",
+      "click", submitFn);
+  test.setup(
+      () => panelCollapsedFn().value === "true",
+      null, "click", hideFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Dave");
@@ -428,13 +436,13 @@ it("Client Sided: collapsed = true; submit empty input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => messagesFn().length === 0,
-      () => {
-        inFn().value = "Eve";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
-  test.setup(() => panelCollapsedFn().value === "true",
-      () => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => messagesFn().length === 0,
+      () => inFn().value = "Eve",
+      "click", submitFn);
+  test.setup(
+      () => panelCollapsedFn().value === "true",
+      null, "click", hideFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => expect(messagesFn().length).toBe(0));
@@ -454,12 +462,13 @@ it("Ajax: show -> hide transition", function (done) {
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length === 0, () => {
-    clientInFn().value = "Alice";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => panelCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
+  test.setup(
+      () => messagesFn().length === 0,
+      () => clientInFn().value = "Alice",
+      "click", clientSubmitFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.event("click", hideFn, () => panelCollapsedFn().value === "true" && inFn() === null);
@@ -479,12 +488,13 @@ it("Ajax: hide -> show transition", function (done) {
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "true" && inFn() === null,
-      () => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length === 0, () => {
-    clientInFn().value = "Bob";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => panelCollapsedFn().value === "true" && inFn() === null,
+      null, "click", hideFn);
+  test.setup(
+      () => messagesFn().length === 0,
+      () => clientInFn().value = "Bob",
+      "click", clientSubmitFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
   test.event("click", showFn, () => panelCollapsedFn().value === "false" && inFn() !== null);
@@ -502,13 +512,13 @@ it("Ajax: collapsed = false; submit valid input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:ajax\\:inAjax\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length > 0,
-      () => {
-        inFn().value = "";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
+  test.setup(
+      () => panelCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
+  test.setup(
+      () => messagesFn().length > 0,
+      () => inFn().value = "",
+      "click", submitFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Alice");
@@ -525,13 +535,13 @@ it("Ajax: collapsed = false; submit empty input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:ajax\\:inAjax\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length === 0,
-      () => {
-        inFn().value = "Bob";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
+  test.setup(
+      () => panelCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
+  test.setup(
+      () => messagesFn().length === 0,
+      () => inFn().value = "Bob",
+      "click", submitFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => expect(messagesFn().length).toBe(0));
@@ -552,18 +562,19 @@ it("Ajax: valid input; show -> hide transition; submit", function (done) {
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => panelCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Charlie");
   test.event("click", hideFn, () => panelCollapsedFn().value === "true" && inFn() === null);
   test.do(() => expect(panelCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
-  test.setup(() => messagesFn().length > 0, () => {
-    clientInFn().value = "";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => messagesFn().length > 0,
+      () => clientInFn().value = "",
+      "click", clientSubmitFn);
   test.event("click", submitFn, () => messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
@@ -580,18 +591,19 @@ it("Ajax: empty input; show -> hide transition; submit", function (done) {
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => panelCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => panelCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(panelCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "");
   test.event("click", hideFn, () => panelCollapsedFn().value === "true" && inFn() === null);
   test.do(() => expect(panelCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
-  test.setup(() => messagesFn().length > 0, () => {
-    clientInFn().value = "";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => messagesFn().length > 0,
+      () => clientInFn().value = "",
+      "click", clientSubmitFn);
   test.event("click", submitFn, () => messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/53-collapsible/30-collapsible-section/Collapsible_Section.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/53-collapsible/30-collapsible-section/Collapsible_Section.test.js
index cbfaac7..c6fc592 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/53-collapsible/30-collapsible-section/Collapsible_Section.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/53-collapsible/30-collapsible-section/Collapsible_Section.test.js
@@ -26,8 +26,9 @@ it("Simple Section: show -> hide transition", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:simple\\:inSimple\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => sectionCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.event("click", hideFn, () => sectionCollapsedFn().value === "true" && inFn() === null);
@@ -45,8 +46,9 @@ it("Simple Section: hide -> show transition", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:simple\\:inSimple\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "true" && inFn() === null,
-      () => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => sectionCollapsedFn().value === "true" && inFn() === null,
+      null, "click", hideFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
   test.event("click", showFn, () => sectionCollapsedFn().value === "false" && inFn() !== null);
@@ -64,13 +66,13 @@ it("Simple Section: collapsed = false; submit valid input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:simple\\:inSimple\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length > 0,
-      () => {
-        inFn().value = "";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
+  test.setup(
+      () => sectionCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
+  test.setup(
+      () => messagesFn().length > 0,
+      () => inFn().value = "",
+      "click", submitFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Alice");
@@ -87,13 +89,13 @@ it("Simple Section: collapsed = false; submit empty input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:simple\\:inSimple\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length === 0,
-      () => {
-        inFn().value = "Bob";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
+  test.setup(
+      () => sectionCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
+  test.setup(
+      () => messagesFn().length === 0,
+      () => inFn().value = "Bob",
+      "click", submitFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => expect(messagesFn().length).toBe(0));
@@ -114,18 +116,19 @@ it("Simple Section: valid input; show -> hide transition; submit", function (don
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => sectionCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Charlie");
   test.event("click", hideFn, () => sectionCollapsedFn().value === "true" && inFn() === null);
   test.do(() => expect(sectionCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
-  test.setup(() => messagesFn().length > 0, () => {
-    clientInFn().value = "";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => messagesFn().length > 0,
+      () => clientInFn().value = "",
+      "click", clientSubmitFn);
   test.event("click", submitFn, () => messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
@@ -142,18 +145,19 @@ it("Simple Section: empty input; show -> hide transition; submit", function (don
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => sectionCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "");
   test.event("click", hideFn, () => sectionCollapsedFn().value === "true" && inFn() === null);
   test.do(() => expect(sectionCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
-  test.setup(() => messagesFn().length > 0, () => {
-    clientInFn().value = "";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => messagesFn().length > 0,
+      () => clientInFn().value = "",
+      "click", clientSubmitFn);
   test.event("click", submitFn, () => messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
@@ -167,8 +171,9 @@ it("Full Server Request: show -> hide transition", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:server\\:inServer\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => sectionCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.event("click", hideFn, () => sectionCollapsedFn().value === "true" && inFn() === null);
@@ -186,8 +191,9 @@ it("Full Server Request: hide -> show transition", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:server\\:inServer\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "true" && inFn() === null,
-      () => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => sectionCollapsedFn().value === "true" && inFn() === null,
+      null, "click", hideFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
   test.event("click", showFn, () => sectionCollapsedFn().value === "false" && inFn() !== null);
@@ -205,13 +211,13 @@ it("Full Server Request: collapsed = false; submit valid input", function (done)
   let inFn = querySelectorFn("#page\\:mainForm\\:server\\:inServer\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length > 0,
-      () => {
-        inFn().value = "";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
+  test.setup(
+      () => sectionCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
+  test.setup(
+      () => messagesFn().length > 0,
+      () => inFn().value = "",
+      "click", submitFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Alice");
@@ -228,13 +234,13 @@ it("Full Server Request: collapsed = false; submit empty input", function (done)
   let inFn = querySelectorFn("#page\\:mainForm\\:server\\:inServer\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length === 0,
-      () => {
-        inFn().value = "Bob";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
+  test.setup(
+      () => sectionCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
+  test.setup(
+      () => messagesFn().length === 0,
+      () => inFn().value = "Bob",
+      "click", submitFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => expect(messagesFn().length).toBe(0));
@@ -255,18 +261,19 @@ it("Full Server Request: valid input; show -> hide transition; submit", function
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => sectionCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Charlie");
   test.event("click", hideFn, () => sectionCollapsedFn().value === "true" && inFn() === null);
   test.do(() => expect(sectionCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
-  test.setup(() => messagesFn().length > 0, () => {
-    clientInFn().value = "";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => messagesFn().length > 0,
+      () => clientInFn().value = "",
+      "click", clientSubmitFn);
   test.event("click", submitFn, () => messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
@@ -283,18 +290,19 @@ it("Full Server Request: empty input; show -> hide transition; submit", function
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => sectionCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "");
   test.event("click", hideFn, () => sectionCollapsedFn().value === "true" && inFn() === null);
   test.do(() => expect(sectionCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
-  test.setup(() => messagesFn().length > 0, () => {
-    clientInFn().value = "";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => messagesFn().length > 0,
+      () => clientInFn().value = "",
+      "click", clientSubmitFn);
   test.event("click", submitFn, () => messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
@@ -309,13 +317,13 @@ it("Client Sided: show -> hide transition", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => messagesFn().length === 0,
-      () => {
-        inFn().value = "Alice";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
-  test.setup(() => sectionCollapsedFn().value === "false",
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => messagesFn().length === 0,
+      () => inFn().value = "Alice",
+      "click", submitFn);
+  test.setup(
+      () => sectionCollapsedFn().value === "false",
+      null, "click", showFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.event("click", hideFn, () => sectionCollapsedFn().value === "true");
@@ -334,13 +342,13 @@ it("Client Sided: hide -> show transition", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => messagesFn().length === 0,
-      () => {
-        inFn().value = "Bob";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
-  test.setup(() => sectionCollapsedFn().value === "true",
-      () => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => messagesFn().length === 0,
+      () => inFn().value = "Bob",
+      "click", submitFn);
+  test.setup(
+      () => sectionCollapsedFn().value === "true",
+      null, "click", hideFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.event("click", showFn, () => sectionCollapsedFn().value === "false");
@@ -358,13 +366,13 @@ it("Client Sided: collapsed = false; submit valid input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => messagesFn().length > 0,
-      () => {
-        inFn().value = "";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
-  test.setup(() => sectionCollapsedFn().value === "false",
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => messagesFn().length > 0,
+      () => inFn().value = "",
+      "click", submitFn);
+  test.setup(
+      () => sectionCollapsedFn().value === "false",
+      null, "click", showFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Charlie");
@@ -381,13 +389,13 @@ it("Client Sided: collapsed = false; submit empty input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => messagesFn().length === 0,
-      () => {
-        inFn().value = "Bob";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
-  test.setup(() => sectionCollapsedFn().value === "false",
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => messagesFn().length === 0,
+      () => inFn().value = "Bob",
+      "click", submitFn);
+  test.setup(
+      () => sectionCollapsedFn().value === "false",
+      null, "click", showFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => expect(messagesFn().length).toBe(0));
@@ -405,13 +413,13 @@ it("Client Sided: collapsed = true; submit valid input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => messagesFn().length > 0,
-      () => {
-        inFn().value = "";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
-  test.setup(() => sectionCollapsedFn().value === "true",
-      () => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => messagesFn().length > 0,
+      () => inFn().value = "",
+      "click", submitFn);
+  test.setup(
+      () => sectionCollapsedFn().value === "true",
+      null, "click", hideFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Dave");
@@ -428,13 +436,13 @@ it("Client Sided: collapsed = true; submit empty input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => messagesFn().length === 0,
-      () => {
-        inFn().value = "Eve";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
-  test.setup(() => sectionCollapsedFn().value === "true",
-      () => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => messagesFn().length === 0,
+      () => inFn().value = "Eve",
+      "click", submitFn);
+  test.setup(
+      () => sectionCollapsedFn().value === "true",
+      null, "click", hideFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => expect(messagesFn().length).toBe(0));
@@ -454,12 +462,13 @@ it("Ajax: show -> hide transition", function (done) {
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length === 0, () => {
-    clientInFn().value = "Alice";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => sectionCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
+  test.setup(
+      () => messagesFn().length === 0,
+      () => clientInFn().value = "Alice",
+      "click", clientSubmitFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.event("click", hideFn, () => sectionCollapsedFn().value === "true" && inFn() === null);
@@ -479,12 +488,13 @@ it("Ajax: hide -> show transition", function (done) {
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "true" && inFn() === null,
-      () => hideFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length === 0, () => {
-    clientInFn().value = "Bob";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => sectionCollapsedFn().value === "true" && inFn() === null,
+      null, "click", hideFn);
+  test.setup(
+      () => messagesFn().length === 0,
+      () => clientInFn().value = "Bob",
+      "click", clientSubmitFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
   test.event("click", showFn, () => sectionCollapsedFn().value === "false" && inFn() !== null);
@@ -502,13 +512,13 @@ it("Ajax: collapsed = false; submit valid input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:ajax\\:inAjax\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length > 0,
-      () => {
-        inFn().value = "";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
+  test.setup(
+      () => sectionCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
+  test.setup(
+      () => messagesFn().length > 0,
+      () => inFn().value = "",
+      "click", submitFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Alice");
@@ -525,13 +535,13 @@ it("Ajax: collapsed = false; submit empty input", function (done) {
   let inFn = querySelectorFn("#page\\:mainForm\\:ajax\\:inAjax\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.setup(() => messagesFn().length === 0,
-      () => {
-        inFn().value = "Bob";
-        submitFn().dispatchEvent(new Event("click", {bubbles: true}))
-      });
+  test.setup(
+      () => sectionCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
+  test.setup(
+      () => messagesFn().length === 0,
+      () => inFn().value = "Bob",
+      "click", submitFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => expect(messagesFn().length).toBe(0));
@@ -552,18 +562,19 @@ it("Ajax: valid input; show -> hide transition; submit", function (done) {
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => sectionCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "Charlie");
   test.event("click", hideFn, () => sectionCollapsedFn().value === "true" && inFn() === null);
   test.do(() => expect(sectionCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
-  test.setup(() => messagesFn().length > 0, () => {
-    clientInFn().value = "";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => messagesFn().length > 0,
+      () => clientInFn().value = "",
+      "click", clientSubmitFn);
   test.event("click", submitFn, () => messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
@@ -580,18 +591,19 @@ it("Ajax: empty input; show -> hide transition; submit", function (done) {
   let clientInFn = querySelectorFn("#page\\:mainForm\\:client\\:inClient\\:\\:field");
 
   let test = new JasmineTestTool(done);
-  test.setup(() => sectionCollapsedFn().value === "false" && inFn() !== null,
-      () => showFn().dispatchEvent(new Event("click", {bubbles: true})));
+  test.setup(
+      () => sectionCollapsedFn().value === "false" && inFn() !== null,
+      null, "click", showFn);
   test.do(() => expect(sectionCollapsedFn().value).toBe("false"));
   test.do(() => expect(inFn()).not.toBeNull());
   test.do(() => inFn().value = "");
   test.event("click", hideFn, () => sectionCollapsedFn().value === "true" && inFn() === null);
   test.do(() => expect(sectionCollapsedFn().value).toBe("true"));
   test.do(() => expect(inFn()).toBeNull());
-  test.setup(() => messagesFn().length > 0, () => {
-    clientInFn().value = "";
-    clientSubmitFn().dispatchEvent(new Event("click", {bubbles: true}))
-  });
+  test.setup(
+      () => messagesFn().length > 0,
+      () => clientInFn().value = "",
+      "click", clientSubmitFn);
   test.event("click", submitFn, () => messagesFn().length === 0);
   test.do(() => expect(messagesFn().length).toBe(0));
   test.start();
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/40000-style/100-headings/Headings.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/40000-style/100-headings/Headings.test.js
index 4c57bc5..e82a7c1 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/40000-style/100-headings/Headings.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/40000-style/100-headings/Headings.test.js
@@ -75,13 +75,12 @@ function testFont(done, alinkFn, buttonlinkFn) {
 it("Ajax reload for section 2", function (done) {
   let reloadButtonFn = querySelectorFn("#page\\:mainForm\\:reloadSection2");
   let section2HeaderFn = querySelectorFn("#page\\:mainForm\\:levelTwoSection h3");
-  let timestampFn = querySelectorFn("#page\\:mainForm\\:timestamp span");
+  let timestampFn = querySelectorFn("#page\\:mainForm\\:timestamp tobago-out");
   let firstTimestamp = timestampFn().textContent;
 
   let test = new JasmineTestTool(done);
   test.do(() => expect(section2HeaderFn() !== null).toBe(true));
-  test.do(() => reloadButtonFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => reloadButtonFn() && section2HeaderFn() !== null);
+  test.event("click", reloadButtonFn, () => firstTimestamp < timestampFn().textContent);
   test.do(() => expect(section2HeaderFn() !== null).toBe(true));
   test.do(() => expect(firstTimestamp < timestampFn().textContent).toBe(true, "value of new timestamp must be higher"));
   test.start();
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/50000-java/30-ajax-special-character/Ajax_Special_Character.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/50000-java/30-ajax-special-character/Ajax_Special_Character.test.js
index b58a59d..0514db2 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/50000-java/30-ajax-special-character/Ajax_Special_Character.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/50000-java/30-ajax-special-character/Ajax_Special_Character.test.js
@@ -29,8 +29,7 @@ it("ajax execute", function (done) {
   let tipValue = tipFn().getAttribute('title');
 
   let test = new JasmineTestTool(done);
-  test.do(() => buttonFn().dispatchEvent(new Event("click", {bubbles: true})));
-  test.wait(() => timestampFn() && timestampFn().textContent !== timestampValue);
+  test.event("click", buttonFn, () => timestampFn() && timestampFn().textContent !== timestampValue);
   test.do(() => expect(timestampFn().textContent).not.toBe(timestampValue));
   test.do(() => expect(textFn().textContent).toBe(textValue));
   test.do(() => expect(tipFn().getAttribute('title')).toBe(tipValue));
diff --git a/tobago-tool/tobago-tool-test/src/main/resources/META-INF/resources/tobago/test/tobago-test-tool.js b/tobago-tool/tobago-tool-test/src/main/resources/META-INF/resources/tobago/test/tobago-test-tool.js
index 0fe3a2e..45c47b0 100644
--- a/tobago-tool/tobago-tool-test/src/main/resources/META-INF/resources/tobago/test/tobago-test-tool.js
+++ b/tobago-tool/tobago-tool-test/src/main/resources/META-INF/resources/tobago/test/tobago-test-tool.js
@@ -15,18 +15,6 @@
  * limitations under the License.
  */
 
-function TobagoTestTool(assert) {
-  this.assert = assert;
-  this.steps = [];
-}
-
-TobagoTestTool.stepType = {
-  ACTION: 1,
-  WAIT_RESPONSE: 2,
-  WAIT_MS: 3,
-  ASSERTS: 4
-};
-
 class JasmineUtils {
   static isMsie() {
     return navigator.userAgent.indexOf("MSIE") > -1 || navigator.userAgent.indexOf("Trident") > -1;
@@ -69,167 +57,6 @@ class JasmineUtils {
   };
 }
 
-TobagoTestTool.prototype = {
-  action: function (func) {
-    this.steps.push({
-      type: TobagoTestTool.stepType.ACTION,
-      func: func
-    });
-  },
-  waitForResponse: function () {
-    this.steps.push({
-      type: TobagoTestTool.stepType.WAIT_RESPONSE
-    });
-  },
-  waitMs: function (ms) {
-    this.steps.push({
-      type: TobagoTestTool.stepType.WAIT_MS,
-      ms: ms ? ms : 0
-    });
-  },
-  asserts: function (numOfAssertions, func) {
-    this.steps.push({
-      type: TobagoTestTool.stepType.ASSERTS,
-      numOfAssertions: numOfAssertions ? numOfAssertions : 0,
-      func: func
-    });
-  },
-  startTest: function () {
-    const steps = this.steps.slice(0);
-    const cycleTiming = 50;
-    let currentStep = 0;
-    let testStepTimeout;
-
-    function getAssertExpect() {
-      var expect = 0;
-      steps.forEach(function (step) {
-        if (step.type === TobagoTestTool.stepType.ASSERTS) {
-          expect += step.numOfAssertions;
-        }
-      });
-      return expect;
-    }
-
-    function getAssertAsync() {
-      var async = 0;
-      steps.forEach(function (step) {
-        if (step.type === TobagoTestTool.stepType.ASSERTS) {
-          async++;
-        }
-      });
-      return async;
-    }
-
-    this.assert.expect(getAssertExpect());
-    const done = this.assert.async(getAssertAsync());
-    const assert = this.assert;
-
-    function resetTestStepTimeout(additionalMs) {
-      const timeout = additionalMs ? 20000 + additionalMs : 20000;
-      testStepTimeout = Date.now() + timeout;
-    }
-
-    let waitForResponse = false;
-    let ajaxRequestDetected = false;
-    let ajaxRequestDone = false;
-    let fullPageReloadDetected = false;
-    let fullPageReloadDone = false;
-
-    function registerAjaxReadyStateListener() {
-      let oldXHR = document.getElementById("page:testframe").contentWindow.XMLHttpRequest;
-
-      function newXHR() {
-        let realXHR = new oldXHR();
-        realXHR.addEventListener("readystatechange", function () {
-          if (realXHR.readyState !== XMLHttpRequest.UNSENT && realXHR.readyState !== XMLHttpRequest.DONE) {
-            ajaxRequestDetected = true;
-          } else if (ajaxRequestDetected && realXHR.readyState === XMLHttpRequest.DONE) {
-            ajaxRequestDone = true;
-            waitForResponse = false;
-          }
-        }, false);
-        return realXHR;
-      }
-
-      document.getElementById("page:testframe").contentWindow.XMLHttpRequest = newXHR;
-    }
-
-    function fullPageReloadPolling() {
-      const testframe = document.getElementById("page:testframe");
-      if (testframe === null
-          || testframe.contentWindow.document.readyState !== "complete"
-          || testframe.contentWindow.document.querySelector("html") === null) {
-        fullPageReloadDetected = true;
-      } else if (fullPageReloadDetected) {
-        fullPageReloadDone = true;
-        waitForResponse = false;
-      }
-
-      if (!fullPageReloadDone && !ajaxRequestDone) {
-        setTimeout(fullPageReloadPolling, cycleTiming);
-      }
-    }
-
-    function cycle() {
-      if (currentStep >= steps.length) {
-        // we are done here
-      } else if (Date.now() >= testStepTimeout) {
-        assert.ok(false, "Timeout!");
-        if (steps[currentStep].stepType === TobagoTestTool.stepType.ASSERTS) {
-          done();
-        }
-        currentStep++;
-        cycle();
-      } else if (waitForResponse) {
-        // we need to wait more
-        setTimeout(cycle, cycleTiming);
-      } else if (steps[currentStep].type === TobagoTestTool.stepType.ACTION) {
-        if (currentStep + 1 < steps.length && steps[currentStep + 1].type === TobagoTestTool.stepType.WAIT_RESPONSE) {
-          // register listener for ajax before action is executed, otherwise the ajax listener is registered too late
-          registerAjaxReadyStateListener();
-          steps[currentStep].func();
-          currentStep++;
-          cycle();
-        } else {
-          steps[currentStep].func();
-          currentStep++;
-          resetTestStepTimeout();
-          setTimeout(cycle, cycleTiming);
-        }
-      } else if (steps[currentStep].type === TobagoTestTool.stepType.WAIT_RESPONSE) {
-        waitForResponse = true;
-        ajaxRequestDetected = false;
-        ajaxRequestDone = false;
-        fullPageReloadDetected = false;
-        fullPageReloadDone = false;
-        registerAjaxReadyStateListener();
-        fullPageReloadPolling();
-
-        currentStep++;
-        resetTestStepTimeout();
-        setTimeout(cycle, cycleTiming);
-      } else if (steps[currentStep].type === TobagoTestTool.stepType.WAIT_MS) {
-        const ms = steps[currentStep].ms;
-
-        currentStep++;
-        resetTestStepTimeout(ms);
-        setTimeout(cycle, ms);
-      } else if (steps[currentStep].type === TobagoTestTool.stepType.ASSERTS) {
-        steps[currentStep].func();
-        currentStep++;
-        done();
-        resetTestStepTimeout();
-        setTimeout(cycle, cycleTiming);
-      }
-    }
-
-    resetTestStepTimeout();
-    cycle();
-  }
-};
-
-export {TobagoTestTool};
-
 class JasmineTestTool {
 
   static ajaxReadyStateChangeEvent = "tobago.jtt.ajax.readyStateChange";
@@ -237,7 +64,6 @@ class JasmineTestTool {
   steps = [];
   done;
   timeout;
-  lastStepExecution;
 
   /**
    * @param done function from Jasmine; must called if all Steps done or timeout
@@ -246,53 +72,61 @@ class JasmineTestTool {
   constructor(done, timeout) {
     this.done = done;
     this.timeout = timeout ? timeout : 20000;
-    this.registerAjaxReadyStateListener();
   }
 
   /**
-   * The require function (require) defines the desired state before the main test starts. The fix function (fix)
-   * will be executed if the require function returns false.
-   * @param require function
-   * @param fix function
+   * Setup a test.
+   * @param startConditionFn the desired start condition
+   * @param doFn may be null; if start condition is not fulfilled, doFn is executed
+   * @param eventType if start condition is not fulfilled, an event is dispatched
+   * @param eventElement if start condition is not fulfilled, an event is dispatched
    */
-  setup(require, fix) {
-    this.do(() => {
-      if (!require()) {
-        console.debug("[JasmineTestTool] require() returns false, execute fix()");
-        fix();
-      }
-    })
-    this.wait(require);
+  setup(startConditionFn, doFn, eventType, eventElement) {
+    let eventFn;
+    if (typeof eventElement !== "function") {
+      eventFn = () => fail("'eventElement' must be a function but was: " + eventElement);
+    } else {
+      eventFn = () => eventElement().dispatchEvent(new Event(eventType, {bubbles: true}));
+    }
+
+    this.steps.push({
+      type: "setup",
+      startConditionFunc: startConditionFn,
+      doFunc: doFn,
+      eventFunc: eventFn,
+      substep: 4
+    });
   }
 
   /**
-   * Execute dispatchEvent() on the given element. The result function must return false before dispatch.
-   * After the dispatch
-   * @param type of the event
-   * @param element function
-   * @param result function
+   * Execute dispatchEvent() on the given element.
+   * @param type of the event, e.g. 'click' or 'change'
+   * @param element on which the event is dispatched, like a querySelector for a button element
+   * @param result function to indicate if the event dispatched correctly. Must be return 'false' before the event is
+   * dispatched. Must be return 'true' after the event is dispatched.
    */
   event(type, element, result) {
-    if (typeof element === "function") {
-      this.do(() => {
-        if (result()) {
-          fail("The result function (" + result + ") returns true BEFORE the '" + type + "' event is dispatched."
-              + " Please define a result function that return false before dispatch event and return true after" +
-              " dispatch event.");
-        }
-      });
-      this.do(() => element().dispatchEvent(new Event(type, {bubbles: true})));
-      this.wait(result);
+    let eventFn;
+    if (typeof element !== "function") {
+      eventFn = () => fail("'element' must be a function but was: " + element);
     } else {
-      fail("event(type, element, result); 'element' must be a function but was: " + element);
+      eventFn = () => element().dispatchEvent(new Event(type, {bubbles: true}));
     }
+
+    this.steps.push({
+      type: "event",
+      eventType: type,
+      eventFunc: eventFn,
+      resultFunc: result,
+      substep: 3
+    });
   }
 
   do(fn) {
     this.steps.push({
       type: "do",
       func: fn,
-      done: false
+      substep: 1
     });
   }
 
@@ -300,108 +134,166 @@ class JasmineTestTool {
     this.steps.push({
       type: "wait",
       func: fn,
-      done: false
+      substep: 1
     });
   }
 
   start() {
+    const steps = this.steps;
+    let done = this.done;
+    const timeout = this.timeout;
+    let lastStepExecution;
+
     console.debug("[JasmineTestTool] start");
-    this.resetTimeout();
-    this.cycle();
-  }
+    registerAjaxReadyStateListener();
+    resetTimeout();
+    cycle();
 
-  cycle() {
-    const nextStep = this.getNextStep();
+    function cycle() {
+      const nextStep = getNextStep();
 
-    if (this.isFinished()) {
-      this.done();
-    } else if (this.isTimeout()) {
-      fail("Timeout of '" + nextStep.type + "'-step: " + nextStep.func);
-      nextStep.done = true;
-      this.resetTimeout();
-      window.setTimeout(this.cycle.bind(this), 0);
-    } else if (!this.isDocumentReady() || !this.isAjaxReady()) {
-      console.debug("[JasmineTestTool] documentReady: " + this.isDocumentReady()
-          + " - ajaxReady: " + this.isAjaxReady());
-      window.setTimeout(this.cycle.bind(this), 50);
-    } else if (nextStep.type === "do") {
-      console.debug("[JasmineTestTool] do-step: " + nextStep.func);
-      this.registerCustomXmlHttpRequest();
-      nextStep.func();
-      nextStep.done = true;
-      this.resetTimeout();
-      window.setTimeout(this.cycle.bind(this), 0);
-    } else if (nextStep.type === "wait") {
-      console.debug("[JasmineTestTool] wait-step: " + nextStep.func);
-      if (nextStep.func()) {
-        nextStep.done = true;
-        this.resetTimeout();
+      if (isFinished()) {
+        done();
+        console.debug("[JasmineTestTool] finished");
+      } else if (isTimeout()) {
+        fail("Timeout of " + JSON.stringify(nextStep));
+        nextStep.substep = 0;
+        resetTimeout();
+        window.setTimeout(cycle, 1);
+      } else if (!isDocumentReady() || !isAjaxReady()) {
+        console.debug("[JasmineTestTool] documentReady: " + isDocumentReady() + " - ajaxReady: " + isAjaxReady());
+        window.setTimeout(cycle, 50);
+      } else if (nextStep.type === "setup" && nextStep.substep === 4) {
+        console.debug("[JasmineTestTool] setup/4-step: " + nextStep.startConditionFunc);
+        if (nextStep.startConditionFunc()) {
+          nextStep.substep = 0;
+        } else {
+          nextStep.substep--;
+        }
+        resetTimeout();
+        window.setTimeout(cycle, 1);
+      } else if (nextStep.type === "setup" && nextStep.substep === 3) {
+        console.debug("[JasmineTestTool] setup/3-step: " + nextStep.doFunc);
+        if (nextStep.doFunc === null) {
+          nextStep.substep--;
+          resetTimeout();
+          window.setTimeout(cycle, 1);
+        } else {
+          execute(nextStep.doFunc, nextStep);
+        }
+      } else if (nextStep.type === "setup" && nextStep.substep === 2) {
+        console.debug("[JasmineTestTool] setup/2-step: " + nextStep.eventFunc);
+        execute(nextStep.eventFunc, nextStep);
+      } else if (nextStep.type === "setup" && nextStep.substep === 1) {
+        console.debug("[JasmineTestTool] setup/1-step: wait for " + nextStep.startConditionFunc);
+        waitFor(nextStep.startConditionFunc, nextStep);
+      } else if (nextStep.type === "event" && nextStep.substep === 3) {
+        console.debug("[JasmineTestTool] event/3-step: " + nextStep.resultFunc);
+        if (nextStep.resultFunc()) {
+          fail("The result function (" + nextStep.resultFunc + ") returns already 'true' BEFORE the '"
+              + nextStep.eventType + "' event is dispatched. Please define a result function which return 'false' before"
+              + " dispatch event and return 'true' after dispatch event.");
+          nextStep.substep = 0;
+          resetTimeout();
+          window.setTimeout(cycle, 1);
+        } else {
+          nextStep.substep--;
+          resetTimeout();
+          window.setTimeout(cycle, 1);
+        }
+      } else if (nextStep.type === "event" && nextStep.substep === 2) {
+        console.debug("[JasmineTestTool] event/2-step: " + nextStep.eventFunc);
+        execute(nextStep.eventFunc, nextStep);
+      } else if (nextStep.type === "event" && nextStep.substep === 1) {
+        console.debug("[JasmineTestTool] event/1-step: wait for " + nextStep.resultFunc);
+        waitFor(nextStep.resultFunc, nextStep);
+      } else if (nextStep.type === "do") {
+        console.debug("[JasmineTestTool] do-step: " + nextStep.func);
+        execute(nextStep.func, nextStep);
+      } else if (nextStep.type === "wait") {
+        console.debug("[JasmineTestTool] wait-step: " + nextStep.func);
+        waitFor(nextStep.func, nextStep);
+      } else {
+        fail("an unexpected error has occurred!");
+        done();
       }
-      window.setTimeout(this.cycle.bind(this), 50);
-    } else {
-      fail("an unexpected error has occurred!");
-      this.done();
     }
-  }
 
-  isFinished() {
-    for (let step of this.steps) {
-      if (!step.done) {
-        return false;
+    function getNextStep() {
+      for (let step of steps) {
+        if (step.substep > 0) {
+          return step;
+        }
       }
+      return null;
     }
-    return true;
-  }
 
-  isDocumentReady() {
-    return document.getElementById("page:testframe").contentWindow.document.readyState === "complete";
-  }
+    function isFinished() {
+      for (let step of steps) {
+        if (step.substep > 0) {
+          return false;
+        }
+      }
+      return true;
+    }
 
-  registerAjaxReadyStateListener() {
-    JasmineTestTool.ajaxReadyState = XMLHttpRequest.UNSENT;
-    window.removeEventListener(JasmineTestTool.ajaxReadyStateChangeEvent, JasmineTestTool.changeAjaxReadyState);
-    window.addEventListener(JasmineTestTool.ajaxReadyStateChangeEvent, JasmineTestTool.changeAjaxReadyState);
-  }
+    function isDocumentReady() {
+      return document.getElementById("page:testframe").contentWindow.document.readyState === "complete";
+    }
 
-  static changeAjaxReadyState(event) {
-    JasmineTestTool.ajaxReadyState = event.detail.readyState;
-    console.debug("[JasmineTestTool] ajaxReadyState: " + JasmineTestTool.ajaxReadyState);
-  }
+    function registerAjaxReadyStateListener() {
+      JasmineTestTool.ajaxReadyState = XMLHttpRequest.UNSENT;
+      window.removeEventListener(JasmineTestTool.ajaxReadyStateChangeEvent, JasmineTestTool.changeAjaxReadyState);
+      window.addEventListener(JasmineTestTool.ajaxReadyStateChangeEvent, JasmineTestTool.changeAjaxReadyState);
+    }
 
-  registerCustomXmlHttpRequest() {
-    class JasmineXMLHttpRequest extends XMLHttpRequest {
-      constructor() {
-        super();
-        this.addEventListener("readystatechange", function () {
-          window.dispatchEvent(new CustomEvent(JasmineTestTool.ajaxReadyStateChangeEvent,
-              {detail: {readyState: this.readyState}}));
-        });
+    function registerCustomXmlHttpRequest() {
+      class JasmineXMLHttpRequest extends XMLHttpRequest {
+        constructor() {
+          super();
+          this.addEventListener("readystatechange", function () {
+            window.dispatchEvent(new CustomEvent(JasmineTestTool.ajaxReadyStateChangeEvent,
+                {detail: {readyState: this.readyState}}));
+          });
+        }
       }
+
+      document.getElementById("page:testframe").contentWindow.XMLHttpRequest = JasmineXMLHttpRequest;
     }
 
-    document.getElementById("page:testframe").contentWindow.XMLHttpRequest = JasmineXMLHttpRequest;
-  }
+    function isAjaxReady() {
+      return JasmineTestTool.ajaxReadyState === XMLHttpRequest.UNSENT
+          || JasmineTestTool.ajaxReadyState === XMLHttpRequest.DONE;
+    }
 
-  isAjaxReady() {
-    return JasmineTestTool.ajaxReadyState === XMLHttpRequest.UNSENT
-        || JasmineTestTool.ajaxReadyState === XMLHttpRequest.DONE;
-  }
+    function isTimeout() {
+      return Date.now() > (lastStepExecution + timeout);
+    }
 
-  getNextStep() {
-    for (let step of this.steps) {
-      if (!step.done) {
-        return step;
-      }
+    function resetTimeout() {
+      lastStepExecution = Date.now();
+    }
+
+    function execute(fn, nextStep) {
+      registerCustomXmlHttpRequest();
+      fn();
+      nextStep.substep--;
+      resetTimeout();
+      window.setTimeout(cycle, 1);
     }
-    return null;
-  }
 
-  isTimeout() {
-    return Date.now() > (this.lastStepExecution + this.timeout);
+    function waitFor(fn, nextStep) {
+      if (fn()) {
+        nextStep.substep--;
+        resetTimeout();
+      }
+      window.setTimeout(cycle, 50);
+    }
   }
 
-  resetTimeout() {
-    this.lastStepExecution = Date.now();
+  static changeAjaxReadyState(event) {
+    JasmineTestTool.ajaxReadyState = event.detail.readyState;
+    console.debug("[JasmineTestTool] ajaxReadyState: " + JasmineTestTool.ajaxReadyState);
   }
 }