update to v2.3.0

add `.rep()` & `.replaceChildren()` function
update `.app()` & `.append()` to support rest params
update build target to ES6
fix comment color on demo page
add (more) tests
This commit is contained in:
2025-09-13 19:44:55 -04:00
parent af2e0d2eec
commit 96c8f980e9
19 changed files with 732 additions and 101 deletions

View File

@@ -0,0 +1,219 @@
// @vitest-environment happy-dom
import { beforeEach, describe, expect, it } from "vitest";
import $d from "../fluent-dom-esm";
describe(".app() and .append()", () => {
beforeEach(() => {
document.body.replaceChildren();
});
it("should support single FluentDomObject argument", () => {
// default state
const body = $d(document.body).toDom();
expect(body).not.toBeNull();
expect(body).toBeEmptyDOMElement();
// add child
$d(document.body).app($d.c("div").id("item1").t("Item 1"));
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 1");
expect(body?.children.length).toEqual(1);
expect(body?.children[0]).toHaveAttribute("id", "item1");
// add child
$d(document.body).app($d.c("div").id("item2").t("Item 2"));
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 2");
expect(body?.children.length).toEqual(2);
expect(body?.children[1]).toHaveAttribute("id", "item2");
// reset
body?.replaceChildren();
// add child
$d(document.body).append($d.c("div").id("item1").t("Item 1"));
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 1");
expect(body?.children.length).toEqual(1);
expect(body?.children[0]).toHaveAttribute("id", "item1");
// add child
$d(document.body).append($d.c("div").id("item2").t("Item 2"));
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 2");
expect(body?.children.length).toEqual(2);
expect(body?.children[1]).toHaveAttribute("id", "item2");
});
it("should support multiple FluentDomObject arguments for `.app()` and `.append()`", () => {
// default state
const body = $d(document.body).toDom();
expect(body).not.toBeNull();
expect(body).toBeEmptyDOMElement();
// add children
$d(document.body).app(
$d.c("div").id("item1").t("Item 1"),
$d.c("div").id("item2").t("Item 2"),
);
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 1");
expect(body).toHaveTextContent("Item 2");
expect(body?.children.length).toEqual(2);
expect(body?.children[0]).toHaveAttribute("id", "item1");
expect(body?.children[1]).toHaveAttribute("id", "item2");
// reset
body?.replaceChildren();
// add children
$d(document.body).append(
$d.c("div").id("item1").t("Item 1"),
$d.c("div").id("item2").t("Item 2"),
);
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 1");
expect(body).toHaveTextContent("Item 2");
expect(body?.children.length).toEqual(2);
expect(body?.children[0]).toHaveAttribute("id", "item1");
expect(body?.children[1]).toHaveAttribute("id", "item2");
});
it("should support single HTMLElement argument for `.app()` and `.append()`", () => {
// default state
const body = $d(document.body).toDom();
expect(body).not.toBeNull();
expect(body).toBeEmptyDOMElement();
// add child
$d(document.body).app(
$d.c("div").id("item1").t("Item 1").toDom() ?? "",
);
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 1");
expect(body?.children.length).toEqual(1);
expect(body?.children[0]).toHaveAttribute("id", "item1");
// add child
$d(document.body).app(
$d.c("div").id("item2").t("Item 2").toDom() ?? "",
);
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 2");
expect(body?.children.length).toEqual(2);
expect(body?.children[1]).toHaveAttribute("id", "item2");
// reset
body?.replaceChildren();
// add child
$d(document.body).append(
$d.c("div").id("item1").t("Item 1").toDom() ?? "",
);
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 1");
expect(body?.children.length).toEqual(1);
expect(body?.children[0]).toHaveAttribute("id", "item1");
// add child
$d(document.body).append(
$d.c("div").id("item2").t("Item 2").toDom() ?? "",
);
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 2");
expect(body?.children.length).toEqual(2);
expect(body?.children[1]).toHaveAttribute("id", "item2");
});
it("should support multiple HTMLElement arguments for `.app()` and `.append()`", () => {
// default state
const body = $d(document.body).toDom();
expect(body).not.toBeNull();
expect(body).toBeEmptyDOMElement();
// add children
$d(document.body).app(
$d.c("div").id("item1").t("Item 1").toDom() ?? "",
$d.c("div").id("item2").t("Item 2").toDom() ?? "",
);
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 1");
expect(body).toHaveTextContent("Item 2");
expect(body?.children.length).toEqual(2);
expect(body?.children[0]).toHaveAttribute("id", "item1");
expect(body?.children[1]).toHaveAttribute("id", "item2");
// reset
body?.replaceChildren();
// add child
$d(document.body).append(
$d.c("div").id("item1").t("Item 1").toDom() ?? "",
$d.c("div").id("item2").t("Item 2").toDom() ?? "",
);
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 1");
expect(body).toHaveTextContent("Item 2");
expect(body?.children.length).toEqual(2);
expect(body?.children[0]).toHaveAttribute("id", "item1");
expect(body?.children[1]).toHaveAttribute("id", "item2");
});
it("should support single string argument for `.app()` and `.append()`", () => {
// default state
const body = $d(document.body).toDom();
expect(body).not.toBeNull();
expect(body).toBeEmptyDOMElement();
// add child
$d(document.body).append("Item 1");
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 1");
expect(body?.children.length).toEqual(0);
// add child
$d(document.body).app("Item 2");
expect(body).toHaveTextContent("Item 2");
expect(body?.children.length).toEqual(0);
// reset
body?.replaceChildren();
// add child
$d(document.body).append("Item 1");
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 1");
expect(body?.children.length).toEqual(0);
// add child
$d(document.body).app("Item 2");
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 2");
expect(body?.children.length).toEqual(0);
});
it("should support multiple string arguments for `.app()` and `.append()`", () => {
// default state
const body = $d(document.body).toDom();
expect(body).not.toBeNull();
expect(body).toBeEmptyDOMElement();
// add children
$d(document.body).append("Item 1", "Item 2");
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 1");
expect(body).toHaveTextContent("Item 2");
expect(body?.children.length).toEqual(0);
// reset
body?.replaceChildren();
// add child
$d(document.body).append("Item 1", "Item 2");
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 1");
expect(body).toHaveTextContent("Item 2");
expect(body?.children.length).toEqual(0);
});
});

