1import { patch } from 'web/runtime/patch'2import VNode, { createEmptyVNode } from 'core/vdom/vnode'34function prop(name) {5 return obj => {6 return obj[name]7 }8}910function map(fn, list) {11 const ret: any[] = []12 for (let i = 0; i < list.length; i++) {13 ret[i] = fn(list[i])14 }15 return ret16}1718function spanNum(n) {19 if (typeof n === 'string') {20 return new VNode('span', {}, undefined, n)21 } else {22 return new VNode('span', { key: n }, undefined, n.toString())23 }24}2526function shuffle(array) {27 let currentIndex = array.length28 let temporaryValue29 let randomIndex3031 // while there remain elements to shuffle...32 while (currentIndex !== 0) {33 // pick a remaining element...34 randomIndex = Math.floor(Math.random() * currentIndex)35 currentIndex -= 136 // and swap it with the current element.37 temporaryValue = array[currentIndex]38 array[currentIndex] = array[randomIndex]39 array[randomIndex] = temporaryValue40 }41 return array42}4344const inner = prop('innerHTML')45const tag = prop('tagName')4647describe('vdom patch: children', () => {48 let vnode049 beforeEach(() => {50 vnode0 = new VNode('p', { attrs: { id: '1' } }, [51 createTextVNode('hello world')52 ])53 patch(null, vnode0)54 })5556 it('should appends elements', () => {57 const vnode1 = new VNode('p', {}, [1].map(spanNum))58 const vnode2 = new VNode('p', {}, [1, 2, 3].map(spanNum))59 let elm = patch(vnode0, vnode1)60 expect(elm.children.length).toBe(1)61 elm = patch(vnode1, vnode2)62 expect(elm.children.length).toBe(3)63 expect(elm.children[1].innerHTML).toBe('2')64 expect(elm.children[2].innerHTML).toBe('3')65 })6667 it('should prepends elements', () => {68 const vnode1 = new VNode('p', {}, [4, 5].map(spanNum))69 const vnode2 = new VNode('p', {}, [1, 2, 3, 4, 5].map(spanNum))70 let elm = patch(vnode0, vnode1)71 expect(elm.children.length).toBe(2)72 elm = patch(vnode1, vnode2)73 expect(map(inner, elm.children)).toEqual(['1', '2', '3', '4', '5'])74 })7576 it('should add elements in the middle', () => {77 const vnode1 = new VNode('p', {}, [1, 2, 4, 5].map(spanNum))78 const vnode2 = new VNode('p', {}, [1, 2, 3, 4, 5].map(spanNum))79 let elm = patch(vnode0, vnode1)80 expect(elm.children.length).toBe(4)81 elm = patch(vnode1, vnode2)82 expect(map(inner, elm.children)).toEqual(['1', '2', '3', '4', '5'])83 })8485 it('should add elements at begin and end', () => {86 const vnode1 = new VNode('p', {}, [2, 3, 4].map(spanNum))87 const vnode2 = new VNode('p', {}, [1, 2, 3, 4, 5].map(spanNum))88 let elm = patch(vnode0, vnode1)89 expect(elm.children.length).toBe(3)90 elm = patch(vnode1, vnode2)91 expect(map(inner, elm.children)).toEqual(['1', '2', '3', '4', '5'])92 })9394 it('should add children to parent with no children', () => {95 const vnode1 = new VNode('p', { key: 'p' })96 const vnode2 = new VNode('p', { key: 'p' }, [1, 2, 3].map(spanNum))97 let elm = patch(vnode0, vnode1)98 expect(elm.children.length).toBe(0)99 elm = patch(vnode1, vnode2)100 expect(map(inner, elm.children)).toEqual(['1', '2', '3'])101 })102103 it('should remove all children from parent', () => {104 const vnode1 = new VNode('p', { key: 'p' }, [1, 2, 3].map(spanNum))105 const vnode2 = new VNode('p', { key: 'p' })106 let elm = patch(vnode0, vnode1)107 expect(map(inner, elm.children)).toEqual(['1', '2', '3'])108 elm = patch(vnode1, vnode2)109 expect(elm.children.length).toBe(0)110 })111112 it('should remove elements from the beginning', () => {113 const vnode1 = new VNode('p', {}, [1, 2, 3, 4, 5].map(spanNum))114 const vnode2 = new VNode('p', {}, [3, 4, 5].map(spanNum))115 let elm = patch(vnode0, vnode1)116 expect(elm.children.length).toBe(5)117 elm = patch(vnode1, vnode2)118 expect(map(inner, elm.children)).toEqual(['3', '4', '5'])119 })120121 it('should removes elements from end', () => {122 const vnode1 = new VNode('p', {}, [1, 2, 3, 4, 5].map(spanNum))123 const vnode2 = new VNode('p', {}, [1, 2, 3].map(spanNum))124 let elm = patch(vnode0, vnode1)125 expect(elm.children.length).toBe(5)126 elm = patch(vnode1, vnode2)127 expect(elm.children.length).toBe(3)128 expect(map(inner, elm.children)).toEqual(['1', '2', '3'])129 })130131 it('should remove elements from the middle', () => {132 const vnode1 = new VNode('p', {}, [1, 2, 3, 4, 5].map(spanNum))133 const vnode2 = new VNode('p', {}, [1, 2, 4, 5].map(spanNum))134 let elm = patch(vnode0, vnode1)135 expect(elm.children.length).toBe(5)136 elm = patch(vnode1, vnode2)137 expect(elm.children.length).toBe(4)138 expect(map(inner, elm.children)).toEqual(['1', '2', '4', '5'])139 })140141 it('should moves element forward', () => {142 const vnode1 = new VNode('p', {}, [1, 2, 3, 4].map(spanNum))143 const vnode2 = new VNode('p', {}, [2, 3, 1, 4].map(spanNum))144 let elm = patch(vnode0, vnode1)145 expect(elm.children.length).toBe(4)146 elm = patch(vnode1, vnode2)147 expect(elm.children.length).toBe(4)148 expect(map(inner, elm.children)).toEqual(['2', '3', '1', '4'])149 })150151 it('should move elements to end', () => {152 const vnode1 = new VNode('p', {}, [1, 2, 3].map(spanNum))153 const vnode2 = new VNode('p', {}, [2, 3, 1].map(spanNum))154 let elm = patch(vnode0, vnode1)155 expect(elm.children.length).toBe(3)156 elm = patch(vnode1, vnode2)157 expect(elm.children.length).toBe(3)158 expect(map(inner, elm.children)).toEqual(['2', '3', '1'])159 })160161 it('should move element backwards', () => {162 const vnode1 = new VNode('p', {}, [1, 2, 3, 4].map(spanNum))163 const vnode2 = new VNode('p', {}, [1, 4, 2, 3].map(spanNum))164 let elm = patch(vnode0, vnode1)165 expect(elm.children.length).toBe(4)166 elm = patch(vnode1, vnode2)167 expect(elm.children.length).toBe(4)168 expect(map(inner, elm.children)).toEqual(['1', '4', '2', '3'])169 })170171 it('should swap first and last', () => {172 const vnode1 = new VNode('p', {}, [1, 2, 3, 4].map(spanNum))173 const vnode2 = new VNode('p', {}, [4, 2, 3, 1].map(spanNum))174 let elm = patch(vnode0, vnode1)175 expect(elm.children.length).toBe(4)176 elm = patch(vnode1, vnode2)177 expect(elm.children.length).toBe(4)178 expect(map(inner, elm.children)).toEqual(['4', '2', '3', '1'])179 })180181 it('should move to left and replace', () => {182 const vnode1 = new VNode('p', {}, [1, 2, 3, 4, 5].map(spanNum))183 const vnode2 = new VNode('p', {}, [4, 1, 2, 3, 6].map(spanNum))184 let elm = patch(vnode0, vnode1)185 expect(elm.children.length).toBe(5)186 elm = patch(vnode1, vnode2)187 expect(elm.children.length).toBe(5)188 expect(map(inner, elm.children)).toEqual(['4', '1', '2', '3', '6'])189 })190191 it('should move to left and leaves hold', () => {192 const vnode1 = new VNode('p', {}, [1, 4, 5].map(spanNum))193 const vnode2 = new VNode('p', {}, [4, 6].map(spanNum))194 let elm = patch(vnode0, vnode1)195 expect(elm.children.length).toBe(3)196 elm = patch(vnode1, vnode2)197 expect(map(inner, elm.children)).toEqual(['4', '6'])198 })199200 it('should handle moved and set to undefined element ending at the end', () => {201 const vnode1 = new VNode('p', {}, [2, 4, 5].map(spanNum))202 const vnode2 = new VNode('p', {}, [4, 5, 3].map(spanNum))203 let elm = patch(vnode0, vnode1)204 expect(elm.children.length).toBe(3)205 elm = patch(vnode1, vnode2)206 expect(elm.children.length).toBe(3)207 expect(map(inner, elm.children)).toEqual(['4', '5', '3'])208 })209210 it('should move a key in non-keyed nodes with a size up', () => {211 const vnode1 = new VNode('p', {}, [1, 'a', 'b', 'c'].map(spanNum))212 const vnode2 = new VNode('p', {}, ['d', 'a', 'b', 'c', 1, 'e'].map(spanNum))213 let elm = patch(vnode0, vnode1)214 expect(elm.children.length).toBe(4)215 expect(elm.textContent, '1abc')216 elm = patch(vnode1, vnode2)217 expect(elm.children.length).toBe(6)218 expect(elm.textContent, 'dabc1e')219 })220221 it('should reverse element', () => {222 const vnode1 = new VNode('p', {}, [1, 2, 3, 4, 5, 6, 7, 8].map(spanNum))223 const vnode2 = new VNode('p', {}, [8, 7, 6, 5, 4, 3, 2, 1].map(spanNum))224 let elm = patch(vnode0, vnode1)225 expect(elm.children.length).toBe(8)226 elm = patch(vnode1, vnode2)227 expect(map(inner, elm.children)).toEqual([228 '8',229 '7',230 '6',231 '5',232 '4',233 '3',234 '2',235 '1'236 ])237 })238239 it('something', () => {240 const vnode1 = new VNode('p', {}, [0, 1, 2, 3, 4, 5].map(spanNum))241 const vnode2 = new VNode('p', {}, [4, 3, 2, 1, 5, 0].map(spanNum))242 let elm = patch(vnode0, vnode1)243 expect(elm.children.length).toBe(6)244 elm = patch(vnode1, vnode2)245 expect(map(inner, elm.children)).toEqual(['4', '3', '2', '1', '5', '0'])246 })247248 it('should handle random shuffle', () => {249 let n250 let i251 const arr: any[] = []252 const opacities: any[] = []253 const elms = 14254 const samples = 5255 function spanNumWithOpacity(n, o) {256 return new VNode(257 'span',258 { key: n, style: { opacity: o } },259 undefined,260 n.toString()261 )262 }263264 for (n = 0; n < elms; ++n) {265 arr[n] = n266 }267 for (n = 0; n < samples; ++n) {268 const vnode1 = new VNode(269 'span',270 {},271 arr.map(n => {272 return spanNumWithOpacity(n, '1')273 })274 )275 const shufArr = shuffle(arr.slice(0))276 let elm = patch(vnode0, vnode1)277 for (i = 0; i < elms; ++i) {278 expect(elm.children[i].innerHTML).toBe(i.toString())279 opacities[i] = Math.random().toFixed(5).toString()280 }281 const vnode2 = new VNode(282 'span',283 {},284 arr.map(n => {285 return spanNumWithOpacity(shufArr[n], opacities[n])286 })287 )288 elm = patch(vnode1, vnode2)289 for (i = 0; i < elms; ++i) {290 expect(elm.children[i].innerHTML).toBe(shufArr[i].toString())291 expect(opacities[i].indexOf(elm.children[i].style.opacity)).toBe(0)292 }293 }294 })295296 it('should append elements with updating children without keys', () => {297 const vnode1 = new VNode('div', {}, [298 new VNode('span', {}, undefined, 'hello')299 ])300 const vnode2 = new VNode('div', {}, [301 new VNode('span', {}, undefined, 'hello'),302 new VNode('span', {}, undefined, 'world')303 ])304 let elm = patch(vnode0, vnode1)305 expect(map(inner, elm.children)).toEqual(['hello'])306 elm = patch(vnode1, vnode2)307 expect(map(inner, elm.children)).toEqual(['hello', 'world'])308 })309310 it('should handle unmoved text nodes with updating children without keys', () => {311 const vnode1 = new VNode('div', {}, [312 createTextVNode('text'),313 new VNode('span', {}, undefined, 'hello')314 ])315 const vnode2 = new VNode('div', {}, [316 createTextVNode('text'),317 new VNode('span', {}, undefined, 'hello')318 ])319 let elm = patch(vnode0, vnode1)320 expect(elm.childNodes[0].textContent).toBe('text')321 elm = patch(vnode1, vnode2)322 expect(elm.childNodes[0].textContent).toBe('text')323 })324325 it('should handle changing text children with updating children without keys', () => {326 const vnode1 = new VNode('div', {}, [327 createTextVNode('text'),328 new VNode('span', {}, undefined, 'hello')329 ])330 const vnode2 = new VNode('div', {}, [331 createTextVNode('text2'),332 new VNode('span', {}, undefined, 'hello')333 ])334 let elm = patch(vnode0, vnode1)335 expect(elm.childNodes[0].textContent).toBe('text')336 elm = patch(vnode1, vnode2)337 expect(elm.childNodes[0].textContent).toBe('text2')338 })339340 it('should prepend element with updating children without keys', () => {341 const vnode1 = new VNode('div', {}, [342 new VNode('span', {}, undefined, 'world')343 ])344 const vnode2 = new VNode('div', {}, [345 new VNode('span', {}, undefined, 'hello'),346 new VNode('span', {}, undefined, 'world')347 ])348 let elm = patch(vnode0, vnode1)349 expect(map(inner, elm.children)).toEqual(['world'])350 elm = patch(vnode1, vnode2)351 expect(map(inner, elm.children)).toEqual(['hello', 'world'])352 })353354 it('should prepend element of different tag type with updating children without keys', () => {355 const vnode1 = new VNode('div', {}, [356 new VNode('span', {}, undefined, 'world')357 ])358 const vnode2 = new VNode('div', {}, [359 new VNode('div', {}, undefined, 'hello'),360 new VNode('span', {}, undefined, 'world')361 ])362 let elm = patch(vnode0, vnode1)363 expect(map(inner, elm.children)).toEqual(['world'])364 elm = patch(vnode1, vnode2)365 expect(map(prop('tagName'), elm.children)).toEqual(['DIV', 'SPAN'])366 expect(map(inner, elm.children)).toEqual(['hello', 'world'])367 })368369 it('should remove elements with updating children without keys', () => {370 const vnode1 = new VNode('div', {}, [371 new VNode('span', {}, undefined, 'one'),372 new VNode('span', {}, undefined, 'two'),373 new VNode('span', {}, undefined, 'three')374 ])375 const vnode2 = new VNode('div', {}, [376 new VNode('span', {}, undefined, 'one'),377 new VNode('span', {}, undefined, 'three')378 ])379 let elm = patch(vnode0, vnode1)380 expect(map(inner, elm.children)).toEqual(['one', 'two', 'three'])381 elm = patch(vnode1, vnode2)382 expect(map(inner, elm.children)).toEqual(['one', 'three'])383 })384385 it('should remove a single text node with updating children without keys', () => {386 const vnode1 = new VNode('div', {}, undefined, 'one')387 const vnode2 = new VNode('div', {})388 let elm = patch(vnode0, vnode1)389 expect(elm.textContent).toBe('one')390 elm = patch(vnode1, vnode2)391 expect(elm.textContent).toBe('')392 })393394 it('should remove a single text node when children are updated', () => {395 const vnode1 = new VNode('div', {}, undefined, 'one')396 const vnode2 = new VNode('div', {}, [397 new VNode('div', {}, undefined, 'two'),398 new VNode('span', {}, undefined, 'three')399 ])400 let elm = patch(vnode0, vnode1)401 expect(elm.textContent).toBe('one')402 elm = patch(vnode1, vnode2)403 expect(map(prop('textContent'), elm.childNodes)).toEqual(['two', 'three'])404 })405406 it('should remove a text node among other elements', () => {407 const vnode1 = new VNode('div', {}, [408 createTextVNode('one'),409 new VNode('span', {}, undefined, 'two')410 ])411 const vnode2 = new VNode('div', {}, [412 new VNode('div', {}, undefined, 'three')413 ])414 let elm = patch(vnode0, vnode1)415 expect(map(prop('textContent'), elm.childNodes)).toEqual(['one', 'two'])416 elm = patch(vnode1, vnode2)417 expect(elm.childNodes.length).toBe(1)418 expect(elm.childNodes[0].tagName).toBe('DIV')419 expect(elm.childNodes[0].textContent).toBe('three')420 })421422 it('should reorder elements', () => {423 const vnode1 = new VNode('div', {}, [424 new VNode('span', {}, undefined, 'one'),425 new VNode('div', {}, undefined, 'two'),426 new VNode('b', {}, undefined, 'three')427 ])428 const vnode2 = new VNode('div', {}, [429 new VNode('b', {}, undefined, 'three'),430 new VNode('span', {}, undefined, 'two'),431 new VNode('div', {}, undefined, 'one')432 ])433 let elm = patch(vnode0, vnode1)434 expect(map(inner, elm.children)).toEqual(['one', 'two', 'three'])435 elm = patch(vnode1, vnode2)436 expect(map(inner, elm.children)).toEqual(['three', 'two', 'one'])437 })438439 it('should handle children with the same key but with different tag', () => {440 const vnode1 = new VNode('div', {}, [441 new VNode('div', { key: 1 }, undefined, 'one'),442 new VNode('div', { key: 2 }, undefined, 'two'),443 new VNode('div', { key: 3 }, undefined, 'three'),444 new VNode('div', { key: 4 }, undefined, 'four')445 ])446 const vnode2 = new VNode('div', {}, [447 new VNode('div', { key: 4 }, undefined, 'four'),448 new VNode('span', { key: 3 }, undefined, 'three'),449 new VNode('span', { key: 2 }, undefined, 'two'),450 new VNode('div', { key: 1 }, undefined, 'one')451 ])452 let elm = patch(vnode0, vnode1)453 expect(map(tag, elm.children)).toEqual(['DIV', 'DIV', 'DIV', 'DIV'])454 expect(map(inner, elm.children)).toEqual(['one', 'two', 'three', 'four'])455 elm = patch(vnode1, vnode2)456 expect(map(tag, elm.children)).toEqual(['DIV', 'SPAN', 'SPAN', 'DIV'])457 expect(map(inner, elm.children)).toEqual(['four', 'three', 'two', 'one'])458 })459460 it('should handle children with the same tag, same key, but one with data and one without data', () => {461 const vnode1 = new VNode('div', {}, [462 new VNode('div', { class: 'hi' }, undefined, 'one')463 ])464 const vnode2 = new VNode('div', {}, [465 new VNode('div', undefined, undefined, 'four')466 ])467 let elm = patch(vnode0, vnode1)468 const child1 = elm.children[0]469 expect(child1.className).toBe('hi')470 elm = patch(vnode1, vnode2)471 const child2 = elm.children[0]472 expect(child1).not.toBe(child2)473 expect(child2.className).toBe('')474 })475476 it('should handle static vnodes properly', () => {477 function makeNode(text) {478 return new VNode('div', undefined, [479 new VNode(undefined, undefined, undefined, text)480 ])481 }482 const b = makeNode('B')483 b.isStatic = true484 b.key = `__static__1`485 const vnode1 = new VNode('div', {}, [makeNode('A'), b, makeNode('C')])486 const vnode2 = new VNode('div', {}, [b])487 const vnode3 = new VNode('div', {}, [makeNode('A'), b, makeNode('C')])488489 let elm = patch(vnode0, vnode1)490 expect(elm.textContent).toBe('ABC')491 elm = patch(vnode1, vnode2)492 expect(elm.textContent).toBe('B')493 elm = patch(vnode2, vnode3)494 expect(elm.textContent).toBe('ABC')495 })496497 it('should handle static vnodes inside ', () => {498 function makeNode(text) {499 return new VNode('div', undefined, [500 new VNode(undefined, undefined, undefined, text)501 ])502 }503 const b = makeNode('B')504 b.isStatic = true505 b.key = `__static__1`506 const vnode1 = new VNode('div', {}, [makeNode('A'), b, makeNode('C')])507 const vnode2 = new VNode('div', {}, [b])508 const vnode3 = new VNode('div', {}, [makeNode('A'), b, makeNode('C')])509510 let elm = patch(vnode0, vnode1)511 expect(elm.textContent).toBe('ABC')512 elm = patch(vnode1, vnode2)513 expect(elm.textContent).toBe('B')514 elm = patch(vnode2, vnode3)515 expect(elm.textContent).toBe('ABC')516 })517518 // #6502519 it('should not de-opt when both head and tail are changed', () => {520 const vnode1 = new VNode('div', {}, [521 createEmptyVNode(),522 new VNode('div'),523 createEmptyVNode()524 ])525 const vnode2 = new VNode('div', {}, [526 new VNode('p'),527 new VNode('div'),528 new VNode('p')529 ])530 let root = patch(null, vnode1)531 const original = root.childNodes[1]532533 root = patch(vnode1, vnode2)534 const postPatch = root.childNodes[1]535536 expect(postPatch).toBe(original)537 })538539 it('should warn with duplicate keys: createChildren', () => {540 function makeNode(key) {541 return new VNode('div', { key: key })542 }543544 const vnode = new VNode('p', {}, ['b', 'a', 'c', 'b'].map(makeNode))545 patch(null, vnode)546 expect(`Duplicate keys detected: 'b'`).toHaveBeenWarned()547 })548549 it('should warn with duplicate keys: updateChildren', () => {550 function makeNode(key) {551 return new VNode('div', { key: key })552 }553554 const vnode2 = new VNode('p', {}, ['b', 'a', 'c', 'b'].map(makeNode))555 const vnode3 = new VNode('p', {}, ['b', 'x', 'd', 'b'].map(makeNode))556 patch(vnode0, vnode2)557 expect(`Duplicate keys detected: 'b'`).toHaveBeenWarned()558 patch(vnode2, vnode3)559 expect(`Duplicate keys detected: 'b'`).toHaveBeenWarned()560 })561562 it('should warn with duplicate keys: patchVnode with empty oldVnode', () => {563 function makeNode(key) {564 return new VNode('li', { key: key })565 }566567 const vnode1 = new VNode('div')568 const vnode2 = new VNode(569 'div',570 undefined,571 ['1', '2', '3', '4', '4'].map(makeNode)572 )573574 patch(vnode1, vnode2)575 expect(`Duplicate keys detected: '4'`).toHaveBeenWarned()576 })577})
Findings
✓ No findings reported for this file.