diff --git a/addons/website/static/tests/core/interaction.test.js b/addons/website/static/tests/core/interaction.test.js
index 18eb925d974fe..37e7b86111e2d 100644
--- a/addons/website/static/tests/core/interaction.test.js
+++ b/addons/website/static/tests/core/interaction.test.js
@@ -18,6 +18,12 @@ const TemplateTest = `
coucou
`
+const TemplateTestDoubleSpan = `
+
+ span1
+ span2
+
`
+
const getTemplateWithAttribute = function (attribute) {
return `
@@ -25,148 +31,151 @@ const getTemplateWithAttribute = function (attribute) {
`
}
-describe("event handling", () => {
+describe("adding listeners", () => {
test("can add a listener on a single element", async () => {
- let clicked = false;
+ let clicked = 0;
class Test extends Interaction {
static selector = ".test";
dynamicContent = {
- "span:t-on-click": this.doSomething,
+ "span:t-on-click": () => clicked++,
};
- doSomething() {
- clicked = true;
- }
}
+ await startInteraction(Test, TemplateTest);
+ expect(clicked).toBe(0);
+ await click("span");
+ expect(clicked).toBe(1);
+ });
- const { el } = await startInteraction(
- Test,
- TemplateTest,
- );
- expect(clicked).toBe(false);
- await click(el.querySelector("span"));
- expect(clicked).toBe(true);
+ test("can add a listener on a multiple elements", async () => {
+ let clicked = 0;
+ class Test extends Interaction {
+ static selector = ".test";
+ dynamicContent = {
+ "span:t-on-click": () => clicked++,
+ };
+ }
+ const { el } = await startInteraction(Test, TemplateTestDoubleSpan);
+ expect(clicked).toBe(0);
+ const spans = el.querySelectorAll("span");
+ await click(spans[0]);
+ await click(spans[1]);
+ expect(clicked).toBe(2);
});
- test("can add a listener on root element", async () => {
- let clicked = false;
+ test.tags("desktop")("can add multiple listeners on a element", async () => {
+ let clicked = 0;
class Test extends Interaction {
static selector = ".test";
+ dynamicContent = {
+ "span:t-on-click": () => clicked++,
+ "span:t-on-dblclick": () => clicked++,
+ };
+ }
+ const { el } = await startInteraction(Test, TemplateTest);
+ expect(clicked).toBe(0);
+ await dblclick("span");
+ expect(clicked).toBe(3); // event dblclick = click + click + dblclick
+ });
+ test("listener is added between willstart and start", async () => {
+ class Test extends Interaction {
+ static selector = ".test";
dynamicContent = {
- "_root:t-on-click": this.doSomething,
+ "span:t-on-click": () => expect.step("click"),
};
- doSomething() {
- clicked = true;
+ setup() {
+ expect.step("setup");
+ }
+ async willStart() {
+ await click("span");
+ expect.step("willStart");
+ }
+ start() {
+ expect.step("start");
}
}
+ await startInteraction(Test, TemplateTest);
+ await click("span");
+ expect.verifySteps(["setup", "willStart", "start", "click"]);
+ });
+});
- const { el } = await startInteraction(
- Test,
- TemplateTest,
- );
- expect(clicked).toBe(false);
- await click(el.querySelector(".test"));
- expect(clicked).toBe(true);
+describe("using selectors", () => {
+ test("can add a listener on root element", async () => {
+ let clicked = 0;
+ class Test extends Interaction {
+ static selector = ".test";
+ dynamicContent = {
+ "_root:t-on-click": () => clicked++,
+ };
+ }
+ await startInteraction(Test, TemplateTest);
+ expect(clicked).toBe(0);
+ await click(".test");
+ expect(clicked).toBe(1);
});
test("can add a listener on body element", async () => {
- let clicked = false;
+ let clicked = 0;
class Test extends Interaction {
static selector = ".test";
-
dynamicContent = {
- "_body:t-on-click": this.doSomething,
+ "_body:t-on-click": () => clicked++,
};
- doSomething() {
- clicked = true;
- }
}
-
- await startInteraction(
- Test,
- TemplateTest,
- );
- expect(clicked).toBe(false);
+ await startInteraction(Test, TemplateTest);
+ expect(clicked).toBe(0);
await click(document.body);
- expect(clicked).toBe(true);
+ expect(clicked).toBe(1);
});
test("can add a listener on window element", async () => {
- let clicked = false;
+ let clicked = 0;
class Test extends Interaction {
static selector = ".test";
-
dynamicContent = {
- "_window:t-on-someevent": this.doSomething,
+ "_window:t-on-event": () => clicked++,
};
- doSomething() {
- clicked = true;
- }
}
-
- await startInteraction(
- Test,
- TemplateTest,
- );
- expect(clicked).toBe(false);
- window.dispatchEvent(new Event("someevent"));
- expect(clicked).toBe(true);
+ await startInteraction(Test, TemplateTest);
+ expect(clicked).toBe(0);
+ await window.dispatchEvent(new Event("event"));
+ expect(clicked).toBe(1);
});
test("can add a listener on document ", async () => {
- let clicked = false;
+ let clicked = 0;
class Test extends Interaction {
static selector = ".test";
-
dynamicContent = {
- "_document:t-on-someevent": this.doSomething,
+ "_document:t-on-event": () => clicked++,
};
- doSomething() {
- clicked = true;
- }
}
-
- await startInteraction(
- Test,
- TemplateTest,
- );
- expect(clicked).toBe(false);
- window.document.dispatchEvent(new Event("someevent"));
- expect(clicked).toBe(true);
+ await startInteraction(Test, TemplateTest);
+ expect(clicked).toBe(0);
+ await window.document.dispatchEvent(new Event("event"));
+ expect(clicked).toBe(1);
});
test("can add a listener on modal element, if any", async () => {
- let clicked = false;
+ let clicked = 0;
class Test extends Interaction {
static selector = ".test";
-
dynamicSelectors = {
"_modal": () => this.el.closest(".modal"),
};
dynamicContent = {
- "_modal:t-on-click": this.doSomething,
+ "_modal:t-on-click": () => clicked++,
};
- doSomething() {
- clicked = true;
- }
}
-
- await startInteraction(
- Test,
- `
- `,
- );
- expect(clicked).toBe(false);
- await click(document.querySelector(".modal"));
- expect(clicked).toBe(true);
+ await startInteraction(Test, `${TemplateTest}
`,);
+ expect(clicked).toBe(0);
+ await click(".modal");
+ expect(clicked).toBe(1);
});
test("does not crash if no modal is found", async () => {
- let clicked = false;
+ let clicked = 0;
class Test extends Interaction {
static selector = ".test";
dynamicSelectors = {
@@ -175,144 +184,67 @@ describe("event handling", () => {
return null;
},
}
-
dynamicContent = {
- "_modal:t-on-click": this.doSomething,
+ "_modal:t-on-click": () => clicked++,
};
- doSomething() {
- clicked = true;
- }
}
-
- await startInteraction(
- Test,
- TemplateTest,
- );
+ await startInteraction(Test, TemplateTest);
expect.verifySteps(["check"])
- expect(clicked).toBe(false);
- });
-
- test("crash if a function is not provided to addListener", async () => {
- let inError = false;
- class Test extends Interaction {
- static selector = ".test";
-
- start() {
- try {
- this.addListener(this.el, "click", null);
- } catch (e) {
- inError = true;
- expect(e.message).toBe("Invalid listener for event 'click' (received falsy value)");
- }
- }
- }
- const { el } = await startInteraction(
- Test,
- TemplateTest,
- );
-
- el.querySelector(".test").click();
- expect(inError).toBe(true);
- });
-
- test("can add a listener on a multiple elements", async () => {
- let clicked = 0;
- class Test extends Interaction {
- static selector = ".test";
-
- dynamicContent = {
- "span:t-on-click": this.doSomething,
- };
- doSomething() {
- clicked++;
- }
- }
-
- const { el } = await startInteraction(
- Test,
- `
-
- coucou1
- coucou2
-
`,
- );
expect(clicked).toBe(0);
- for (let span of el.querySelectorAll("span")) {
- await click(span);
- }
- expect(clicked).toBe(2);
});
- test.tags("desktop")("can add multiple listeners on a element", async () => {
- let clicked = 0;
+ test("allow pseudo-classes in inline format in dynamicContent", async () => {
class Test extends Interaction {
static selector = ".test";
dynamicContent = {
- "span:t-on-click": this.doSomething,
- "span:t-on-dblclick": this.doSomething,
- };
- doSomething() {
- clicked++;
+ ".btn:not(.off):t-on-click": () => expect.step("doStuff"),
}
}
-
- const { el } = await startInteraction(
- Test,
- TemplateTest,
- );
- expect(clicked).toBe(0);
- const span = el.querySelector("span");
- await dblclick(span);
- // dblclick = 2 clicks and 1 dblcli
- expect(clicked).toBe(3);
+ await startInteraction(Test, `
+
+
+
+
`);
+ expect.verifySteps([]);
+ await click(".btn:not(.off)");
+ expect.verifySteps(["doStuff"]);
+ await click(".btn.off");
+ expect.verifySteps([]);
});
- test("listener is cleaned up when interaction is stopped", async () => {
- let clicked = 0;
+ test("allow customized special selector", async () => {
class Test extends Interaction {
static selector = ".test";
+ dynamicSelectors = {
+ "_myselector": () => this.el.querySelector(".my-selector")
+ };
dynamicContent = {
- "span:t-on-click": this.doSomething,
+ "_myselector:t-att-animal": () => "colibri",
};
- doSomething() {
- clicked++;
- }
}
-
- const { el, core } = await startInteraction(
- Test,
- TemplateTest,
- );
- expect(clicked).toBe(0);
- await click(el.querySelector("span"));
- expect(clicked).toBe(1);
- core.stopInteractions();
- await click(el.querySelector("span"));
- expect(clicked).toBe(1);
+ const { el } = await startInteraction(Test, `
+
+ coucou
+
`);
+ expect("span").toHaveAttribute("animal", "colibri")
});
+});
+describe("removing listeners", () => {
test("listener added with addListener is cleaned up", async () => {
let clicked = 0;
class Test extends Interaction {
static selector = ".test";
-
start() {
- this.addListener("span", "click", this.doSomething);
- }
- doSomething() {
- clicked++;
+ this.addListener("span", "click", () => clicked++);
}
}
-
- const { el, core } = await startInteraction(
- Test,
- TemplateTest,
- );
+ const { core } = await startInteraction(Test, TemplateTest);
expect(clicked).toBe(0);
- await click(el.querySelector("span"));
+ await click("span");
expect(clicked).toBe(1);
core.stopInteractions();
- await click(el.querySelector("span"));
+ await click("span");
expect(clicked).toBe(1);
});
@@ -320,24 +252,16 @@ describe("event handling", () => {
let clicked = 0;
class Test extends Interaction {
static selector = ".test";
-
start() {
- this.removeListener = this.addListener("span", "click", this.doSomething);
- }
- doSomething() {
- clicked++;
+ this.removeListener = this.addListener("span", "click", () => clicked++);
}
}
-
- const { el, core } = await startInteraction(
- Test,
- TemplateTest,
- );
+ const { core } = await startInteraction(Test, TemplateTest);
expect(clicked).toBe(0);
- await click(el.querySelector("span"));
+ await click("span");
expect(clicked).toBe(1);
core.interactions[0].interaction.removeListener();
- await click(el.querySelector("span"));
+ await click("span");
expect(clicked).toBe(1);
});
@@ -345,43 +269,45 @@ describe("event handling", () => {
let clicked = 0;
class Test extends Interaction {
static selector = ".test";
-
start() {
- this.removeListeners = this.addListener("span", "click", this.doSomething);
- }
- doSomething() {
- clicked++;
+ this.removeListener = this.addListener("span", "click", () => clicked++);
}
}
-
- const { el, core } = await startInteraction(
- Test,
- `
-
- coucou
- hello
-
`,
- );
+ const { el, core } = await startInteraction(Test, TemplateTestDoubleSpan);
expect(clicked).toBe(0);
const spans = el.querySelectorAll("span");
- for (let i = 0; i < spans.length; i++) {
- await click(spans[i]);
- }
+ await click(spans[0]);
+ await click(spans[1]);
expect(clicked).toBe(2);
- core.interactions[0].interaction.removeListeners();
- for (let i = 0; i < spans.length; i++) {
- await click(spans[i]);
- }
+ core.interactions[0].interaction.removeListener();
+ await click(spans[0]);
+ await click(spans[1]);
expect(clicked).toBe(2);
});
+ test("listener is cleaned up when interaction is stopped", async () => {
+ let clicked = 0;
+ class Test extends Interaction {
+ static selector = ".test";
+ dynamicContent = {
+ "span:t-on-click": () => clicked++,
+ };
+ }
+ const { core } = await startInteraction(Test, TemplateTest);
+ expect(clicked).toBe(0);
+ await click("span");
+ expect(clicked).toBe(1);
+ core.stopInteractions();
+ await click("span");
+ expect(clicked).toBe(1);
+ });
+
test("side effects are cleaned up in reverse order", async () => {
class Test extends Interaction {
static selector = ".test";
dynamicContent = {
"_root:t-on-click": () => expect.step("click1"),
};
-
setup() {
expect.step("setup");
this.el.click(); // we check that event handler is not bound yet
@@ -408,65 +334,45 @@ describe("event handling", () => {
this.el.click(); // check that handlers have been cleaned
}
}
-
- const { el, core } = await startInteraction(
- Test,
- TemplateTest,
- );
+ const { core } = await startInteraction(Test, TemplateTest);
expect.verifySteps(["setup", "start", "click1"]);
core.stopInteractions();
expect.verifySteps(["d", "click1", "click2", "c", "click1", "b", "a"]);
- el.click();
+ await click(".test");
expect.verifySteps([]);
});
+});
- test("listener is added between willstart and start", async () => {
+describe("handling crashes", () => {
+ test("crash if a function is not provided to addListener", async () => {
+ let inError = false;
class Test extends Interaction {
static selector = ".test";
- dynamicContent = {
- "span:t-on-click": this.onClick,
- };
- setup() {
- expect.step("setup");
- }
- async willStart() {
- await click(this.el.querySelector("span"));
- expect.step("willStart");
- }
start() {
- expect.step("start");
- }
- onClick() {
- expect.step("click");
+ try {
+ this.addListener(this.el, "click", null);
+ } catch (e) {
+ inError = true;
+ expect(e.message).toBe("Invalid listener for event 'click' (received falsy value)");
+ }
}
}
-
- const { el } = await startInteraction(
- Test,
- TemplateTest,
- );
- await click(el.querySelector("span"));
-
- expect.verifySteps(["setup", "willStart", "start", "click"]);
+ await startInteraction(Test, TemplateTest);
+ await click(".test");
+ expect(inError).toBe(true);
});
test("this.addListener crashes if interaction is not started", async () => {
let clicked = 0;
class Test extends Interaction {
static selector = ".test";
-
setup() {
- this.addListener("span", "click", this.doSomething);
- }
- doSomething() {
- clicked++;
+ this.addListener("span", "click", () => clicked++);
}
}
let error = null;
try {
- await startInteraction( Test,
- TemplateTest,
- );
+ await startInteraction(Test, TemplateTest);
} catch (e) {
error = e;
}
@@ -474,36 +380,60 @@ describe("event handling", () => {
expect(error.message).toInclude("this.addListener can only be called after the interaction is started");
});
-
test("dom is updated after event is dispatched", async () => {
class Test extends Interaction {
static selector = ".test";
dynamicContent = {
- "span:t-on-click": this.doSomething,
- "span:t-att-data-count": () => this.n,
+ "span:t-on-click": () => this.clickCount++,
+ "span:t-att-data-count": () => this.clickCount,
};
-
setup() {
- this.n = 1;
- }
-
- doSomething() {
- this.n++;
+ this.clickCount = 1;
}
}
-
- const { el } = await startInteraction(
- Test,
- TemplateTest,
- );
+ const { el } = await startInteraction(Test, TemplateTest);
const span = el.querySelector("span");
expect(span.dataset.count).toBe("1");
- await click(span);
+ await click("span");
expect(span.dataset.count).toBe("2");
await animationFrame();
expect(span.dataset.count).toBe("2");
});
+ test("crashes if a dynamic content element does not start with t-", async () => {
+ class Test extends Interaction {
+ static selector = ".test";
+ dynamicContent = {
+ "span:click": () => { },
+ };
+ }
+ let error = null;
+ try {
+ await startInteraction(Test, TemplateTest);
+ } catch (e) {
+ error = e;
+ }
+ expect(error).not.toBe(null);
+ expect(error.message).toBe("Invalid directive: 'click' (should start with t-)");
+ });
+
+ test("crash if dynamicContent is defined on class, not on instance", async () => {
+ class Test extends Interaction {
+ static selector = ".test";
+ static dynamicContent = {}
+ }
+ let error = null;
+ try {
+ await startInteraction(Test, TemplateTest);
+ } catch (e) {
+ error = e;
+ }
+ expect(error).not.toBe(null);
+ expect(error.message).toBe("The dynamic content object should be defined on the instance, not on the class (Test)");
+ });
+});
+
+describe("using qualifiers", () => {
test("add a listener with the .stop qualifier", async () => {
let clicked = false;
class Test extends Interaction {
@@ -513,17 +443,14 @@ describe("event handling", () => {
};
doSomething(ev) {
clicked = true;
- expect(event.defaultPrevented).toBe(false);
- expect(event.cancelBubble).toBe(true);
+ console.log(ev);
+ expect(ev.defaultPrevented).toBe(false);
+ expect(ev.cancelBubble).toBe(true);
}
}
-
- const { el } = await startInteraction(
- Test,
- TemplateTest,
- );
+ await startInteraction(Test, TemplateTest);
expect(clicked).toBe(false);
- await click(el.querySelector("span"));
+ await click("span");
expect(clicked).toBe(true);
});
@@ -536,17 +463,13 @@ describe("event handling", () => {
};
doSomething(ev) {
clicked = true;
- expect(event.defaultPrevented).toBe(true);
- expect(event.cancelBubble).toBe(false);
+ expect(ev.defaultPrevented).toBe(true);
+ expect(ev.cancelBubble).toBe(false);
}
}
-
- const { el } = await startInteraction(
- Test,
- TemplateTest,
- );
+ await startInteraction(Test, TemplateTest);
expect(clicked).toBe(false);
- await click(el.querySelector("span"));
+ await click("span");
expect(clicked).toBe(true);
});
@@ -554,26 +477,19 @@ describe("event handling", () => {
class Test extends Interaction {
static selector = ".test";
dynamicContent = {
- "strong:t-on-click": this.doStrong,
- "span:t-on-click.capture": this.doSpan,
+ "strong:t-on-click": () => expect.step("strong"),
+ "span:t-on-click.capture": () => expect.step("span"),
};
- doStrong(ev) {
- expect.step("strong");
- }
- doSpan(ev) {
- expect.step("span");
- }
}
-
- const { el } = await startInteraction(
- Test,
- `
-
- coucou
-
`,
+ await startInteraction(Test, `
+
+
+ coucou
+
+
`,
);
expect.verifySteps([]);
- await click(el.querySelector("strong"));
+ await click("strong");
expect.verifySteps(["span", "strong"]);
});
@@ -581,26 +497,19 @@ describe("event handling", () => {
class Test extends Interaction {
static selector = ".test";
dynamicContent = {
- "strong:t-on-click": this.doStrong,
- "span:t-on-click": this.doSpan,
+ "strong:t-on-click": () => expect.step("strong"),
+ "span:t-on-click": () => expect.step("span"),
};
- doStrong(ev) {
- expect.step("strong");
- }
- doSpan(ev) {
- expect.step("span");
- }
}
-
- const { el } = await startInteraction(
- Test,
- `
-
- coucou
-
`,
+ const { el } = await startInteraction(Test, `
+
+
+ coucou
+
+
`,
);
expect.verifySteps([]);
- await click(el.querySelector("strong"));
+ await click("strong");
expect.verifySteps(["strong", "span"]);
});
@@ -610,25 +519,22 @@ describe("event handling", () => {
static selector = ".test";
dynamicContent = {
"span:t-on-click.noupdate": this.doSomething,
- "span:t-att-class": () => ({"a": clicked}),
+ "span:t-att-class": () => ({ "a": clicked }),
};
doSomething(ev) {
clicked = true;
- expect(event.defaultPrevented).toBe(false);
- expect(event.cancelBubble).toBe(false);
+ expect(ev.defaultPrevented).toBe(false);
+ expect(ev.cancelBubble).toBe(false);
}
}
- const { el, core } = await startInteraction(
- Test,
- TemplateTest,
- );
+ const { core } = await startInteraction(Test, TemplateTest);
expect(clicked).toBe(false);
- await click(el.querySelector("span"));
+ await click("span");
expect(clicked).toBe(true);
- expect(el.querySelector("span")).not.toHaveClass("a");
+ expect("span").not.toHaveClass("a");
core.interactions[0].interaction.updateContent();
- expect(el.querySelector("span")).toHaveClass("a");
+ expect("span").toHaveClass("a");
});
test("add a listener with several qualifiers", async () => {
@@ -637,91 +543,27 @@ describe("event handling", () => {
static selector = ".test";
dynamicContent = {
"span:t-on-click.noupdate.stop.prevent": this.doSomething,
- "span:t-att-class": () => ({"a": clicked}),
+ "span:t-att-class": () => ({ "a": clicked }),
};
doSomething(ev) {
clicked = true;
- expect(event.defaultPrevented).toBe(true);
- expect(event.cancelBubble).toBe(true);
+ expect(ev.defaultPrevented).toBe(true);
+ expect(ev.cancelBubble).toBe(true);
}
}
-
- const { el, core } = await startInteraction(
- Test,
- TemplateTest,
- );
+ const { core } = await startInteraction(Test, TemplateTest);
expect(clicked).toBe(false);
- await click(el.querySelector("span"));
+ await click("span");
expect(clicked).toBe(true);
- expect(el.querySelector("span")).not.toHaveClass("a");
+ expect("span").not.toHaveClass("a");
core.interactions[0].interaction.updateContent();
- expect(el.querySelector("span")).toHaveClass("a");
- });
-
- test("allow pseudo-classes in inline format in dynamicContent", async () => {
- class Test extends Interaction {
- static selector = ".test";
- dynamicContent = {
- ".btn:not(.off):t-on-click": this.doStuff,
- }
- doStuff() {
- expect.step("doStuff");
- }
- }
-
- const { el } = await startInteraction(Test, `
`);
- expect.verifySteps([]);
- const btn1 = el.querySelector(".btn:not(.off)");
- const btn2 = el.querySelector(".btn.off");
- await click(btn1);
- expect.verifySteps(["doStuff"]);
- await click(btn2);
- expect.verifySteps([]);
- });
-});
-
-describe("special selectors", () => {
- test("can register a special selector", async () => {
-
- class Test extends Interaction {
- static selector = ".test";
- dynamicSelectors = {
- "_myselector": () => this.el.querySelector(".my-selector")
- };
- dynamicContent = {
- "_myselector:t-att-animal": () => "colibri",
- };
- }
-
- const { el } = await startInteraction(
- Test,
- `coucou
`,
- );
- expect(el.querySelector("span").outerHTML).toBe(`coucou`);
- });
-});
-
-describe("t-out", () => {
- test("can do a simple t-out", async () => {
- class Test extends Interaction {
- static selector = ".test";
- dynamicContent = {
- "span:t-out": () => "colibri",
- };
- }
-
- const { el } = await startInteraction(
- Test,
- TemplateTest,
- );
- expect(el.querySelector("span").outerHTML).toBe(`colibri`);
+ expect("span").toHaveClass("a");
});
});
describe("lifecycle", () => {
test("lifecycle methods are called in order", async () => {
let interaction = null;
-
class Test extends Interaction {
static selector = ".test";
setup() {
@@ -738,12 +580,7 @@ describe("lifecycle", () => {
expect.step("destroy");
}
}
-
- const { el, core } = await startInteraction(
- Test,
- TemplateTest,
- );
-
+ const { core } = await startInteraction(Test, TemplateTest);
expect.verifySteps(["setup", "willStart", "start"]);
core.stopInteractions();
expect.verifySteps(["destroy"]);
@@ -753,12 +590,11 @@ describe("lifecycle", () => {
} catch (_e) {
e = _e;
}
- expect(e.message).toBe("Cannot update content of an interaction that is not ready or is destroyed")
+ expect(e.message).toBe("Cannot update content of an interaction that is not ready or is destroyed");
});
test("willstart delayed, then destroy => start should not be called", async () => {
const def = new Deferred();
-
class Test extends Interaction {
static selector = ".test";
setup() {
@@ -775,16 +611,8 @@ describe("lifecycle", () => {
expect.step("destroy");
}
}
-
- const { core } = await startInteraction(
- Test,
- TemplateTest,
- {
- waitForStart: false,
- },
- );
+ const { core } = await startInteraction(Test, TemplateTest, { waitForStart: false });
expect.verifySteps(["setup", "willStart"]);
- // destroy the interaction
core.stopInteractions();
expect.verifySteps(["destroy"]);
def.resolve();
@@ -795,7 +623,6 @@ describe("lifecycle", () => {
test("willstart delayed => update => willstart complete", async () => {
const def = new Deferred();
let interaction;
-
class Test extends Interaction {
static selector = ".test";
setup() {
@@ -809,23 +636,15 @@ describe("lifecycle", () => {
expect.step("start");
}
}
-
- const { core } = await startInteraction(
- Test,
- TemplateTest,
- {
- waitForStart: false,
- },
- );
+ await startInteraction(Test, TemplateTest, { waitForStart: false });
expect.verifySteps(["willStart"]);
let e = null;
try {
- // trigger an update
interaction.updateContent();
} catch (_e) {
e = _e;
}
- expect(e.message).toBe("Cannot update content of an interaction that is not ready or is destroyed")
+ expect(e.message).toBe("Cannot update content of an interaction that is not ready or is destroyed");
await animationFrame();
expect.verifySteps([]);
@@ -835,47 +654,7 @@ describe("lifecycle", () => {
});
});
-describe("miscellaneous", () => {
- test("crashes if a dynamic content element does not start with t-", async () => {
- class Test extends Interaction {
- static selector = ".test";
- dynamicContent = {
- "span:click": this.doSomething,
- };
- doSomething() {}
- }
-
- let error = null;
- try {
- await startInteraction(Test, ``);
- } catch (e) {
- error = e;
- }
- expect(error).not.toBe(null);
- expect(error.message).toBe(
- "Invalid directive: 'click' (should start with t-)",
- );
- });
-
- test("crash if dynamicContent is defined on class, not on instance", async () => {
-
- class Test extends Interaction {
- static selector = ".test";
- static dynamicContent = {}
- }
-
- let error = null;
- try {
- await startInteraction(Test, ``);
- } catch (e) {
- error = e;
- }
- expect(error).not.toBe(null);
- expect(error.message).toBe(
- "The dynamic content object should be defined on the instance, not on the class (Test)",
- );
- });
-
+describe("register cleanup", () => {
test("can register a cleanup", async () => {
class Test extends Interaction {
static selector = ".test";
@@ -885,14 +664,10 @@ describe("miscellaneous", () => {
});
}
destroy() {
- expect.step("destroy");
- }
- }
- const { core } = await startInteraction(
- Test,
- ``,
- );
-
+ expect.step("destroy");
+ }
+ }
+ const { core } = await startInteraction(Test, TemplateTest);
expect.verifySteps([]);
core.stopInteractions();
expect.verifySteps(["cleanup", "destroy"]);
@@ -912,11 +687,7 @@ describe("miscellaneous", () => {
return expect.step(this.value);
}
}
- const { core } = await startInteraction(
- Test,
- ``,
- );
-
+ const { core } = await startInteraction(Test, TemplateTest);
expect.verifySteps([]);
core.stopInteractions();
expect.verifySteps(["value", "destroy"]);
@@ -934,157 +705,140 @@ describe("miscellaneous", () => {
});
}
}
- const { core } = await startInteraction(
- Test,
- ``,
- );
-
+ const { core } = await startInteraction(Test, TemplateTest);
expect.verifySteps([]);
core.stopInteractions();
expect.verifySteps(["cleanup2", "cleanup1"]);
});
+});
- test("waitFor does not trigger update if interaction is not ready yet", async () => {
- class Test extends Interaction {
- static selector = ".test";
-
- async willStart() {
- await this.waitFor(Promise.resolve(expect.step("waitfor")));
- expect.step("willstart");
- return new Promise(resolve => {
- setTimeout(() => {
- expect.step("timeout");
- resolve();
- }, 100);
- })
- }
- start() {
- expect.step("start");
+describe("waitFor...", () => {
+ describe("waitFor", () => {
+ test("waitFor does not trigger update if interaction is not ready yet", async () => {
+ class Test extends Interaction {
+ static selector = ".test";
+ async willStart() {
+ await this.waitFor(Promise.resolve(expect.step("waitfor")));
+ expect.step("willstart");
+ return new Promise(resolve => {
+ setTimeout(() => {
+ expect.step("timeout");
+ resolve();
+ }, 100);
+ })
+ }
+ start() {
+ expect.step("start");
+ }
}
- }
- await startInteraction(
- Test,
- ``,{ waitForStart: false}
- );
- expect.verifySteps(["waitfor", "willstart"]);
- await advanceTime(150);
- expect.verifySteps(["timeout", "start"]);
+ await startInteraction(Test, TemplateTest, { waitForStart: false });
+ expect.verifySteps(["waitfor", "willstart"]);
+ await advanceTime(150);
+ expect.verifySteps(["timeout", "start"]);
+ });
});
-
- test("waitForTimeout does not trigger update if interaction is not ready yet", async () => {
- class Test extends Interaction {
- static selector = ".test";
-
- async willStart() {
- await this.waitForTimeout(() => expect.step("waitfortimeout"), 50);
- expect.step("willstart");
- return new Promise(resolve => {
- setTimeout(() => {
- expect.step("timeout");
- resolve();
- }, 100);
- })
- }
- start() {
- expect.step("start");
+ describe("waitForTimeout", () => {
+ test("waitForTimeout does not trigger update if interaction is not ready yet", async () => {
+ class Test extends Interaction {
+ static selector = ".test";
+ async willStart() {
+ await this.waitForTimeout(() => expect.step("waitfortimeout"), 50);
+ expect.step("willstart");
+ return new Promise(resolve => {
+ setTimeout(() => {
+ expect.step("timeout");
+ resolve();
+ }, 100);
+ })
+ }
+ start() {
+ expect.step("start");
+ }
}
- }
- await startInteraction(
- Test,
- ``,{ waitForStart: false}
- );
- expect.verifySteps(["willstart"]);
- await advanceTime(75);
- expect.verifySteps(["waitfortimeout"]);
- await advanceTime(75);
- expect.verifySteps(["timeout", "start"]);
- });
+ await startInteraction(Test, TemplateTest, { waitForStart: false });
+ expect.verifySteps(["willstart"]);
+ await advanceTime(75);
+ expect.verifySteps(["waitfortimeout"]);
+ await advanceTime(75);
+ expect.verifySteps(["timeout", "start"]);
+ });
- test("waitForTimeout is autobound to this", async () => {
- class Test extends Interaction {
- static selector = ".test";
- setup() {
- this.waitForTimeout(this.fn, 100);
- this.waitForTimeout(() => {
+ test("waitForTimeout is autobound to this", async () => {
+ class Test extends Interaction {
+ static selector = ".test";
+ setup() {
+ this.waitForTimeout(this.fn, 100);
+ this.waitForTimeout(() => {
+ expect(this instanceof Interaction).toBe(true);
+ expect.step("anonymous function");
+ }, 50);
+ }
+ fn() {
expect(this instanceof Interaction).toBe(true);
- expect.step("anonymous function");
- }, 50);
- }
- fn() {
- expect(this instanceof Interaction).toBe(true);
- expect.step("named function");
+ expect.step("named function");
+ }
}
- }
- const { core } = await startInteraction(
- Test,
- ``,
- );
- expect.verifySteps([]);
- await advanceTime(50);
- expect.verifySteps(["anonymous function"]);
- await advanceTime(50);
- expect.verifySteps(["named function"]);
+ await startInteraction(Test, TemplateTest, { waitForStart: false });
+ expect.verifySteps([]);
+ await advanceTime(50);
+ expect.verifySteps(["anonymous function"]);
+ await advanceTime(50);
+ expect.verifySteps(["named function"]);
+ });
});
- test("waitForAnimationFrame does not trigger update if interaction is not ready yet", async () => {
- class Test extends Interaction {
- static selector = ".test";
-
- async willStart() {
- await this.waitForAnimationFrame(() => expect.step("waitForAnimationFrame"));
- expect.step("willstart");
- return new Promise(resolve => {
- setTimeout(() => {
- expect.step("timeout");
- resolve();
- }, 100);
- })
- }
- start() {
- expect.step("start");
+ describe("waitForAnimationFrame", () => {
+ test("waitForAnimationFrame does not trigger update if interaction is not ready yet", async () => {
+ class Test extends Interaction {
+ static selector = ".test";
+
+ async willStart() {
+ await this.waitForAnimationFrame(() => expect.step("waitForAnimationFrame"));
+ expect.step("willstart");
+ return new Promise(resolve => {
+ setTimeout(() => {
+ expect.step("timeout");
+ resolve();
+ }, 100);
+ })
+ }
+ start() {
+ expect.step("start");
+ }
}
- }
- await startInteraction(
- Test,
- ``, { waitForStart: false }
- );
- expect.verifySteps(["willstart"]);
- await animationFrame();
- expect.verifySteps(["waitForAnimationFrame"]);
- await advanceTime(100);
- expect.verifySteps(["timeout", "start"]);
- });
+ await startInteraction(Test, TemplateTest, { waitForStart: false });
+ expect.verifySteps(["willstart"]);
+ await animationFrame();
+ expect.verifySteps(["waitForAnimationFrame"]);
+ await advanceTime(100);
+ expect.verifySteps(["timeout", "start"]);
+ });
- test("waitForAnimationFrame is autobound to this", async () => {
- class Test extends Interaction {
- static selector = ".test";
- setup() {
- this.waitForAnimationFrame(this.fn);
- this.waitForAnimationFrame(() => {
+ test("waitForAnimationFrame is autobound to this", async () => {
+ class Test extends Interaction {
+ static selector = ".test";
+ setup() {
+ this.waitForAnimationFrame(this.fn);
+ this.waitForAnimationFrame(() => {
+ expect(this instanceof Interaction).toBe(true);
+ expect.step("anonymous function");
+ });
+ }
+ fn() {
expect(this instanceof Interaction).toBe(true);
- expect.step("anonymous function");
- });
- }
- fn() {
- expect(this instanceof Interaction).toBe(true);
- expect.step("named function");
+ expect.step("named function");
+ }
}
- }
- const { core } = await startInteraction(
- Test,
- ``,
- );
- expect.verifySteps([]);
- await animationFrame();
- expect.verifySteps(["named function", "anonymous function"]);
+ await startInteraction(Test, TemplateTest, { waitForStart: false });
+ expect.verifySteps([]);
+ await animationFrame();
+ expect.verifySteps(["named function", "anonymous function"]);
+ });
});
});
-describe("dynamic attributes", () => {
-
- // T-ATT-CLASS
-
+describe("t-att-class", () => {
test("t-att-class can add a class ", async () => {
class Test extends Interaction {
static selector = "span";
@@ -1225,9 +979,9 @@ describe("dynamic attributes", () => {
expect("span").toHaveClass(["a", "c"]);
expect("span").not.toHaveClass("b");
});
+});
- // T-ATT-STYLE
-
+describe("t-att-style", () => {
test("t-att-style can add a style", async () => {
class Test extends Interaction {
static selector = "span";
@@ -1401,8 +1155,9 @@ describe("dynamic attributes", () => {
// expect("span").toHaveStyle({ backgroundColor: "rgb(0, 0, 255)", color: "rgb(255, 0, 0) !important" });
});
- // T-ATT
+});
+describe("t-att and t-out", () => {
test("t-att-... can add an attribute", async () => {
class Test extends Interaction {
static selector = "span";
@@ -1472,6 +1227,18 @@ describe("dynamic attributes", () => {
expect("span").toHaveAttribute("a", "b");
expect(target).toBe(el.querySelector("span"));
});
+
+ test("can do a simple t-out", async () => {
+ class Test extends Interaction {
+ static selector = ".test";
+ dynamicContent = {
+ "span:t-out": () => "colibri",
+ };
+ }
+ const { el } = await startInteraction(Test, TemplateTest);
+ expect(el.querySelector("span").outerHTML).toBe(`colibri`);
+ });
+
});
describe("components", () => {
@@ -1486,7 +1253,7 @@ describe("components", () => {
}
class Test extends Interaction {
- static selector =".test";
+ static selector = ".test";
dynamicContent = {
"_root:t-component": C,
};
@@ -1524,9 +1291,9 @@ describe("components", () => {
}
class Test extends Interaction {
- static selector =".test";
+ static selector = ".test";
dynamicContent = {
- "_root:t-component": () => [C, {prop: "hello"}],
+ "_root:t-component": () => [C, { prop: "hello" }],
};
}
const { core, el } = await startInteraction(
@@ -1585,7 +1352,7 @@ describe("components", () => {
static selector = ".test";
setup() {
- this.mountComponent(this.el, C, {prop: "with prop"});
+ this.mountComponent(this.el, C, { prop: "with prop" });
}
}
const { el } = await startInteraction(
@@ -1603,19 +1370,16 @@ describe("components", () => {
});
describe("insert", () => {
- test("can insert an element", async () => {
+ test("can insert an element after another nested", async () => {
class Test extends Interaction {
static selector = ".test";
setup() {
- const el = document.createElement("inserted");
- this.insert(el);
+ const node = document.createElement("inserted");
+ this.insert(node, this.el); // "beforeend"
}
}
- const { core, el } = await startInteraction(
- Test,
- TemplateTest,
- );
+ const { core, el } = await startInteraction(Test, TemplateTest);
const testEl = el.querySelector(".test");
let insertedEl = testEl.querySelector("inserted");
expect(insertedEl).not.toBe(null);
@@ -1625,28 +1389,38 @@ describe("insert", () => {
expect(insertedEl).toBe(null);
});
- test("can insert an element before another one", async () => {
+ test("can insert an element before another nested", async () => {
class Test extends Interaction {
static selector = ".test";
setup() {
- const spanEls = this.el.querySelectorAll("span");
- const el = document.createElement("inserted");
- this.insert(el, spanEls[1], "beforebegin");
+ const node = document.createElement("inserted");
+ this.insert(node, this.el, "afterbegin");
}
}
+ const { core, el } = await startInteraction(Test, TemplateTest);
+ const testEl = el.querySelector(".test");
+ let insertedEl = testEl.querySelector("inserted");
+ expect(insertedEl).not.toBe(null);
+ expect(insertedEl).toBe(testEl.firstElementChild);
+ core.stopInteractions();
+ insertedEl = el.querySelector("inserted");
+ expect(insertedEl).toBe(null);
+ });
- const { core, el } = await startInteraction(
- Test,
- `
-
- first
- last
-
`,
- );
+ test("can insert an element before another one", async () => {
+ class Test extends Interaction {
+ static selector = ".test";
+ setup() {
+ const span = this.el.querySelector("span");
+ const node = document.createElement("inserted");
+ this.insert(node, span, "beforebegin");
+ }
+ }
+ const { core, el } = await startInteraction(Test, TemplateTest);
const testEl = el.querySelector(".test");
let insertedEl = testEl.querySelector("inserted");
expect(insertedEl).not.toBe(null);
- expect(insertedEl).toBe(testEl.children[1]);
+ expect(insertedEl).toBe(testEl.children[0]);
core.stopInteractions();
insertedEl = el.querySelector("inserted");
expect(insertedEl).toBe(null);
@@ -1656,20 +1430,12 @@ describe("insert", () => {
class Test extends Interaction {
static selector = ".test";
setup() {
- const spanEls = this.el.querySelectorAll("span");
- const el = document.createElement("inserted");
- this.insert(el, spanEls[0], "afterend");
+ const span = this.el.querySelector("span");
+ const node = document.createElement("inserted");
+ this.insert(node, span, "afterend");
}
}
-
- const { core, el } = await startInteraction(
- Test,
- `
-
- first
- last
-
`,
- );
+ const { core, el } = await startInteraction(Test, TemplateTest);
const testEl = el.querySelector(".test");
let insertedEl = testEl.querySelector("inserted");
expect(insertedEl).not.toBe(null);
@@ -1680,7 +1446,7 @@ describe("insert", () => {
});
});
-describe("debounced", () => {
+describe("debounced (1)", () => {
beforeEach(async () => {
patchWithCleanup(Colibri.prototype, {
updateContent() {
@@ -1700,14 +1466,11 @@ describe("debounced", () => {
expect.step("done");
}
}
- const { core, el } = await startInteraction(
- Test,
- TemplateTest,
- );
+ const { core, el } = await startInteraction(Test, TemplateTest);
this.core = core;
expect.verifySteps(["updateContent"]);
this.testEl = el.querySelector(".test");
- }),
+ });
test("debounced event handler delays and groups calls", async () => {
await click(this.testEl);
@@ -1733,7 +1496,7 @@ describe("debounced", () => {
expect.verifySteps([]);
await advanceTime(500);
expect.verifySteps(["done", "updateContent"]);
- });
+ });
test("debounced event handler cancels events on destroy", async () => {
await click(this.testEl);
@@ -1756,78 +1519,68 @@ describe("debounced", () => {
});
});
-test("debounced with long willstart", async () => {
- class Test extends Interaction {
- static selector = ".test";
-
- setup() {
- const fn = this.debounced(() => expect.step("debounced"), 50);
- fn();
- }
-
- async willStart() {
- expect.step("willstart");
- await new Promise(resolve => {
- setTimeout(resolve, 100);
- });
- }
- start() {
- expect.step("start");
+describe("debounced (2)", () => {
+ test("debounced with long willstart", async () => {
+ class Test extends Interaction {
+ static selector = ".test";
+ setup() {
+ const fn = this.debounced(() => expect.step("debounced"), 50);
+ fn();
+ }
+ async willStart() {
+ expect.step("willstart");
+ await new Promise(resolve => {
+ setTimeout(resolve, 100);
+ });
+ }
+ start() {
+ expect.step("start");
+ }
+ updateContent() {
+ expect.step("updatecontent");
+ super.updateContent();
+ }
}
+ await startInteraction(Test, TemplateTest);
+ expect.verifySteps(["willstart", "debounced", "start"]);
+ });
- updateContent() {
- expect.step("updatecontent");
- super.updateContent();
+ test("debounced is not called if the interaction is destroyed in the meantime", async () => {
+ class Test extends Interaction {
+ static selector = ".test";
+ setup() {
+ const fn = this.debounced(() => expect.step("debounced"), 50);
+ fn();
+ }
+ updateContent() {
+ expect.step("updatecontent");
+ super.updateContent();
+ }
+ async willStart() {
+ expect.step("willstart");
+ await new Promise(resolve => {
+ setTimeout(resolve, 100);
+ });
+ }
+ start() {
+ expect.step("start");
+ }
+ destroy() {
+ expect.step("destroy");
+ }
}
- }
- await startInteraction(
- Test,
- `
-
-
`,
- );
- expect.verifySteps(["willstart", "debounced", "start"]);
+ const { core } = await startInteraction(Test, TemplateTest, { waitForStart: false });
+ expect.verifySteps(["willstart"]);
+ await advanceTime(25);
+ expect.verifySteps([]);
+ core.stopInteractions();
+ expect.verifySteps(["destroy"]);
+ await advanceTime(500);
+ expect.verifySteps([]);
+ });
});
-test("debounced is not called if interaction is destroyed in the meantime", async () => {
- class Test extends Interaction {
- static selector = ".test";
-
- setup() {
- const fn = this.debounced(() => expect.step("debounced"), 50);
- fn();
- }
-
- updateContent() {
- expect.step("updatecontent");
- super.updateContent();
- }
- async willStart() {
- expect.step("willstart");
- await new Promise(resolve => {
- setTimeout(resolve, 100);
- });
- }
- start() {
- expect.step("start");
- }
- destroy() {
- expect.step("destroy");
- }
-
- }
- const { core } = await startInteraction(Test, ``, { waitForStart: false });
- expect.verifySteps(["willstart"]);
- await advanceTime(25);
- expect.verifySteps([]);
- core.stopInteractions();
- expect.verifySteps(["destroy"]);
- await advanceTime(500);
- expect.verifySteps([]);
-}),
-
-
-describe("throttledForAnimation", () => {
+describe("throttled_for_animation (1)", () => {
beforeEach(async () => {
patchWithCleanup(Colibri.prototype, {
updateContent() {
@@ -1847,51 +1600,48 @@ describe("throttledForAnimation", () => {
expect.step("done");
}
}
- const { core, el } = await startInteraction(
- Test,
- TemplateTest,
- );
+ const { core, el } = await startInteraction(Test, TemplateTest);
this.core = core;
expect.verifySteps(["updateContent"]);
this.testEl = el.querySelector(".test");
}),
- test("throttled event handler executes call right away", async () => {
- await click(this.testEl);
- expect.verifySteps(["done", "updateContent"]);
- }),
-
- test("throttled event handler delays further calls", async () => {
- await click(this.testEl);
- await click(this.testEl);
- expect.verifySteps(["done", "updateContent"]);
- await animationFrame();
- expect.verifySteps(["done", "updateContent"]);
- await animationFrame();
- expect.verifySteps([]);
- }),
-
- test("throttled event handler delays and groups further calls", async () => {
- await click(this.testEl);
- await click(this.testEl);
- await click(this.testEl);
- expect.verifySteps(["done", "updateContent"]);
- await animationFrame();
- expect.verifySteps(["done", "updateContent"]);
- await animationFrame();
- expect.verifySteps([]);
- }),
-
- test("throttled event handler cancels delayed calls", async () => {
- await click(this.testEl);
- await click(this.testEl);
- await click(this.testEl);
- expect.verifySteps(["done", "updateContent"]);
- this.core.stopInteractions();
- expect.verifySteps([]);
- await animationFrame();
- expect.verifySteps([]);
- });
+ test("throttled event handler executes call right away", async () => {
+ await click(this.testEl);
+ expect.verifySteps(["done", "updateContent"]);
+ }),
+
+ test("throttled event handler delays further calls", async () => {
+ await click(this.testEl);
+ await click(this.testEl);
+ expect.verifySteps(["done", "updateContent"]);
+ await animationFrame();
+ expect.verifySteps(["done", "updateContent"]);
+ await animationFrame();
+ expect.verifySteps([]);
+ }),
+
+ test("throttled event handler delays and groups further calls", async () => {
+ await click(this.testEl);
+ await click(this.testEl);
+ await click(this.testEl);
+ expect.verifySteps(["done", "updateContent"]);
+ await animationFrame();
+ expect.verifySteps(["done", "updateContent"]);
+ await animationFrame();
+ expect.verifySteps([]);
+ }),
+
+ test("throttled event handler cancels delayed calls", async () => {
+ await click(this.testEl);
+ await click(this.testEl);
+ await click(this.testEl);
+ expect.verifySteps(["done", "updateContent"]);
+ this.core.stopInteractions();
+ expect.verifySteps([]);
+ await animationFrame();
+ expect.verifySteps([]);
+ });
test("can cancel throttled event handler", async () => {
await click(this.testEl);
@@ -1903,40 +1653,34 @@ describe("throttledForAnimation", () => {
});
});
-test("throttleForAnimation with long willstart", async () => {
- patchWithCleanup(Colibri.prototype, {
- updateContent() {
- expect.step("updatecontent");
- super.updateContent();
- },
- });
-
- class Test extends Interaction {
- static selector = ".test";
- dynamicContent = { "_root:t-att-a": () => "b" }
-
- setup() {
- const fn = this.throttledForAnimation(() => expect.step("throttle"));
- fn();
- }
-
- async willStart() {
- expect.step("willstart");
- await new Promise(resolve => {
- setTimeout(resolve, 100);
- });
- }
- start() {
- expect.step("start");
+describe("throttled_for_animation (2)", () => {
+ test("throttled functions work with long willstart", async () => {
+ patchWithCleanup(Colibri.prototype, {
+ updateContent() {
+ expect.step("updatecontent");
+ super.updateContent();
+ },
+ });
+ class Test extends Interaction {
+ static selector = ".test";
+ dynamicContent = { "_root:t-att-a": () => "b" }
+ setup() {
+ const fn = this.throttledForAnimation(() => expect.step("throttle"));
+ fn();
+ }
+ async willStart() {
+ expect.step("willstart");
+ await new Promise(resolve => {
+ setTimeout(resolve, 100);
+ });
+ }
+ start() {
+ expect.step("start");
+ }
}
- }
- await startInteraction(
- Test,
- ``,
- { waitForStart: false }
- );
- expect.verifySteps(["throttle", "willstart"]);
- await advanceTime(150);
- expect.verifySteps(["updatecontent", "start"]);
-
+ await startInteraction(Test, TemplateTest, { waitForStart: false });
+ expect.verifySteps(["throttle", "willstart"]);
+ await advanceTime(150);
+ expect.verifySteps(["updatecontent", "start"]);
+ });
});