View File

@@ -0,0 +1,67 @@
// @vitest-environment happy-dom
import { describe, expect, it } from "vitest";
import $d from "../fluent-dom-esm";
describe(".a() and .attr()", () => {
it("adds an attribute to the wrapped HTMLElement", () => {
// create base anchor
const $a1 = $d.c("a");
expect($a1.toDom()).not.toBeNull();
expect($a1.toDom()).toBeEmptyDOMElement();
expect($a1.toDom()?.outerHTML).toEqual("<a></a>");
expect($a1.toDom()?.getAttribute("href")).toBeNull();
// add href attribute
$a1.a("href", "http://example.com");
expect($a1.toDom()?.getAttribute("href")).not.toBeNull();
expect($a1.toDom()?.getAttribute("href")).toEqual("http://example.com");
// create base anchor
const $a2 = $d.c("a");
expect($a2.toDom()).not.toBeNull();
expect($a2.toDom()).toBeEmptyDOMElement();
expect($a2.toDom()?.outerHTML).toEqual("<a></a>");
expect($a2.toDom()?.getAttribute("href")).toBeNull();
// add href attribute
$a2.attr("href", "mailto:someone@somewhere.com");
expect($a2.toDom()?.getAttribute("href")).not.toBeNull();
expect($a2.toDom()?.getAttribute("href")).toEqual(
"mailto:someone@somewhere.com",
);
});
it("disables a form element when the 'disabled' attribute is set", () => {
const $button1 = $d.c("button").t("click me");
expect($button1.toDom()).not.toBeNull();
expect($button1.toDom()).not.toBeDisabled();
$button1.a("disabled", "");
expect($button1.toDom()).toBeDisabled();
$button1.a("name", "button1");
expect($button1.toDom()?.getAttribute("name")).toEqual("button1");
const $button2 = $d.c("button").t("click me");
expect($button2.toDom()).not.toBeNull();
expect($button2.toDom()).not.toBeDisabled();
$button2.attr("disabled", "");
expect($button2.toDom()).toBeDisabled();
$button1.attr("name", "button2");
expect($button1.toDom()?.getAttribute("name")).toEqual("button2");
});
it("updates the HTMLElements dataset when a data attribute is set", () => {
const $div1 = $d.c("div");
expect($div1.toDom()).not.toBeNull();
expect($div1.toDom()?.dataset).toEqual({});
$div1.a("data-test", "some data");
expect($div1.toDom()?.dataset).toEqual({ test: "some data" });
const $div2 = $d.c("div");
expect($div2.toDom()).not.toBeNull();
expect($div2.toDom()?.dataset).toEqual({});
$div2.a("data-test", "some more data");
expect($div2.toDom()?.dataset).toEqual({ test: "some more data" });
});
});

