import { computed, mount, signal } from "$volt";
import { describe, expect, it } from "vitest";
describe("integration: list rendering", () => {
it("creates a reactive todo list", () => {
const container = document.createElement("div");
container.innerHTML = `
`;
type Todo = { id: number; text: string; completed: boolean };
let nextId = 1;
const todos = signal([{ id: nextId++, text: "Learn Volt.js", completed: false }, {
id: nextId++,
text: "Build an app",
completed: false,
}]);
const remaining = computed(() => {
return todos.get().filter((t) => !t.completed).length;
});
const addTodo = () => {
const input = container.querySelector("#new-todo") as HTMLInputElement;
if (input.value.trim()) {
todos.set([...todos.get(), { id: nextId++, text: input.value, completed: false }]);
input.value = "";
}
};
const toggleTodo = (event: Event) => {
const checkbox = event.target as HTMLInputElement;
const li = checkbox.closest("li");
const index = [...(li?.parentElement?.children || [])].indexOf(li!);
const updated = todos.get().map((todo, i) => (i === index ? { ...todo, completed: checkbox.checked } : todo));
todos.set(updated);
};
const deleteTodo = (event: Event) => {
const button = event.target as HTMLButtonElement;
const li = button.closest("li");
const index = [...(li?.parentElement?.children || [])].indexOf(li!);
todos.set(todos.get().filter((_, i) => i !== index));
};
mount(container, { todos, remaining, addTodo, toggleTodo, deleteTodo });
const listItems = container.querySelectorAll("#todo-list li");
expect(listItems.length).toBe(2);
expect(listItems[0]?.querySelector("span")?.textContent).toBe("Learn Volt.js");
expect(listItems[1]?.querySelector("span")?.textContent).toBe("Build an app");
const remainingDiv = container.querySelector("div[data-volt-text='remaining']");
expect(remainingDiv?.textContent).toBe("2");
const checkboxes = container.querySelectorAll("input[type='checkbox']");
(checkboxes[0] as HTMLInputElement).checked = true;
checkboxes[0]?.dispatchEvent(new Event("click", { bubbles: true }));
expect(remainingDiv?.textContent).toBe("1");
const deleteButtons = container.querySelectorAll("button[data-volt-on-click='deleteTodo']");
deleteButtons[1]?.dispatchEvent(new Event("click", { bubbles: true }));
const updatedListItems = container.querySelectorAll("#todo-list li");
expect(updatedListItems.length).toBe(1);
expect(updatedListItems[0]?.querySelector("span")?.textContent).toBe("Learn Volt.js");
});
it("renders filtered lists with computed signals", () => {
const container = document.createElement("div");
container.innerHTML = `
`;
const items = signal([{ name: "Item 1", active: true }, { name: "Item 2", active: false }, {
name: "Item 3",
active: true,
}]);
const activeItems = computed(() => items.get().filter((item) => item.active));
mount(container, { allItems: items, activeItems });
const allItemDivs = container.querySelectorAll("#all-items > div[data-volt-for]");
const activeItemDivs = container.querySelectorAll("#active-items > div[data-volt-for]");
expect(allItemDivs.length).toBe(0);
expect(activeItemDivs.length).toBe(0);
const renderedAll = container.querySelectorAll("#all-items div[data-volt-text]");
const renderedActive = container.querySelectorAll("#active-items div[data-volt-text]");
expect(renderedAll.length).toBe(3);
expect(renderedActive.length).toBe(2);
expect(renderedActive[0]?.textContent).toBe("Item 1");
expect(renderedActive[1]?.textContent).toBe("Item 3");
items.set([{ name: "Item 1", active: false }, { name: "Item 2", active: true }, { name: "Item 3", active: true }]);
const updatedActive = container.querySelectorAll("#active-items div[data-volt-text]");
expect(updatedActive.length).toBe(2);
expect(updatedActive[0]?.textContent).toBe("Item 2");
expect(updatedActive[1]?.textContent).toBe("Item 3");
});
it("handles complex nested data structures", () => {
const container = document.createElement("div");
container.innerHTML = `
`;
const categories = signal([{
name: "Electronics",
products: [{ name: "Laptop", price: 999 }, { name: "Phone", price: 699 }],
}, { name: "Books", products: [{ name: "JavaScript Guide", price: 29 }, { name: "CSS Mastery", price: 35 }] }]);
mount(container, { categories });
const categoryDivs = container.querySelectorAll("div > div > h2");
expect(categoryDivs.length).toBe(2);
expect(categoryDivs[0]?.textContent).toBe("Electronics");
expect(categoryDivs[1]?.textContent).toBe("Books");
const categoryDivElements = container.querySelectorAll("div > div > div");
expect(categoryDivElements.length).toBe(2);
const electronicsProducts = categoryDivElements[0]?.querySelectorAll("ul li");
expect(electronicsProducts?.length).toBe(2);
expect(electronicsProducts?.[0]?.textContent).toContain("Laptop");
expect(electronicsProducts?.[0]?.textContent).toContain("999");
const booksProducts = categoryDivElements[1]?.querySelectorAll("ul li");
expect(booksProducts?.length).toBe(2);
expect(booksProducts?.[1]?.textContent).toContain("CSS Mastery");
expect(booksProducts?.[1]?.textContent).toContain("35");
});
});