* Chore: Add move most types from rrweb to @rrweb/types package * Split off type imports * Split off type import to its own line * Get vite to generate type definitions * Apply formatting changes * noEmit not allowed in tsconfig, moved it to build step * Migrate rrdom-nodejs build to vite * Apply formatting changes * Migrate rrweb-snapshot to vite * Unify configs * Chore: Migrate rrdom to vite Turns out what we where doing by overwriting `public textContent: string | undefined` as a getter in a subclass is something that isn't allowed in typescript. Because we where using `// @ts-ignore` to hide this error our bundler chose to allow the overwrite. Vite choses to disallow the overwrite making all subclasses' `textContent` undefined. To mitigate this we're using an abstract class, which does allow sub classes to decide if they wan't to use getters or not. * Chore: Migrate rrweb to vite WIP * build:browser was removed (for now) * BREAKING: moved rrweb-plugin-console to its own npm module This removes console from rrweb-all.js * Support cjs files in startServer * Move canvas-webrtc plugin to its own package * Chore: move sequential-id plugin to its own package * Chore: Configure rrweb's vite bundling * `Id` had lowercase `d` before, making it lowercase again * Test: Move console tests to their own package * remove unused utils from rrdom * pull in latest version of master something when wrong earlier when resolving merge conflicts, this should be correct * Fix type casting issue in diff.ts * Fix typo * Fix duplicate entries in package.json and tsconfig.json * Apply formatting changes * Update dependencies in package.json files * Update dependencies to use Vite 5.2.8 in package.json files * Get tests passing for rrdom `apply virtual style rules to node` tests need to be moved to rrweb to avoid circular dependencies * Fix image loading issue in integration tests * Move pack/unpack to its own @rrweb/packer module * Get tests to work in rrdom-nodejs * Port tests in rrweb-snapshot to vitest and fix them * Fix tests for rrweb-plugin-console-record * Add @rrweb/all package * Fix publint and attw errors for rrdom and @rrweb/types * Use shared vitest.config.ts in rrweb-snapshot package * Fix publint and attw issues for rrweb-snapshot * Export `ReplayPlugin` type directly from rrweb * Fix publint and attw issues for packages * Fix publint & attw issue. I was bumping into this issue:3729bc2a3c/docs/problems/NoResolution.mdAnd had to choose one of these three methods described here: https://github.com/andrewbranch/example-subpath-exports-ts-compat?tab=readme-ov-file#typescript-friendly-strategies-for-packagejson-subpath-exports-compatibility And I ended up going for the method described here:1ffe3425b0/examples/node_modules/package-json-redirects (package-json-redirects)The redirect method seemed the least invasive and most effective. * Fix publint & attw issue. I was bumping into this issue:3729bc2a3c/docs/problems/NoResolution.mdAnd had to choose one of these three methods described here: https://github.com/andrewbranch/example-subpath-exports-ts-compat?tab=readme-ov-file#typescript-friendly-strategies-for-packagejson-subpath-exports-compatibility And I ended up going for the method described here:1ffe3425b0/examples/node_modules/package-json-redirects (package-json-redirects)The redirect method seemed the least invasive and most effective. * move some rrdom tests that require rrweb to rrweb package * Use pre-jest 29 syntax for snapshotting * get rrweb passing publint and attw * const enum does not work with isolated modules flag * Fix script tag type in webgl.test.ts.snap and update rrweb.umd.cjs path in webgl.test.ts * Fix paths * Move tests for console record plugin and fix bundle path * Fix tests for rrweb * pack integration tests were moved to @rrweb/all * Update rrweb bundle path in test files * Fix flaky scroll emit from test * Migrate rrweb's tests over to vitest and make them pass * Make sure benchmarks & updating tests work * Remove jest from rrweb * Fix paths * always use rrweb's own cssom * Update tsconfig.json for rrweb-plugin-sequential-id-record Fixes this error: Error: @rrweb/rrweb-plugin-sequential-id-record:prepublish: tsconfig.json(9,5): error TS6377: Cannot write file '/home/runner/work/rrweb/rrweb/tsconfig.tsbuildinfo' because it will overwrite '.tsbuildinfo' file generated by referenced project '/home/runner/work/rrweb/rrweb/packages/rrweb' * Add tsbuildinfo config to extended tsconfig files * Move rrdom over to vitest * Apply formatting changes * Update rrweb imports to use the new package structure * extend rrweb-snapshot's tsconfig from monorepo base config * extend @rrweb/types's tsconfig from monorepo base config * extend rrdom's tsconfig from monorepo base config * extend rrdom-nodejs's tsconfig from monorepo base config * extend web-extension's tsconfig from monorepo base config * unify tsconfigs * Continue when tests fail * Add stricter type checking * Add check-types global command * remove jest * Remove unused code * Add check-types command to build script * Fix linting issues * Add setup Chrome action for CI/CD workflow * Update puppeteer version in package.json for rrweb * Update Chrome setup in CI/CD workflow * Update Chrome setup in CI/CD workflow * Add Chrome setup and test cache location * Update CI/CD workflow to test chrome cache location * Add chrome installation step to CI/CD workflow * Update Puppeteer configuration for headless testing * Update dependencies and workflow configuration * Use same version of chrome on CI as is run locally * Use version of chrome that seems to work with rrdom tests * Try using puppeteerrc to define chrome version * Add .cache directory to .gitignore * Move global flag to vitest config * Update puppeteer version to 20.9.0 * Update console log messages in rrweb-plugin-console-record for new puppeteer version * Remove redundant Chrome setup from CI/CD workflow * Add minification and umd for all built files * Update import paths for rrweb dist files * Add @rrweb/replay and @rrweb/record * Add script to lint packages * Apply formatting changes * exclude styles export from typescript package type checking * WIP Move rrweb-player over to vite * Apply formatting changes * chore: Update rrweb plugin import paths * Remove rollup from rrweb-player * Fix typing issues * Fix typing issues * chore: Update rrweb-player to use vite for build process * Apply formatting changes * chore: Export Player class in rrweb-player/src/main.ts Makes attw happy * Apply formatting changes * Gets wiped by yarn workspaces-to-typescript-project-references * Add .eslintignore and .eslintrc.cjs files for rrweb-player package * Apply formatting changes * Update dependencies in rrweb-player/package.json * Apply formatting changes * chore: Update eslint configuration for rrweb-player package * Apply formatting changes * chore: Remove unused files from rrweb-player package * Apply formatting changes * chore: Update rrweb-player import path to use rrweb-player.cjs * chore: Update addEventListener signature in rrweb-player * Apply formatting changes * Add .eslintignore and update .gitignore files for to root * Apply formatting changes * Update documentation * Update @rrweb/types package description * Apply formatting changes * Update build and run commands in CONTRIBUTING.md * Apply formatting changes * Update package versions to 2.0.0-alpha.13 * Apply formatting changes * Apply formatting changes * Fix import statement in media/index.ts * Apply formatting changes * chore: Update .gitignore to exclude build and dist directories * Apply formatting changes * Apply formatting changes * Migrate setTimeout to vitest * Apply formatting changes * Apply formatting changes * Fix isNativeShadowDom function signature in utils.ts * try out jsr * Apply formatting changes * Update package versions to 2.0.0-alpha.14 * Apply formatting changes * Fix name of rrwebSnapshot object * Apply formatting changes * Remove unused lock files * Apply formatting changes * Update rrweb bundle path to use umd.cjs format * Apply formatting changes * Trigger tests to run again * Rename snapshots for vitest * Apply formatting changes * Ping CI * Apply formatting changes * Ping CI * Apply formatting changes * Ignore files generated by svelte-kit for prettier * Correct Player object
549 lines
20 KiB
TypeScript
549 lines
20 KiB
TypeScript
/**
|
|
* @vitest-environment jsdom
|
|
*/
|
|
import { describe, it, expect, beforeAll } from 'vitest';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import { NodeType as RRNodeType } from 'rrweb-snapshot';
|
|
import {
|
|
RRCanvasElement,
|
|
RRCDATASection,
|
|
RRComment,
|
|
RRDocument,
|
|
RRElement,
|
|
RRIFrameElement,
|
|
RRImageElement,
|
|
RRMediaElement,
|
|
RRStyleElement,
|
|
RRText,
|
|
} from '../src/document-nodejs';
|
|
import { buildFromDom } from 'rrdom';
|
|
|
|
describe('RRDocument for nodejs environment', () => {
|
|
describe('RRDocument API', () => {
|
|
let rrdom: RRDocument;
|
|
beforeAll(() => {
|
|
// initialize rrdom
|
|
document.write(getHtml('main.html'));
|
|
rrdom = new RRDocument();
|
|
buildFromDom(document, undefined, rrdom);
|
|
});
|
|
|
|
it('can create different type of RRNodes', () => {
|
|
const document = rrdom.createDocument('', '');
|
|
expect(document).toBeInstanceOf(RRDocument);
|
|
const audio = rrdom.createElement('audio');
|
|
expect(audio).toBeInstanceOf(RRMediaElement);
|
|
const video = rrdom.createElement('video');
|
|
expect(video).toBeInstanceOf(RRMediaElement);
|
|
const iframe = rrdom.createElement('iframe');
|
|
expect(iframe).toBeInstanceOf(RRIFrameElement);
|
|
const image = rrdom.createElement('img');
|
|
expect(image).toBeInstanceOf(RRImageElement);
|
|
const canvas = rrdom.createElement('canvas');
|
|
expect(canvas).toBeInstanceOf(RRCanvasElement);
|
|
const style = rrdom.createElement('style');
|
|
expect(style).toBeInstanceOf(RRStyleElement);
|
|
const elementNS = rrdom.createElementNS(
|
|
'http://www.w3.org/2000/svg',
|
|
'div',
|
|
);
|
|
expect(elementNS).toBeInstanceOf(RRElement);
|
|
expect(elementNS.tagName).toEqual('DIV');
|
|
const text = rrdom.createTextNode('text');
|
|
expect(text).toBeInstanceOf(RRText);
|
|
expect(text.textContent).toEqual('text');
|
|
const comment = rrdom.createComment('comment');
|
|
expect(comment).toBeInstanceOf(RRComment);
|
|
expect(comment.textContent).toEqual('comment');
|
|
const CDATA = rrdom.createCDATASection('data');
|
|
expect(CDATA).toBeInstanceOf(RRCDATASection);
|
|
expect(CDATA.data).toEqual('data');
|
|
});
|
|
|
|
it('can get head element', () => {
|
|
expect(rrdom.head).toBeDefined();
|
|
expect(rrdom.head!.tagName).toBe('HEAD');
|
|
expect(rrdom.head!.parentElement).toBe(rrdom.documentElement);
|
|
});
|
|
|
|
it('can get body element', () => {
|
|
expect(rrdom.body).toBeDefined();
|
|
expect(rrdom.body!.tagName).toBe('BODY');
|
|
expect(rrdom.body!.parentElement).toBe(rrdom.documentElement);
|
|
});
|
|
|
|
it('can get implementation', () => {
|
|
expect(rrdom.implementation).toBeDefined();
|
|
expect(rrdom.implementation).toBe(rrdom);
|
|
});
|
|
|
|
it('can insert elements', () => {
|
|
expect(() =>
|
|
rrdom.insertBefore(rrdom.createDocumentType('', '', ''), null),
|
|
).toThrowErrorMatchingInlineSnapshot(
|
|
`[Error: RRDomException: Failed to execute 'insertBefore' on 'RRNode': Only one RRDoctype on RRDocument allowed.]`,
|
|
);
|
|
expect(() =>
|
|
rrdom.insertBefore(rrdom.createElement('div'), null),
|
|
).toThrowErrorMatchingInlineSnapshot(
|
|
`[Error: RRDomException: Failed to execute 'insertBefore' on 'RRNode': Only one RRElement on RRDocument allowed.]`,
|
|
);
|
|
const node = new RRDocument();
|
|
const doctype = rrdom.createDocumentType('', '', '');
|
|
const documentElement = node.createElement('html');
|
|
node.insertBefore(documentElement, null);
|
|
node.insertBefore(doctype, documentElement);
|
|
expect(node.childNodes.length).toEqual(2);
|
|
expect(node.childNodes[0]).toBe(doctype);
|
|
expect(node.childNodes[1]).toBe(documentElement);
|
|
expect(node.documentElement).toBe(documentElement);
|
|
});
|
|
|
|
it('get firstElementChild', () => {
|
|
expect(rrdom.firstElementChild).toBeDefined();
|
|
expect(rrdom.firstElementChild!.tagName).toEqual('HTML');
|
|
|
|
const div1 = rrdom.getElementById('block1');
|
|
expect(div1).toBeDefined();
|
|
expect(div1!.firstElementChild).toBeDefined();
|
|
expect(div1!.firstElementChild!.id).toEqual('block2');
|
|
const div2 = div1!.firstElementChild;
|
|
expect(div2!.firstElementChild!.id).toEqual('block3');
|
|
});
|
|
|
|
it('getElementsByTagName', () => {
|
|
for (let tagname of [
|
|
'HTML',
|
|
'BODY',
|
|
'HEAD',
|
|
'STYLE',
|
|
'META',
|
|
'TITLE',
|
|
'SCRIPT',
|
|
'LINK',
|
|
'DIV',
|
|
'H1',
|
|
'P',
|
|
'BUTTON',
|
|
'IMG',
|
|
'CANVAS',
|
|
'FORM',
|
|
'INPUT',
|
|
]) {
|
|
const expectedResult = document.getElementsByTagName(tagname).length;
|
|
expect(rrdom.getElementsByTagName(tagname).length).toEqual(
|
|
expectedResult,
|
|
);
|
|
expect(
|
|
rrdom.getElementsByTagName(tagname.toLowerCase()).length,
|
|
).toEqual(expectedResult);
|
|
for (let node of rrdom.getElementsByTagName(tagname)) {
|
|
expect(node.tagName).toEqual(tagname);
|
|
}
|
|
}
|
|
const node = new RRDocument();
|
|
expect(node.getElementsByTagName('h2').length).toEqual(0);
|
|
});
|
|
|
|
it('getElementsByClassName', () => {
|
|
for (let className of [
|
|
'blocks',
|
|
'blocks1',
|
|
':hover',
|
|
'blocks1 blocks',
|
|
'blocks blocks1',
|
|
':hover blocks1',
|
|
':hover blocks1 blocks',
|
|
':hover blocks1 block',
|
|
]) {
|
|
const msg = `queried class name: '${className}'`;
|
|
expect({
|
|
message: msg,
|
|
result: rrdom.getElementsByClassName(className).length,
|
|
}).toEqual({
|
|
message: msg,
|
|
result: document.getElementsByClassName(className).length,
|
|
});
|
|
}
|
|
const node = new RRDocument();
|
|
expect(node.getElementsByClassName('block').length).toEqual(0);
|
|
});
|
|
|
|
it('getElementById', () => {
|
|
for (let elementId of ['block1', 'block2', 'block3']) {
|
|
expect(rrdom.getElementById(elementId)).not.toBeNull();
|
|
expect(rrdom.getElementById(elementId)!.id).toEqual(elementId);
|
|
}
|
|
for (let elementId of ['block', 'blocks', 'blocks1'])
|
|
expect(rrdom.getElementById(elementId)).toBeNull();
|
|
const node = new RRDocument();
|
|
expect(node.getElementById('id')).toBeNull();
|
|
});
|
|
|
|
it('querySelectorAll querying tag name', () => {
|
|
expect(rrdom.querySelectorAll('H1')).toHaveLength(2);
|
|
expect(rrdom.querySelectorAll('H1')[0]).toBeInstanceOf(RRElement);
|
|
expect((rrdom.querySelectorAll('H1')[0] as RRElement).tagName).toEqual(
|
|
'H1',
|
|
);
|
|
expect(rrdom.querySelectorAll('H1')[1]).toBeInstanceOf(RRElement);
|
|
expect((rrdom.querySelectorAll('H1')[1] as RRElement).tagName).toEqual(
|
|
'H1',
|
|
);
|
|
});
|
|
|
|
it('querySelectorAll querying class name', () => {
|
|
for (let className of [
|
|
'.blocks',
|
|
'.blocks1',
|
|
'.\\:hover',
|
|
'.blocks1.blocks',
|
|
'.blocks.blocks1',
|
|
'.\\:hover.blocks1',
|
|
'.\\:hover.blocks1.blocks',
|
|
'.\\:hover.blocks1.block',
|
|
]) {
|
|
const msg = `queried class name: '${className}'`;
|
|
expect({
|
|
message: msg,
|
|
result: rrdom.querySelectorAll(className).length,
|
|
}).toEqual({
|
|
message: msg,
|
|
result: document.querySelectorAll(className).length,
|
|
});
|
|
}
|
|
for (let element of rrdom.querySelectorAll('.\\:hover')) {
|
|
expect(element).toBeInstanceOf(RRElement);
|
|
expect((element as RRElement).classList.classes).toContain(':hover');
|
|
}
|
|
});
|
|
|
|
it('querySelectorAll querying id', () => {
|
|
for (let query of ['#block1', '#block2', '#block3']) {
|
|
expect(rrdom.querySelectorAll(query).length).toEqual(1);
|
|
const targetElement = rrdom.querySelectorAll(query)[0] as RRElement;
|
|
expect(targetElement.id).toEqual(query.substring(1, query.length));
|
|
}
|
|
for (let query of ['#block', '#blocks', '#block1#block2'])
|
|
expect(rrdom.querySelectorAll(query).length).toEqual(0);
|
|
});
|
|
|
|
it('querySelectorAll', () => {
|
|
expect(rrdom.querySelectorAll('link[rel="stylesheet"]').length).toEqual(
|
|
1,
|
|
);
|
|
const targetLink = rrdom.querySelectorAll(
|
|
'link[rel="stylesheet"]',
|
|
)[0] as RRElement;
|
|
expect(targetLink.tagName).toEqual('LINK');
|
|
expect(targetLink.getAttribute('rel')).toEqual('stylesheet');
|
|
|
|
expect(rrdom.querySelectorAll('.blocks#block1').length).toEqual(1);
|
|
expect(rrdom.querySelectorAll('.blocks#block3').length).toEqual(0);
|
|
});
|
|
});
|
|
|
|
describe('RRElement API', () => {
|
|
let rrdom: RRDocument;
|
|
beforeAll(() => {
|
|
// initialize rrdom
|
|
document.write(getHtml('main.html'));
|
|
rrdom = new RRDocument();
|
|
buildFromDom(document, undefined, rrdom);
|
|
});
|
|
|
|
it('can get attribute', () => {
|
|
expect(
|
|
rrdom.getElementsByTagName('DIV')[0].getAttribute('class'),
|
|
).toEqual('blocks blocks1');
|
|
expect(
|
|
rrdom.getElementsByTagName('dIv')[0].getAttribute('cLaSs'),
|
|
).toEqual('blocks blocks1');
|
|
expect(rrdom.getElementsByTagName('DIV')[0].getAttribute('id')).toEqual(
|
|
'block1',
|
|
);
|
|
expect(rrdom.getElementsByTagName('div')[0].getAttribute('iD')).toEqual(
|
|
'block1',
|
|
);
|
|
expect(
|
|
rrdom.getElementsByTagName('p')[0].getAttribute('class'),
|
|
).toBeNull();
|
|
});
|
|
|
|
it('can set attribute', () => {
|
|
const node = rrdom.createElement('div');
|
|
expect(node.getAttribute('class')).toEqual(null);
|
|
node.setAttribute('class', 'className');
|
|
expect(node.getAttribute('cLass')).toEqual('className');
|
|
expect(node.getAttribute('iD')).toEqual(null);
|
|
node.setAttribute('iD', 'id');
|
|
expect(node.getAttribute('id')).toEqual('id');
|
|
});
|
|
|
|
it('can remove attribute', () => {
|
|
const node = rrdom.createElement('div');
|
|
node.setAttribute('Class', 'className');
|
|
expect(node.getAttribute('class')).toEqual('className');
|
|
node.removeAttribute('clAss');
|
|
expect(node.getAttribute('class')).toEqual(null);
|
|
node.removeAttribute('Id');
|
|
expect(node.getAttribute('id')).toEqual(null);
|
|
});
|
|
|
|
it('get nextElementSibling', () => {
|
|
expect(rrdom.documentElement!.firstElementChild).not.toBeNull();
|
|
expect(rrdom.documentElement!.firstElementChild!.tagName).toEqual('HEAD');
|
|
expect(
|
|
rrdom.documentElement!.firstElementChild!.nextElementSibling,
|
|
).not.toBeNull();
|
|
expect(
|
|
rrdom.documentElement!.firstElementChild!.nextElementSibling!.tagName,
|
|
).toEqual('BODY');
|
|
expect(
|
|
rrdom.documentElement!.firstElementChild!.nextElementSibling!
|
|
.nextElementSibling,
|
|
).toBeNull();
|
|
|
|
expect(rrdom.getElementsByTagName('h1').length).toEqual(2);
|
|
const element1 = rrdom.getElementsByTagName('h1')[0];
|
|
const element2 = rrdom.getElementsByTagName('h1')[1];
|
|
expect(element1.tagName).toEqual('H1');
|
|
expect(element2.tagName).toEqual('H1');
|
|
expect(element1.nextElementSibling).toEqual(element2);
|
|
expect(element2.nextElementSibling).not.toBeNull();
|
|
expect(element2.nextElementSibling!.id).toEqual('block1');
|
|
expect(element2.nextElementSibling!.nextElementSibling).toBeNull();
|
|
|
|
const node = rrdom.createElement('div');
|
|
expect(node.nextElementSibling).toBeNull();
|
|
});
|
|
|
|
it('can get CSS style declaration', () => {
|
|
const node = rrdom.createElement('div');
|
|
const style = node.style;
|
|
expect(style).toBeDefined();
|
|
expect(style.setProperty).toBeDefined();
|
|
expect(style.removeProperty).toBeDefined();
|
|
|
|
node.attributes.style =
|
|
'color: blue; background-color: red; width: 78%; height: 50vh !important;';
|
|
expect(node.style.color).toBe('blue');
|
|
expect(node.style.backgroundColor).toBe('red');
|
|
expect(node.style.width).toBe('78%');
|
|
expect(node.style.height).toBe('50vh');
|
|
});
|
|
|
|
it('can set CSS property', () => {
|
|
const node = rrdom.createElement('div');
|
|
const style = node.style;
|
|
style.setProperty('color', 'red');
|
|
expect(node.attributes.style).toEqual('color: red;');
|
|
// camelCase style is unacceptable
|
|
style.setProperty('backgroundColor', 'blue');
|
|
expect(node.attributes.style).toEqual('color: red;');
|
|
style.setProperty('height', '50vh', 'important');
|
|
expect(node.attributes.style).toEqual(
|
|
'color: red; height: 50vh !important;',
|
|
);
|
|
|
|
// kebab-case
|
|
style.setProperty('background-color', 'red');
|
|
expect(node.attributes.style).toEqual(
|
|
'color: red; height: 50vh !important; background-color: red;',
|
|
);
|
|
|
|
// remove the property
|
|
style.setProperty('background-color', null);
|
|
expect(node.attributes.style).toEqual(
|
|
'color: red; height: 50vh !important;',
|
|
);
|
|
});
|
|
|
|
it('can remove CSS property', () => {
|
|
const node = rrdom.createElement('div');
|
|
node.attributes.style =
|
|
'color: blue; background-color: red; width: 78%; height: 50vh;';
|
|
const style = node.style;
|
|
expect(style.removeProperty('color')).toEqual('blue');
|
|
expect(node.attributes.style).toEqual(
|
|
'background-color: red; width: 78%; height: 50vh;',
|
|
);
|
|
expect(style.removeProperty('height')).toEqual('50vh');
|
|
expect(node.attributes.style).toEqual(
|
|
'background-color: red; width: 78%;',
|
|
);
|
|
// kebab-case
|
|
expect(style.removeProperty('background-color')).toEqual('red');
|
|
expect(node.attributes.style).toEqual('width: 78%;');
|
|
style.setProperty('background-color', 'red');
|
|
expect(node.attributes.style).toEqual(
|
|
'width: 78%; background-color: red;',
|
|
);
|
|
expect(style.removeProperty('backgroundColor')).toEqual('');
|
|
expect(node.attributes.style).toEqual(
|
|
'width: 78%; background-color: red;',
|
|
);
|
|
// remove a non-exist property
|
|
expect(style.removeProperty('margin')).toEqual('');
|
|
});
|
|
|
|
it('can parse more inline styles correctly', () => {
|
|
const node = rrdom.createElement('div');
|
|
// general
|
|
node.attributes.style =
|
|
'display: inline-block; margin: 0 auto; border: 5px solid #BADA55; font-size: .75em; position:absolute;width: 33.3%; z-index:1337; font-family: "Goudy Bookletter 1911", Gill Sans Extrabold, sans-serif;';
|
|
|
|
const style = node.style;
|
|
expect(style.display).toEqual('inline-block');
|
|
expect(style.margin).toEqual('0px auto');
|
|
expect(style.border).toEqual('5px solid #bada55');
|
|
expect(style.fontSize).toEqual('.75em');
|
|
expect(style.position).toEqual('absolute');
|
|
expect(style.width).toEqual('33.3%');
|
|
expect(style.zIndex).toEqual('1337');
|
|
expect(style.fontFamily).toEqual(
|
|
'"Goudy Bookletter 1911", Gill Sans Extrabold, sans-serif',
|
|
);
|
|
|
|
// multiple of same property
|
|
node.attributes.style = 'color:rgba(0,0,0,1);color:white';
|
|
expect(style.color).toEqual('white');
|
|
|
|
// url
|
|
node.attributes.style =
|
|
'background-image: url("http://example.com/img.png")';
|
|
expect(node.style.backgroundImage).toEqual(
|
|
'url(http://example.com/img.png)',
|
|
);
|
|
|
|
// comment
|
|
node.attributes.style =
|
|
'top: 0; /* comment1 */ bottom: /* comment2 */42rem;';
|
|
expect(node.style.top).toEqual('0px');
|
|
expect(node.style.bottom).toEqual('42rem');
|
|
// empty comment
|
|
node.attributes.style = 'top: /**/0;';
|
|
expect(node.style.top).toEqual('0px');
|
|
|
|
// incomplete
|
|
node.attributes.style = 'overflow:';
|
|
expect(node.style.overflow).toEqual('');
|
|
});
|
|
|
|
it('querySelectorAll', () => {
|
|
const element = rrdom.getElementById('block2')!;
|
|
expect(element).toBeDefined();
|
|
expect(element.id).toEqual('block2');
|
|
|
|
const result = element.querySelectorAll('div');
|
|
expect(result.length).toBe(1);
|
|
expect((result[0]! as RRElement).tagName).toEqual('DIV');
|
|
expect(element.querySelectorAll('.blocks').length).toEqual(0);
|
|
|
|
const element2 = rrdom.getElementById('block1')!;
|
|
expect(element2).toBeDefined();
|
|
expect(element2.id).toEqual('block1');
|
|
expect(element2.querySelectorAll('div').length).toEqual(2);
|
|
expect(element2.querySelectorAll('.blocks').length).toEqual(1);
|
|
});
|
|
|
|
it('can attach shadow dom', () => {
|
|
const node = rrdom.createElement('div');
|
|
expect(node.shadowRoot).toBeNull();
|
|
node.attachShadow({ mode: 'open' });
|
|
expect(node.shadowRoot).not.toBeNull();
|
|
expect(node.shadowRoot!.RRNodeType).toBe(RRNodeType.Element);
|
|
expect(node.shadowRoot!.tagName).toBe('SHADOWROOT');
|
|
expect(node.parentNode).toBeNull();
|
|
});
|
|
|
|
it('can insert new child before an existing child', () => {
|
|
const node = rrdom.createElement('div');
|
|
const child1 = rrdom.createElement('h1');
|
|
const child2 = rrdom.createElement('h2');
|
|
expect(() =>
|
|
node.insertBefore(node, child1),
|
|
).toThrowErrorMatchingInlineSnapshot(
|
|
`[Error: Failed to execute 'insertBefore' on 'RRNode': The RRNode before which the new node is to be inserted is not a child of this RRNode.]`,
|
|
);
|
|
expect(node.insertBefore(child1, null)).toBe(child1);
|
|
expect(node.childNodes[0]).toBe(child1);
|
|
expect(child1.parentNode).toBe(node);
|
|
expect(child1.parentElement).toBe(node);
|
|
|
|
expect(node.insertBefore(child2, child1)).toBe(child2);
|
|
expect(node.childNodes.length).toBe(2);
|
|
expect(node.childNodes[0]).toBe(child2);
|
|
expect(node.childNodes[1]).toBe(child1);
|
|
expect(child2.parentNode).toBe(node);
|
|
expect(child2.parentElement).toBe(node);
|
|
});
|
|
|
|
it('style element', () => {
|
|
expect(rrdom.getElementsByTagName('style').length).not.toEqual(0);
|
|
expect(rrdom.getElementsByTagName('style')[0].tagName).toEqual('STYLE');
|
|
const styleElement = rrdom.getElementsByTagName(
|
|
'style',
|
|
)[0] as RRStyleElement;
|
|
expect(styleElement.sheet).toBeDefined();
|
|
expect(styleElement.sheet!.cssRules).toBeDefined();
|
|
expect(styleElement.sheet!.cssRules.length).toEqual(5);
|
|
const rules = styleElement.sheet!.cssRules;
|
|
expect(rules[0].cssText).toEqual(`h1 {color: 'black';}`);
|
|
expect(rules[1].cssText).toEqual(`.blocks {padding: 0;}`);
|
|
expect(rules[2].cssText).toEqual(`.blocks1 {margin: 0;}`);
|
|
expect(rules[3].cssText).toEqual(
|
|
`#block1 {width: 100px; height: 200px;}`,
|
|
);
|
|
expect(rules[4].cssText).toEqual(`@import url(main.css);`);
|
|
expect((rules[4] as CSSImportRule).href).toEqual('main.css');
|
|
|
|
expect(styleElement.sheet!.insertRule).toBeDefined();
|
|
const newRule = "p {color: 'black';}";
|
|
styleElement.sheet!.insertRule(newRule, 5);
|
|
expect(rules[5].cssText).toEqual(newRule);
|
|
|
|
expect(styleElement.sheet!.deleteRule).toBeDefined();
|
|
styleElement.sheet!.deleteRule(5);
|
|
expect(rules[5]).toBeUndefined();
|
|
expect(rules[4].cssText).toEqual(`@import url(main.css);`);
|
|
});
|
|
|
|
it('can create an RRIframeElement', () => {
|
|
const iframe = rrdom.createElement('iframe');
|
|
expect(iframe.tagName).toEqual('IFRAME');
|
|
expect(iframe.width).toEqual('');
|
|
expect(iframe.height).toEqual('');
|
|
expect(iframe.contentDocument).toBeDefined();
|
|
expect(iframe.contentDocument!.childNodes.length).toBe(1);
|
|
expect(iframe.contentDocument!.documentElement).toBeDefined();
|
|
expect(iframe.contentDocument!.head).toBeDefined();
|
|
expect(iframe.contentDocument!.body).toBeDefined();
|
|
expect(iframe.contentWindow).toBeDefined();
|
|
expect(iframe.contentWindow!.scrollTop).toEqual(0);
|
|
expect(iframe.contentWindow!.scrollLeft).toEqual(0);
|
|
expect(iframe.contentWindow!.scrollTo).toBeDefined();
|
|
|
|
// empty parameter and did nothing
|
|
iframe.contentWindow!.scrollTo();
|
|
expect(iframe.contentWindow!.scrollTop).toEqual(0);
|
|
expect(iframe.contentWindow!.scrollLeft).toEqual(0);
|
|
|
|
iframe.contentWindow!.scrollTo({ top: 10, left: 20 });
|
|
expect(iframe.contentWindow!.scrollTop).toEqual(10);
|
|
expect(iframe.contentWindow!.scrollLeft).toEqual(20);
|
|
});
|
|
|
|
it('should have a RRCanvasElement', () => {
|
|
const canvas = rrdom.createElement('canvas');
|
|
expect(canvas.getContext()).toBeNull();
|
|
});
|
|
});
|
|
});
|
|
|
|
function getHtml(fileName: string) {
|
|
const filePath = path.resolve(__dirname, `../../rrdom/test/html/${fileName}`);
|
|
return fs.readFileSync(filePath, 'utf8');
|
|
}
|