View File

@@ -0,0 +1,50 @@
// @vitest-environment happy-dom
import { describe, expect, it } from "vitest";
import $d from "../fluent-dom-esm";
describe(".c() and .create()", () => {
it("should create an FluentDomObject-wrapped HTMLElement", () => {
const $div1 = $d(document.body).c("div");
expect($div1.toDom()).not.toBeNull();
expect($div1.toDom()).toBeEmptyDOMElement();
expect($div1.toDom()?.outerHTML).toEqual("<div></div>");
expect($div1.fluentDom).toBeDefined();
expect($div1.fluentDom).toEqual($div1.version());
const $span1 = $d(document.body).c("span");
expect($span1.toDom()).not.toBeNull();
expect($span1.toDom()).toBeEmptyDOMElement();
expect($span1.toDom()?.outerHTML).toEqual("<span></span>");
expect($span1.fluentDom).toBeDefined();
expect($span1.fluentDom).toEqual($span1.version());
const $div2 = $d(document.body).create("div");
expect($div2.toDom()).not.toBeNull();
expect($div2.toDom()).toBeEmptyDOMElement();
expect($div2.toDom()?.outerHTML).toEqual("<div></div>");
expect($div2.fluentDom).toBeDefined();
expect($div2.fluentDom).toEqual($div2.version());
const $span2 = $d(document.body).create("span");
expect($span2.toDom()).not.toBeNull();
expect($span2.toDom()).toBeEmptyDOMElement();
expect($span2.toDom()?.outerHTML).toEqual("<span></span>");
expect($span2.fluentDom).toBeDefined();
expect($span2.fluentDom).toEqual($span2.version());
});
it("should replace the current object with a new object", () => {
// default state
let $el = $d(document.body);
expect($el.toDom()).not.toBeNull();
expect($el.toDom()).toBeEmptyDOMElement();
expect($el.toDom()?.outerHTML).toEqual("<body></body>");
$el = $el.c("ul");
expect($el.toDom()).not.toBeNull();
expect($el.toDom()).toBeEmptyDOMElement();
expect($el.toDom()?.outerHTML).toEqual("<ul></ul>");
});
});

View File

@@ -0,0 +1,67 @@
// @vitest-environment happy-dom
import { beforeEach, describe, expect, it } from "vitest";
import $d from "../fluent-dom-esm";
describe("base object", () => {
beforeEach(() => {
document.body.replaceChildren();
});
it("should allow for wrapping an existing node", () => {
const $body = $d(document.body);
$d(document.body).app($d.c("div").cls("test-class").t("text content"));
expect($body.toDom()?.innerHTML).toEqual(
'<div class="test-class">text content</div>',
);
expect(document.body?.innerHTML).toEqual(
'<div class="test-class">text content</div>',
);
});
it("should allow for wrapping an existing node via a querySelector for an ID", () => {
$d(document.body)
.app($d.c("div").id("item1").t("Item 1"))
.app($d.c("div").id("item2").t("Item 2"))
.app($d.c("div").id("item3").cls("item-num3").t("Item 3"));
expect($d("#item2").toDom()).toHaveTextContent("Item 2");
expect($d("#item3").toDom()).toHaveClass("item-num3");
});
it("should create an FluentDomObject-wrapped HTMLElement via .c() and .create()", () => {
const $div1 = $d.c("div");
expect($div1.toDom()).not.toBeNull();
expect($div1.toDom()).toBeEmptyDOMElement();
expect($div1.toDom()?.outerHTML).toEqual("<div></div>");
expect($div1.fluentDom).toBeDefined();
expect($div1.fluentDom).toEqual($div1.version());
const $span1 = $d.c("span");
expect($span1.toDom()).not.toBeNull();
expect($span1.toDom()).toBeEmptyDOMElement();
expect($span1.toDom()?.outerHTML).toEqual("<span></span>");
expect($span1.fluentDom).toBeDefined();
expect($span1.fluentDom).toEqual($span1.version());
const $div2 = $d.create("div");
expect($div2.toDom()).not.toBeNull();
expect($div2.toDom()).toBeEmptyDOMElement();
expect($div2.toDom()?.outerHTML).toEqual("<div></div>");
expect($div2.fluentDom).toBeDefined();
expect($div2.fluentDom).toEqual($div2.version());
const $span2 = $d.create("span");
expect($span2.toDom()).not.toBeNull();
expect($span2.toDom()).toBeEmptyDOMElement();
expect($span2.toDom()?.outerHTML).toEqual("<span></span>");
expect($span2.fluentDom).toBeDefined();
expect($span2.fluentDom).toEqual($span2.version());
});
it("should return the FluentDomObject version via .v() and .version()", () => {
const $div = $d.c("div");
expect($d.v()).toEqual($div.fluentDom);
expect($d.version()).toEqual($div.fluentDom);
});
});

