Add observers for stylesheet mutations (#177)
* hack together stylesheet observer * Add test coverage for insertRule/deleteRule on stylesheets * Add new observers * update patch based on changes to master * Functioning event recording * Remove print statements * Fix ID usage and mark add vs remove * Correct type Co-authored-by: Jon Perl <perl.jonathan@gmail.com>
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
"description": "record and replay the web",
|
"description": "record and replay the web",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "npm run bundle:browser && cross-env TS_NODE_CACHE=false TS_NODE_FILES=true mocha -r ts-node/register test/**/*.test.ts",
|
"test": "npm run bundle:browser && cross-env TS_NODE_CACHE=false TS_NODE_FILES=true mocha -r ts-node/register test/**/*.test.ts",
|
||||||
|
"test:watch": "PUPPETEER_HEADLESS=true npm run test -- --watch --watch-extensions js,ts",
|
||||||
"repl": "npm run bundle:browser && cross-env TS_NODE_CACHE=false TS_NODE_FILES=true ts-node scripts/repl.ts",
|
"repl": "npm run bundle:browser && cross-env TS_NODE_CACHE=false TS_NODE_FILES=true ts-node scripts/repl.ts",
|
||||||
"bundle:browser": "cross-env BROWSER_ONLY=true rollup --config",
|
"bundle:browser": "cross-env BROWSER_ONLY=true rollup --config",
|
||||||
"bundle": "rollup --config",
|
"bundle": "rollup --config",
|
||||||
|
|||||||
@@ -82,9 +82,11 @@ function record(options: recordOptions = {}): listenerHandler | undefined {
|
|||||||
inlineStylesheet,
|
inlineStylesheet,
|
||||||
maskAllInputs,
|
maskAllInputs,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return console.warn('Failed to snapshot the document');
|
return console.warn('Failed to snapshot the document');
|
||||||
}
|
}
|
||||||
|
|
||||||
mirror.map = idNodeMap;
|
mirror.map = idNodeMap;
|
||||||
wrappedEmit(
|
wrappedEmit(
|
||||||
wrapEvent({
|
wrapEvent({
|
||||||
@@ -188,6 +190,16 @@ function record(options: recordOptions = {}): listenerHandler | undefined {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
styleSheetRuleCb: r =>
|
||||||
|
wrappedEmit(
|
||||||
|
wrapEvent({
|
||||||
|
type: EventType.IncrementalSnapshot,
|
||||||
|
data: {
|
||||||
|
source: IncrementalSource.StyleSheetRule,
|
||||||
|
...r,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
blockClass,
|
blockClass,
|
||||||
ignoreClass,
|
ignoreClass,
|
||||||
maskAllInputs,
|
maskAllInputs,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
MouseInteractions,
|
MouseInteractions,
|
||||||
listenerHandler,
|
listenerHandler,
|
||||||
scrollCallback,
|
scrollCallback,
|
||||||
|
styleSheetRuleCallback,
|
||||||
viewportResizeCallback,
|
viewportResizeCallback,
|
||||||
inputValue,
|
inputValue,
|
||||||
inputCallback,
|
inputCallback,
|
||||||
@@ -519,6 +520,31 @@ function initInputObserver(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initStyleSheetObserver(cb: styleSheetRuleCallback): listenerHandler {
|
||||||
|
const insertRule = CSSStyleSheet.prototype.insertRule;
|
||||||
|
CSSStyleSheet.prototype.insertRule = function(rule: string, index?: number) {
|
||||||
|
cb({
|
||||||
|
id: mirror.getId(this.ownerNode as INode),
|
||||||
|
adds: [{ rule, index }],
|
||||||
|
});
|
||||||
|
return insertRule.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteRule = CSSStyleSheet.prototype.deleteRule;
|
||||||
|
CSSStyleSheet.prototype.deleteRule = function(index: number) {
|
||||||
|
cb({
|
||||||
|
id: mirror.getId(this.ownerNode as INode),
|
||||||
|
removes: [{ index }],
|
||||||
|
});
|
||||||
|
return deleteRule.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
CSSStyleSheet.prototype.insertRule = insertRule;
|
||||||
|
CSSStyleSheet.prototype.deleteRule = deleteRule;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function initMediaInteractionObserver(
|
function initMediaInteractionObserver(
|
||||||
mediaInteractionCb: mediaInteractionCallback,
|
mediaInteractionCb: mediaInteractionCallback,
|
||||||
blockClass: blockClass,
|
blockClass: blockClass,
|
||||||
@@ -548,6 +574,7 @@ function mergeHooks(o: observerParam, hooks: hooksParam) {
|
|||||||
viewportResizeCb,
|
viewportResizeCb,
|
||||||
inputCb,
|
inputCb,
|
||||||
mediaInteractionCb,
|
mediaInteractionCb,
|
||||||
|
styleSheetRuleCb,
|
||||||
} = o;
|
} = o;
|
||||||
o.mutationCb = (...p: Arguments<mutationCallBack>) => {
|
o.mutationCb = (...p: Arguments<mutationCallBack>) => {
|
||||||
if (hooks.mutation) {
|
if (hooks.mutation) {
|
||||||
@@ -591,6 +618,12 @@ function mergeHooks(o: observerParam, hooks: hooksParam) {
|
|||||||
}
|
}
|
||||||
mediaInteractionCb(...p);
|
mediaInteractionCb(...p);
|
||||||
};
|
};
|
||||||
|
o.styleSheetRuleCb = (...p: Arguments<styleSheetRuleCallback>) => {
|
||||||
|
if (hooks.styleSheetRule) {
|
||||||
|
hooks.styleSheetRule(...p);
|
||||||
|
}
|
||||||
|
styleSheetRuleCb(...p);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function initObservers(
|
export default function initObservers(
|
||||||
@@ -621,6 +654,8 @@ export default function initObservers(
|
|||||||
o.mediaInteractionCb,
|
o.mediaInteractionCb,
|
||||||
o.blockClass,
|
o.blockClass,
|
||||||
);
|
);
|
||||||
|
const styleSheetObserver = initStyleSheetObserver(o.styleSheetRuleCb);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
mutationObserver.disconnect();
|
mutationObserver.disconnect();
|
||||||
mousemoveHandler();
|
mousemoveHandler();
|
||||||
@@ -629,5 +664,6 @@ export default function initObservers(
|
|||||||
viewportResizeHandler();
|
viewportResizeHandler();
|
||||||
inputHandler();
|
inputHandler();
|
||||||
mediaInteractionHandler();
|
mediaInteractionHandler();
|
||||||
|
styleSheetObserver();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
29
src/types.ts
29
src/types.ts
@@ -52,6 +52,8 @@ export type customEvent<T = unknown> = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type styleSheetEvent = {};
|
||||||
|
|
||||||
export enum IncrementalSource {
|
export enum IncrementalSource {
|
||||||
Mutation,
|
Mutation,
|
||||||
MouseMove,
|
MouseMove,
|
||||||
@@ -61,6 +63,7 @@ export enum IncrementalSource {
|
|||||||
Input,
|
Input,
|
||||||
TouchMove,
|
TouchMove,
|
||||||
MediaInteraction,
|
MediaInteraction,
|
||||||
|
StyleSheetRule,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type mutationData = {
|
export type mutationData = {
|
||||||
@@ -93,6 +96,10 @@ export type mediaInteractionData = {
|
|||||||
source: IncrementalSource.MediaInteraction;
|
source: IncrementalSource.MediaInteraction;
|
||||||
} & mediaInteractionParam;
|
} & mediaInteractionParam;
|
||||||
|
|
||||||
|
export type styleSheetRuleData = {
|
||||||
|
source: IncrementalSource.StyleSheetRule;
|
||||||
|
} & styleSheetRuleParam;
|
||||||
|
|
||||||
export type incrementalData =
|
export type incrementalData =
|
||||||
| mutationData
|
| mutationData
|
||||||
| mousemoveData
|
| mousemoveData
|
||||||
@@ -100,7 +107,8 @@ export type incrementalData =
|
|||||||
| scrollData
|
| scrollData
|
||||||
| viewportResizeData
|
| viewportResizeData
|
||||||
| inputData
|
| inputData
|
||||||
| mediaInteractionData;
|
| mediaInteractionData
|
||||||
|
| styleSheetRuleData;
|
||||||
|
|
||||||
export type event =
|
export type event =
|
||||||
| domContentLoadedEvent
|
| domContentLoadedEvent
|
||||||
@@ -141,6 +149,7 @@ export type observerParam = {
|
|||||||
ignoreClass: string;
|
ignoreClass: string;
|
||||||
maskAllInputs: boolean;
|
maskAllInputs: boolean;
|
||||||
inlineStylesheet: boolean;
|
inlineStylesheet: boolean;
|
||||||
|
styleSheetRuleCb: styleSheetRuleCallback;
|
||||||
mousemoveWait: number;
|
mousemoveWait: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -152,6 +161,7 @@ export type hooksParam = {
|
|||||||
viewportResize?: viewportResizeCallback;
|
viewportResize?: viewportResizeCallback;
|
||||||
input?: inputCallback;
|
input?: inputCallback;
|
||||||
mediaInteaction?: mediaInteractionCallback;
|
mediaInteaction?: mediaInteractionCallback;
|
||||||
|
styleSheetRule?: styleSheetRuleCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type textCursor = {
|
export type textCursor = {
|
||||||
@@ -239,6 +249,23 @@ export type scrollPosition = {
|
|||||||
|
|
||||||
export type scrollCallback = (p: scrollPosition) => void;
|
export type scrollCallback = (p: scrollPosition) => void;
|
||||||
|
|
||||||
|
export type styleSheetAddRule = {
|
||||||
|
rule: string;
|
||||||
|
index?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type styleSheetDeleteRule = {
|
||||||
|
index: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type styleSheetRuleParam = {
|
||||||
|
id: number;
|
||||||
|
removes?: styleSheetDeleteRule[];
|
||||||
|
adds?: styleSheetAddRule[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type styleSheetRuleCallback = (s: styleSheetRuleParam) => void;
|
||||||
|
|
||||||
export type viewportResizeDimention = {
|
export type viewportResizeDimention = {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ exports[`block 1`] = `
|
|||||||
\\"attributes\\": {
|
\\"attributes\\": {
|
||||||
\\"class\\": \\"rr-block\\",
|
\\"class\\": \\"rr-block\\",
|
||||||
\\"rr_width\\": \\"1904px\\",
|
\\"rr_width\\": \\"1904px\\",
|
||||||
\\"rr_height\\": \\"21px\\"
|
\\"rr_height\\": \\"19px\\"
|
||||||
},
|
},
|
||||||
\\"childNodes\\": [],
|
\\"childNodes\\": [],
|
||||||
\\"id\\": 18
|
\\"id\\": 18
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ exports[`async-checkout 1`] = `
|
|||||||
\\"type\\": 4,
|
\\"type\\": 4,
|
||||||
\\"data\\": {
|
\\"data\\": {
|
||||||
\\"href\\": \\"about:blank\\",
|
\\"href\\": \\"about:blank\\",
|
||||||
\\"width\\": 800,
|
\\"width\\": 1920,
|
||||||
\\"height\\": 600
|
\\"height\\": 1080
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -132,8 +132,8 @@ exports[`async-checkout 1`] = `
|
|||||||
\\"type\\": 4,
|
\\"type\\": 4,
|
||||||
\\"data\\": {
|
\\"data\\": {
|
||||||
\\"href\\": \\"about:blank\\",
|
\\"href\\": \\"about:blank\\",
|
||||||
\\"width\\": 800,
|
\\"width\\": 1920,
|
||||||
\\"height\\": 600
|
\\"height\\": 1080
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -204,44 +204,6 @@ exports[`async-checkout 1`] = `
|
|||||||
\\"top\\": 0
|
\\"top\\": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
\\"type\\": 3,
|
|
||||||
\\"data\\": {
|
|
||||||
\\"source\\": 0,
|
|
||||||
\\"texts\\": [],
|
|
||||||
\\"attributes\\": [],
|
|
||||||
\\"removes\\": [
|
|
||||||
{
|
|
||||||
\\"parentId\\": 8,
|
|
||||||
\\"id\\": 9
|
|
||||||
}
|
|
||||||
],
|
|
||||||
\\"adds\\": [
|
|
||||||
{
|
|
||||||
\\"parentId\\": 4,
|
|
||||||
\\"previousId\\": 8,
|
|
||||||
\\"nextId\\": null,
|
|
||||||
\\"node\\": {
|
|
||||||
\\"type\\": 2,
|
|
||||||
\\"tagName\\": \\"span\\",
|
|
||||||
\\"attributes\\": {},
|
|
||||||
\\"childNodes\\": [],
|
|
||||||
\\"id\\": 9
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
\\"parentId\\": 9,
|
|
||||||
\\"previousId\\": null,
|
|
||||||
\\"nextId\\": null,
|
|
||||||
\\"node\\": {
|
|
||||||
\\"type\\": 3,
|
|
||||||
\\"textContent\\": \\"test\\",
|
|
||||||
\\"id\\": 10
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
`;
|
`;
|
||||||
@@ -252,8 +214,8 @@ exports[`custom-event 1`] = `
|
|||||||
\\"type\\": 4,
|
\\"type\\": 4,
|
||||||
\\"data\\": {
|
\\"data\\": {
|
||||||
\\"href\\": \\"about:blank\\",
|
\\"href\\": \\"about:blank\\",
|
||||||
\\"width\\": 800,
|
\\"width\\": 1920,
|
||||||
\\"height\\": 600
|
\\"height\\": 1080
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -331,3 +293,146 @@ exports[`custom-event 1`] = `
|
|||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`stylesheet-rules 1`] = `
|
||||||
|
"[
|
||||||
|
{
|
||||||
|
\\"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\\": \\"input\\",
|
||||||
|
\\"attributes\\": {
|
||||||
|
\\"type\\": \\"text\\"
|
||||||
|
},
|
||||||
|
\\"childNodes\\": [],
|
||||||
|
\\"id\\": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"textContent\\": \\"\\\\n \\\\n \\\\n \\",
|
||||||
|
\\"id\\": 7
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\\"id\\": 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\\"id\\": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
\\"id\\": 1
|
||||||
|
},
|
||||||
|
\\"initialOffset\\": {
|
||||||
|
\\"left\\": 0,
|
||||||
|
\\"top\\": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"data\\": {
|
||||||
|
\\"source\\": 8,
|
||||||
|
\\"id\\": -1,
|
||||||
|
\\"adds\\": [
|
||||||
|
{
|
||||||
|
\\"rule\\": \\"body { background: #000; }\\"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"data\\": {
|
||||||
|
\\"source\\": 0,
|
||||||
|
\\"texts\\": [],
|
||||||
|
\\"attributes\\": [],
|
||||||
|
\\"removes\\": [],
|
||||||
|
\\"adds\\": [
|
||||||
|
{
|
||||||
|
\\"parentId\\": 3,
|
||||||
|
\\"previousId\\": null,
|
||||||
|
\\"nextId\\": null,
|
||||||
|
\\"node\\": {
|
||||||
|
\\"type\\": 2,
|
||||||
|
\\"tagName\\": \\"style\\",
|
||||||
|
\\"attributes\\": {
|
||||||
|
\\"_cssText\\": \\"body { background: rgb(0, 0, 0); }\\"
|
||||||
|
},
|
||||||
|
\\"childNodes\\": [],
|
||||||
|
\\"id\\": 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"data\\": {
|
||||||
|
\\"source\\": 8,
|
||||||
|
\\"id\\": 8,
|
||||||
|
\\"adds\\": [
|
||||||
|
{
|
||||||
|
\\"rule\\": \\"body { color: #fff; }\\"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"data\\": {
|
||||||
|
\\"source\\": 8,
|
||||||
|
\\"id\\": 8,
|
||||||
|
\\"removes\\": [
|
||||||
|
{
|
||||||
|
\\"index\\": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
\\"type\\": 3,
|
||||||
|
\\"data\\": {
|
||||||
|
\\"source\\": 8,
|
||||||
|
\\"id\\": 8,
|
||||||
|
\\"adds\\": [
|
||||||
|
{
|
||||||
|
\\"rule\\": \\"body { color: #ccc; }\\"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
`;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as puppeteer from 'puppeteer';
|
import * as puppeteer from 'puppeteer';
|
||||||
import { assertSnapshot } from './utils';
|
import { assertSnapshot, launchPuppeteer } from './utils';
|
||||||
import { Suite } from 'mocha';
|
import { Suite } from 'mocha';
|
||||||
import { recordOptions } from '../src/types';
|
import { recordOptions } from '../src/types';
|
||||||
|
|
||||||
@@ -35,14 +35,7 @@ describe('record integration tests', function(this: ISuite) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
this.browser = await puppeteer.launch({
|
this.browser = await launchPuppeteer();
|
||||||
defaultViewport: {
|
|
||||||
width: 1920,
|
|
||||||
height: 1080,
|
|
||||||
},
|
|
||||||
headless: false,
|
|
||||||
args: ['--no-sandbox'],
|
|
||||||
});
|
|
||||||
|
|
||||||
const bundlePath = path.resolve(__dirname, '../dist/rrweb.min.js');
|
const bundlePath = path.resolve(__dirname, '../dist/rrweb.min.js');
|
||||||
this.code = fs.readFileSync(bundlePath, 'utf8');
|
this.code = fs.readFileSync(bundlePath, 'utf8');
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
eventWithTime,
|
eventWithTime,
|
||||||
EventType,
|
EventType,
|
||||||
} from '../src/types';
|
} from '../src/types';
|
||||||
import { assertSnapshot } from './utils';
|
import { assertSnapshot, launchPuppeteer } from './utils';
|
||||||
import { Suite } from 'mocha';
|
import { Suite } from 'mocha';
|
||||||
|
|
||||||
interface ISuite extends Suite {
|
interface ISuite extends Suite {
|
||||||
@@ -30,10 +30,7 @@ interface IWindow extends Window {
|
|||||||
|
|
||||||
describe('record', function(this: ISuite) {
|
describe('record', function(this: ISuite) {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
this.browser = await puppeteer.launch({
|
this.browser = await launchPuppeteer();
|
||||||
headless: false,
|
|
||||||
args: ['--no-sandbox'],
|
|
||||||
});
|
|
||||||
|
|
||||||
const bundlePath = path.resolve(__dirname, '../dist/rrweb.min.js');
|
const bundlePath = path.resolve(__dirname, '../dist/rrweb.min.js');
|
||||||
this.code = fs.readFileSync(bundlePath, 'utf8');
|
this.code = fs.readFileSync(bundlePath, 'utf8');
|
||||||
@@ -196,4 +193,32 @@ describe('record', function(this: ISuite) {
|
|||||||
await this.page.waitFor(50);
|
await this.page.waitFor(50);
|
||||||
assertSnapshot(this.events, __filename, 'custom-event');
|
assertSnapshot(this.events, __filename, 'custom-event');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('captures stylesheet rules', async () => {
|
||||||
|
await this.page.evaluate(() => {
|
||||||
|
const { record } = ((window as unknown) as IWindow).rrweb;
|
||||||
|
|
||||||
|
record({
|
||||||
|
emit: ((window as unknown) as IWindow).emit,
|
||||||
|
});
|
||||||
|
|
||||||
|
const styleElement = document.createElement('style');
|
||||||
|
document.head.appendChild(styleElement);
|
||||||
|
|
||||||
|
const styleSheet = <CSSStyleSheet>styleElement.sheet;
|
||||||
|
const ruleIdx0 = styleSheet.insertRule('body { background: #000; }');
|
||||||
|
setTimeout(() => {
|
||||||
|
styleSheet.insertRule('body { color: #fff; }');
|
||||||
|
}, 0);
|
||||||
|
setTimeout(() => {
|
||||||
|
styleSheet.deleteRule(ruleIdx0);
|
||||||
|
}, 5);
|
||||||
|
setTimeout(() => {
|
||||||
|
styleSheet.insertRule('body { color: #ccc; }');
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
await this.page.waitFor(10);
|
||||||
|
expect(this.events.length).to.equal(7);
|
||||||
|
assertSnapshot(this.events, __filename, 'stylesheet-rules');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
MouseInteractions,
|
MouseInteractions,
|
||||||
} from '../src/types';
|
} from '../src/types';
|
||||||
import { Replayer } from '../src';
|
import { Replayer } from '../src';
|
||||||
|
import { launchPuppeteer } from './utils';
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
@@ -122,10 +123,7 @@ interface ISuite extends Suite {
|
|||||||
|
|
||||||
describe('replayer', function(this: ISuite) {
|
describe('replayer', function(this: ISuite) {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
this.browser = await puppeteer.launch({
|
this.browser = await launchPuppeteer();
|
||||||
headless: false,
|
|
||||||
args: ['--no-sandbox'],
|
|
||||||
});
|
|
||||||
|
|
||||||
const bundlePath = path.resolve(__dirname, '../dist/rrweb.min.js');
|
const bundlePath = path.resolve(__dirname, '../dist/rrweb.min.js');
|
||||||
this.code = fs.readFileSync(bundlePath, 'utf8');
|
this.code = fs.readFileSync(bundlePath, 'utf8');
|
||||||
|
|||||||
@@ -2,6 +2,18 @@ import { SnapshotState, toMatchSnapshot } from 'jest-snapshot';
|
|||||||
import { NodeType } from 'rrweb-snapshot';
|
import { NodeType } from 'rrweb-snapshot';
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { EventType, IncrementalSource, eventWithTime } from '../src/types';
|
import { EventType, IncrementalSource, eventWithTime } from '../src/types';
|
||||||
|
import * as puppeteer from 'puppeteer';
|
||||||
|
|
||||||
|
export async function launchPuppeteer() {
|
||||||
|
return await puppeteer.launch({
|
||||||
|
headless: process.env.PUPPETEER_HEADLESS ? true : false,
|
||||||
|
defaultViewport: {
|
||||||
|
width: 1920,
|
||||||
|
height: 1080,
|
||||||
|
},
|
||||||
|
args: ['--no-sandbox'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function matchSnapshot(actual: string, testFile: string, testTitle: string) {
|
function matchSnapshot(actual: string, testFile: string, testTitle: string) {
|
||||||
const snapshotState = new SnapshotState(testFile, {
|
const snapshotState = new SnapshotState(testFile, {
|
||||||
|
|||||||
Reference in New Issue
Block a user