* rrdom: add a diff function for properties * implement diffChildren function and unit tests * finish basic functions of diff algorithm * fix several bugs in the diff algorithm * replace the virtual parent optimization in applyMutation() * fix: moveAndHover after the diff algorithm is executed * replace virtual style map with rrdom cssom version has to be above 0.5.0 to pass virtual style tests * fix: failed virtual style tests in replayer.test.ts * fix: failed polyfill tests caused by nodejs compatibility of different versions * fix: svg viewBox attribute doesn't work Cause the attribute viewBox is case sensitive, set value for viewbox doesn't work * feat: replace treeIndex optimization with rrdom * fix bug of diffProps and disable smooth scrolling animation in fast-forward mode * feat: add iframe support * fix: @rollup/plugin-typescript build errors in rrweb-player Error: @rollup/plugin-typescript TS1371: This import is never used as a value and must use 'import type' because the 'importsNotUsedAsValues' is set to 'error' * fix: bug when fast-forward input events and add test for it * add test for fast-forward scroll events * fix: custom style rules don't get inserted into some iframe elements * code style tweak * fix: enable to diff iframe elements * fix the jest error "Unexpected token 'export'" * try to fix build error of rrweb-player * correct the attributes definition in rrdom * fix: custom style rules are not inserted in some iframes * add support for shadow dom * add support for MediaInteraction * add canvas support * fix unit test error in rrdom * add support for Text, Comment * try to refactor RRDom * refactor RRDom to reduce duplicate code * rename document-browser to virtual-dom * increase the test coverage for document.ts and add ownerDocument for it * Merge branch 'master' into virtual-dom * add more test for virtual-dom.ts * use cssstyle in document-nodejs * fix: bundle error * improve document-nodejs * enable to diff scroll positions of an element * rename rrdom to virtualDom for more readability and make the tree public * revert unknown change * improve the css style parser for comments * improve code style * update typings * add handling for the case where legacy_missingNodeRetryMap is not empty * only import types from rrweb into rrdom * Apply suggestions from code review Co-authored-by: Justin Halsall <Juice10@users.noreply.github.com> * Apply suggestions from code review * fix building error in rrweb * add a method setDefaultSN to set a default value for a RRNode's __sn * fix rrweb test error and bump up other packages * add support for custom property of css styles * add a switch for virtual-dom optimization * Apply suggestions from code review 1. add an enum type for NodeType 2. rename nodeType from rrweb-snapshot to RRNodeType 3. rename notSerializedId to unserializedId 4. add comments for some confusing variables * adapt changes of #865 to virtual-dom and improve the test case for more coverage * apply review suggestions https://github.com/rrweb-io/rrweb/pull/853#pullrequestreview-922474953 * tweak the diff algorithm * add description of the flag useVirtualDom and remove outdated logConfig * Remove console.log * Contain changes to document * Upgrade rollup to 2.70.2 * Revert "Upgrade rollup to 2.70.2" This reverts commit b1be81a2a76565935c9dc391f31beb7f64d25956. * Fix type checking rrdom * Fix typing error while bundling * Fix tslib error on build Rollup would output the following error: `semantic error TS2343: This syntax requires an imported helper named '__spreadArray' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'.` * Increase memory limit for rollup * Use esbuild for bundling Speeds up bundling significantly * Avoid circular dependencies and import un-bundled rrdom * Fix imports * Revert back to pre-esbuild This reverts the following commits: b7b3c8dbaa551a0129da1477136b1baaad28e6e1 72e23b8e27f9030d911358d3a17fe5ad1b3b5d4f 85d600a20c56cfa764cf1f858932ba14e67b1d23 61e1a5d323212ca8fbe0569e0b3062ddd53fc612 * Set node to lts (12 is no longer supported) * Speed up bundling and use less memory This fixes the out of memory errors happening while bundling * remove __sn from rrdom * fix typo * test: add a test case for StyleSheet mutation exceptions while fast-forwarding * rename Array.prototype.slice.call() to Array.from() * improve test cases * fix: PR #887 in 'virtual-dom' branch * apply justin's suggestion on 'Array.from' refactor related commit 0f6729d27a323260b36fbe79485a86715c0bc98a * improve import code structure Co-authored-by: Yun Feng <yun.feng@anu.edu.au>
551 lines
20 KiB
TypeScript
551 lines
20 KiB
TypeScript
/**
|
|
* @jest-environment jsdom
|
|
*/
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import * as puppeteer from 'puppeteer';
|
|
import * as rollup from 'rollup';
|
|
import resolve from '@rollup/plugin-node-resolve';
|
|
import * as typescript from 'rollup-plugin-typescript2';
|
|
import { JSDOM } from 'jsdom';
|
|
import {
|
|
cdataNode,
|
|
commentNode,
|
|
documentNode,
|
|
documentTypeNode,
|
|
elementNode,
|
|
Mirror,
|
|
NodeType,
|
|
NodeType as RRNodeType,
|
|
textNode,
|
|
} from 'rrweb-snapshot';
|
|
import {
|
|
buildFromDom,
|
|
buildFromNode,
|
|
createMirror,
|
|
getDefaultSN,
|
|
RRCanvasElement,
|
|
RRDocument,
|
|
RRElement,
|
|
RRNode,
|
|
} from '../src/virtual-dom';
|
|
|
|
const _typescript = (typescript as unknown) as typeof typescript.default;
|
|
const printRRDomCode = `
|
|
/**
|
|
* Print the RRDom as a string.
|
|
* @param rootNode the root node of the RRDom tree
|
|
* @returns printed string
|
|
*/
|
|
function printRRDom(rootNode, mirror) {
|
|
return walk(rootNode, mirror, '');
|
|
}
|
|
function walk(node, mirror, blankSpace) {
|
|
let printText = \`\${blankSpace}\${mirror.getId(node)} \${node.toString()}\n\`;
|
|
if(node instanceof rrdom.RRElement && node.shadowRoot)
|
|
printText += walk(node.shadowRoot, mirror, blankSpace + ' ');
|
|
for (const child of node.childNodes)
|
|
printText += walk(child, mirror, blankSpace + ' ');
|
|
if (node instanceof rrdom.RRIFrameElement)
|
|
printText += walk(node.contentDocument, mirror, blankSpace + ' ');
|
|
return printText;
|
|
}
|
|
`;
|
|
|
|
describe('RRDocument for browser environment', () => {
|
|
let mirror: Mirror;
|
|
beforeEach(() => {
|
|
mirror = new Mirror();
|
|
});
|
|
|
|
describe('create a RRNode from a real Node', () => {
|
|
it('should support quicksmode documents', () => {
|
|
// seperate jsdom document as changes to the document would otherwise bleed into other tests
|
|
const dom = new JSDOM();
|
|
const document = dom.window.document;
|
|
|
|
expect(document.doctype).toBeNull(); // confirm compatMode is 'BackCompat' in JSDOM
|
|
|
|
const rrdom = new RRDocument();
|
|
let rrNode = buildFromNode(document, rrdom, mirror)!;
|
|
|
|
expect((rrNode as RRDocument).compatMode).toBe('BackCompat');
|
|
});
|
|
|
|
it('can patch serialized ID for an unserialized node', () => {
|
|
// build from document
|
|
expect(mirror.getMeta(document)).toBeNull();
|
|
const rrdom = new RRDocument();
|
|
let rrNode = buildFromNode(document, rrdom, mirror)!;
|
|
expect(mirror.getMeta(document)).toBeDefined();
|
|
expect(mirror.getId(document)).toEqual(-1);
|
|
expect(rrNode).not.toBeNull();
|
|
expect(rrdom.mirror.getMeta(rrNode)).toBeDefined();
|
|
expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.Document);
|
|
expect(rrdom.mirror.getId(rrNode)).toEqual(-1);
|
|
expect(rrNode).toBe(rrdom);
|
|
|
|
// build from document type
|
|
expect(mirror.getMeta(document.doctype!)).toBeNull();
|
|
rrNode = buildFromNode(document.doctype!, rrdom, mirror)!;
|
|
expect(mirror.getMeta(document.doctype!)).toBeDefined();
|
|
expect(mirror.getId(document.doctype)).toEqual(-2);
|
|
expect(rrNode).not.toBeNull();
|
|
expect(rrdom.mirror.getMeta(rrNode)).toBeDefined();
|
|
expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(
|
|
RRNodeType.DocumentType,
|
|
);
|
|
expect(rrdom.mirror.getId(rrNode)).toEqual(-2);
|
|
|
|
// build from element
|
|
expect(mirror.getMeta(document.documentElement)).toBeNull();
|
|
rrNode = buildFromNode(
|
|
(document.documentElement as unknown) as Node,
|
|
rrdom,
|
|
mirror,
|
|
)!;
|
|
expect(mirror.getMeta(document.documentElement)).toBeDefined();
|
|
expect(mirror.getId(document.documentElement)).toEqual(-3);
|
|
expect(rrNode).not.toBeNull();
|
|
expect(rrdom.mirror.getMeta(rrNode)).toBeDefined();
|
|
expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.Element);
|
|
expect(rrdom.mirror.getId(rrNode)).toEqual(-3);
|
|
|
|
// build from text
|
|
const text = document.createTextNode('text');
|
|
expect(mirror.getMeta(text)).toBeNull();
|
|
rrNode = buildFromNode(text, rrdom, mirror)!;
|
|
expect(mirror.getMeta(text)).toBeDefined();
|
|
expect(mirror.getId(text)).toEqual(-4);
|
|
expect(rrNode).not.toBeNull();
|
|
expect(rrdom.mirror.getMeta(rrNode)).toBeDefined();
|
|
expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.Text);
|
|
expect(rrdom.mirror.getId(rrNode)).toEqual(-4);
|
|
|
|
// build from comment
|
|
const comment = document.createComment('comment');
|
|
expect(mirror.getMeta(comment)).toBeNull();
|
|
rrNode = buildFromNode(comment, rrdom, mirror)!;
|
|
expect(mirror.getMeta(comment)).toBeDefined();
|
|
expect(mirror.getId(comment)).toEqual(-5);
|
|
expect(rrNode).not.toBeNull();
|
|
expect(rrdom.mirror.getMeta(rrNode)).toBeDefined();
|
|
expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.Comment);
|
|
expect(rrdom.mirror.getId(rrNode)).toEqual(-5);
|
|
|
|
// build from CDATASection
|
|
const xmlDoc = new DOMParser().parseFromString(
|
|
'<xml></xml>',
|
|
'application/xml',
|
|
);
|
|
const cdata = 'Some <CDATA> data & then some';
|
|
var cdataSection = xmlDoc.createCDATASection(cdata);
|
|
expect(mirror.getMeta(cdataSection)).toBeNull();
|
|
expect(mirror.getMeta(cdataSection)).toBeNull();
|
|
rrNode = buildFromNode(cdataSection, rrdom, mirror)!;
|
|
expect(mirror.getMeta(cdataSection)).toBeDefined();
|
|
expect(mirror.getId(cdataSection)).toEqual(-6);
|
|
expect(rrNode).not.toBeNull();
|
|
expect(rrdom.mirror.getMeta(rrNode)).toBeDefined();
|
|
expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.CDATA);
|
|
expect(rrdom.mirror.getId(rrNode)).toEqual(-6);
|
|
expect(rrNode.textContent).toEqual(cdata);
|
|
});
|
|
|
|
it('can record scroll position from HTMLElements', () => {
|
|
expect(document.body.scrollLeft).toEqual(0);
|
|
expect(document.body.scrollTop).toEqual(0);
|
|
const rrdom = new RRDocument();
|
|
let rrNode = buildFromNode(document.body, rrdom, mirror)!;
|
|
expect((rrNode as RRElement).scrollLeft).toBeUndefined();
|
|
expect((rrNode as RRElement).scrollTop).toBeUndefined();
|
|
|
|
document.body.scrollLeft = 100;
|
|
document.body.scrollTop = 200;
|
|
expect(document.body.scrollLeft).toEqual(100);
|
|
expect(document.body.scrollTop).toEqual(200);
|
|
rrNode = buildFromNode(document.body, rrdom, mirror)!;
|
|
expect((rrNode as RRElement).scrollLeft).toEqual(100);
|
|
expect((rrNode as RRElement).scrollTop).toEqual(200);
|
|
});
|
|
|
|
it('can build contentDocument from an iframe element', () => {
|
|
const iframe = document.createElement('iframe');
|
|
document.body.appendChild(iframe);
|
|
expect(iframe.contentDocument).not.toBeNull();
|
|
const rrdom = new RRDocument();
|
|
const RRIFrame = rrdom.createElement('iframe');
|
|
const rrNode = buildFromNode(
|
|
iframe.contentDocument!,
|
|
rrdom,
|
|
mirror,
|
|
RRIFrame,
|
|
)!;
|
|
expect(rrNode).not.toBeNull();
|
|
expect(rrdom.mirror.getMeta(rrNode)).toBeDefined();
|
|
expect(rrdom.mirror.getMeta(rrNode)!.type).toEqual(RRNodeType.Document);
|
|
expect(rrdom.mirror.getId(rrNode)).toEqual(-1);
|
|
expect(mirror.getId(iframe.contentDocument)).toEqual(-1);
|
|
expect(rrNode).toBe(RRIFrame.contentDocument);
|
|
});
|
|
|
|
it('can build from a shadow dom', () => {
|
|
const div = document.createElement('div');
|
|
div.attachShadow({ mode: 'open' });
|
|
expect(div.shadowRoot).toBeDefined();
|
|
const rrdom = new RRDocument();
|
|
const parentRRNode = rrdom.createElement('div');
|
|
const rrNode = buildFromNode(
|
|
div.shadowRoot!,
|
|
rrdom,
|
|
mirror,
|
|
parentRRNode,
|
|
)!;
|
|
expect(rrNode).not.toBeNull();
|
|
expect(rrdom.mirror.getMeta(rrNode)).toBeDefined();
|
|
expect(rrdom.mirror.getId(rrNode)).toEqual(-1);
|
|
expect(mirror.getId(div.shadowRoot)).toEqual(-1);
|
|
expect(rrNode.RRNodeType).toEqual(RRNodeType.Element);
|
|
expect((rrNode as RRElement).tagName).toEqual('SHADOWROOT');
|
|
expect(rrNode).toBe(parentRRNode.shadowRoot);
|
|
});
|
|
});
|
|
|
|
describe('create a RRDocument from a html document', () => {
|
|
let browser: puppeteer.Browser;
|
|
let code: string;
|
|
let page: puppeteer.Page;
|
|
|
|
beforeAll(async () => {
|
|
browser = await puppeteer.launch();
|
|
const bundle = await rollup.rollup({
|
|
input: path.resolve(__dirname, '../src/virtual-dom.ts'),
|
|
plugins: [
|
|
resolve(),
|
|
(_typescript({
|
|
tsconfigOverride: { compilerOptions: { module: 'ESNext' } },
|
|
}) as unknown) as rollup.Plugin,
|
|
],
|
|
});
|
|
const {
|
|
output: [{ code: _code }],
|
|
} = await bundle.generate({
|
|
name: 'rrdom',
|
|
format: 'iife',
|
|
});
|
|
code = _code;
|
|
});
|
|
afterAll(async () => {
|
|
await browser.close();
|
|
});
|
|
|
|
beforeEach(async () => {
|
|
page = await browser.newPage();
|
|
await page.goto('about:blank');
|
|
await page.evaluate(code + printRRDomCode);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await page.close();
|
|
});
|
|
it('can build from a common html', async () => {
|
|
await page.setContent(getHtml('main.html'));
|
|
const result = await page.evaluate(`
|
|
const doc = new rrdom.RRDocument();
|
|
rrdom.buildFromDom(document, undefined, doc);
|
|
printRRDom(doc, doc.mirror);
|
|
`);
|
|
expect(result).toMatchSnapshot();
|
|
});
|
|
|
|
it('can build from an iframe html ', async () => {
|
|
await page.setContent(getHtml('iframe.html'));
|
|
const result = await page.evaluate(`
|
|
const doc = new rrdom.RRDocument();
|
|
rrdom.buildFromDom(document, undefined, doc);
|
|
printRRDom(doc, doc.mirror);
|
|
`);
|
|
expect(result).toMatchSnapshot();
|
|
});
|
|
|
|
it('can build from a html containing nested shadow doms', async () => {
|
|
await page.setContent(getHtml('shadow-dom.html'));
|
|
const result = await page.evaluate(`
|
|
const doc = new rrdom.RRDocument();
|
|
rrdom.buildFromDom(document, undefined, doc);
|
|
printRRDom(doc, doc.mirror);
|
|
`);
|
|
expect(result).toMatchSnapshot();
|
|
});
|
|
|
|
it('can build from a xml page', async () => {
|
|
const result = await page.evaluate(`
|
|
var docu = new DOMParser().parseFromString('<xml></xml>', 'application/xml');
|
|
var cdata = docu.createCDATASection('Some <CDATA> data & then some');
|
|
docu.getElementsByTagName('xml')[0].appendChild(cdata);
|
|
// Displays: <xml><![CDATA[Some <CDATA> data & then some]]></xml>
|
|
|
|
const doc = new rrdom.RRDocument();
|
|
rrdom.buildFromDom(docu, undefined, doc);
|
|
printRRDom(doc, doc.mirror);
|
|
`);
|
|
expect(result).toMatchSnapshot();
|
|
});
|
|
});
|
|
|
|
describe('RRDocument build for virtual dom', () => {
|
|
it('can access a unique, decremented unserializedId every time', () => {
|
|
const node = new RRDocument();
|
|
for (let i = 1; i <= 100; i++) expect(node.unserializedId).toBe(-i);
|
|
});
|
|
|
|
it('can create a new RRDocument', () => {
|
|
const dom = new RRDocument();
|
|
const newDom = dom.createDocument('', '');
|
|
expect(newDom).toBeInstanceOf(RRDocument);
|
|
});
|
|
|
|
it('can create a new RRDocument receiving a mirror parameter', () => {
|
|
const mirror = createMirror();
|
|
const dom = new RRDocument(mirror);
|
|
const newDom = dom.createDocument('', '');
|
|
expect(newDom).toBeInstanceOf(RRDocument);
|
|
expect(dom.mirror).toBe(mirror);
|
|
});
|
|
|
|
it('can build a RRDocument from a real Dom', () => {
|
|
const result = buildFromDom(document, mirror);
|
|
expect(result.childNodes.length).toBe(2);
|
|
expect(result.documentElement).toBeDefined();
|
|
expect(result.head).toBeDefined();
|
|
expect(result.head!.tagName).toBe('HEAD');
|
|
expect(result.body).toBeDefined();
|
|
expect(result.body!.tagName).toBe('BODY');
|
|
});
|
|
|
|
it('can destroy a RRDocument tree', () => {
|
|
const dom = new RRDocument();
|
|
const node1 = dom.createDocumentType('', '', '');
|
|
dom.appendChild(node1);
|
|
dom.mirror.add(node1, {
|
|
id: 0,
|
|
type: NodeType.DocumentType,
|
|
name: '',
|
|
publicId: '',
|
|
systemId: '',
|
|
});
|
|
const node2 = dom.createElement('html');
|
|
dom.appendChild(node2);
|
|
dom.mirror.add(node1, {
|
|
id: 1,
|
|
type: NodeType.Document,
|
|
childNodes: [],
|
|
});
|
|
|
|
expect(dom.childNodes.length).toEqual(2);
|
|
expect(dom.mirror.has(0)).toBeTruthy();
|
|
expect(dom.mirror.has(1)).toBeTruthy();
|
|
|
|
dom.destroyTree();
|
|
expect(dom.childNodes.length).toEqual(0);
|
|
expect(dom.mirror.has(0)).toBeFalsy();
|
|
expect(dom.mirror.has(1)).toBeFalsy();
|
|
});
|
|
|
|
it('can close and open a RRDocument', () => {
|
|
const dom = new RRDocument();
|
|
const documentType = dom.createDocumentType('html', '', '');
|
|
dom.appendChild(documentType);
|
|
expect(dom.childNodes[0]).toBe(documentType);
|
|
expect(dom.unserializedId).toBe(-1);
|
|
expect(dom.unserializedId).toBe(-2);
|
|
expect(dom.close());
|
|
expect(dom.open());
|
|
expect(dom.childNodes.length).toEqual(0);
|
|
expect(dom.unserializedId).toBe(-1);
|
|
});
|
|
|
|
it('can execute a dummy getContext function in RRCanvasElement', () => {
|
|
const canvas = new RRCanvasElement('CANVAS');
|
|
expect(canvas.getContext).toBeDefined();
|
|
expect(canvas.getContext()).toBeNull();
|
|
});
|
|
|
|
describe('Mirror in the RRDocument', () => {
|
|
it('should have a mirror to store id and node', () => {
|
|
const dom = new RRDocument();
|
|
expect(dom.mirror).toBeDefined();
|
|
const node1 = dom.createElement('div');
|
|
dom.mirror.add(node1, getDefaultSN(node1, 0));
|
|
|
|
const node2 = dom.createTextNode('text');
|
|
dom.mirror.add(node2, getDefaultSN(node2, 1));
|
|
|
|
expect(dom.mirror.getNode(0)).toBe(node1);
|
|
expect(dom.mirror.getNode(1)).toBe(node2);
|
|
expect(dom.mirror.getNode(2)).toBeNull();
|
|
expect(dom.mirror.getNode(-1)).toBeNull();
|
|
});
|
|
|
|
it('can get node id', () => {
|
|
const dom = new RRDocument();
|
|
const node1 = dom.createElement('div');
|
|
dom.mirror.add(node1, getDefaultSN(node1, 0));
|
|
|
|
expect(dom.mirror.getId(node1)).toEqual(0);
|
|
const node2 = dom.createTextNode('text');
|
|
expect(dom.mirror.getId(node2)).toEqual(-1);
|
|
expect(dom.mirror.getId((null as unknown) as RRNode)).toEqual(-1);
|
|
});
|
|
|
|
it('has() should return whether the mirror has an ID', () => {
|
|
const dom = new RRDocument();
|
|
const node1 = dom.createElement('div');
|
|
dom.mirror.add(node1, getDefaultSN(node1, 0));
|
|
const node2 = dom.createTextNode('text');
|
|
dom.mirror.add(node2, getDefaultSN(node2, 1));
|
|
expect(dom.mirror.has(0)).toBeTruthy();
|
|
expect(dom.mirror.has(1)).toBeTruthy();
|
|
expect(dom.mirror.has(2)).toBeFalsy();
|
|
expect(dom.mirror.has(-1)).toBeFalsy();
|
|
});
|
|
|
|
it('can remove node from the mirror', () => {
|
|
const dom = new RRDocument();
|
|
const node1 = dom.createElement('div');
|
|
dom.mirror.add(node1, getDefaultSN(node1, 0));
|
|
const node2 = dom.createTextNode('text');
|
|
dom.mirror.add(node2, getDefaultSN(node2, 1));
|
|
node1.appendChild(node2);
|
|
expect(dom.mirror.has(0)).toBeTruthy();
|
|
expect(dom.mirror.has(1)).toBeTruthy();
|
|
dom.mirror.removeNodeFromMap(node2);
|
|
expect(dom.mirror.has(0)).toBeTruthy();
|
|
expect(dom.mirror.has(1)).toBeFalsy();
|
|
|
|
dom.mirror.add(node2, getDefaultSN(node2, 1));
|
|
expect(dom.mirror.has(1)).toBeTruthy();
|
|
// To remove node1 and its child node2 from the mirror.
|
|
dom.mirror.removeNodeFromMap(node1);
|
|
expect(dom.mirror.has(0)).toBeFalsy();
|
|
expect(dom.mirror.has(1)).toBeFalsy();
|
|
});
|
|
|
|
it('can reset the mirror', () => {
|
|
const dom = new RRDocument();
|
|
const node1 = dom.createElement('div');
|
|
dom.mirror.add(node1, getDefaultSN(node1, 0));
|
|
const node2 = dom.createTextNode('text');
|
|
dom.mirror.add(node2, getDefaultSN(node2, 1));
|
|
expect(dom.mirror.has(0)).toBeTruthy();
|
|
expect(dom.mirror.has(1)).toBeTruthy();
|
|
|
|
dom.mirror.reset();
|
|
expect(dom.mirror.has(0)).toBeFalsy();
|
|
expect(dom.mirror.has(1)).toBeFalsy();
|
|
});
|
|
|
|
it('hasNode() should return whether the mirror has a node', () => {
|
|
const dom = new RRDocument();
|
|
const node1 = dom.createElement('div');
|
|
const node2 = dom.createTextNode('text');
|
|
expect(dom.mirror.hasNode(node1)).toBeFalsy();
|
|
dom.mirror.add(node1, getDefaultSN(node1, 0));
|
|
expect(dom.mirror.hasNode(node1)).toBeTruthy();
|
|
expect(dom.mirror.hasNode(node2)).toBeFalsy();
|
|
dom.mirror.add(node2, getDefaultSN(node2, 1));
|
|
expect(dom.mirror.hasNode(node2)).toBeTruthy();
|
|
});
|
|
|
|
it('can get all IDs from the mirror', () => {
|
|
const dom = new RRDocument();
|
|
expect(dom.mirror.getIds().length).toBe(0);
|
|
const node1 = dom.createElement('div');
|
|
dom.mirror.add(node1, getDefaultSN(node1, 0));
|
|
const node2 = dom.createTextNode('text');
|
|
dom.mirror.add(node2, getDefaultSN(node2, 1));
|
|
expect(dom.mirror.getIds().length).toBe(2);
|
|
expect(dom.mirror.getIds()).toStrictEqual([0, 1]);
|
|
});
|
|
|
|
it('can replace nodes', () => {
|
|
const dom = new RRDocument();
|
|
expect(dom.mirror.getIds().length).toBe(0);
|
|
const node1 = dom.createElement('div');
|
|
dom.mirror.add(node1, getDefaultSN(node1, 0));
|
|
expect(dom.mirror.getNode(0)).toBe(node1);
|
|
const node2 = dom.createTextNode('text');
|
|
dom.mirror.replace(0, node2);
|
|
expect(dom.mirror.getNode(0)).toBe(node2);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('can get default SN value from a RRNode', () => {
|
|
const rrdom = new RRDocument();
|
|
it('can get from RRDocument', () => {
|
|
const node = rrdom;
|
|
const sn = getDefaultSN(node, 1);
|
|
expect(sn).toBeDefined();
|
|
expect(sn.type).toEqual(RRNodeType.Document);
|
|
expect((sn as documentNode).childNodes).toBeInstanceOf(Array);
|
|
});
|
|
|
|
it('can get from RRDocumentType', () => {
|
|
const name = 'name',
|
|
publicId = 'publicId',
|
|
systemId = 'systemId';
|
|
const node = rrdom.createDocumentType(name, publicId, systemId);
|
|
const sn = getDefaultSN(node, 1);
|
|
|
|
expect(sn).toBeDefined();
|
|
expect(sn.type).toEqual(RRNodeType.DocumentType);
|
|
expect((sn as documentTypeNode).name).toEqual(name);
|
|
expect((sn as documentTypeNode).publicId).toEqual(publicId);
|
|
expect((sn as documentTypeNode).systemId).toEqual(systemId);
|
|
});
|
|
|
|
it('can get from RRElement', () => {
|
|
const node = rrdom.createElement('div');
|
|
const sn = getDefaultSN(node, 1);
|
|
|
|
expect(sn).toBeDefined();
|
|
expect(sn.type).toEqual(RRNodeType.Element);
|
|
expect((sn as elementNode).tagName).toEqual('div');
|
|
expect((sn as elementNode).attributes).toBeDefined();
|
|
expect((sn as elementNode).childNodes).toBeInstanceOf(Array);
|
|
});
|
|
|
|
it('can get from RRText', () => {
|
|
const node = rrdom.createTextNode('text');
|
|
const sn = getDefaultSN(node, 1);
|
|
|
|
expect(sn).toBeDefined();
|
|
expect(sn.type).toEqual(RRNodeType.Text);
|
|
expect((sn as textNode).textContent).toEqual('text');
|
|
});
|
|
|
|
it('can get from RRComment', () => {
|
|
const node = rrdom.createComment('comment');
|
|
const sn = getDefaultSN(node, 1);
|
|
|
|
expect(sn).toBeDefined();
|
|
expect(sn.type).toEqual(RRNodeType.Comment);
|
|
expect((sn as commentNode).textContent).toEqual('comment');
|
|
});
|
|
|
|
it('can get from RRCDATASection', () => {
|
|
const node = rrdom.createCDATASection('data');
|
|
const sn = getDefaultSN(node, 1);
|
|
|
|
expect(sn).toBeDefined();
|
|
expect(sn.type).toEqual(RRNodeType.CDATA);
|
|
expect((sn as cdataNode).textContent).toEqual('');
|
|
});
|
|
});
|
|
});
|
|
function getHtml(fileName: string) {
|
|
const filePath = path.resolve(__dirname, `./html/${fileName}`);
|
|
return fs.readFileSync(filePath, 'utf8');
|
|
}
|