a reactive (signals based) hypermedia web framework (wip) stormlightlabs.github.io/volt/
hypermedia frontend signals
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 233 lines 7.4 kB view raw
1import { mount } from "$core/binder"; 2import { signal } from "$core/signal"; 3import { describe, expect, it } from "vitest"; 4 5describe("binder", () => { 6 describe("mount", () => { 7 it("returns a cleanup function", () => { 8 const element = document.createElement("div"); 9 const cleanup = mount(element, {}); 10 11 expect(typeof cleanup).toBe("function"); 12 cleanup(); 13 }); 14 15 it("binds data-volt-text to element text content", () => { 16 const element = document.createElement("div"); 17 element.dataset.voltText = "message"; 18 19 const scope = { message: "Hello, World!" }; 20 mount(element, scope); 21 22 expect(element.textContent).toBe("Hello, World!"); 23 }); 24 25 it("updates text content when signal changes", () => { 26 const element = document.createElement("div"); 27 element.dataset.voltText = "count"; 28 29 const count = signal(0); 30 const scope = { count }; 31 mount(element, scope); 32 33 expect(element.textContent).toBe("0"); 34 35 count.set(5); 36 expect(element.textContent).toBe("5"); 37 38 count.set(10); 39 expect(element.textContent).toBe("10"); 40 }); 41 42 it("binds data-volt-html to element HTML content", () => { 43 const element = document.createElement("div"); 44 element.dataset.voltHtml = "content"; 45 46 const scope = { content: "<strong>Bold</strong>" }; 47 mount(element, scope); 48 49 expect(element.innerHTML).toBe("<strong>Bold</strong>"); 50 }); 51 52 it("updates HTML content when signal changes", () => { 53 const element = document.createElement("div"); 54 element.dataset.voltHtml = "html"; 55 56 const html = signal("<em>Italic</em>"); 57 const scope = { html }; 58 mount(element, scope); 59 60 expect(element.innerHTML).toBe("<em>Italic</em>"); 61 62 html.set("<strong>Bold</strong>"); 63 expect(element.innerHTML).toBe("<strong>Bold</strong>"); 64 }); 65 66 it("binds data-volt-class with string value", () => { 67 const element = document.createElement("div"); 68 element.dataset.voltClass = "classes"; 69 70 const scope = { classes: "active highlight" }; 71 mount(element, scope); 72 73 expect(element.classList.contains("active")).toBe(true); 74 expect(element.classList.contains("highlight")).toBe(true); 75 }); 76 77 it("binds data-volt-class with object value", () => { 78 const element = document.createElement("div"); 79 element.dataset.voltClass = "classes"; 80 81 const scope = { classes: { active: true, disabled: false } }; 82 mount(element, scope); 83 84 expect(element.classList.contains("active")).toBe(true); 85 expect(element.classList.contains("disabled")).toBe(false); 86 }); 87 88 it("updates classes when signal changes", () => { 89 const element = document.createElement("div"); 90 element.dataset.voltClass = "classes"; 91 92 const classes = signal({ active: false, disabled: false }); 93 const scope = { classes }; 94 mount(element, scope); 95 96 expect(element.classList.contains("active")).toBe(false); 97 98 classes.set({ active: true, disabled: false }); 99 expect(element.classList.contains("active")).toBe(true); 100 expect(element.classList.contains("disabled")).toBe(false); 101 102 classes.set({ active: false, disabled: true }); 103 expect(element.classList.contains("active")).toBe(false); 104 expect(element.classList.contains("disabled")).toBe(true); 105 }); 106 107 it("removes old classes when signal changes", () => { 108 const element = document.createElement("div"); 109 element.dataset.voltClass = "classes"; 110 111 const classes = signal("foo bar"); 112 const scope = { classes }; 113 mount(element, scope); 114 115 expect(element.classList.contains("foo")).toBe(true); 116 expect(element.classList.contains("bar")).toBe(true); 117 118 classes.set("baz"); 119 expect(element.classList.contains("foo")).toBe(false); 120 expect(element.classList.contains("bar")).toBe(false); 121 expect(element.classList.contains("baz")).toBe(true); 122 }); 123 124 it("binds data-volt-class with multiple signal dependencies", () => { 125 const element = document.createElement("div"); 126 element.dataset.voltClass = "{active: isActive, disabled: isDisabled}"; 127 128 const isActive = signal(true); 129 const isDisabled = signal(false); 130 const scope = { isActive, isDisabled }; 131 mount(element, scope); 132 133 expect(element.classList.contains("active")).toBe(true); 134 expect(element.classList.contains("disabled")).toBe(false); 135 136 isActive.set(false); 137 expect(element.classList.contains("active")).toBe(false); 138 expect(element.classList.contains("disabled")).toBe(false); 139 140 isDisabled.set(true); 141 expect(element.classList.contains("active")).toBe(false); 142 expect(element.classList.contains("disabled")).toBe(true); 143 144 isActive.set(true); 145 isDisabled.set(false); 146 expect(element.classList.contains("active")).toBe(true); 147 expect(element.classList.contains("disabled")).toBe(false); 148 }); 149 150 it("binds nested elements", () => { 151 const parent = document.createElement("div"); 152 const child1 = document.createElement("span"); 153 const child2 = document.createElement("span"); 154 parent.append(child1, child2); 155 156 child1.dataset.voltText = "first"; 157 child2.dataset.voltText = "second"; 158 159 const scope = { first: "First", second: "Second" }; 160 mount(parent, scope); 161 162 expect(child1.textContent).toBe("First"); 163 expect(child2.textContent).toBe("Second"); 164 expect(parent.textContent).toBe("FirstSecond"); 165 }); 166 167 it("cleans up subscriptions on unmount", () => { 168 const element = document.createElement("div"); 169 element.dataset.voltText = "count"; 170 171 const count = signal(0); 172 const scope = { count }; 173 const cleanup = mount(element, scope); 174 175 count.set(5); 176 expect(element.textContent).toBe("5"); 177 178 cleanup(); 179 180 count.set(10); 181 expect(element.textContent).toBe("5"); 182 }); 183 184 it("handles multiple bindings on the same element", () => { 185 const element = document.createElement("div"); 186 element.dataset.voltText = "message"; 187 element.dataset.voltClass = "classes"; 188 189 const message = signal("Hello"); 190 const classes = signal("active"); 191 const scope = { message, classes }; 192 mount(element, scope); 193 194 expect(element.textContent).toBe("Hello"); 195 expect(element.classList.contains("active")).toBe(true); 196 197 message.set("Goodbye"); 198 classes.set("inactive"); 199 200 expect(element.textContent).toBe("Goodbye"); 201 expect(element.classList.contains("inactive")).toBe(true); 202 }); 203 204 it("evaluates nested property paths", () => { 205 const element = document.createElement("div"); 206 element.dataset.voltText = "user.name"; 207 208 const scope = { user: { name: "Alice" } }; 209 mount(element, scope); 210 211 expect(element.textContent).toBe("Alice"); 212 }); 213 214 it("handles static values (no signals)", () => { 215 const element = document.createElement("div"); 216 element.dataset.voltText = "message"; 217 218 const scope = { message: "Static" }; 219 mount(element, scope); 220 221 expect(element.textContent).toBe("Static"); 222 }); 223 224 it("handles literal expressions", () => { 225 const element = document.createElement("div"); 226 element.dataset.voltText = "'Hello'"; 227 228 mount(element, {}); 229 230 expect(element.textContent).toBe("Hello"); 231 }); 232 }); 233});