import { mount } from "$core/binder";
import { signal } from "$core/signal";
import { describe, expect, it } from "vitest";
describe("binder", () => {
describe("mount", () => {
it("returns a cleanup function", () => {
const element = document.createElement("div");
const cleanup = mount(element, {});
expect(typeof cleanup).toBe("function");
cleanup();
});
it("binds data-volt-text to element text content", () => {
const element = document.createElement("div");
element.dataset.voltText = "message";
const scope = { message: "Hello, World!" };
mount(element, scope);
expect(element.textContent).toBe("Hello, World!");
});
it("updates text content when signal changes", () => {
const element = document.createElement("div");
element.dataset.voltText = "count";
const count = signal(0);
const scope = { count };
mount(element, scope);
expect(element.textContent).toBe("0");
count.set(5);
expect(element.textContent).toBe("5");
count.set(10);
expect(element.textContent).toBe("10");
});
it("binds data-volt-html to element HTML content", () => {
const element = document.createElement("div");
element.dataset.voltHtml = "content";
const scope = { content: "Bold" };
mount(element, scope);
expect(element.innerHTML).toBe("Bold");
});
it("updates HTML content when signal changes", () => {
const element = document.createElement("div");
element.dataset.voltHtml = "html";
const html = signal("Italic");
const scope = { html };
mount(element, scope);
expect(element.innerHTML).toBe("Italic");
html.set("Bold");
expect(element.innerHTML).toBe("Bold");
});
it("binds data-volt-class with string value", () => {
const element = document.createElement("div");
element.dataset.voltClass = "classes";
const scope = { classes: "active highlight" };
mount(element, scope);
expect(element.classList.contains("active")).toBe(true);
expect(element.classList.contains("highlight")).toBe(true);
});
it("binds data-volt-class with object value", () => {
const element = document.createElement("div");
element.dataset.voltClass = "classes";
const scope = { classes: { active: true, disabled: false } };
mount(element, scope);
expect(element.classList.contains("active")).toBe(true);
expect(element.classList.contains("disabled")).toBe(false);
});
it("updates classes when signal changes", () => {
const element = document.createElement("div");
element.dataset.voltClass = "classes";
const classes = signal({ active: false, disabled: false });
const scope = { classes };
mount(element, scope);
expect(element.classList.contains("active")).toBe(false);
classes.set({ active: true, disabled: false });
expect(element.classList.contains("active")).toBe(true);
expect(element.classList.contains("disabled")).toBe(false);
classes.set({ active: false, disabled: true });
expect(element.classList.contains("active")).toBe(false);
expect(element.classList.contains("disabled")).toBe(true);
});
it("removes old classes when signal changes", () => {
const element = document.createElement("div");
element.dataset.voltClass = "classes";
const classes = signal("foo bar");
const scope = { classes };
mount(element, scope);
expect(element.classList.contains("foo")).toBe(true);
expect(element.classList.contains("bar")).toBe(true);
classes.set("baz");
expect(element.classList.contains("foo")).toBe(false);
expect(element.classList.contains("bar")).toBe(false);
expect(element.classList.contains("baz")).toBe(true);
});
it("binds data-volt-class with multiple signal dependencies", () => {
const element = document.createElement("div");
element.dataset.voltClass = "{active: isActive, disabled: isDisabled}";
const isActive = signal(true);
const isDisabled = signal(false);
const scope = { isActive, isDisabled };
mount(element, scope);
expect(element.classList.contains("active")).toBe(true);
expect(element.classList.contains("disabled")).toBe(false);
isActive.set(false);
expect(element.classList.contains("active")).toBe(false);
expect(element.classList.contains("disabled")).toBe(false);
isDisabled.set(true);
expect(element.classList.contains("active")).toBe(false);
expect(element.classList.contains("disabled")).toBe(true);
isActive.set(true);
isDisabled.set(false);
expect(element.classList.contains("active")).toBe(true);
expect(element.classList.contains("disabled")).toBe(false);
});
it("binds nested elements", () => {
const parent = document.createElement("div");
const child1 = document.createElement("span");
const child2 = document.createElement("span");
parent.append(child1, child2);
child1.dataset.voltText = "first";
child2.dataset.voltText = "second";
const scope = { first: "First", second: "Second" };
mount(parent, scope);
expect(child1.textContent).toBe("First");
expect(child2.textContent).toBe("Second");
expect(parent.textContent).toBe("FirstSecond");
});
it("cleans up subscriptions on unmount", () => {
const element = document.createElement("div");
element.dataset.voltText = "count";
const count = signal(0);
const scope = { count };
const cleanup = mount(element, scope);
count.set(5);
expect(element.textContent).toBe("5");
cleanup();
count.set(10);
expect(element.textContent).toBe("5");
});
it("handles multiple bindings on the same element", () => {
const element = document.createElement("div");
element.dataset.voltText = "message";
element.dataset.voltClass = "classes";
const message = signal("Hello");
const classes = signal("active");
const scope = { message, classes };
mount(element, scope);
expect(element.textContent).toBe("Hello");
expect(element.classList.contains("active")).toBe(true);
message.set("Goodbye");
classes.set("inactive");
expect(element.textContent).toBe("Goodbye");
expect(element.classList.contains("inactive")).toBe(true);
});
it("evaluates nested property paths", () => {
const element = document.createElement("div");
element.dataset.voltText = "user.name";
const scope = { user: { name: "Alice" } };
mount(element, scope);
expect(element.textContent).toBe("Alice");
});
it("handles static values (no signals)", () => {
const element = document.createElement("div");
element.dataset.voltText = "message";
const scope = { message: "Static" };
mount(element, scope);
expect(element.textContent).toBe("Static");
});
it("handles literal expressions", () => {
const element = document.createElement("div");
element.dataset.voltText = "'Hello'";
mount(element, {});
expect(element.textContent).toBe("Hello");
});
});
});