Skip to content
This repository was archived by the owner on Oct 26, 2021. It is now read-only.

Commit 3e12e97

Browse files
committed
ChildNode#replaceWith: fix and add tests.
1 parent e8bc2d2 commit 3e12e97

2 files changed

Lines changed: 238 additions & 9 deletions

File tree

src/Patch/Interface/ChildNode.js

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,24 +76,46 @@ export default function(internals, destination, builtIn) {
7676
* @param {...(!Node|string)} nodes
7777
*/
7878
function(...nodes) {
79-
// TODO: Fix this for when one of `nodes` is a DocumentFragment!
80-
const connectedBefore = /** @type {!Array<!Node>} */ (nodes.filter(node => {
81-
// DocumentFragments are not connected and will not be added to the list.
82-
return node instanceof Node && Utilities.isConnected(node);
83-
}));
79+
/**
80+
* A copy of `nodes`, with any DocumentFragment replaced by its children.
81+
* @type {!Array<!Node>}
82+
*/
83+
const flattenedNodes = [];
84+
85+
/**
86+
* Elements in `nodes` that were connected before this call.
87+
* @type {!Array<!Node>}
88+
*/
89+
const connectedElements = [];
90+
91+
for (var i = 0; i < nodes.length; i++) {
92+
const node = nodes[i];
93+
94+
if (node instanceof Element && Utilities.isConnected(node)) {
95+
connectedElements.push(node);
96+
}
97+
98+
if (node instanceof DocumentFragment) {
99+
for (let child = node.firstChild; child; child = child.nextSibling) {
100+
flattenedNodes.push(child);
101+
}
102+
} else {
103+
flattenedNodes.push(node);
104+
}
105+
}
84106

85107
const wasConnected = Utilities.isConnected(this);
86108

87109
builtIn.replaceWith.apply(this, nodes);
88110

89-
for (let i = 0; i < connectedBefore.length; i++) {
90-
internals.disconnectTree(connectedBefore[i]);
111+
for (let i = 0; i < connectedElements.length; i++) {
112+
internals.disconnectTree(connectedElements[i]);
91113
}
92114

93115
if (wasConnected) {
94116
internals.disconnectTree(this);
95-
for (let i = 0; i < nodes.length; i++) {
96-
const node = nodes[i];
117+
for (let i = 0; i < flattenedNodes.length; i++) {
118+
const node = flattenedNodes[i];
97119
if (node instanceof Element) {
98120
internals.connectTree(node);
99121
}

tests/html/Interface/ChildNode/index.html

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,213 @@
204204

205205
beforeAfterSuite('before');
206206
beforeAfterSuite('after');
207+
208+
suite('replaceWith', function() {
209+
let localName;
210+
let constructor;
211+
let element1;
212+
let element2;
213+
let connectedElement1;
214+
let connectedElement2;
215+
let disconnectedElement1;
216+
let disconnectedElement2;
217+
let documentFragment1;
218+
219+
setup(function() {
220+
localName = generateLocalName();
221+
defineWithLocalName(localName);
222+
223+
element1 = document.createElement(localName);
224+
assert.equal(element1.connectedCallbackCount, 0);
225+
assert.equal(element1.disconnectedCallbackCount, 0);
226+
227+
element2 = document.createElement(localName);
228+
assert.equal(element2.connectedCallbackCount, 0);
229+
assert.equal(element2.disconnectedCallbackCount, 0);
230+
231+
connectedElement1 = document.createElement(localName);
232+
document.body.appendChild(connectedElement1);
233+
assert.equal(connectedElement1.connectedCallbackCount, 1);
234+
assert.equal(connectedElement1.disconnectedCallbackCount, 0);
235+
236+
connectedElement2 = document.createElement(localName);
237+
document.body.appendChild(connectedElement2);
238+
assert.equal(connectedElement2.connectedCallbackCount, 1);
239+
assert.equal(connectedElement2.disconnectedCallbackCount, 0);
240+
241+
// Make sure the disconnected nodes have parents so that `replaceWith`
242+
// has a place to put the given elements.
243+
var disconnectedContainer1 = document.createElement('div');
244+
disconnectedElement1 = document.createElement(localName);
245+
disconnectedContainer1.appendChild(disconnectedElement1);
246+
247+
var disconnectedContainer2 = document.createElement('div');
248+
disconnectedElement2 = document.createElement(localName);
249+
disconnectedContainer2.appendChild(disconnectedElement2);
250+
251+
documentFragment1 = document.createDocumentFragment();
252+
});
253+
254+
teardown(function() {
255+
[
256+
element1,
257+
element2,
258+
connectedElement1,
259+
connectedElement2,
260+
disconnectedElement1,
261+
disconnectedElement2,
262+
].forEach(function(node) {
263+
if (node.parentNode) {
264+
node.parentNode.removeChild(node);
265+
}
266+
});
267+
});
268+
269+
test('Moving nodes from a disconnected tree to a connected tree calls ' +
270+
'`connectedCallback` of the inserted nodes.', function() {
271+
disconnectedElement1.appendChild(element1);
272+
disconnectedElement1.appendChild(element2);
273+
274+
assert.equal(connectedElement1.connectedCallbackCount, 1);
275+
assert.equal(connectedElement1.disconnectedCallbackCount, 0);
276+
277+
assert.equal(element1.connectedCallbackCount, 0);
278+
assert.equal(element1.disconnectedCallbackCount, 0);
279+
assert.equal(element2.connectedCallbackCount, 0);
280+
assert.equal(element2.disconnectedCallbackCount, 0);
281+
282+
connectedElement1.replaceWith(element1, element2);
283+
284+
assert.equal(connectedElement1.connectedCallbackCount, 1);
285+
assert.equal(connectedElement1.disconnectedCallbackCount, 1);
286+
287+
assert.equal(element1.connectedCallbackCount, 1);
288+
assert.equal(element1.disconnectedCallbackCount, 0);
289+
assert.equal(element2.connectedCallbackCount, 1);
290+
assert.equal(element2.disconnectedCallbackCount, 0);
291+
});
292+
293+
test('Moving nodes from a connected tree to a connected tree calls ' +
294+
'`disconnectedCallback` and `connectedCallback` of the inserted nodes.',
295+
function() {
296+
connectedElement1.appendChild(element1);
297+
connectedElement1.appendChild(element2);
298+
299+
assert.equal(connectedElement2.connectedCallbackCount, 1);
300+
assert.equal(connectedElement2.disconnectedCallbackCount, 0);
301+
302+
assert.equal(element1.connectedCallbackCount, 1);
303+
assert.equal(element1.disconnectedCallbackCount, 0);
304+
assert.equal(element2.connectedCallbackCount, 1);
305+
assert.equal(element2.disconnectedCallbackCount, 0);
306+
307+
connectedElement2.replaceWith(element1, element2);
308+
309+
assert.equal(connectedElement2.connectedCallbackCount, 1);
310+
assert.equal(connectedElement2.disconnectedCallbackCount, 1);
311+
312+
assert.equal(element1.connectedCallbackCount, 2);
313+
assert.equal(element1.disconnectedCallbackCount, 1);
314+
assert.equal(element2.connectedCallbackCount, 2);
315+
assert.equal(element2.disconnectedCallbackCount, 1);
316+
});
317+
318+
test('Moving nodes from a connected tree to a disconnected tree calls ' +
319+
'`disconnectedCallback` of the inserted nodes.', function() {
320+
connectedElement1.appendChild(element1);
321+
connectedElement1.appendChild(element2);
322+
323+
assert.equal(disconnectedElement1.connectedCallbackCount, 0);
324+
assert.equal(disconnectedElement1.disconnectedCallbackCount, 0);
325+
326+
assert.equal(element1.connectedCallbackCount, 1);
327+
assert.equal(element1.disconnectedCallbackCount, 0);
328+
assert.equal(element2.connectedCallbackCount, 1);
329+
assert.equal(element2.disconnectedCallbackCount, 0);
330+
331+
disconnectedElement1.replaceWith(element1, element2);
332+
333+
assert.equal(disconnectedElement1.connectedCallbackCount, 0);
334+
assert.equal(disconnectedElement1.disconnectedCallbackCount, 0);
335+
336+
assert.equal(element1.connectedCallbackCount, 1);
337+
assert.equal(element1.disconnectedCallbackCount, 1);
338+
assert.equal(element2.connectedCallbackCount, 1);
339+
assert.equal(element2.disconnectedCallbackCount, 1);
340+
});
341+
342+
test('Moving nodes from a disconnected tree to a disconnected tree does ' +
343+
'not call any callbacks.', function() {
344+
disconnectedElement1.appendChild(element1);
345+
disconnectedElement1.appendChild(element2);
346+
347+
assert.equal(disconnectedElement1.connectedCallbackCount, 0);
348+
assert.equal(disconnectedElement1.disconnectedCallbackCount, 0);
349+
350+
assert.equal(element1.connectedCallbackCount, 0);
351+
assert.equal(element1.disconnectedCallbackCount, 0);
352+
assert.equal(element2.connectedCallbackCount, 0);
353+
assert.equal(element2.disconnectedCallbackCount, 0);
354+
355+
disconnectedElement1.replaceWith(element1, element2);
356+
357+
assert.equal(disconnectedElement1.connectedCallbackCount, 0);
358+
assert.equal(disconnectedElement1.disconnectedCallbackCount, 0);
359+
360+
assert.equal(element1.connectedCallbackCount, 0);
361+
assert.equal(element1.disconnectedCallbackCount, 0);
362+
assert.equal(element2.connectedCallbackCount, 0);
363+
assert.equal(element2.disconnectedCallbackCount, 0);
364+
});
365+
366+
test('Moving nodes from a DocumentFragment to a connected tree calls ' +
367+
'`connectedCallback` of the inserted nodes.', function() {
368+
documentFragment1.appendChild(element1);
369+
documentFragment1.appendChild(element2);
370+
371+
assert.equal(connectedElement1.connectedCallbackCount, 1);
372+
assert.equal(connectedElement1.disconnectedCallbackCount, 0);
373+
374+
assert.equal(element1.connectedCallbackCount, 0);
375+
assert.equal(element1.disconnectedCallbackCount, 0);
376+
assert.equal(element2.connectedCallbackCount, 0);
377+
assert.equal(element2.disconnectedCallbackCount, 0);
378+
379+
connectedElement1.replaceWith(element1, element2);
380+
381+
assert.equal(connectedElement1.connectedCallbackCount, 1);
382+
assert.equal(connectedElement1.disconnectedCallbackCount, 1);
383+
384+
assert.equal(element1.connectedCallbackCount, 1);
385+
assert.equal(element1.disconnectedCallbackCount, 0);
386+
assert.equal(element2.connectedCallbackCount, 1);
387+
assert.equal(element2.disconnectedCallbackCount, 0);
388+
});
389+
390+
test('Moving nodes from a DocumentFragment to a disconnected tree does ' +
391+
'not call any callbacks.', function() {
392+
documentFragment1.appendChild(element1);
393+
documentFragment1.appendChild(element2);
394+
395+
assert.equal(disconnectedElement1.connectedCallbackCount, 0);
396+
assert.equal(disconnectedElement1.disconnectedCallbackCount, 0);
397+
398+
assert.equal(element1.connectedCallbackCount, 0);
399+
assert.equal(element1.disconnectedCallbackCount, 0);
400+
assert.equal(element2.connectedCallbackCount, 0);
401+
assert.equal(element2.disconnectedCallbackCount, 0);
402+
403+
disconnectedElement1.replaceWith(element1, element2);
404+
405+
assert.equal(disconnectedElement1.connectedCallbackCount, 0);
406+
assert.equal(disconnectedElement1.disconnectedCallbackCount, 0);
407+
408+
assert.equal(element1.connectedCallbackCount, 0);
409+
assert.equal(element1.disconnectedCallbackCount, 0);
410+
assert.equal(element2.connectedCallbackCount, 0);
411+
assert.equal(element2.disconnectedCallbackCount, 0);
412+
});
413+
});
207414
});
208415
</script>
209416
</body>

0 commit comments

Comments
 (0)