View File

@@ -0,0 +1,135 @@
// @vitest-environment happy-dom
import { beforeEach, describe, expect, it } from "vitest";
import $d from "../fluent-dom-esm";
describe("fluent-dom-esm tests", () => {
beforeEach(() => {
document.body.replaceChildren();
});
it("should support no arguments for `.rep()` and `.replaceChildren()`", () => {
$d(document.body).app($d.c("div").id("item1").t("Item 1"));
expect($d(document.body).rep().toDom()).toBeEmptyDOMElement();
$d(document.body).app($d.c("div").id("item1").t("Item 1"));
expect(
$d(document.body).replaceChildren().toDom(),
).toBeEmptyDOMElement();
});
it("should support an empty array for `.rep()` and `.replaceChildren()`", () => {
$d(document.body).app($d.c("div").id("item1").t("Item 1"));
expect(
$d(document.body)
.rep(...[])
.toDom(),
).toBeEmptyDOMElement();
$d(document.body).app($d.c("div").id("item1").t("Item 1"));
expect(
$d(document.body)
.replaceChildren(...[])
.toDom(),
).toBeEmptyDOMElement();
});
it("should support HTMLElement arguments for `.rep()` and `.replaceChildren()`", () => {
const newChild1 =
$d.c("div").id("new-child1").t("New Child 1").toDom() ?? "";
const newChild2 =
$d.c("div").id("new-child2").t("New Child 2").toDom() ?? "";
// default state
const body = $d(document.body).toDom();
expect(body).not.toBeNull();
expect(body).toBeEmptyDOMElement();
// add child
$d(document.body).app($d.c("div").id("item1").t("Item 1"));
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 1");
expect(body?.children.length).toEqual(1);
expect(body?.children[0]).toHaveAttribute("id", "item1");
// replace with one child
$d(document.body).rep(newChild1);
expect(body).not.toBeEmptyDOMElement();
expect(body).not.toHaveTextContent("Item 1");
expect(body).toHaveTextContent("New Child 1");
expect(body?.children.length).toEqual(1);
expect(body?.children[0]).toHaveAttribute("id", "new-child1");
// replace with two children
$d(document.body).rep(newChild1, newChild2);
expect(body).not.toBeEmptyDOMElement();
expect(body?.children.length).toEqual(2);
expect(body?.children[0]).toHaveAttribute("id", "new-child1");
expect(body?.children[0]).toHaveAttribute("id", "new-child1");
expect(body).toHaveTextContent("New Child 1");
expect(body).toHaveTextContent("New Child 2");
// replace with one child
$d(document.body).replaceChildren(newChild1);
expect(body).not.toBeEmptyDOMElement();
expect(body).not.toHaveTextContent("Item 1");
expect(body).toHaveTextContent("New Child 1");
expect(body?.children.length).toEqual(1);
expect(body?.children[0]).toHaveAttribute("id", "new-child1");
// replace with two children
$d(document.body).replaceChildren(newChild1, newChild2);
expect(body).not.toBeEmptyDOMElement();
expect(body?.children.length).toEqual(2);
expect(body?.children[0]).toHaveAttribute("id", "new-child1");
expect(body?.children[0]).toHaveAttribute("id", "new-child1");
expect(body).toHaveTextContent("New Child 1");
expect(body).toHaveTextContent("New Child 2");
});
it("should support string arguments for `.rep()` and `.replaceChildren()`", () => {
const newChild1 = "New Child 1";
const newChild2 = "New Child 2";
// default state
const body = $d(document.body).toDom();
expect(body).not.toBeNull();
expect(body).toBeEmptyDOMElement();
// add child
$d(document.body).app($d.c("div").id("item1").t("Item 1"));
expect(body).not.toBeEmptyDOMElement();
expect(body).toHaveTextContent("Item 1");
expect(body?.children.length).toEqual(1);
expect(body?.children[0]).toHaveAttribute("id", "item1");
// replace with one child
$d(document.body).rep(newChild1);
expect(body).not.toBeEmptyDOMElement();
expect(body).not.toHaveTextContent("Item 1");
expect(body).toHaveTextContent("New Child 1");
expect(body?.children.length).toEqual(0);
// replace with two children
$d(document.body).rep(newChild1, newChild2);
expect(body).not.toBeEmptyDOMElement();
expect(body?.children.length).toEqual(0);
expect(body).toHaveTextContent("New Child 1");
expect(body).toHaveTextContent("New Child 2");
// replace with one child
$d(document.body).replaceChildren(newChild1);
expect(body).not.toBeEmptyDOMElement();
expect(body).not.toHaveTextContent("Item 1");
expect(body).toHaveTextContent("New Child 1");
expect(body?.children.length).toEqual(0);
// replace with two children
$d(document.body).replaceChildren(newChild1, newChild2);
expect(body).not.toBeEmptyDOMElement();
expect(body?.children.length).toEqual(0);
expect(body).toHaveTextContent("New Child 1");
expect(body).toHaveTextContent("New Child 2");
});
});

