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 290 lines 8.3 kB view raw
1import { mount } from "$core/binder"; 2import { extractDeps } from "$core/shared"; 3import { signal } from "$core/signal"; 4import { describe, expect, it } from "vitest"; 5 6describe("data-volt-if binding", () => { 7 it("shows element when condition is truthy", () => { 8 const container = document.createElement("div"); 9 container.innerHTML = ` 10 <div> 11 <p data-volt-if="show" data-volt-text="message">Hidden</p> 12 </div> 13 `; 14 15 const show = signal(true); 16 const message = "Visible!"; 17 18 mount(container, { show, message }); 19 20 const paragraph = container.querySelector("p"); 21 expect(paragraph).toBeTruthy(); 22 expect(paragraph?.textContent).toBe("Visible!"); 23 }); 24 25 it("hides element when condition is falsy", () => { 26 const container = document.createElement("div"); 27 container.innerHTML = ` 28 <div> 29 <p data-volt-if="show">Should not appear</p> 30 </div> 31 `; 32 33 const show = signal(false); 34 mount(container, { show }); 35 36 const paragraph = container.querySelector("p"); 37 expect(paragraph).toBeNull(); 38 }); 39 40 it("toggles element visibility when signal changes", () => { 41 const container = document.createElement("div"); 42 container.innerHTML = ` 43 <div> 44 <span data-volt-if="visible">Toggle Me</span> 45 </div> 46 `; 47 48 const visible = signal(false); 49 mount(container, { visible }); 50 51 expect(container.querySelector("span")).toBeNull(); 52 53 visible.set(true); 54 expect(container.querySelector("span")).toBeTruthy(); 55 expect(container.querySelector("span")?.textContent).toBe("Toggle Me"); 56 57 visible.set(false); 58 expect(container.querySelector("span")).toBeNull(); 59 60 visible.set(true); 61 expect(container.querySelector("span")).toBeTruthy(); 62 }); 63 64 it("works with static truthy values", () => { 65 const container = document.createElement("div"); 66 container.innerHTML = ` 67 <div> 68 <p data-volt-if="alwaysTrue">Always visible</p> 69 </div> 70 `; 71 72 mount(container, { alwaysTrue: true }); 73 74 const paragraph = container.querySelector("p"); 75 expect(paragraph).toBeTruthy(); 76 expect(paragraph?.textContent).toBe("Always visible"); 77 }); 78 79 it("works with static falsy values", () => { 80 const container = document.createElement("div"); 81 container.innerHTML = ` 82 <div> 83 <p data-volt-if="alwaysFalse">Never visible</p> 84 </div> 85 `; 86 87 mount(container, { alwaysFalse: false }); 88 89 expect(container.querySelector("p")).toBeNull(); 90 }); 91 92 it("preserves element bindings when re-rendering", () => { 93 const container = document.createElement("div"); 94 container.innerHTML = ` 95 <div> 96 <div data-volt-if="show"> 97 <span data-volt-text="message">Default</span> 98 </div> 99 </div> 100 `; 101 102 const show = signal(true); 103 const message = signal("First"); 104 105 mount(container, { show, message }); 106 107 expect(container.querySelector("span")?.textContent).toBe("First"); 108 109 message.set("Second"); 110 expect(container.querySelector("span")?.textContent).toBe("Second"); 111 112 show.set(false); 113 expect(container.querySelector("div[data-volt-if]")).toBeNull(); 114 115 message.set("Third"); 116 show.set(true); 117 118 expect(container.querySelector("span")?.textContent).toBe("Third"); 119 }); 120 121 it("handles nested bindings correctly", () => { 122 const container = document.createElement("div"); 123 container.innerHTML = ` 124 <div> 125 <div data-volt-if="showOuter"> 126 <p>Outer</p> 127 <div data-volt-if="showInner"> 128 <p>Inner</p> 129 </div> 130 </div> 131 </div> 132 `; 133 134 const showOuter = signal(true); 135 const showInner = signal(true); 136 137 mount(container, { showOuter, showInner }); 138 139 expect(container.textContent?.trim()).toContain("Outer"); 140 expect(container.textContent?.trim()).toContain("Inner"); 141 142 showInner.set(false); 143 expect(container.textContent?.trim()).toContain("Outer"); 144 expect(container.textContent?.trim()).not.toContain("Inner"); 145 146 showOuter.set(false); 147 expect(container.textContent?.trim()).not.toContain("Outer"); 148 expect(container.textContent?.trim()).not.toContain("Inner"); 149 150 showOuter.set(true); 151 expect(container.textContent?.trim()).toContain("Outer"); 152 expect(container.textContent?.trim()).not.toContain("Inner"); 153 154 showInner.set(true); 155 expect(container.textContent?.trim()).toContain("Outer"); 156 expect(container.textContent?.trim()).toContain("Inner"); 157 }); 158 159 it("properly cleans up when unmounting", () => { 160 const container = document.createElement("div"); 161 container.innerHTML = ` 162 <div> 163 <p data-volt-if="show" data-volt-text="message">Hidden</p> 164 </div> 165 `; 166 167 const show = signal(true); 168 const message = signal("Hello"); 169 170 const cleanup = mount(container, { show, message }); 171 172 expect(container.querySelector("p")?.textContent).toBe("Hello"); 173 174 const elementBeforeCleanup = container.querySelector("p"); 175 176 cleanup(); 177 178 message.set("Changed"); 179 expect(elementBeforeCleanup?.textContent).toBe("Hello"); 180 181 show.set(false); 182 expect(container.querySelector("p")).toBe(elementBeforeCleanup); 183 }); 184 185 it("handles event handlers correctly", () => { 186 const container = document.createElement("div"); 187 container.innerHTML = ` 188 <div> 189 <button data-volt-if="show" data-volt-on-click="handleClick">Click Me</button> 190 </div> 191 `; 192 193 let clicked = false; 194 const handleClick = () => { 195 clicked = true; 196 }; 197 const show = signal(true); 198 199 mount(container, { show, handleClick }); 200 201 const button = container.querySelector("button"); 202 expect(button).toBeTruthy(); 203 204 button?.click(); 205 expect(clicked).toBe(true); 206 207 show.set(false); 208 expect(container.querySelector("button")).toBeNull(); 209 210 clicked = false; 211 show.set(true); 212 container.querySelector("button")?.click(); 213 expect(clicked).toBe(true); 214 }); 215 216 it("works with property paths", () => { 217 const container = document.createElement("div"); 218 container.innerHTML = ` 219 <div> 220 <p data-volt-if="user.isActive">User is active</p> 221 </div> 222 `; 223 224 const user = { isActive: signal(true) }; 225 mount(container, { user }); 226 227 expect(container.querySelector("p")).toBeTruthy(); 228 229 user.isActive.set(false); 230 expect(container.querySelector("p")).toBeNull(); 231 232 user.isActive.set(true); 233 expect(container.querySelector("p")).toBeTruthy(); 234 }); 235 236 it("evaluates truthy and falsy values correctly", () => { 237 const container = document.createElement("div"); 238 container.innerHTML = ` 239 <div> 240 <p id="zero" data-volt-if="zero">0</p> 241 <p id="empty" data-volt-if="empty">Empty</p> 242 <p id="one" data-volt-if="one">1</p> 243 <p id="string" data-volt-if="string">String</p> 244 </div> 245 `; 246 247 mount(container, { zero: signal(0), empty: signal(""), one: signal(1), string: signal("text") }); 248 249 expect(container.querySelector("#zero")).toBeNull(); 250 expect(container.querySelector("#empty")).toBeNull(); 251 expect(container.querySelector("#one")).toBeTruthy(); 252 expect(container.querySelector("#string")).toBeTruthy(); 253 }); 254 255 it("reacts to complex expressions with multiple signal dependencies", () => { 256 const container = document.createElement("div"); 257 container.innerHTML = ` 258 <div> 259 <p data-volt-if="value.length > 0 && !isValid">Error message</p> 260 </div> 261 `; 262 263 const value = signal(""); 264 const isValid = signal(true); 265 const scope = { value, isValid }; 266 const deps = extractDeps("value.length > 0 && !isValid", scope); 267 expect(deps.length).toBe(2); 268 expect(deps).toContain(value); 269 expect(deps).toContain(isValid); 270 271 mount(container, scope); 272 expect(container.querySelector("p")).toBeNull(); 273 274 value.set("test"); 275 expect(container.querySelector("p")).toBeNull(); 276 277 isValid.set(false); 278 expect(container.querySelector("p")).toBeTruthy(); 279 expect(container.querySelector("p")?.textContent).toBe("Error message"); 280 281 value.set(""); 282 expect(container.querySelector("p")).toBeNull(); 283 284 value.set("test"); 285 expect(container.querySelector("p")).toBeTruthy(); 286 287 isValid.set(true); 288 expect(container.querySelector("p")).toBeNull(); 289 }); 290});