* inline stylesheets when loaded * set empty link elements to loaded by default * Clean up stylesheet manager * Remove attribute mutation code * Update packages/rrweb/test/record.test.ts * Update packages/rrweb/test/record.test.ts * Update packages/rrweb/test/record.test.ts * Update packages/rrweb/scripts/repl.js * Update packages/rrweb/test/record.test.ts * Update packages/rrweb/src/record/index.ts * Add todo * Move require out of time sensitive assert * Add waitForRAF, its more reliable than waitForTimeout * Remove flaky tests * Add recording stylesheets in iframes * Remove variability from flaky test * Make test more robust * Fix naming * Add test cases for inlineImages * Add test cases for inlineImages * Record iframe mutations cross page * Test: should record images inside iframe with blob url after iframe was reloaded * Handle negative ids in rrdom correctly When iframes get inserted they create untracked elements, both on the dom and rrdom side. Because they are untracked they generate negative numbers when fetching the id from mirror. This creates a problem when comparing and fetching ids across mirrors. This commit tries to get away from using negative ids as much as possible in rrdom's comparisons * Update packages/rrdom/src/diff.ts Co-authored-by: Yun Feng <yun.feng@anu.edu.au> * Start unserialized nodes at -2 This way we don't accidentally think of them as mirror misses * Set unserialized id starting number at -2 * Remove duplication * Use turbo instead of lerna * Skip benchmark as it is unreliable when executed in parallel * Strip port number from serialization, it can vary * Add settimeout to virtual dom test * Remove console.log and refactor blob:url serialization * Include references in tsconfig to indicate which monorepo packages are being used * Add stream setup * Migrate project to es module * Add reference to rrweb from rrdom * Move jest config to ESM * Setup basic WebRTC canvas streaming * Cleanup and refactor WebRTC streaming * Remove ? which isn't propper javascript * Yarn lock * Remove webrtc code from rrweb * Add plugin hooks Record/Replay plugins `.getMirror` exposes the mirror to plugins Replay plugins `.onBuild` called whenever a node was added to the dom * Expose plugins with server * Use unminified version for tests * Don't include simple-peer in rrweb main project * Add canvas webrtc plugin Streams contents of canvas via webrtc * ignore tsconfig.tsbuildinfo * Cleanup unused code * type definition files are no longer committed * Devtools off by default * Extract .css into its own file * Refactor plugin apis and fix multi canvas streaming support * Add readme to rrweb canvas webrtc plugin * Reference canvas-webrtc plugin in documentation * Forbidden non-null assertion * Remove linting of each project, yarn lint:report will do this * Remove test code * Cut down line length * fix CI failure and improve the zh_CN doc * Update packages/rrweb/src/plugins/canvas-webrtc/replay/index.ts Co-authored-by: Yun Feng <yun.feng@anu.edu.au> * Cleaner styling of replay Co-authored-by: Yun Feng <yun.feng@anu.edu.au> * Clean up stream.js based on @Mark-Fenng's feedback * Remove duplicate send Co-authored-by: Yun Feng <yun.feng@anu.edu.au> Co-authored-by: Yun Feng <yun.feng0817@gmail.com>
552 lines
20 KiB
TypeScript
552 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,
|
|
BaseRRNode as RRNode,
|
|
} from '../src';
|
|
|
|
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', () => {
|
|
jest.setTimeout(60_000);
|
|
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(-2);
|
|
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(-2);
|
|
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(-3);
|
|
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(-3);
|
|
|
|
// 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(-4);
|
|
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(-4);
|
|
|
|
// 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(-5);
|
|
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(-5);
|
|
|
|
// 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(-6);
|
|
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(-6);
|
|
|
|
// 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(-7);
|
|
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(-7);
|
|
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(-2);
|
|
expect(mirror.getId(iframe.contentDocument)).toEqual(-2);
|
|
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(-2);
|
|
expect(mirror.getId(div.shadowRoot)).toEqual(-2);
|
|
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/index.ts'),
|
|
plugins: [
|
|
(resolve() as unknown) as rollup.Plugin,
|
|
(_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 = 2; 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(-2);
|
|
expect(dom.unserializedId).toBe(-3);
|
|
expect(dom.close());
|
|
expect(dom.open());
|
|
expect(dom.childNodes.length).toEqual(0);
|
|
expect(dom.unserializedId).toBe(-2);
|
|
});
|
|
|
|
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');
|
|
}
|