Precise DOM morphing
morphing typescript dom
at main 287 lines 9.7 kB view raw
1import { test, expect, describe } from "vitest" 2import { morph } from "../../src/morphlex" 3import { dom } from "./utils" 4 5describe("text input", () => { 6 test("morphing a modified value with preserveChanges enabled", () => { 7 const a = dom(`<input type="text" value="a">`) as HTMLInputElement 8 const b = dom(`<input type="text" value="b">`) as HTMLInputElement 9 10 a.value = "c" 11 morph(a, b, { preserveChanges: true }) 12 13 expect(a.outerHTML).toBe(`<input type="text" value="b">`) 14 expect(a.value).toBe("c") 15 }) 16 17 test("morphing a modified value preserveChanges disabled", () => { 18 const a = dom(`<input type="text" value="a">`) as HTMLInputElement 19 const b = dom(`<input type="text" value="b">`) as HTMLInputElement 20 21 a.value = "c" 22 morph(a, b, { preserveChanges: false }) 23 24 expect(a.outerHTML).toBe(`<input type="text" value="b">`) 25 expect(a.value).toBe("b") 26 }) 27 28 test("morphing an unmodified value with preserveChanges enabled", () => { 29 const a = dom(`<input type="text" value="a">`) as HTMLInputElement 30 const b = dom(`<input type="text" value="b">`) as HTMLInputElement 31 32 morph(a, b, { preserveChanges: true }) 33 34 expect(a.outerHTML).toBe(`<input type="text" value="b">`) 35 expect(a.value).toBe("b") 36 }) 37 38 test("morphing a modified value across multiple preserveChanges morphs updates defaultValue", () => { 39 const input = dom(`<input type="text" value="a">`) as HTMLInputElement 40 const firstTarget = dom(`<input type="text" value="b">`) as HTMLInputElement 41 const secondTarget = dom(`<input type="text" value="c">`) as HTMLInputElement 42 43 input.value = "user one" 44 morph(input, firstTarget, { preserveChanges: true }) 45 46 expect(input.value).toBe("user one") 47 expect(input.defaultValue).toBe("b") 48 expect(input.getAttribute("value")).toBe("b") 49 50 input.value = "user two" 51 morph(input, secondTarget, { preserveChanges: true }) 52 53 expect(input.value).toBe("user two") 54 expect(input.defaultValue).toBe("c") 55 expect(input.getAttribute("value")).toBe("c") 56 }) 57 58 test("morphing sibling inputs keeps modified value but updates the correct defaultValue", () => { 59 const from = dom(`<div><input type="text" name="n" value="a"><input type="text" name="n" value="a"></div>`) as HTMLElement 60 const to = dom(`<div><input type="text" name="n" value="b"><input type="text" name="n" value="a"></div>`) as HTMLElement 61 62 const first = from.children[0] as HTMLInputElement 63 const second = from.children[1] as HTMLInputElement 64 65 first.value = "user typed" 66 morph(from, to, { preserveChanges: true }) 67 68 expect(first.value).toBe("user typed") 69 expect(first.defaultValue).toBe("b") 70 expect(first.getAttribute("value")).toBe("b") 71 expect(second.value).toBe("a") 72 expect(second.defaultValue).toBe("a") 73 expect(second.getAttribute("value")).toBe("a") 74 }) 75 76 test("morphing updates default while dirty and keeps value dirty", () => { 77 const input = dom(`<input type="text" value="a">`) as HTMLInputElement 78 79 input.value = "b" 80 expect(input.value).toBe("b") 81 expect(input.defaultValue).toBe("a") 82 83 morph(input, dom(`<input type="text" value="c">`), { preserveChanges: true }) 84 expect(input.value).toBe("b") 85 expect(input.defaultValue).toBe("c") 86 87 input.value = "c" 88 expect(input.value).toBe("c") 89 expect(input.defaultValue).toBe("c") 90 91 morph(input, dom(`<input type="text" value="d">`), { preserveChanges: true }) 92 expect(input.value).toBe("c") 93 expect(input.defaultValue).toBe("d") 94 }) 95}) 96 97describe("checkbox", () => { 98 test("morphing a modified checkbox checked with preserveChanges enabled", () => { 99 const a = dom(`<input type="checkbox">`) as HTMLInputElement 100 const b = dom(`<input type="checkbox" checked>`) as HTMLInputElement 101 102 a.checked = true 103 morph(a, b, { preserveChanges: true }) 104 105 expect(a.hasAttribute("checked")).toBe(true) 106 expect(a.checked).toBe(true) 107 }) 108 109 test("morphing a modified checkbox checked with preserveChanges disabled", () => { 110 const a = dom(`<input type="checkbox">`) as HTMLInputElement 111 const b = dom(`<input type="checkbox" checked>`) as HTMLInputElement 112 113 a.checked = true 114 morph(a, b, { preserveChanges: false }) 115 116 expect(a.hasAttribute("checked")).toBe(true) 117 expect(a.checked).toBe(true) 118 }) 119 120 test("morphing an unmodified checkbox with preserveChanges enabled", () => { 121 const a = dom(`<input type="checkbox">`) as HTMLInputElement 122 const b = dom(`<input type="checkbox" checked>`) as HTMLInputElement 123 124 morph(a, b, { preserveChanges: true }) 125 126 expect(a.hasAttribute("checked")).toBe(true) 127 expect(a.checked).toBe(true) 128 }) 129 130 test("morphing an unmodified checkbox checked with preserveChanges enabled", () => { 131 const a = dom(`<input type="checkbox" checked>`) as HTMLInputElement 132 const b = dom(`<input type="checkbox">`) as HTMLInputElement 133 134 morph(a, b, { preserveChanges: true }) 135 136 expect(a.hasAttribute("checked")).toBe(false) 137 expect(a.checked).toBe(false) 138 }) 139 140 test("morphing a modified checkbox unchecked with preserveChanges enabled", () => { 141 const a = dom(`<input type="checkbox" checked>`) as HTMLInputElement 142 const b = dom(`<input type="checkbox">`) as HTMLInputElement 143 144 a.checked = false 145 morph(a, b, { preserveChanges: true }) 146 147 expect(a.hasAttribute("checked")).toBe(false) 148 expect(a.checked).toBe(false) 149 }) 150 151 test("morphing a modified checkbox unchecked with preserveChanges disabled", () => { 152 const a = dom(`<input type="checkbox" checked>`) as HTMLInputElement 153 const b = dom(`<input type="checkbox">`) as HTMLInputElement 154 155 a.checked = false 156 morph(a, b, { preserveChanges: false }) 157 158 expect(a.hasAttribute("checked")).toBe(false) 159 expect(a.checked).toBe(false) 160 }) 161}) 162 163describe("select", () => { 164 test("morphing a modified select option with preserveChanges enabled", () => { 165 const a = dom(`<select><option value="a">A</option><option value="b">B</option></select>`) as HTMLSelectElement 166 const b = dom(`<select><option value="a">A</option><option value="b" selected>B</option></select>`) as HTMLSelectElement 167 168 a.value = "b" 169 morph(a, b, { preserveChanges: true }) 170 171 expect(a.options[1].hasAttribute("selected")).toBe(true) 172 expect(a.value).toBe("b") 173 expect(a.options[1].selected).toBe(true) 174 }) 175 176 test("morphing a modified select option with preserveChanges disabled", () => { 177 const a = dom(`<select><option value="a">A</option><option value="b">B</option></select>`) as HTMLSelectElement 178 const b = dom(`<select><option value="a">A</option><option value="b" selected>B</option></select>`) as HTMLSelectElement 179 180 a.value = "b" 181 morph(a, b, { preserveChanges: false }) 182 183 expect(a.options[1].hasAttribute("selected")).toBe(true) 184 expect(a.value).toBe("b") 185 expect(a.options[1].selected).toBe(true) 186 }) 187 188 test("morphing an unmodified select option with preserveChanges enabled", () => { 189 const a = dom(`<select><option value="a">A</option><option value="b">B</option></select>`) as HTMLSelectElement 190 const b = dom(`<select><option value="a">A</option><option value="b" selected>B</option></select>`) as HTMLSelectElement 191 192 morph(a, b, { preserveChanges: true }) 193 194 expect(a.options[1].hasAttribute("selected")).toBe(true) 195 expect(a.value).toBe("b") 196 expect(a.options[1].selected).toBe(true) 197 }) 198 199 test("morphing a modified select option back to default with preserveChanges enabled", () => { 200 const a = dom(`<select><option value="a">A</option><option value="b" selected>B</option></select>`) as HTMLSelectElement 201 const b = dom(`<select><option value="a">A</option><option value="b">B</option></select>`) as HTMLSelectElement 202 203 a.value = "a" 204 morph(a, b, { preserveChanges: true }) 205 206 expect(a.options[1].hasAttribute("selected")).toBe(false) 207 expect(a.value).toBe("a") 208 expect(a.options[0].selected).toBe(true) 209 }) 210 211 test("morphing a modified select option back to default with preserveChanges disabled", () => { 212 const a = dom(`<select><option value="a">A</option><option value="b" selected>B</option></select>`) as HTMLSelectElement 213 const b = dom(`<select><option value="a">A</option><option value="b">B</option></select>`) as HTMLSelectElement 214 215 a.value = "a" 216 morph(a, b, { preserveChanges: false }) 217 218 expect(a.options[1].hasAttribute("selected")).toBe(false) 219 expect(a.value).toBe("a") 220 expect(a.options[0].selected).toBe(true) 221 }) 222 223 test("morphing a select option with no value", () => { 224 const a = dom( 225 ` 226 <select> 227 <option></option> 228 <option></option> 229 </select> 230 `, 231 ) 232 233 const b = dom( 234 ` 235 <select> 236 <option value="1">A</option> 237 <option value="2">B</option> 238 </select> 239 `, 240 ) 241 242 morph(a, b) 243 244 expect(a.outerHTML).toBe( 245 ` 246 <select> 247 <option value="1">A</option> 248 <option value="2">B</option> 249 </select> 250 `.trim(), 251 ) 252 }) 253}) 254 255describe("textarea", () => { 256 test("morphing a modified textarea value with preserveChanges enabled", () => { 257 const a = dom(`<textarea>a</textarea>`) as HTMLTextAreaElement 258 const b = dom(`<textarea>b</textarea>`) as HTMLTextAreaElement 259 260 a.value = "c" 261 morph(a, b, { preserveChanges: true }) 262 263 expect(a.textContent).toBe("b") 264 expect(a.value).toBe("c") 265 }) 266 267 test("morphing a modified textarea value with preserveChanges disabled", () => { 268 const a = dom(`<textarea>a</textarea>`) as HTMLTextAreaElement 269 const b = dom(`<textarea>b</textarea>`) as HTMLTextAreaElement 270 271 a.value = "c" 272 morph(a, b, { preserveChanges: false }) 273 274 expect(a.textContent).toBe("b") 275 expect(a.value).toBe("b") 276 }) 277 278 test("morphing an unmodified textarea value with preserveChanges enabled", () => { 279 const a = dom(`<textarea>a</textarea>`) as HTMLTextAreaElement 280 const b = dom(`<textarea>b</textarea>`) as HTMLTextAreaElement 281 282 morph(a, b, { preserveChanges: true }) 283 284 expect(a.textContent).toBe("b") 285 expect(a.value).toBe("b") 286 }) 287})