import { mount } from "$core/binder";
import { signal } from "$core/signal";
import { describe, expect, it } from "vitest";
describe("data-volt-model binding", () => {
describe("text inputs", () => {
it("binds signal to text input value", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const name = signal("Alice");
mount(container, { name });
const input = container.querySelector("input")!;
expect(input.value).toBe("Alice");
});
it("updates input when signal changes", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const message = signal("Hello");
mount(container, { message });
const input = container.querySelector("input")!;
expect(input.value).toBe("Hello");
message.set("World");
expect(input.value).toBe("World");
});
it("updates signal when input changes", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const text = signal("initial");
mount(container, { text });
const input = container.querySelector("input")!;
input.value = "changed";
input.dispatchEvent(new Event("input"));
expect(text.get()).toBe("changed");
});
it("handles bidirectional updates", () => {
const container = document.createElement("div");
container.innerHTML = `
`;
const value = signal("test");
mount(container, { value });
const input = container.querySelector("input")!;
const span = container.querySelector("span")!;
expect(input.value).toBe("test");
expect(span.textContent).toBe("test");
input.value = "updated";
input.dispatchEvent(new Event("input"));
expect(value.get()).toBe("updated");
expect(span.textContent).toBe("updated");
});
});
describe("number inputs", () => {
it("binds signal to number input", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const count = signal(42);
mount(container, { count });
const input = container.querySelector("input")!;
expect(input.value).toBe("42");
});
it("updates signal with numeric value", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const quantity = signal(0);
mount(container, { quantity });
const input = container.querySelector("input")!;
input.value = "10";
input.dispatchEvent(new Event("input"));
expect(quantity.get()).toBe(10);
expect(typeof quantity.get()).toBe("number");
});
});
describe("checkbox inputs", () => {
it("binds signal to checkbox checked state", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const agreed = signal(true);
mount(container, { agreed });
const checkbox = container.querySelector("input")!;
expect(checkbox.checked).toBe(true);
});
it("updates checkbox when signal changes", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const enabled = signal(false);
mount(container, { enabled });
const checkbox = container.querySelector("input")!;
expect(checkbox.checked).toBe(false);
enabled.set(true);
expect(checkbox.checked).toBe(true);
});
it("updates signal when checkbox is clicked", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const checked = signal(false);
mount(container, { checked });
const checkbox = container.querySelector("input")!;
checkbox.checked = true;
checkbox.dispatchEvent(new Event("change"));
expect(checked.get()).toBe(true);
});
});
describe("radio buttons", () => {
it("binds signal to radio button selection", () => {
const container = document.createElement("div");
container.innerHTML = `
`;
const selected = signal("b");
mount(container, { selected });
const radios = container.querySelectorAll("input");
expect(radios[0].checked).toBe(false);
expect(radios[1].checked).toBe(true);
});
it("updates signal when radio is selected", () => {
const container = document.createElement("div");
container.innerHTML = `
`;
const choice = signal("x");
mount(container, { choice });
const radios = container.querySelectorAll("input");
radios[1].checked = true;
radios[1].dispatchEvent(new Event("change"));
expect(choice.get()).toBe("y");
});
});
describe("select elements", () => {
it("binds signal to select value", () => {
const container = document.createElement("div");
container.innerHTML = `
`;
const color = signal("blue");
mount(container, { color });
const select = container.querySelector("select")!;
expect(select.value).toBe("blue");
});
it("updates select when signal changes", () => {
const container = document.createElement("div");
container.innerHTML = `
`;
const size = signal("s");
mount(container, { size });
const select = container.querySelector("select")!;
expect(select.value).toBe("s");
size.set("l");
expect(select.value).toBe("l");
});
it("updates signal when selection changes", () => {
const container = document.createElement("div");
container.innerHTML = `
`;
const fruit = signal("apple");
mount(container, { fruit });
const select = container.querySelector("select")!;
select.value = "banana";
select.dispatchEvent(new Event("input"));
expect(fruit.get()).toBe("banana");
});
});
describe("textarea elements", () => {
it("binds signal to textarea value", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const content = signal("Hello World");
mount(container, { content });
const textarea = container.querySelector("textarea")!;
expect(textarea.value).toBe("Hello World");
});
it("updates textarea when signal changes", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const notes = signal("Initial");
mount(container, { notes });
const textarea = container.querySelector("textarea")!;
expect(textarea.value).toBe("Initial");
notes.set("Updated");
expect(textarea.value).toBe("Updated");
});
it("updates signal when textarea changes", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const message = signal("");
mount(container, { message });
const textarea = container.querySelector("textarea")!;
textarea.value = "New content";
textarea.dispatchEvent(new Event("input"));
expect(message.get()).toBe("New content");
});
});
});
describe("data-volt-bind:attr binding", () => {
describe("boolean attributes", () => {
it("binds disabled attribute", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const isDisabled = signal(true);
mount(container, { isDisabled });
const button = container.querySelector("button")!;
expect(button.hasAttribute("disabled")).toBe(true);
isDisabled.set(false);
expect(button.hasAttribute("disabled")).toBe(false);
});
it("binds readonly attribute", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const locked = signal(false);
mount(container, { locked });
const input = container.querySelector("input")!;
expect(input.hasAttribute("readonly")).toBe(false);
locked.set(true);
expect(input.hasAttribute("readonly")).toBe(true);
});
it("binds checked attribute", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const isChecked = signal(true);
mount(container, { isChecked });
const input = container.querySelector("input")!;
expect(input.hasAttribute("checked")).toBe(true);
});
it("binds required attribute", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const mandatory = signal(true);
mount(container, { mandatory });
const input = container.querySelector("input")!;
expect(input.hasAttribute("required")).toBe(true);
});
});
describe("string attributes", () => {
it("binds href attribute", () => {
const container = document.createElement("div");
container.innerHTML = `Link`;
const url = signal("https://example.com");
mount(container, { url });
const link = container.querySelector("a")!;
expect(link.getAttribute("href")).toBe("https://example.com");
url.set("https://volt.js.org");
expect(link.getAttribute("href")).toBe("https://volt.js.org");
});
it("binds src attribute", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const image = signal("/placeholder.png");
mount(container, { image });
const img = container.querySelector("img")!;
expect(img.getAttribute("src")).toBe("/placeholder.png");
});
it("binds title attribute", () => {
const container = document.createElement("div");
container.innerHTML = `Hover me`;
const tooltip = signal("Help text");
mount(container, { tooltip });
const span = container.querySelector("span")!;
expect(span.getAttribute("title")).toBe("Help text");
});
it("binds aria-label attribute", () => {
const container = document.createElement("div");
container.innerHTML = ``;
const label = signal("Close");
mount(container, { label });
const button = container.querySelector("button")!;
expect(button.getAttribute("aria-label")).toBe("Close");
});
});
describe("dynamic values", () => {
it("updates attribute when expression changes", () => {
const container = document.createElement("div");
container.innerHTML = `Link`;
const baseUrl = signal("/page1");
mount(container, { baseUrl });
const link = container.querySelector("a")!;
expect(link.getAttribute("href")).toBe("/page1");
baseUrl.set("/page2");
expect(link.getAttribute("href")).toBe("/page2");
});
it("removes attribute when value is null/undefined/false", () => {
const container = document.createElement("div");
container.innerHTML = `
If content
Else content
If content
Else content
Condition true
No condition