diff --git a/guide.md b/guide.md
index e2dbf0d23f..7200df1f9e 100644
--- a/guide.md
+++ b/guide.md
@@ -144,8 +144,11 @@ The parameter of `rrweb.record` accepts the following options.
| blockSelector | null | Use a string to configure which selector should be blocked, refer to the [privacy](#privacy) chapter |
| ignoreClass | 'rr-ignore' | Use a string or RegExp to configure which elements should be ignored, refer to the [privacy](#privacy) chapter |
| ignoreCSSAttributes | null | array of CSS attributes that should be ignored |
+| maskAllText | false | mask all text content as \* |
| maskTextClass | 'rr-mask' | Use a string or RegExp to configure which elements should be masked, refer to the [privacy](#privacy) chapter |
+| unmaskTextClass | 'rr-unmask' | Use a string or RegExp to configure which elements should be unmasked, refer to the [privacy](#privacy) chapter |
| maskTextSelector | null | Use a string to configure which selector should be masked, refer to the [privacy](#privacy) chapter |
+| unmaskTextSelector | null | Use a string to configure which selector should be unmasked, refer to the [privacy](#privacy) chapter |
| maskAllInputs | false | mask all input content as \* |
| maskInputOptions | { password: true } | mask some kinds of input \*
refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L77-L95) |
| maskInputFn | - | customize mask input content recording logic |
@@ -172,6 +175,7 @@ You may find some contents on the webpage which are not willing to be recorded,
- An element with the class name `.rr-block` will not be recorded. Instead, it will replay as a placeholder with the same dimension.
- An element with the class name `.rr-ignore` will not record its input events.
- All text of elements with the class name `.rr-mask` and their children will be masked.
+- All text of elements with the class name `.rr-unmask` and their children will be unmasked, unless any child is marked with `.rr-mask`.
- `input[type="password"]` will be masked by default.
- Mask options to mask the content in input elements.
diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts
index f587bd83ed..95ccd7af76 100644
--- a/packages/rrweb-snapshot/src/snapshot.ts
+++ b/packages/rrweb-snapshot/src/snapshot.ts
@@ -292,26 +292,89 @@ export function classMatchesRegex(
regex: RegExp,
checkAncestors: boolean,
): boolean {
- if (!node) return false;
+ return distanceToClassRegexMatch(node, regex, checkAncestors) >= 0;
+}
+
+function distanceToClassRegexMatch(
+ node: Node | null,
+ regex: RegExp,
+ checkAncestors: boolean,
+ distance = 0,
+): number {
+ if (!node) return -1;
if (node.nodeType !== node.ELEMENT_NODE) {
- if (!checkAncestors) return false;
- return classMatchesRegex(node.parentNode, regex, checkAncestors);
+ if (!checkAncestors) return -1;
+ return distanceToClassRegexMatch(node.parentNode, regex, checkAncestors);
}
for (let eIndex = (node as HTMLElement).classList.length; eIndex--; ) {
const className = (node as HTMLElement).classList[eIndex];
if (regex.test(className)) {
- return true;
+ return distance;
}
}
- if (!checkAncestors) return false;
- return classMatchesRegex(node.parentNode, regex, checkAncestors);
+ if (!checkAncestors) return -1;
+ return distanceToClassRegexMatch(
+ node.parentNode,
+ regex,
+ checkAncestors,
+ distance + 1,
+ );
+}
+
+function distanceToSelectorMatch(el: HTMLElement, selector: string): number {
+ if (!el) return -1;
+ if (el.matches(selector)) return 0;
+ const closestParent = el.closest(selector);
+ if (closestParent) {
+ let current = el;
+ let distance = 0;
+ while (current && current !== closestParent) {
+ current = current.parentNode as HTMLElement;
+ if (!current) {
+ return -1;
+ }
+ distance++;
+ }
+ return distance;
+ }
+ return -1;
+}
+
+function distanceToMatch(
+ el: HTMLElement,
+ className: string | RegExp,
+ selector: string | null,
+): number {
+ let classDistance = -1;
+ let selectorDistance = -1;
+
+ if (typeof className === 'string') {
+ classDistance = distanceToSelectorMatch(el, `.${className}`);
+ } else {
+ classDistance = distanceToClassRegexMatch(el, className, true);
+ }
+
+ if (selector) {
+ selectorDistance = distanceToSelectorMatch(el, selector);
+ }
+
+ return selectorDistance >= 0
+ ? classDistance >= 0
+ ? Math.min(classDistance, selectorDistance)
+ : selectorDistance
+ : classDistance >= 0
+ ? classDistance
+ : -1;
}
export function needMaskingText(
node: Node,
maskTextClass: string | RegExp,
maskTextSelector: string | null,
+ unmaskTextClass: string | RegExp,
+ unmaskTextSelector: string | null,
+ maskAllText: boolean,
): boolean {
const el: HTMLElement | null =
node.nodeType === node.ELEMENT_NODE
@@ -319,18 +382,20 @@ export function needMaskingText(
: node.parentElement;
if (el === null) return false;
- if (typeof maskTextClass === 'string') {
- if (el.classList.contains(maskTextClass)) return true;
- if (el.closest(`.${maskTextClass}`)) return true;
- } else {
- if (classMatchesRegex(el, maskTextClass, true)) return true;
- }
+ const maskDistance = distanceToMatch(el, maskTextClass, maskTextSelector);
+ const unmaskDistance = distanceToMatch(
+ el,
+ unmaskTextClass,
+ unmaskTextSelector,
+ );
- if (maskTextSelector) {
- if (el.matches(maskTextSelector)) return true;
- if (el.closest(maskTextSelector)) return true;
- }
- return false;
+ return maskDistance >= 0
+ ? unmaskDistance >= 0
+ ? maskDistance <= unmaskDistance
+ : true
+ : unmaskDistance >= 0
+ ? false
+ : !!maskAllText;
}
// https://stackoverflow.com/a/36155560
@@ -424,8 +489,11 @@ function serializeNode(
mirror: Mirror;
blockClass: string | RegExp;
blockSelector: string | null;
+ maskAllText: boolean;
maskTextClass: string | RegExp;
+ unmaskTextClass: string | RegExp;
maskTextSelector: string | null;
+ unmaskTextSelector: string | null;
inlineStylesheet: boolean;
maskInputOptions: MaskInputOptions;
maskTextFn: MaskTextFn | undefined;
@@ -445,8 +513,11 @@ function serializeNode(
mirror,
blockClass,
blockSelector,
+ maskAllText,
maskTextClass,
+ unmaskTextClass,
maskTextSelector,
+ unmaskTextSelector,
inlineStylesheet,
maskInputOptions = {},
maskTextFn,
@@ -498,8 +569,11 @@ function serializeNode(
});
case n.TEXT_NODE:
return serializeTextNode(n as Text, {
+ maskAllText,
maskTextClass,
+ unmaskTextClass,
maskTextSelector,
+ unmaskTextSelector,
maskTextFn,
rootId,
});
@@ -529,13 +603,24 @@ function getRootId(doc: Document, mirror: Mirror): number | undefined {
function serializeTextNode(
n: Text,
options: {
+ maskAllText: boolean;
maskTextClass: string | RegExp;
+ unmaskTextClass: string | RegExp;
maskTextSelector: string | null;
+ unmaskTextSelector: string | null;
maskTextFn: MaskTextFn | undefined;
rootId: number | undefined;
},
): serializedNode {
- const { maskTextClass, maskTextSelector, maskTextFn, rootId } = options;
+ const {
+ maskAllText,
+ maskTextClass,
+ unmaskTextClass,
+ maskTextSelector,
+ unmaskTextSelector,
+ maskTextFn,
+ rootId,
+ } = options;
// The parent node may not be a html element which has a tagName attribute.
// So just let it be undefined which is ok in this use case.
const parentTagName = n.parentNode && (n.parentNode as HTMLElement).tagName;
@@ -570,7 +655,14 @@ function serializeTextNode(
!isStyle &&
!isScript &&
textContent &&
- needMaskingText(n, maskTextClass, maskTextSelector)
+ needMaskingText(
+ n,
+ maskTextClass,
+ maskTextSelector,
+ unmaskTextClass,
+ unmaskTextSelector,
+ maskAllText,
+ )
) {
textContent = maskTextFn
? maskTextFn(textContent)
@@ -923,11 +1015,14 @@ export function serializeNodeWithId(
blockClass: string | RegExp;
blockSelector: string | null;
maskTextClass: string | RegExp;
+ unmaskTextClass: string | RegExp;
maskTextSelector: string | null;
+ unmaskTextSelector: string | null;
skipChild: boolean;
inlineStylesheet: boolean;
newlyAddedElement?: boolean;
maskInputOptions?: MaskInputOptions;
+ maskAllText: boolean;
maskTextFn: MaskTextFn | undefined;
maskInputFn: MaskInputFn | undefined;
slimDOMOptions: SlimDOMOptions;
@@ -954,8 +1049,11 @@ export function serializeNodeWithId(
mirror,
blockClass,
blockSelector,
+ maskAllText,
maskTextClass,
+ unmaskTextClass,
maskTextSelector,
+ unmaskTextSelector,
skipChild = false,
inlineStylesheet = true,
maskInputOptions = {},
@@ -979,8 +1077,11 @@ export function serializeNodeWithId(
mirror,
blockClass,
blockSelector,
+ maskAllText,
maskTextClass,
+ unmaskTextClass,
maskTextSelector,
+ unmaskTextSelector,
inlineStylesheet,
maskInputOptions,
maskTextFn,
@@ -1051,8 +1152,11 @@ export function serializeNodeWithId(
mirror,
blockClass,
blockSelector,
+ maskAllText,
maskTextClass,
+ unmaskTextClass,
maskTextSelector,
+ unmaskTextSelector,
skipChild,
inlineStylesheet,
maskInputOptions,
@@ -1111,8 +1215,11 @@ export function serializeNodeWithId(
mirror,
blockClass,
blockSelector,
+ maskAllText,
maskTextClass,
+ unmaskTextClass,
maskTextSelector,
+ unmaskTextSelector,
skipChild: false,
inlineStylesheet,
maskInputOptions,
@@ -1158,8 +1265,11 @@ export function serializeNodeWithId(
mirror,
blockClass,
blockSelector,
+ maskAllText,
maskTextClass,
+ unmaskTextClass,
maskTextSelector,
+ unmaskTextSelector,
skipChild: false,
inlineStylesheet,
maskInputOptions,
@@ -1199,8 +1309,11 @@ function snapshot(
mirror?: Mirror;
blockClass?: string | RegExp;
blockSelector?: string | null;
+ maskAllText?: boolean;
maskTextClass?: string | RegExp;
+ unmaskTextClass?: string | RegExp;
maskTextSelector?: string | null;
+ unmaskTextSelector?: string | null;
inlineStylesheet?: boolean;
maskAllInputs?: boolean | MaskInputOptions;
maskTextFn?: MaskTextFn;
@@ -1228,8 +1341,11 @@ function snapshot(
mirror = new Mirror(),
blockClass = 'rr-block',
blockSelector = null,
+ maskAllText = false,
maskTextClass = 'rr-mask',
+ unmaskTextClass = 'rr-unmask',
maskTextSelector = null,
+ unmaskTextSelector = null,
inlineStylesheet = true,
inlineImages = false,
recordCanvas = false,
@@ -1294,8 +1410,11 @@ function snapshot(
mirror,
blockClass,
blockSelector,
+ maskAllText,
maskTextClass,
+ unmaskTextClass,
maskTextSelector,
+ unmaskTextSelector,
skipChild: false,
inlineStylesheet,
maskInputOptions,
diff --git a/packages/rrweb-snapshot/test/snapshot.test.ts b/packages/rrweb-snapshot/test/snapshot.test.ts
index 75d635e0c0..9926d1c8bb 100644
--- a/packages/rrweb-snapshot/test/snapshot.test.ts
+++ b/packages/rrweb-snapshot/test/snapshot.test.ts
@@ -6,6 +6,7 @@ import {
absoluteToStylesheet,
serializeNodeWithId,
_isBlockedElement,
+ needMaskingText,
} from '../src/snapshot';
import { serializedNodeWithId } from '../src/types';
import { Mirror } from '../src/utils';
@@ -143,8 +144,11 @@ describe('style elements', () => {
mirror: new Mirror(),
blockClass: 'blockblock',
blockSelector: null,
+ maskAllText: false,
maskTextClass: 'maskmask',
+ unmaskTextClass: 'unmaskmask',
maskTextSelector: null,
+ unmaskTextSelector: null,
skipChild: false,
inlineStylesheet: true,
maskTextFn: undefined,
@@ -188,8 +192,11 @@ describe('scrollTop/scrollLeft', () => {
mirror: new Mirror(),
blockClass: 'blockblock',
blockSelector: null,
+ maskAllText: false,
maskTextClass: 'maskmask',
+ unmaskTextClass: 'unmaskmask',
maskTextSelector: null,
+ unmaskTextSelector: null,
skipChild: false,
inlineStylesheet: true,
maskTextFn: undefined,
@@ -218,3 +225,222 @@ describe('scrollTop/scrollLeft', () => {
});
});
});
+
+describe('needMaskingText', () => {
+ const render = (html: string): HTMLDivElement => {
+ document.write(html);
+ return document.querySelector('div')!;
+ };
+
+ it('should not mask by default', () => {
+ const el = render(`
Lorem ipsum
`);
+ expect(
+ needMaskingText(el, 'maskmask', null, 'unmaskmask', null, false),
+ ).toEqual(false);
+ });
+
+ it('should mask if the masking class is matched', () => {
+ const el = render(`Lorem ipsum
`);
+ expect(
+ needMaskingText(el, 'maskmask', null, 'unmaskmask', null, false),
+ ).toEqual(true);
+ expect(
+ needMaskingText(el, /^maskmask$/, null, /^unmaskmask$/, null, false),
+ ).toEqual(true);
+ });
+
+ it('should mask if the masking class is matched on an ancestor', () => {
+ const el = render(
+ `Lorem ipsum
`,
+ );
+ expect(
+ needMaskingText(
+ el.children[0],
+ 'maskmask',
+ null,
+ 'unmaskmask',
+ null,
+ false,
+ ),
+ ).toEqual(true);
+ expect(
+ needMaskingText(
+ el.children[0],
+ /^maskmask$/,
+ null,
+ /^unmaskmask$/,
+ null,
+ false,
+ ),
+ ).toEqual(true);
+ });
+
+ it('should mask if the masking selector is matched', () => {
+ const el = render(`Lorem ipsum
`);
+ expect(
+ needMaskingText(el, 'maskmask', '.foo', 'unmaskmask', null, false),
+ ).toEqual(true);
+ });
+
+ it('should mask if the masking selector is matched on an ancestor', () => {
+ const el = render(`Lorem ipsum
`);
+ expect(
+ needMaskingText(
+ el.children[0],
+ 'maskmask',
+ '.foo',
+ 'unmaskmask',
+ null,
+ false,
+ ),
+ ).toEqual(true);
+ });
+
+ it('should mask by default', () => {
+ const el = render(`Lorem ipsum
`);
+ expect(
+ needMaskingText(el, 'maskmask', null, 'unmaskmask', null, true),
+ ).toEqual(true);
+ });
+
+ it('should not mask if the un-masking class is matched', () => {
+ const el = render(`Lorem ipsum
`);
+ expect(
+ needMaskingText(el, 'maskmask', null, 'unmaskmask', null, true),
+ ).toEqual(false);
+ expect(
+ needMaskingText(el, /^maskmask$/, null, /^unmaskmask$/, null, true),
+ ).toEqual(false);
+ });
+
+ it('should not mask if the un-masking class is matched on an ancestor', () => {
+ const el = render(
+ `Lorem ipsum
`,
+ );
+ expect(
+ needMaskingText(
+ el.children[0],
+ 'maskmask',
+ null,
+ 'unmaskmask',
+ null,
+ true,
+ ),
+ ).toEqual(false);
+ expect(
+ needMaskingText(
+ el.children[0],
+ /^maskmask$/,
+ null,
+ /^unmaskmask$/,
+ null,
+ true,
+ ),
+ ).toEqual(false);
+ });
+
+ it('should mask if the masking class is more specific than the unmasking class', () => {
+ const el = render(
+ `Lorem ipsum`,
+ );
+ expect(
+ needMaskingText(
+ el.children[0].children[0],
+ 'maskmask',
+ null,
+ 'unmaskmask',
+ null,
+ true,
+ ),
+ ).toEqual(true);
+ expect(
+ needMaskingText(
+ el.children[0].children[0],
+ /^maskmask$/,
+ null,
+ /^unmaskmask$/,
+ null,
+ true,
+ ),
+ ).toEqual(true);
+ });
+
+ it('should not mask if the unmasking class is more specific than the masking class', () => {
+ const el = render(
+ `
Lorem ipsum`,
+ );
+ expect(
+ needMaskingText(
+ el.children[0].children[0],
+ 'maskmask',
+ null,
+ 'unmaskmask',
+ null,
+ true,
+ ),
+ ).toEqual(false);
+ expect(
+ needMaskingText(
+ el.children[0].children[0],
+ /^maskmask$/,
+ null,
+ /^unmaskmask$/,
+ null,
+ true,
+ ),
+ ).toEqual(false);
+ });
+
+ it('should not mask if the unmasking selector is matched', () => {
+ const el = render(`
Lorem ipsum
`);
+ expect(
+ needMaskingText(el, 'maskmask', null, 'unmaskmask', '.foo', true),
+ ).toEqual(false);
+ });
+
+ it('should not mask if the unmasking selector is matched on an ancestor', () => {
+ const el = render(`
Lorem ipsum
`);
+ expect(
+ needMaskingText(
+ el.children[0],
+ 'maskmask',
+ null,
+ 'unmaskmask',
+ '.foo',
+ true,
+ ),
+ ).toEqual(false);
+ });
+
+ it('should mask if the masking selector is more specific than the unmasking selector', () => {
+ const el = render(
+ `
Lorem ipsum`,
+ );
+ expect(
+ needMaskingText(
+ el.children[0].children[0],
+ 'maskmask',
+ '.bar',
+ 'unmaskmask',
+ '.foo',
+ true,
+ ),
+ ).toEqual(true);
+ });
+
+ it('should not mask if the unmasking selector is more specific than the masking selector', () => {
+ const el = render(
+ `
Lorem ipsum`,
+ );
+ expect(
+ needMaskingText(
+ el.children[0].children[0],
+ 'maskmask',
+ '.bar',
+ 'unmaskmask',
+ '.foo',
+ true,
+ ),
+ ).toEqual(false);
+ });
+});
diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts
index c0e69a025f..e5c772cd35 100644
--- a/packages/rrweb/src/record/index.ts
+++ b/packages/rrweb/src/record/index.ts
@@ -63,8 +63,11 @@ function record
(
blockClass = 'rr-block',
blockSelector = null,
ignoreClass = 'rr-ignore',
+ maskAllText = false,
maskTextClass = 'rr-mask',
+ unmaskTextClass = 'rr-unmask',
maskTextSelector = null,
+ unmaskTextSelector = null,
inlineStylesheet = true,
maskAllInputs,
maskInputOptions: _maskInputOptions,
@@ -322,8 +325,11 @@ function record(
bypassOptions: {
blockClass,
blockSelector,
+ maskAllText,
maskTextClass,
+ unmaskTextClass,
maskTextSelector,
+ unmaskTextSelector,
inlineStylesheet,
maskInputOptions,
dataURLOptions,
@@ -365,8 +371,11 @@ function record(
mirror,
blockClass,
blockSelector,
+ maskAllText,
maskTextClass,
+ unmaskTextClass,
maskTextSelector,
+ unmaskTextSelector,
inlineStylesheet,
maskAllInputs: maskInputOptions,
maskTextFn,
@@ -521,8 +530,11 @@ function record(
},
blockClass,
ignoreClass,
+ maskAllText,
maskTextClass,
+ unmaskTextClass,
maskTextSelector,
+ unmaskTextSelector,
maskInputOptions,
inlineStylesheet,
sampling,
diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts
index aa351fee62..30488a9ec8 100644
--- a/packages/rrweb/src/record/mutation.ts
+++ b/packages/rrweb/src/record/mutation.ts
@@ -162,8 +162,11 @@ export default class MutationBuffer {
private mutationCb: observerParam['mutationCb'];
private blockClass: observerParam['blockClass'];
private blockSelector: observerParam['blockSelector'];
+ private maskAllText: observerParam['maskAllText'];
private maskTextClass: observerParam['maskTextClass'];
+ private unmaskTextClass: observerParam['unmaskTextClass'];
private maskTextSelector: observerParam['maskTextSelector'];
+ private unmaskTextSelector: observerParam['unmaskTextSelector'];
private inlineStylesheet: observerParam['inlineStylesheet'];
private maskInputOptions: observerParam['maskInputOptions'];
private maskTextFn: observerParam['maskTextFn'];
@@ -187,8 +190,11 @@ export default class MutationBuffer {
'mutationCb',
'blockClass',
'blockSelector',
+ 'maskAllText',
'maskTextClass',
+ 'unmaskTextClass',
'maskTextSelector',
+ 'unmaskTextSelector',
'inlineStylesheet',
'maskInputOptions',
'maskTextFn',
@@ -288,8 +294,11 @@ export default class MutationBuffer {
mirror: this.mirror,
blockClass: this.blockClass,
blockSelector: this.blockSelector,
+ maskAllText: this.maskAllText,
maskTextClass: this.maskTextClass,
+ unmaskTextClass: this.unmaskTextClass,
maskTextSelector: this.maskTextSelector,
+ unmaskTextSelector: this.unmaskTextSelector,
skipChild: true,
newlyAddedElement: true,
inlineStylesheet: this.inlineStylesheet,
@@ -475,6 +484,9 @@ export default class MutationBuffer {
m.target,
this.maskTextClass,
this.maskTextSelector,
+ this.unmaskTextClass,
+ this.unmaskTextSelector,
+ this.maskAllText,
) && value
? this.maskTextFn
? this.maskTextFn(value)
diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts
index dd9a516709..9c4cbd3600 100644
--- a/packages/rrweb/src/types.ts
+++ b/packages/rrweb/src/types.ts
@@ -25,6 +25,7 @@ import type {
KeepIframeSrcFn,
listenerHandler,
maskTextClass,
+ unmaskTextClass,
mediaInteractionCallback,
mouseInteractionCallBack,
mousemoveCallBack,
@@ -46,8 +47,11 @@ export type recordOptions = {
blockClass?: blockClass;
blockSelector?: string;
ignoreClass?: string;
+ maskAllText?: boolean;
maskTextClass?: maskTextClass;
+ unmaskTextClass?: unmaskTextClass;
maskTextSelector?: string;
+ unmaskTextSelector?: string;
maskAllInputs?: boolean;
maskInputOptions?: MaskInputOptions;
maskInputFn?: MaskInputFn;
@@ -84,8 +88,11 @@ export type observerParam = {
blockClass: blockClass;
blockSelector: string | null;
ignoreClass: string;
+ maskAllText: boolean;
maskTextClass: maskTextClass;
+ unmaskTextClass: unmaskTextClass;
maskTextSelector: string | null;
+ unmaskTextSelector: string | null;
maskInputOptions: MaskInputOptions;
maskInputFn?: MaskInputFn;
maskTextFn?: MaskTextFn;
@@ -126,8 +133,11 @@ export type MutationBufferParam = Pick<
| 'mutationCb'
| 'blockClass'
| 'blockSelector'
+ | 'maskAllText'
| 'maskTextClass'
+ | 'unmaskTextClass'
| 'maskTextSelector'
+ | 'unmaskTextSelector'
| 'inlineStylesheet'
| 'maskInputOptions'
| 'maskTextFn'
diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap
index 7c6fdfbfda..fcc3d7a55d 100644
--- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap
+++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap
@@ -3106,6 +3106,349 @@ exports[`record integration tests can record node mutations 1`] = `
]"
`;
+exports[`record integration tests can selectively unmask parts of the page 1`] = `
+"[
+ {
+ \\"type\\": 0,
+ \\"data\\": {}
+ },
+ {
+ \\"type\\": 1,
+ \\"data\\": {}
+ },
+ {
+ \\"type\\": 4,
+ \\"data\\": {
+ \\"href\\": \\"about:blank\\",
+ \\"width\\": 1920,
+ \\"height\\": 1080
+ }
+ },
+ {
+ \\"type\\": 2,
+ \\"data\\": {
+ \\"node\\": {
+ \\"type\\": 0,
+ \\"childNodes\\": [
+ {
+ \\"type\\": 1,
+ \\"name\\": \\"html\\",
+ \\"publicId\\": \\"\\",
+ \\"systemId\\": \\"\\",
+ \\"id\\": 2
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"html\\",
+ \\"attributes\\": {
+ \\"lang\\": \\"en\\"
+ },
+ \\"childNodes\\": [
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"head\\",
+ \\"attributes\\": {},
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 5
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"meta\\",
+ \\"attributes\\": {
+ \\"charset\\": \\"UTF-8\\"
+ },
+ \\"childNodes\\": [],
+ \\"id\\": 6
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 7
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"meta\\",
+ \\"attributes\\": {
+ \\"name\\": \\"viewport\\",
+ \\"content\\": \\"width=device-width, initial-scale=1.0\\"
+ },
+ \\"childNodes\\": [],
+ \\"id\\": 8
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 9
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"meta\\",
+ \\"attributes\\": {
+ \\"http-equiv\\": \\"X-UA-Compatible\\",
+ \\"content\\": \\"ie=edge\\"
+ },
+ \\"childNodes\\": [],
+ \\"id\\": 10
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 11
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"title\\",
+ \\"attributes\\": {},
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"Unmask text\\",
+ \\"id\\": 13
+ }
+ ],
+ \\"id\\": 12
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 14
+ }
+ ],
+ \\"id\\": 4
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 15
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"body\\",
+ \\"attributes\\": {},
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 17
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"div\\",
+ \\"attributes\\": {
+ \\"class\\": \\"rr-unmask\\"
+ },
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"unmask1\\\\n \\",
+ \\"id\\": 19
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"div\\",
+ \\"attributes\\": {
+ \\"class\\": \\"rr-mask\\"
+ },
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 21
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"span\\",
+ \\"attributes\\": {},
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"*****\\",
+ \\"id\\": 23
+ }
+ ],
+ \\"id\\": 22
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 24
+ }
+ ],
+ \\"id\\": 20
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 25
+ }
+ ],
+ \\"id\\": 18
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 26
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"div\\",
+ \\"attributes\\": {
+ \\"data-masking\\": \\"false\\"
+ },
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 28
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"div\\",
+ \\"attributes\\": {},
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 30
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"div\\",
+ \\"attributes\\": {
+ \\"data-masking\\": \\"true\\"
+ },
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"*****\\",
+ \\"id\\": 32
+ }
+ ],
+ \\"id\\": 31
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 33
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"div\\",
+ \\"attributes\\": {},
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"unmask2\\",
+ \\"id\\": 35
+ }
+ ],
+ \\"id\\": 34
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 36
+ }
+ ],
+ \\"id\\": 29
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 37
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"ul\\",
+ \\"attributes\\": {},
+ \\"childNodes\\": [],
+ \\"id\\": 38
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 39
+ }
+ ],
+ \\"id\\": 27
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\\\n \\",
+ \\"id\\": 40
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"script\\",
+ \\"attributes\\": {},
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
+ \\"id\\": 42
+ }
+ ],
+ \\"id\\": 41
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\",
+ \\"id\\": 43
+ }
+ ],
+ \\"id\\": 16
+ }
+ ],
+ \\"id\\": 3
+ }
+ ],
+ \\"id\\": 1
+ },
+ \\"initialOffset\\": {
+ \\"left\\": 0,
+ \\"top\\": 0
+ }
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 0,
+ \\"texts\\": [],
+ \\"attributes\\": [],
+ \\"removes\\": [],
+ \\"adds\\": [
+ {
+ \\"parentId\\": 38,
+ \\"nextId\\": null,
+ \\"node\\": {
+ \\"type\\": 2,
+ \\"tagName\\": \\"li\\",
+ \\"attributes\\": {
+ \\"class\\": \\"rr-mask\\"
+ },
+ \\"childNodes\\": [],
+ \\"id\\": 44
+ }
+ },
+ {
+ \\"parentId\\": 44,
+ \\"nextId\\": null,
+ \\"node\\": {
+ \\"type\\": 3,
+ \\"textContent\\": \\"*** **** ****\\",
+ \\"id\\": 45
+ }
+ }
+ ]
+ }
+ }
+]"
+`;
+
exports[`record integration tests can use maskInputOptions to configure which type of inputs should be masked 1`] = `
"[
{
@@ -3588,34 +3931,861 @@ exports[`record integration tests can use maskInputOptions to configure which ty
\\"type\\": 3,
\\"data\\": {
\\"source\\": 5,
- \\"text\\": \\"t\\",
- \\"isChecked\\": false,
- \\"id\\": 22
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"te\\",
- \\"isChecked\\": false,
- \\"id\\": 22
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"tes\\",
- \\"isChecked\\": false,
- \\"id\\": 22
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"test\\",
+ \\"text\\": \\"t\\",
+ \\"isChecked\\": false,
+ \\"id\\": 22
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"te\\",
+ \\"isChecked\\": false,
+ \\"id\\": 22
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"tes\\",
+ \\"isChecked\\": false,
+ \\"id\\": 22
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"test\\",
+ \\"isChecked\\": false,
+ \\"id\\": 22
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 2,
+ \\"type\\": 1,
+ \\"id\\": 27
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 2,
+ \\"type\\": 6,
+ \\"id\\": 22
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 2,
+ \\"type\\": 5,
+ \\"id\\": 27
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 2,
+ \\"type\\": 0,
+ \\"id\\": 27
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 2,
+ \\"type\\": 2,
+ \\"id\\": 27
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"on\\",
+ \\"isChecked\\": true,
+ \\"id\\": 27
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"off\\",
+ \\"isChecked\\": false,
+ \\"id\\": 32
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 2,
+ \\"type\\": 1,
+ \\"id\\": 37
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 2,
+ \\"type\\": 6,
+ \\"id\\": 27
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 2,
+ \\"type\\": 5,
+ \\"id\\": 37
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 2,
+ \\"type\\": 0,
+ \\"id\\": 37
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 2,
+ \\"type\\": 2,
+ \\"id\\": 37
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"on\\",
+ \\"isChecked\\": true,
+ \\"id\\": 37
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 2,
+ \\"type\\": 6,
+ \\"id\\": 37
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 2,
+ \\"type\\": 5,
+ \\"id\\": 42
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"t\\",
+ \\"isChecked\\": false,
+ \\"id\\": 42
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"te\\",
+ \\"isChecked\\": false,
+ \\"id\\": 42
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"tex\\",
+ \\"isChecked\\": false,
+ \\"id\\": 42
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"text\\",
+ \\"isChecked\\": false,
+ \\"id\\": 42
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"texta\\",
+ \\"isChecked\\": false,
+ \\"id\\": 42
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"textar\\",
+ \\"isChecked\\": false,
+ \\"id\\": 42
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"textare\\",
+ \\"isChecked\\": false,
+ \\"id\\": 42
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"textarea\\",
+ \\"isChecked\\": false,
+ \\"id\\": 42
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"textarea \\",
+ \\"isChecked\\": false,
+ \\"id\\": 42
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"textarea t\\",
+ \\"isChecked\\": false,
+ \\"id\\": 42
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"textarea te\\",
+ \\"isChecked\\": false,
+ \\"id\\": 42
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"textarea tes\\",
+ \\"isChecked\\": false,
+ \\"id\\": 42
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"textarea test\\",
+ \\"isChecked\\": false,
+ \\"id\\": 42
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 2,
+ \\"type\\": 6,
+ \\"id\\": 42
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 2,
+ \\"type\\": 5,
+ \\"id\\": 59
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"*\\",
+ \\"isChecked\\": false,
+ \\"id\\": 59
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"**\\",
+ \\"isChecked\\": false,
+ \\"id\\": 59
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"***\\",
+ \\"isChecked\\": false,
+ \\"id\\": 59
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"****\\",
+ \\"isChecked\\": false,
+ \\"id\\": 59
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"*****\\",
+ \\"isChecked\\": false,
+ \\"id\\": 59
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"******\\",
+ \\"isChecked\\": false,
+ \\"id\\": 59
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"*******\\",
+ \\"isChecked\\": false,
+ \\"id\\": 59
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"********\\",
+ \\"isChecked\\": false,
+ \\"id\\": 59
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"1\\",
+ \\"isChecked\\": false,
+ \\"id\\": 47
+ }
+ }
+]"
+`;
+
+exports[`record integration tests can use maskTextSelector to configure which inputs should be masked 1`] = `
+"[
+ {
+ \\"type\\": 0,
+ \\"data\\": {}
+ },
+ {
+ \\"type\\": 1,
+ \\"data\\": {}
+ },
+ {
+ \\"type\\": 4,
+ \\"data\\": {
+ \\"href\\": \\"about:blank\\",
+ \\"width\\": 1920,
+ \\"height\\": 1080
+ }
+ },
+ {
+ \\"type\\": 2,
+ \\"data\\": {
+ \\"node\\": {
+ \\"type\\": 0,
+ \\"childNodes\\": [
+ {
+ \\"type\\": 1,
+ \\"name\\": \\"html\\",
+ \\"publicId\\": \\"\\",
+ \\"systemId\\": \\"\\",
+ \\"id\\": 2
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"html\\",
+ \\"attributes\\": {
+ \\"lang\\": \\"en\\"
+ },
+ \\"childNodes\\": [
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"head\\",
+ \\"attributes\\": {},
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 5
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"meta\\",
+ \\"attributes\\": {
+ \\"charset\\": \\"UTF-8\\"
+ },
+ \\"childNodes\\": [],
+ \\"id\\": 6
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 7
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"meta\\",
+ \\"attributes\\": {
+ \\"name\\": \\"viewport\\",
+ \\"content\\": \\"width=device-width, initial-scale=1.0\\"
+ },
+ \\"childNodes\\": [],
+ \\"id\\": 8
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 9
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"meta\\",
+ \\"attributes\\": {
+ \\"http-equiv\\": \\"X-UA-Compatible\\",
+ \\"content\\": \\"ie=edge\\"
+ },
+ \\"childNodes\\": [],
+ \\"id\\": 10
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 11
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"title\\",
+ \\"attributes\\": {},
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"form fields\\",
+ \\"id\\": 13
+ }
+ ],
+ \\"id\\": 12
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 14
+ }
+ ],
+ \\"id\\": 4
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n\\\\n \\",
+ \\"id\\": 15
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"body\\",
+ \\"attributes\\": {},
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 17
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"form\\",
+ \\"attributes\\": {},
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 19
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"label\\",
+ \\"attributes\\": {
+ \\"for\\": \\"text\\"
+ },
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 21
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"input\\",
+ \\"attributes\\": {
+ \\"type\\": \\"text\\"
+ },
+ \\"childNodes\\": [],
+ \\"id\\": 22
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 23
+ }
+ ],
+ \\"id\\": 20
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 24
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"label\\",
+ \\"attributes\\": {},
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 26
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"input\\",
+ \\"attributes\\": {
+ \\"type\\": \\"radio\\",
+ \\"name\\": \\"toggle\\",
+ \\"value\\": \\"on\\"
+ },
+ \\"childNodes\\": [],
+ \\"id\\": 27
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 28
+ }
+ ],
+ \\"id\\": 25
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 29
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"label\\",
+ \\"attributes\\": {},
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 31
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"input\\",
+ \\"attributes\\": {
+ \\"type\\": \\"radio\\",
+ \\"name\\": \\"toggle\\",
+ \\"value\\": \\"off\\",
+ \\"checked\\": true
+ },
+ \\"childNodes\\": [],
+ \\"id\\": 32
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 33
+ }
+ ],
+ \\"id\\": 30
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 34
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"label\\",
+ \\"attributes\\": {
+ \\"for\\": \\"checkbox\\"
+ },
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 36
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"input\\",
+ \\"attributes\\": {
+ \\"type\\": \\"checkbox\\"
+ },
+ \\"childNodes\\": [],
+ \\"id\\": 37
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 38
+ }
+ ],
+ \\"id\\": 35
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 39
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"label\\",
+ \\"attributes\\": {
+ \\"for\\": \\"textarea\\"
+ },
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 41
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"textarea\\",
+ \\"attributes\\": {
+ \\"name\\": \\"\\",
+ \\"id\\": \\"\\",
+ \\"cols\\": \\"30\\",
+ \\"rows\\": \\"10\\"
+ },
+ \\"childNodes\\": [],
+ \\"id\\": 42
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 43
+ }
+ ],
+ \\"id\\": 40
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 44
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"label\\",
+ \\"attributes\\": {
+ \\"for\\": \\"select\\"
+ },
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 46
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"select\\",
+ \\"attributes\\": {
+ \\"name\\": \\"\\",
+ \\"id\\": \\"\\",
+ \\"value\\": \\"1\\"
+ },
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 48
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"option\\",
+ \\"attributes\\": {
+ \\"value\\": \\"1\\",
+ \\"selected\\": true
+ },
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"1\\",
+ \\"id\\": 50
+ }
+ ],
+ \\"id\\": 49
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 51
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"option\\",
+ \\"attributes\\": {
+ \\"value\\": \\"2\\"
+ },
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"2\\",
+ \\"id\\": 53
+ }
+ ],
+ \\"id\\": 52
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 54
+ }
+ ],
+ \\"id\\": 47
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 55
+ }
+ ],
+ \\"id\\": 45
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 56
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"label\\",
+ \\"attributes\\": {
+ \\"for\\": \\"password\\"
+ },
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 58
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"input\\",
+ \\"attributes\\": {
+ \\"type\\": \\"password\\"
+ },
+ \\"childNodes\\": [],
+ \\"id\\": 59
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 60
+ }
+ ],
+ \\"id\\": 57
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\",
+ \\"id\\": 61
+ }
+ ],
+ \\"id\\": 18
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\\\n \\",
+ \\"id\\": 62
+ },
+ {
+ \\"type\\": 2,
+ \\"tagName\\": \\"script\\",
+ \\"attributes\\": {},
+ \\"childNodes\\": [
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
+ \\"id\\": 64
+ }
+ ],
+ \\"id\\": 63
+ },
+ {
+ \\"type\\": 3,
+ \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\",
+ \\"id\\": 65
+ }
+ ],
+ \\"id\\": 16
+ }
+ ],
+ \\"id\\": 3
+ }
+ ],
+ \\"id\\": 1
+ },
+ \\"initialOffset\\": {
+ \\"left\\": 0,
+ \\"top\\": 0
+ }
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 2,
+ \\"type\\": 5,
+ \\"id\\": 22
+ }
+ },
+ {
+ \\"type\\": 3,
+ \\"data\\": {
+ \\"source\\": 5,
+ \\"text\\": \\"**********\\",
\\"isChecked\\": false,
\\"id\\": 22
}
@@ -3623,319 +4793,148 @@ exports[`record integration tests can use maskInputOptions to configure which ty
{
\\"type\\": 3,
\\"data\\": {
- \\"source\\": 2,
- \\"type\\": 1,
- \\"id\\": 27
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 2,
- \\"type\\": 6,
- \\"id\\": 22
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 2,
- \\"type\\": 5,
- \\"id\\": 27
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 2,
- \\"type\\": 0,
- \\"id\\": 27
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 2,
- \\"type\\": 2,
- \\"id\\": 27
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"on\\",
- \\"isChecked\\": true,
- \\"id\\": 27
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"off\\",
- \\"isChecked\\": false,
- \\"id\\": 32
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 2,
- \\"type\\": 1,
- \\"id\\": 37
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 2,
- \\"type\\": 6,
- \\"id\\": 27
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 2,
- \\"type\\": 5,
- \\"id\\": 37
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 2,
- \\"type\\": 0,
- \\"id\\": 37
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 2,
- \\"type\\": 2,
- \\"id\\": 37
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"on\\",
- \\"isChecked\\": true,
- \\"id\\": 37
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 2,
- \\"type\\": 6,
- \\"id\\": 37
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 2,
- \\"type\\": 5,
- \\"id\\": 42
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"t\\",
- \\"isChecked\\": false,
- \\"id\\": 42
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"te\\",
- \\"isChecked\\": false,
- \\"id\\": 42
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"tex\\",
- \\"isChecked\\": false,
- \\"id\\": 42
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"text\\",
- \\"isChecked\\": false,
- \\"id\\": 42
- }
- },
- {
- \\"type\\": 3,
- \\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"texta\\",
- \\"isChecked\\": false,
- \\"id\\": 42
+ \\"source\\": 2,
+ \\"type\\": 1,
+ \\"id\\": 27
}
},
{
\\"type\\": 3,
\\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"textar\\",
- \\"isChecked\\": false,
- \\"id\\": 42
+ \\"source\\": 2,
+ \\"type\\": 6,
+ \\"id\\": 22
}
},
{
\\"type\\": 3,
\\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"textare\\",
- \\"isChecked\\": false,
- \\"id\\": 42
+ \\"source\\": 2,
+ \\"type\\": 5,
+ \\"id\\": 27
}
},
{
\\"type\\": 3,
\\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"textarea\\",
- \\"isChecked\\": false,
- \\"id\\": 42
+ \\"source\\": 2,
+ \\"type\\": 0,
+ \\"id\\": 27
}
},
{
\\"type\\": 3,
\\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"textarea \\",
- \\"isChecked\\": false,
- \\"id\\": 42
+ \\"source\\": 2,
+ \\"type\\": 2,
+ \\"id\\": 27
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 5,
- \\"text\\": \\"textarea t\\",
- \\"isChecked\\": false,
- \\"id\\": 42
+ \\"text\\": \\"on\\",
+ \\"isChecked\\": true,
+ \\"id\\": 27
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 5,
- \\"text\\": \\"textarea te\\",
+ \\"text\\": \\"off\\",
\\"isChecked\\": false,
- \\"id\\": 42
+ \\"id\\": 32
}
},
{
\\"type\\": 3,
\\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"textarea tes\\",
- \\"isChecked\\": false,
- \\"id\\": 42
+ \\"source\\": 2,
+ \\"type\\": 1,
+ \\"id\\": 37
}
},
{
\\"type\\": 3,
\\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"textarea test\\",
- \\"isChecked\\": false,
- \\"id\\": 42
+ \\"source\\": 2,
+ \\"type\\": 6,
+ \\"id\\": 27
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 2,
- \\"type\\": 6,
- \\"id\\": 42
+ \\"type\\": 5,
+ \\"id\\": 37
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 2,
- \\"type\\": 5,
- \\"id\\": 59
+ \\"type\\": 0,
+ \\"id\\": 37
}
},
{
\\"type\\": 3,
\\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"*\\",
- \\"isChecked\\": false,
- \\"id\\": 59
+ \\"source\\": 2,
+ \\"type\\": 2,
+ \\"id\\": 37
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 5,
- \\"text\\": \\"**\\",
- \\"isChecked\\": false,
- \\"id\\": 59
+ \\"text\\": \\"on\\",
+ \\"isChecked\\": true,
+ \\"id\\": 37
}
},
{
\\"type\\": 3,
\\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"***\\",
- \\"isChecked\\": false,
- \\"id\\": 59
+ \\"source\\": 2,
+ \\"type\\": 6,
+ \\"id\\": 37
}
},
{
\\"type\\": 3,
\\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"****\\",
- \\"isChecked\\": false,
- \\"id\\": 59
+ \\"source\\": 2,
+ \\"type\\": 5,
+ \\"id\\": 42
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 5,
- \\"text\\": \\"*****\\",
+ \\"text\\": \\"**********\\",
\\"isChecked\\": false,
- \\"id\\": 59
+ \\"id\\": 42
}
},
{
\\"type\\": 3,
\\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"******\\",
- \\"isChecked\\": false,
- \\"id\\": 59
+ \\"source\\": 2,
+ \\"type\\": 6,
+ \\"id\\": 42
}
},
{
\\"type\\": 3,
\\"data\\": {
- \\"source\\": 5,
- \\"text\\": \\"*******\\",
- \\"isChecked\\": false,
+ \\"source\\": 2,
+ \\"type\\": 5,
\\"id\\": 59
}
},
@@ -3943,7 +4942,7 @@ exports[`record integration tests can use maskInputOptions to configure which ty
\\"type\\": 3,
\\"data\\": {
\\"source\\": 5,
- \\"text\\": \\"********\\",
+ \\"text\\": \\"**********\\",
\\"isChecked\\": false,
\\"id\\": 59
}
@@ -5083,6 +6082,226 @@ exports[`record integration tests should handle recursive console messages 1`] =
}
}
},
+ {
+ \\"type\\": 6,
+ \\"data\\": {
+ \\"plugin\\": \\"rrweb/console@1\\",
+ \\"payload\\": {
+ \\"level\\": \\"warn\\",
+ \\"trace\\": [
+ \\"Object.get (__puppeteer_evaluation_script__:13:33)\\",
+ \\"isObjTooDeep (:8503:25)\\",
+ \\"isObjTooDeep (:8503:35)\\",
+ \\"shouldIgnore (:8567:31)\\",
+ \\"Object. (:8536:13)\\",
+ \\"stringify (:8517:19)\\",
+ \\":8656:47\\"
+ ],
+ \\"payload\\": [
+ \\"\\\\\\"proxied was accessed so triggering a console.warn\\\\\\"\\",
+ \\"\\\\\\"[object Object]\\\\\\"\\"
+ ]
+ }
+ }
+ },
+ {
+ \\"type\\": 6,
+ \\"data\\": {
+ \\"plugin\\": \\"rrweb/console@1\\",
+ \\"payload\\": {
+ \\"level\\": \\"warn\\",
+ \\"trace\\": [
+ \\"Object.get (__puppeteer_evaluation_script__:13:33)\\",
+ \\"isObjTooDeep (:8503:25)\\",
+ \\"isObjTooDeep (:8503:35)\\",
+ \\"shouldIgnore (:8567:31)\\",
+ \\"Object. (:8536:13)\\",
+ \\"stringify (:8517:19)\\",
+ \\":8656:47\\"
+ ],
+ \\"payload\\": [
+ \\"\\\\\\"proxied was accessed so triggering a console.warn\\\\\\"\\",
+ \\"\\\\\\"[object Object]\\\\\\"\\"
+ ]
+ }
+ }
+ },
+ {
+ \\"type\\": 6,
+ \\"data\\": {
+ \\"plugin\\": \\"rrweb/console@1\\",
+ \\"payload\\": {
+ \\"level\\": \\"warn\\",
+ \\"trace\\": [
+ \\"Object.get (__puppeteer_evaluation_script__:13:33)\\",
+ \\"isObjTooDeep (:8503:25)\\",
+ \\"isObjTooDeep (:8503:35)\\",
+ \\"shouldIgnore (:8567:31)\\",
+ \\"Object. (:8536:13)\\",
+ \\"stringify (:8517:19)\\",
+ \\":8656:47\\"
+ ],
+ \\"payload\\": [
+ \\"\\\\\\"proxied was accessed so triggering a console.warn\\\\\\"\\",
+ \\"\\\\\\"[object Object]\\\\\\"\\"
+ ]
+ }
+ }
+ },
+ {
+ \\"type\\": 6,
+ \\"data\\": {
+ \\"plugin\\": \\"rrweb/console@1\\",
+ \\"payload\\": {
+ \\"level\\": \\"warn\\",
+ \\"trace\\": [
+ \\"Object.get (__puppeteer_evaluation_script__:13:33)\\",
+ \\"isObjTooDeep (:8503:25)\\",
+ \\"isObjTooDeep (:8503:35)\\",
+ \\"shouldIgnore (:8567:31)\\",
+ \\"Object. (:8536:13)\\",
+ \\"stringify (:8517:19)\\",
+ \\":8656:47\\"
+ ],
+ \\"payload\\": [
+ \\"\\\\\\"proxied was accessed so triggering a console.warn\\\\\\"\\",
+ \\"\\\\\\"[object Object]\\\\\\"\\"
+ ]
+ }
+ }
+ },
+ {
+ \\"type\\": 6,
+ \\"data\\": {
+ \\"plugin\\": \\"rrweb/console@1\\",
+ \\"payload\\": {
+ \\"level\\": \\"warn\\",
+ \\"trace\\": [
+ \\"Object.get (__puppeteer_evaluation_script__:13:33)\\",
+ \\"isObjTooDeep (:8503:25)\\",
+ \\"isObjTooDeep (:8503:35)\\",
+ \\"shouldIgnore (:8567:31)\\",
+ \\"Object. (:8536:13)\\",
+ \\"stringify (:8517:19)\\",
+ \\":8656:47\\"
+ ],
+ \\"payload\\": [
+ \\"\\\\\\"proxied was accessed so triggering a console.warn\\\\\\"\\",
+ \\"\\\\\\"[object Object]\\\\\\"\\"
+ ]
+ }
+ }
+ },
+ {
+ \\"type\\": 6,
+ \\"data\\": {
+ \\"plugin\\": \\"rrweb/console@1\\",
+ \\"payload\\": {
+ \\"level\\": \\"warn\\",
+ \\"trace\\": [
+ \\"Object.get (__puppeteer_evaluation_script__:13:33)\\",
+ \\"isObjTooDeep (:8503:25)\\",
+ \\"isObjTooDeep (:8503:35)\\",
+ \\"shouldIgnore (:8567:31)\\",
+ \\"Object. (:8536:13)\\",
+ \\"stringify (:8517:19)\\",
+ \\":8656:47\\"
+ ],
+ \\"payload\\": [
+ \\"\\\\\\"proxied was accessed so triggering a console.warn\\\\\\"\\",
+ \\"\\\\\\"[object Object]\\\\\\"\\"
+ ]
+ }
+ }
+ },
+ {
+ \\"type\\": 6,
+ \\"data\\": {
+ \\"plugin\\": \\"rrweb/console@1\\",
+ \\"payload\\": {
+ \\"level\\": \\"warn\\",
+ \\"trace\\": [
+ \\"Object.get (__puppeteer_evaluation_script__:13:33)\\",
+ \\"isObjTooDeep (:8503:25)\\",
+ \\"isObjTooDeep (:8503:35)\\",
+ \\"shouldIgnore (:8567:31)\\",
+ \\"Object. (:8536:13)\\",
+ \\"stringify (:8517:19)\\",
+ \\":8656:47\\"
+ ],
+ \\"payload\\": [
+ \\"\\\\\\"proxied was accessed so triggering a console.warn\\\\\\"\\",
+ \\"\\\\\\"[object Object]\\\\\\"\\"
+ ]
+ }
+ }
+ },
+ {
+ \\"type\\": 6,
+ \\"data\\": {
+ \\"plugin\\": \\"rrweb/console@1\\",
+ \\"payload\\": {
+ \\"level\\": \\"warn\\",
+ \\"trace\\": [
+ \\"Object.get (__puppeteer_evaluation_script__:13:33)\\",
+ \\"isObjTooDeep (:8503:25)\\",
+ \\"isObjTooDeep (:8503:35)\\",
+ \\"shouldIgnore (:8567:31)\\",
+ \\"Object. (:8536:13)\\",
+ \\"stringify (:8517:19)\\",
+ \\":8656:47\\"
+ ],
+ \\"payload\\": [
+ \\"\\\\\\"proxied was accessed so triggering a console.warn\\\\\\"\\",
+ \\"\\\\\\"[object Object]\\\\\\"\\"
+ ]
+ }
+ }
+ },
+ {
+ \\"type\\": 6,
+ \\"data\\": {
+ \\"plugin\\": \\"rrweb/console@1\\",
+ \\"payload\\": {
+ \\"level\\": \\"warn\\",
+ \\"trace\\": [
+ \\"Object.get (__puppeteer_evaluation_script__:13:33)\\",
+ \\"isObjTooDeep (:8503:25)\\",
+ \\"isObjTooDeep (:8503:35)\\",
+ \\"shouldIgnore (:8567:31)\\",
+ \\"Object. (:8536:13)\\",
+ \\"stringify (:8517:19)\\",
+ \\":8656:47\\"
+ ],
+ \\"payload\\": [
+ \\"\\\\\\"proxied was accessed so triggering a console.warn\\\\\\"\\",
+ \\"\\\\\\"[object Object]\\\\\\"\\"
+ ]
+ }
+ }
+ },
+ {
+ \\"type\\": 6,
+ \\"data\\": {
+ \\"plugin\\": \\"rrweb/console@1\\",
+ \\"payload\\": {
+ \\"level\\": \\"warn\\",
+ \\"trace\\": [
+ \\"Object.get (__puppeteer_evaluation_script__:13:33)\\",
+ \\"isObjTooDeep (:8503:25)\\",
+ \\"shouldIgnore (:8567:31)\\",
+ \\"Object. (:8536:13)\\",
+ \\"stringify (:8517:19)\\",
+ \\":8656:47\\",
+ \\"console.log (:8656:36)\\"
+ ],
+ \\"payload\\": [
+ \\"\\\\\\"proxied was accessed so triggering a console.warn\\\\\\"\\",
+ \\"\\\\\\"[object Object]\\\\\\"\\"
+ ]
+ }
+ }
+ },
{
\\"type\\": 6,
\\"data\\": {
@@ -8877,6 +10096,10 @@ exports[`record integration tests should record after DOMContentLoaded event 1`]
\\"type\\": 0,
\\"data\\": {}
},
+ {
+ \\"type\\": 1,
+ \\"data\\": {}
+ },
{
\\"type\\": 4,
\\"data\\": {
@@ -8946,10 +10169,6 @@ exports[`record integration tests should record after DOMContentLoaded event 1`]
\\"top\\": 0
}
}
- },
- {
- \\"type\\": 1,
- \\"data\\": {}
}
]"
`;
diff --git a/packages/rrweb/test/html/unmask-text.html b/packages/rrweb/test/html/unmask-text.html
new file mode 100644
index 0000000000..a71dc04205
--- /dev/null
+++ b/packages/rrweb/test/html/unmask-text.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ Unmask text
+
+
+
+
+
+
diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts
index fec8c36902..90d24d2089 100644
--- a/packages/rrweb/test/integration.test.ts
+++ b/packages/rrweb/test/integration.test.ts
@@ -1064,6 +1064,31 @@ describe('record integration tests', function (this: ISuite) {
assertSnapshot(snapshots);
});
+ it('can selectively unmask parts of the page', async () => {
+ const page: puppeteer.Page = await browser.newPage();
+ await page.goto('about:blank');
+ await page.setContent(
+ getHtml.call(this, 'unmask-text.html', {
+ maskAllText: true,
+ maskTextSelector: '[data-masking="true"]',
+ unmaskTextSelector: '[data-masking="false"]',
+ }),
+ );
+
+ await page.evaluate(() => {
+ const li = document.createElement('li');
+ const ul = document.querySelector('ul') as HTMLUListElement;
+ li.className = 'rr-mask';
+ ul.appendChild(li);
+ li.innerText = 'new list item';
+ });
+
+ const snapshots = (await page.evaluate(
+ 'window.snapshots',
+ )) as eventWithTime[];
+ assertSnapshot(snapshots);
+ });
+
it('should record after DOMContentLoaded event', async () => {
const page: puppeteer.Page = await browser.newPage();
await page.goto('about:blank');
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index 1188ef2fb7..283eeeea63 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -179,6 +179,7 @@ export type canvasEventWithTime = eventWithTime & {
export type blockClass = string | RegExp;
export type maskTextClass = string | RegExp;
+export type unmaskTextClass = string | RegExp;
export type SamplingStrategy = Partial<{
/**