View File

@@ -5,28 +5,43 @@ import { describe, expect, it } from "vitest";
import $d from "../fluent-dom-esm";
describe("fluent-dom-esm tests", () => {
it("should allow for wrapping an existing node", () => {
const $body = $d(document.body);
$d(document.body).app($d.c("div").cls("test-class").t("text content"));
expect($body.toDom()?.innerHTML).toEqual(
'<div class="test-class">text content</div>',
);
expect(document.body?.innerHTML).toEqual(
'<div class="test-class">text content</div>',
it("should update an anchor's href via .h() and .href()", () => {
// create base anchor
const $a1 = $d.c("a");
expect($a1.toDom()).not.toBeNull();
expect($a1.toDom()).toBeEmptyDOMElement();
expect($a1.toDom()?.outerHTML).toEqual("<a></a>");
expect($a1.toDom()?.getAttribute("href")).toBeNull();
// add href attribute
$a1.h("http://example.com");
expect($a1.toDom()?.getAttribute("href")).not.toBeNull();
expect($a1.toDom()?.getAttribute("href")).toEqual("http://example.com");
// create base anchor
const $a2 = $d.c("a");
expect($a2.toDom()).not.toBeNull();
expect($a2.toDom()).toBeEmptyDOMElement();
expect($a2.toDom()?.outerHTML).toEqual("<a></a>");
expect($a2.toDom()?.getAttribute("href")).toBeNull();
// add href attribute
$a2.href("mailto:someone@somewhere.com");
expect($a2.toDom()?.getAttribute("href")).not.toBeNull();
expect($a2.toDom()?.getAttribute("href")).toEqual(
"mailto:someone@somewhere.com",
);
});
it("should allow for wrapping an existing node via a querySelector for an ID", () => {
$d(document.body)
.app($d.c("div").id("item1").t("Item 1"))
.app($d.c("div").id("item2").t("Item 2"))
.app($d.c("div").id("item3").cls("item-num3").t("Item 3"));
expect($d("#item2").toDom()).toHaveTextContent("Item 2");
expect($d("#item3").toDom()).toHaveClass("item-num3");
});
it("should append text provided via `.text()`", () => {
it("should append text provided via `.t()` and `.text()`", () => {
const testVal = "testing 123";
expect($d.c("div").t(testVal).toDom()).toHaveTextContent(testVal);
expect($d.c("div").text(testVal).toDom()).toHaveTextContent(testVal);
});
it("should return the FluentDomObject version via .v() and .version()", () => {
const $div = $d.c("div");
expect($div.v()).toEqual($div.fluentDom);
expect($div.version()).toEqual($div.fluentDom);
});
});