Upgrade the DOM mutation observer
This is an important patch contains some crtical bug fixes for the DOM mutation observer. Previously the observer did not handle complex DOM movement very well. So in this patch we optimized this by distinguishing moved node better and added a resolving queue to avoid the error caused by ordering.
This commit is contained in:
@@ -29,13 +29,13 @@ export function isParentRemoved(
|
||||
return isParentRemoved(removes, parentNode);
|
||||
}
|
||||
|
||||
export function isParentDropped(droppedSet: Set<Node>, n: Node): boolean {
|
||||
export function isAncestorInSet(set: Set<Node>, n: Node): boolean {
|
||||
const { parentNode } = n;
|
||||
if (!parentNode) {
|
||||
return false;
|
||||
}
|
||||
if (droppedSet.has(parentNode)) {
|
||||
if (set.has(parentNode)) {
|
||||
return true;
|
||||
}
|
||||
return isParentDropped(droppedSet, parentNode);
|
||||
return isAncestorInSet(set, parentNode);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,12 @@ import {
|
||||
attributeCursor,
|
||||
blockClass,
|
||||
} from '../types';
|
||||
import { deepDelete, isParentRemoved, isParentDropped } from './collection';
|
||||
import { deepDelete, isParentRemoved, isAncestorInSet } from './collection';
|
||||
|
||||
const moveKey = (id: number, parentId: number) => `${id}@${parentId}`;
|
||||
function isINode(n: Node | INode): n is INode {
|
||||
return '__sn' in n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutation observer will merge several mutations into an array and pass
|
||||
@@ -55,20 +60,35 @@ function initMutationObserver(
|
||||
const observer = new MutationObserver(mutations => {
|
||||
const texts: textCursor[] = [];
|
||||
const attributes: attributeCursor[] = [];
|
||||
const removes: removedNodeMutation[] = [];
|
||||
let removes: removedNodeMutation[] = [];
|
||||
const adds: addedNodeMutation[] = [];
|
||||
|
||||
const addsSet = new Set<Node>();
|
||||
const addedSet = new Set<Node>();
|
||||
const movedSet = new Set<Node>();
|
||||
const droppedSet = new Set<Node>();
|
||||
|
||||
const genAdds = (n: Node) => {
|
||||
const movedMap: Record<string, true> = {};
|
||||
|
||||
const genAdds = (n: Node | INode, target?: Node | INode) => {
|
||||
if (isBlocked(n, blockClass)) {
|
||||
return;
|
||||
}
|
||||
addsSet.add(n);
|
||||
droppedSet.delete(n);
|
||||
if (isINode(n)) {
|
||||
movedSet.add(n);
|
||||
let targetId: number | null = null;
|
||||
if (target && isINode(target)) {
|
||||
targetId = target.__sn.id;
|
||||
}
|
||||
if (targetId) {
|
||||
movedMap[moveKey(n.__sn.id, targetId)] = true;
|
||||
}
|
||||
} else {
|
||||
addedSet.add(n);
|
||||
droppedSet.delete(n);
|
||||
}
|
||||
n.childNodes.forEach(childN => genAdds(childN));
|
||||
};
|
||||
|
||||
mutations.forEach(mutation => {
|
||||
const {
|
||||
type,
|
||||
@@ -109,7 +129,7 @@ function initMutationObserver(
|
||||
break;
|
||||
}
|
||||
case 'childList': {
|
||||
addedNodes.forEach(n => genAdds(n));
|
||||
addedNodes.forEach(n => genAdds(n, target));
|
||||
removedNodes.forEach(n => {
|
||||
const nodeId = mirror.getId(n as INode);
|
||||
const parentId = mirror.getId(target as INode);
|
||||
@@ -117,14 +137,15 @@ function initMutationObserver(
|
||||
return;
|
||||
}
|
||||
// removed node has not been serialized yet, just remove it from the Set
|
||||
if (addsSet.has(n)) {
|
||||
deepDelete(addsSet, n);
|
||||
if (addedSet.has(n)) {
|
||||
deepDelete(addedSet, n);
|
||||
droppedSet.add(n);
|
||||
} else if (addsSet.has(target) && nodeId === -1) {
|
||||
} else if (addedSet.has(target) && nodeId === -1) {
|
||||
/**
|
||||
* If target was newly added and removed child node was
|
||||
* not serialized, it means the child node has been removed
|
||||
* before callback fired, so we can ignore it.
|
||||
* before callback fired, so we can ignore it because
|
||||
* newly added node will be serialized without child nodes.
|
||||
* TODO: verify this
|
||||
*/
|
||||
} else if (isAncestorRemoved(target as INode)) {
|
||||
@@ -134,6 +155,8 @@ function initMutationObserver(
|
||||
* the node is also removed which we do not need to track
|
||||
* and replay.
|
||||
*/
|
||||
} else if (movedSet.has(n) && movedMap[moveKey(nodeId, parentId)]) {
|
||||
deepDelete(movedSet, n);
|
||||
} else {
|
||||
removes.push({
|
||||
parentId,
|
||||
@@ -149,23 +172,63 @@ function initMutationObserver(
|
||||
}
|
||||
});
|
||||
|
||||
Array.from(addsSet).forEach(n => {
|
||||
if (!isParentDropped(droppedSet, n) && !isParentRemoved(removes, n)) {
|
||||
adds.push({
|
||||
parentId: mirror.getId((n.parentNode as Node) as INode),
|
||||
previousId: !n.previousSibling
|
||||
? n.previousSibling
|
||||
: mirror.getId(n.previousSibling as INode),
|
||||
nextId: !n.nextSibling
|
||||
? n.nextSibling
|
||||
: mirror.getId((n.nextSibling as unknown) as INode),
|
||||
node: serializeNodeWithId(n, document, mirror.map, blockClass, true)!,
|
||||
});
|
||||
/**
|
||||
* Sometimes child node may be pushed before its newly added
|
||||
* parent, so we init a queue to store these nodes.
|
||||
*/
|
||||
const addQueue: Node[] = [];
|
||||
const pushAdd = (n: Node) => {
|
||||
const parentId = mirror.getId((n.parentNode as Node) as INode);
|
||||
if (parentId === -1) {
|
||||
return addQueue.push(n);
|
||||
}
|
||||
adds.push({
|
||||
parentId,
|
||||
previousId: !n.previousSibling
|
||||
? n.previousSibling
|
||||
: mirror.getId(n.previousSibling as INode),
|
||||
nextId: !n.nextSibling
|
||||
? n.nextSibling
|
||||
: mirror.getId((n.nextSibling as unknown) as INode),
|
||||
node: serializeNodeWithId(
|
||||
n,
|
||||
document,
|
||||
mirror.map,
|
||||
blockClass,
|
||||
true,
|
||||
inlineStylesheet,
|
||||
)!,
|
||||
});
|
||||
};
|
||||
|
||||
Array.from(movedSet).forEach(pushAdd);
|
||||
|
||||
Array.from(addedSet).forEach(n => {
|
||||
if (!isAncestorInSet(droppedSet, n) && !isParentRemoved(removes, n)) {
|
||||
pushAdd(n);
|
||||
} else if (isAncestorInSet(movedSet, n)) {
|
||||
pushAdd(n);
|
||||
} else {
|
||||
droppedSet.add(n);
|
||||
}
|
||||
});
|
||||
|
||||
while (addQueue.length) {
|
||||
if (
|
||||
addQueue.every(
|
||||
n => mirror.getId((n.parentNode as Node) as INode) === -1,
|
||||
)
|
||||
) {
|
||||
/**
|
||||
* If all nodes in queue could not find a serialized parent,
|
||||
* it may be a bug or corner case. We need to escape the
|
||||
* dead while loop at once.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
pushAdd(addQueue.shift()!);
|
||||
}
|
||||
|
||||
const payload = {
|
||||
texts: texts
|
||||
.map(text => ({
|
||||
|
||||
@@ -353,17 +353,19 @@ export class Replayer {
|
||||
});
|
||||
|
||||
const missingNodeMap: missingNodeMap = { ...this.missingNodeRetryMap };
|
||||
d.adds.forEach(mutation => {
|
||||
const queue: addedNodeMutation[] = [];
|
||||
|
||||
const appendNode = (mutation: addedNodeMutation) => {
|
||||
const parent = mirror.getNode(mutation.parentId);
|
||||
if (!parent) {
|
||||
return queue.push(mutation);
|
||||
}
|
||||
const target = buildNodeWithSN(
|
||||
mutation.node,
|
||||
this.iframe.contentDocument!,
|
||||
mirror.map,
|
||||
true,
|
||||
) as Node;
|
||||
const parent = mirror.getNode(mutation.parentId);
|
||||
if (!parent) {
|
||||
return this.warnNodeNotFound(d, mutation.parentId);
|
||||
}
|
||||
let previous: Node | null = null;
|
||||
let next: Node | null = null;
|
||||
if (mutation.previousId) {
|
||||
@@ -396,7 +398,20 @@ export class Replayer {
|
||||
if (mutation.previousId || mutation.nextId) {
|
||||
this.resolveMissingNode(missingNodeMap, parent, target, mutation);
|
||||
}
|
||||
};
|
||||
|
||||
d.adds.forEach(mutation => {
|
||||
appendNode(mutation);
|
||||
});
|
||||
|
||||
while (queue.length) {
|
||||
if (queue.every(m => !Boolean(mirror.getNode(m.parentId)))) {
|
||||
return queue.forEach(m => this.warnNodeNotFound(d, m.node.id));
|
||||
}
|
||||
const mutation = queue.shift()!;
|
||||
appendNode(mutation);
|
||||
}
|
||||
|
||||
if (Object.keys(missingNodeMap).length) {
|
||||
Object.assign(this.missingNodeRetryMap, missingNodeMap);
|
||||
}
|
||||
|
||||
@@ -1613,7 +1613,7 @@ exports[`ignore 1`] = `
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`move-node 1`] = `
|
||||
exports[`move-node-1 1`] = `
|
||||
"[
|
||||
{
|
||||
\\"type\\": 0,
|
||||
@@ -1798,81 +1798,361 @@ exports[`move-node 1`] = `
|
||||
\\"tagName\\": \\"span\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 23
|
||||
\\"id\\": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 23,
|
||||
\\"parentId\\": 11,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": 13,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 24
|
||||
\\"id\\": 12
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 23,
|
||||
\\"previousId\\": 24,
|
||||
\\"parentId\\": 11,
|
||||
\\"previousId\\": 12,
|
||||
\\"nextId\\": 18,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"i\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 25
|
||||
\\"id\\": 13
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 25,
|
||||
\\"parentId\\": 13,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": 15,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 26
|
||||
\\"id\\": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 25,
|
||||
\\"previousId\\": 26,
|
||||
\\"parentId\\": 13,
|
||||
\\"previousId\\": 14,
|
||||
\\"nextId\\": 17,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"b\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 27
|
||||
\\"id\\": 15
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 27,
|
||||
\\"parentId\\": 15,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"1\\",
|
||||
\\"id\\": 28
|
||||
\\"id\\": 16
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 25,
|
||||
\\"previousId\\": 27,
|
||||
\\"parentId\\": 13,
|
||||
\\"previousId\\": 15,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 29
|
||||
\\"id\\": 17
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 23,
|
||||
\\"previousId\\": 25,
|
||||
\\"parentId\\": 11,
|
||||
\\"previousId\\": 13,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 30
|
||||
\\"id\\": 18
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`move-node-2 1`] = `
|
||||
"[
|
||||
{
|
||||
\\"type\\": 0,
|
||||
\\"data\\": {}
|
||||
},
|
||||
{
|
||||
\\"type\\": 1,
|
||||
\\"data\\": {}
|
||||
},
|
||||
{
|
||||
\\"type\\": 4,
|
||||
\\"data\\": {
|
||||
\\"href\\": \\"about:blank\\",
|
||||
\\"width\\": 1920,
|
||||
\\"height\\": 1080
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"data\\": {
|
||||
\\"node\\": {
|
||||
\\"type\\": 0,
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"html\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"head\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 3
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"body\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 5
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 7
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"p\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 8
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 9
|
||||
}
|
||||
],
|
||||
\\"id\\": 6
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 10
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"span\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 12
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"i\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 14
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"b\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"1\\",
|
||||
\\"id\\": 16
|
||||
}
|
||||
],
|
||||
\\"id\\": 15
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 17
|
||||
}
|
||||
],
|
||||
\\"id\\": 13
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 18
|
||||
}
|
||||
],
|
||||
\\"id\\": 11
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\",
|
||||
\\"id\\": 19
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"script\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
|
||||
\\"id\\": 21
|
||||
}
|
||||
],
|
||||
\\"id\\": 20
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\",
|
||||
\\"id\\": 22
|
||||
}
|
||||
],
|
||||
\\"id\\": 4
|
||||
}
|
||||
],
|
||||
\\"id\\": 2
|
||||
}
|
||||
],
|
||||
\\"id\\": 1
|
||||
},
|
||||
\\"initialOffset\\": {
|
||||
\\"left\\": 0,
|
||||
\\"top\\": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [],
|
||||
\\"removes\\": [
|
||||
{
|
||||
\\"parentId\\": 4,
|
||||
\\"id\\": 11
|
||||
}
|
||||
],
|
||||
\\"adds\\": [
|
||||
{
|
||||
\\"parentId\\": 11,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": 13,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 12
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 11,
|
||||
\\"previousId\\": 12,
|
||||
\\"nextId\\": 18,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"i\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 13
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 13,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": 15,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 13,
|
||||
\\"previousId\\": 14,
|
||||
\\"nextId\\": 17,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"b\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 15
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 15,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"1\\",
|
||||
\\"id\\": 16
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 13,
|
||||
\\"previousId\\": 15,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 17
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 11,
|
||||
\\"previousId\\": 13,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 4,
|
||||
\\"previousId\\": 22,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 23
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 23,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"span\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 11
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -2432,20 +2712,20 @@ exports[`select2 1`] = `
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"id\\": 75,
|
||||
\\"id\\": 36,
|
||||
\\"attributes\\": {
|
||||
\\"id\\": \\"select2-drop\\",
|
||||
\\"class\\": \\"select2-drop select2-display-none select2-with-searchbox select2-drop-active\\"
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"id\\": 93,
|
||||
\\"id\\": 75,
|
||||
\\"attributes\\": {
|
||||
\\"style\\": \\"\\"
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"id\\": 81,
|
||||
\\"id\\": 42,
|
||||
\\"attributes\\": {
|
||||
\\"class\\": \\"select2-input select2-focused\\",
|
||||
\\"aria-activedescendant\\": \\"select2-result-label-2\\"
|
||||
@@ -2458,7 +2738,7 @@ exports[`select2 1`] = `
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"id\\": 85,
|
||||
\\"id\\": 67,
|
||||
\\"attributes\\": {
|
||||
\\"class\\": \\"select2-results-dept-0 select2-result select2-result-selectable select2-highlighted\\"
|
||||
}
|
||||
@@ -2488,22 +2768,22 @@ exports[`select2 1`] = `
|
||||
\\"tabindex\\": \\"-1\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 67
|
||||
\\"id\\": 26
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 67,
|
||||
\\"parentId\\": 26,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": 28,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\" \\",
|
||||
\\"id\\": 68
|
||||
\\"id\\": 27
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 67,
|
||||
\\"previousId\\": 68,
|
||||
\\"parentId\\": 26,
|
||||
\\"previousId\\": 27,
|
||||
\\"nextId\\": 30,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
@@ -2513,22 +2793,22 @@ exports[`select2 1`] = `
|
||||
\\"id\\": \\"select2-chosen-1\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 69
|
||||
\\"id\\": 28
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 69,
|
||||
\\"parentId\\": 28,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"A\\",
|
||||
\\"id\\": 70
|
||||
\\"id\\": 29
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 67,
|
||||
\\"previousId\\": 69,
|
||||
\\"parentId\\": 26,
|
||||
\\"previousId\\": 28,
|
||||
\\"nextId\\": 31,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
@@ -2537,22 +2817,22 @@ exports[`select2 1`] = `
|
||||
\\"class\\": \\"select2-search-choice-close\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 71
|
||||
\\"id\\": 30
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 67,
|
||||
\\"previousId\\": 71,
|
||||
\\"parentId\\": 26,
|
||||
\\"previousId\\": 30,
|
||||
\\"nextId\\": 32,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\" \\",
|
||||
\\"id\\": 72
|
||||
\\"id\\": 31
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 67,
|
||||
\\"previousId\\": 72,
|
||||
\\"parentId\\": 26,
|
||||
\\"previousId\\": 31,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
@@ -2562,11 +2842,11 @@ exports[`select2 1`] = `
|
||||
\\"role\\": \\"presentation\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 73
|
||||
\\"id\\": 32
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 73,
|
||||
\\"parentId\\": 32,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
@@ -2576,7 +2856,7 @@ exports[`select2 1`] = `
|
||||
\\"role\\": \\"presentation\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 74
|
||||
\\"id\\": 33
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2591,22 +2871,22 @@ exports[`select2 1`] = `
|
||||
\\"id\\": \\"select2-drop\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 75
|
||||
\\"id\\": 36
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 75,
|
||||
\\"parentId\\": 36,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": 38,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\" \\",
|
||||
\\"id\\": 76
|
||||
\\"id\\": 37
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 75,
|
||||
\\"previousId\\": 76,
|
||||
\\"parentId\\": 36,
|
||||
\\"previousId\\": 37,
|
||||
\\"nextId\\": 44,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
@@ -2615,22 +2895,22 @@ exports[`select2 1`] = `
|
||||
\\"class\\": \\"select2-search\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 77
|
||||
\\"id\\": 38
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 77,
|
||||
\\"parentId\\": 38,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": 40,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\" \\",
|
||||
\\"id\\": 78
|
||||
\\"id\\": 39
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 77,
|
||||
\\"previousId\\": 78,
|
||||
\\"parentId\\": 38,
|
||||
\\"previousId\\": 39,
|
||||
\\"nextId\\": 41,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
@@ -2640,22 +2920,22 @@ exports[`select2 1`] = `
|
||||
\\"class\\": \\"select2-offscreen\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 79
|
||||
\\"id\\": 40
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 77,
|
||||
\\"previousId\\": 79,
|
||||
\\"parentId\\": 38,
|
||||
\\"previousId\\": 40,
|
||||
\\"nextId\\": 42,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\" \\",
|
||||
\\"id\\": 80
|
||||
\\"id\\": 41
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 77,
|
||||
\\"previousId\\": 80,
|
||||
\\"parentId\\": 38,
|
||||
\\"previousId\\": 41,
|
||||
\\"nextId\\": 43,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
@@ -2676,32 +2956,32 @@ exports[`select2 1`] = `
|
||||
\\"aria-activedescendant\\": \\"select2-result-label-2\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 81
|
||||
\\"id\\": 42
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 77,
|
||||
\\"previousId\\": 81,
|
||||
\\"parentId\\": 38,
|
||||
\\"previousId\\": 42,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\" \\",
|
||||
\\"id\\": 82
|
||||
\\"id\\": 43
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 75,
|
||||
\\"previousId\\": 77,
|
||||
\\"parentId\\": 36,
|
||||
\\"previousId\\": 38,
|
||||
\\"nextId\\": 45,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\" \\",
|
||||
\\"id\\": 83
|
||||
\\"id\\": 44
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 75,
|
||||
\\"previousId\\": 83,
|
||||
\\"parentId\\": 36,
|
||||
\\"previousId\\": 44,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
@@ -2712,11 +2992,11 @@ exports[`select2 1`] = `
|
||||
\\"id\\": \\"select2-results-1\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 84
|
||||
\\"id\\": 45
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 84,
|
||||
\\"parentId\\": 45,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": -1,
|
||||
\\"node\\": {
|
||||
@@ -2727,11 +3007,11 @@ exports[`select2 1`] = `
|
||||
\\"role\\": \\"presentation\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 85
|
||||
\\"id\\": 67
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 85,
|
||||
\\"parentId\\": 67,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
@@ -2743,11 +3023,11 @@ exports[`select2 1`] = `
|
||||
\\"role\\": \\"option\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 86
|
||||
\\"id\\": 68
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 86,
|
||||
\\"parentId\\": 68,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": -1,
|
||||
\\"node\\": {
|
||||
@@ -2757,22 +3037,22 @@ exports[`select2 1`] = `
|
||||
\\"class\\": \\"select2-match\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 87
|
||||
\\"id\\": 69
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 86,
|
||||
\\"previousId\\": 87,
|
||||
\\"parentId\\": 68,
|
||||
\\"previousId\\": 69,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"A\\",
|
||||
\\"id\\": 88
|
||||
\\"id\\": 70
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 84,
|
||||
\\"previousId\\": 85,
|
||||
\\"parentId\\": 45,
|
||||
\\"previousId\\": 67,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
@@ -2782,11 +3062,11 @@ exports[`select2 1`] = `
|
||||
\\"role\\": \\"presentation\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 89
|
||||
\\"id\\": 71
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 89,
|
||||
\\"parentId\\": 71,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
@@ -2798,11 +3078,11 @@ exports[`select2 1`] = `
|
||||
\\"role\\": \\"option\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 90
|
||||
\\"id\\": 72
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 90,
|
||||
\\"parentId\\": 72,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": -1,
|
||||
\\"node\\": {
|
||||
@@ -2812,23 +3092,23 @@ exports[`select2 1`] = `
|
||||
\\"class\\": \\"select2-match\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 91
|
||||
\\"id\\": 73
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 90,
|
||||
\\"previousId\\": 91,
|
||||
\\"parentId\\": 72,
|
||||
\\"previousId\\": 73,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"B\\",
|
||||
\\"id\\": 92
|
||||
\\"id\\": 74
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 18,
|
||||
\\"previousId\\": 66,
|
||||
\\"nextId\\": 75,
|
||||
\\"nextId\\": 36,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
@@ -2838,7 +3118,7 @@ exports[`select2 1`] = `
|
||||
\\"style\\": \\"\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 93
|
||||
\\"id\\": 75
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2848,7 +3128,7 @@ exports[`select2 1`] = `
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"2 results are available, use up and down arrow keys to navigate.\\",
|
||||
\\"id\\": 94
|
||||
\\"id\\": 76
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -2859,7 +3139,7 @@ exports[`select2 1`] = `
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 0,
|
||||
\\"id\\": 93
|
||||
\\"id\\": 75
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2868,7 +3148,7 @@ exports[`select2 1`] = `
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 81
|
||||
\\"id\\": 42
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2885,7 +3165,7 @@ exports[`select2 1`] = `
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 1,
|
||||
\\"id\\": 93
|
||||
\\"id\\": 75
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2893,7 +3173,7 @@ exports[`select2 1`] = `
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 6,
|
||||
\\"id\\": 81
|
||||
\\"id\\": 42
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2911,13 +3191,13 @@ exports[`select2 1`] = `
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 95,
|
||||
\\"id\\": 75,
|
||||
\\"attributes\\": {
|
||||
\\"style\\": \\"display: none;\\"
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"id\\": 75,
|
||||
\\"id\\": 36,
|
||||
\\"attributes\\": {
|
||||
\\"id\\": null
|
||||
}
|
||||
@@ -2935,7 +3215,7 @@ exports[`select2 1`] = `
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"id\\": 81,
|
||||
\\"id\\": 42,
|
||||
\\"attributes\\": {
|
||||
\\"class\\": \\"select2-input\\"
|
||||
}
|
||||
@@ -2944,22 +3224,22 @@ exports[`select2 1`] = `
|
||||
\\"removes\\": [
|
||||
{
|
||||
\\"parentId\\": 18,
|
||||
\\"id\\": 93
|
||||
\\"id\\": 75
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 84,
|
||||
\\"id\\": 85
|
||||
\\"parentId\\": 45,
|
||||
\\"id\\": 67
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 84,
|
||||
\\"id\\": 89
|
||||
\\"parentId\\": 45,
|
||||
\\"id\\": 71
|
||||
}
|
||||
],
|
||||
\\"adds\\": [
|
||||
{
|
||||
\\"parentId\\": 18,
|
||||
\\"previousId\\": 66,
|
||||
\\"nextId\\": 75,
|
||||
\\"nextId\\": 36,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
@@ -2969,7 +3249,7 @@ exports[`select2 1`] = `
|
||||
\\"style\\": \\"display: none;\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 95
|
||||
\\"id\\": 75
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -2980,7 +3260,7 @@ exports[`select2 1`] = `
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 0,
|
||||
\\"id\\": 67
|
||||
\\"id\\": 26
|
||||
}
|
||||
}
|
||||
]"
|
||||
|
||||
@@ -167,7 +167,7 @@ exports[`async-checkout 1`] = `
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\\\n \\",
|
||||
\\"id\\": 6
|
||||
\\"id\\": 7
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
@@ -182,13 +182,13 @@ exports[`async-checkout 1`] = `
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"test\\",
|
||||
\\"id\\": 9
|
||||
\\"id\\": 10
|
||||
}
|
||||
],
|
||||
\\"id\\": 8
|
||||
\\"id\\": 9
|
||||
}
|
||||
],
|
||||
\\"id\\": 7
|
||||
\\"id\\": 8
|
||||
}
|
||||
],
|
||||
\\"id\\": 4
|
||||
@@ -213,31 +213,31 @@ exports[`async-checkout 1`] = `
|
||||
\\"attributes\\": [],
|
||||
\\"removes\\": [
|
||||
{
|
||||
\\"parentId\\": 7,
|
||||
\\"id\\": 8
|
||||
\\"parentId\\": 8,
|
||||
\\"id\\": 9
|
||||
}
|
||||
],
|
||||
\\"adds\\": [
|
||||
{
|
||||
\\"parentId\\": 4,
|
||||
\\"previousId\\": 7,
|
||||
\\"previousId\\": 8,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"span\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 10
|
||||
\\"id\\": 9
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 10,
|
||||
\\"parentId\\": 9,
|
||||
\\"previousId\\": null,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"test\\",
|
||||
\\"id\\": 11
|
||||
\\"id\\": 10
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -161,7 +161,7 @@ describe('record integration tests', function(this: ISuite) {
|
||||
assertSnapshot(snapshots, __filename, 'block');
|
||||
});
|
||||
|
||||
it('should record DOM node movement', async () => {
|
||||
it('should record DOM node movement 1', async () => {
|
||||
const page: puppeteer.Page = await this.browser.newPage();
|
||||
await page.goto('about:blank');
|
||||
await page.setContent(getHtml.call(this, 'move-node.html'));
|
||||
@@ -176,6 +176,21 @@ describe('record integration tests', function(this: ISuite) {
|
||||
div.appendChild(span);
|
||||
});
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
assertSnapshot(snapshots, __filename, 'move-node');
|
||||
assertSnapshot(snapshots, __filename, 'move-node-1');
|
||||
});
|
||||
|
||||
it('should record DOM node movement 2', async () => {
|
||||
const page: puppeteer.Page = await this.browser.newPage();
|
||||
await page.goto('about:blank');
|
||||
await page.setContent(getHtml.call(this, 'move-node.html'));
|
||||
|
||||
await page.evaluate(() => {
|
||||
const div = document.createElement('div');
|
||||
const span = document.querySelector('span')!;
|
||||
document.body.appendChild(div);
|
||||
div.appendChild(span);
|
||||
});
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
assertSnapshot(snapshots, __filename, 'move-node-2');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user