// Copyright 2022 Yoshiya Hinosawa. All rights reserved. MIT license. import { assert, assertEquals, assertExists, assertThrows } from "@std/assert"; import { delay } from "@std/async"; import "./dom_polyfill.ts"; import { type Context, mount, register, unmount } from "./mod.ts"; // disable debug logs because it's too verbose for unit testing // deno-lint-ignore no-explicit-any (globalThis as any).__DEV__ = false; Deno.test("Component body is called when the component is mounted", () => { const name = randomName(); document.body.innerHTML = `
`; let called = false; function Component() { called = true; } register(Component, name); assert(called); }); Deno.test("sub() add sub:type class to the dom", () => { const name = randomName(); document.body.innerHTML = ``; const div = queryByClass(name); function Component({ el, sub }: Context) { el.classList.add("foo"); sub("bar"); return "hello
"; } register(Component, name); assert(div.classList.contains("foo")); assert(div.classList.contains("sub:bar")); assert(div.innerHTML === "hello
"); }); /* Deno.test("on.__unmount__ is called when the componet is unmounted", () => { const name = randomName(); const { on } = component(name); document.body.innerHTML = ``; let called = false; on.__unmount__ = () => { called = true; }; mount(); assert(!called); unmount(name, query(`.${name}`)!); assert(called); }); */ Deno.test("unmount removes the event listeners", () => { const name = randomName(); document.body.innerHTML = ``; const div = queryByClass(name); let count = 0; function Component({ on }: Context) { on["my-event"] = () => { count++; }; } register(Component, name); assertEquals(count, 0); div.dispatchEvent(new CustomEvent("my-event")); assertEquals(count, 1); div.dispatchEvent(new CustomEvent("my-event")); assertEquals(count, 2); unmount(name, div!); div.dispatchEvent(new CustomEvent("my-event")); assertEquals(count, 2); }); Deno.test("on[event] is called when the event is dispatched", () => { const name = randomName(); document.body.innerHTML = ``; const div = queryByClass(name); let called = false; function Component({ on }: Context) { on.click = () => { called = true; }; on.click = () => { called = true; }; } register(Component, name); div.dispatchEvent(new Event("click")); assert(called); }); Deno.test("on(selector)[event] is called when the event is dispatched only under the selector", async () => { const name = randomName(); document.body.innerHTML = ``; let onBtn1ClickCalled = false; let onBtn2ClickCalled = false; function Component({ on }: Context) { on(".btn1").click = () => { onBtn1ClickCalled = true; }; on(".btn2").click = () => { onBtn2ClickCalled = true; }; } register(Component, name); const btn = queryByClass("btn1"); // FIXME(kt3k): workaround for deno_dom & deno issue // deno_dom doesn't bubble event when the direct target dom doesn't have event handler btn.addEventListener("click", () => {}); btn.dispatchEvent(new Event("click", { bubbles: true })); await new Promise((r) => setTimeout(r, 100)); assert(onBtn1ClickCalled); assert(!onBtn2ClickCalled); }); Deno.test("on(option)[event] is called with option as AddEventListnerOptions", () => { const name = randomName(); document.body.innerHTML = ``; let count = 0; function Component({ on }: Context) { on({ once: true }).click = () => count++; // for checking type on({ passive: false }).touchmove = (_e: TouchEvent) => {}; } register(Component, name); const div = queryByClass(name); div.dispatchEvent(new Event("click")); div.dispatchEvent(new Event("click")); assertEquals(count, 1); }); Deno.test("on.outside.event works", () => { const name = randomName(); document.body.innerHTML = `foo
bar
baz
hello
"; } register(Component, name); assertEquals(queryByClass(name).innerHTML, "hello
"); }); Deno.test("Resolved string from Component is rendered as innerHTML", async () => { const name = randomName(); document.body.innerHTML = `hello
"); } register(Component, name); await delay(100); assertEquals(queryByClass(name).innerHTML, "hello
"); });