Add userTriggered (#495)
* add `userTriggered` * update snapshots to add userTriggered * add `userTriggered` * update snapshots to add userTriggered * update snapshot to include userTrigger * only set userTriggered on `userTriggeredOnInput: true` * What is user triggered? * correct snapshot * add second radio to demonstrate userTriggered
This commit is contained in:
45
guide.md
45
guide.md
@@ -135,28 +135,29 @@ setInterval(save, 10 * 1000);
|
|||||||
|
|
||||||
The parameter of `rrweb.record` accepts the following options.
|
The parameter of `rrweb.record` accepts the following options.
|
||||||
|
|
||||||
| key | default | description |
|
| key | default | description |
|
||||||
| ---------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| -------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| emit | required | the callback function to get emitted events |
|
| emit | required | the callback function to get emitted events |
|
||||||
| checkoutEveryNth | - | take a full snapshot after every N events<br />refer to the [checkout](#checkout) chapter |
|
| checkoutEveryNth | - | take a full snapshot after every N events<br />refer to the [checkout](#checkout) chapter |
|
||||||
| checkoutEveryNms | - | take a full snapshot after every N ms<br />refer to the [checkout](#checkout) chapter |
|
| checkoutEveryNms | - | take a full snapshot after every N ms<br />refer to the [checkout](#checkout) chapter |
|
||||||
| blockClass | 'rr-block' | Use a string or RegExp to configure which elements should be blocked, refer to the [privacy](#privacy) chapter |
|
| blockClass | 'rr-block' | Use a string or RegExp to configure which elements should be blocked, refer to the [privacy](#privacy) chapter |
|
||||||
| blockSelector | null | Use a string to configure which selector should be blocked, refer to the [privacy](#privacy) chapter |
|
| 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 |
|
| ignoreClass | 'rr-ignore' | Use a string or RegExp to configure which elements should be ignored, refer to the [privacy](#privacy) chapter |
|
||||||
| maskTextClass | 'rr-mask' | Use a string or RegExp to configure which elements should be masked, refer to the [privacy](#privacy) chapter |
|
| maskTextClass | 'rr-mask' | Use a string or RegExp to configure which elements should be masked, refer to the [privacy](#privacy) chapter |
|
||||||
| maskTextSelector | null | Use a string to configure which selector should be masked, refer to the [privacy](#privacy) chapter |
|
| maskTextSelector | null | Use a string to configure which selector should be masked, refer to the [privacy](#privacy) chapter |
|
||||||
| maskAllInputs | false | mask all input content as \* |
|
| maskAllInputs | false | mask all input content as \* |
|
||||||
| maskInputOptions | { password: true } | mask some kinds of input \*<br />refer to the [list](https://github.com/rrweb-io/rrweb-snapshot/blob/0bb95f1ee77fef03166a68f75b959ad997171442/src/types.ts#L77-L95) |
|
| maskInputOptions | { password: true } | mask some kinds of input \*<br />refer to the [list](https://github.com/rrweb-io/rrweb-snapshot/blob/0bb95f1ee77fef03166a68f75b959ad997171442/src/types.ts#L77-L95) |
|
||||||
| maskInputFn | - | customize mask input content recording logic |
|
| maskInputFn | - | customize mask input content recording logic |
|
||||||
| maskTextFn | - | customize mask text content recording logic |
|
| maskTextFn | - | customize mask text content recording logic |
|
||||||
| slimDOMOptions | {} | remove unnecessary parts of the DOM <br />refer to the [list](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L91) |
|
| slimDOMOptions | {} | remove unnecessary parts of the DOM <br />refer to the [list](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L91) |
|
||||||
| inlineStylesheet | true | whether to inline the stylesheet in the events |
|
| inlineStylesheet | true | whether to inline the stylesheet in the events |
|
||||||
| hooks | {} | hooks for events<br />refer to the [list](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) |
|
| hooks | {} | hooks for events<br />refer to the [list](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) |
|
||||||
| packFn | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) |
|
| packFn | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) |
|
||||||
| sampling | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) |
|
| sampling | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) |
|
||||||
| recordCanvas | false | whether to record the canvas element |
|
| recordCanvas | false | whether to record the canvas element |
|
||||||
| collectFonts | false | whether to collect fonts in the website |
|
| collectFonts | false | whether to collect fonts in the website |
|
||||||
| recordLog | false | whether to record console output, refer to the [console recipe](./docs/recipes/console.md) |
|
| recordLog | false | whether to record console output, refer to the [console recipe](./docs/recipes/console.md) |
|
||||||
|
| userTriggeredOnInput | false | whether to add `userTriggered` on input events that indicates if this event was triggered directly by the user or not. [What is `userTriggered`?](https://github.com/rrweb-io/rrweb/pull/495) |
|
||||||
|
|
||||||
#### Privacy
|
#### Privacy
|
||||||
|
|
||||||
|
|||||||
@@ -131,28 +131,29 @@ setInterval(save, 10 * 1000);
|
|||||||
|
|
||||||
`rrweb.record(config)` 的 config 部分接受以下参数
|
`rrweb.record(config)` 的 config 部分接受以下参数
|
||||||
|
|
||||||
| key | 默认值 | 功能 |
|
| key | 默认值 | 功能 |
|
||||||
| ---------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| -------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| emit | 必填 | 获取当前录制的数据 |
|
| emit | 必填 | 获取当前录制的数据 |
|
||||||
| checkoutEveryNth | - | 每 N 次事件重新制作一次全量快照<br />详见[“重新制作快照”](#重新制作快照)章节 |
|
| checkoutEveryNth | - | 每 N 次事件重新制作一次全量快照<br />详见[“重新制作快照”](#重新制作快照)章节 |
|
||||||
| checkoutEveryNms | - | 每 N 毫秒重新制作一次全量快照<br />详见[“重新制作快照”](#重新制作快照)章节 |
|
| checkoutEveryNms | - | 每 N 毫秒重新制作一次全量快照<br />详见[“重新制作快照”](#重新制作快照)章节 |
|
||||||
| blockClass | 'rr-block' | 字符串或正则表达式,可用于自定义屏蔽元素的类名,详见[“隐私”](#隐私)章节 |
|
| blockClass | 'rr-block' | 字符串或正则表达式,可用于自定义屏蔽元素的类名,详见[“隐私”](#隐私)章节 |
|
||||||
| blockSelector | null | 所有 element.matches(blockSelector)为 true 的元素都不会被录制,回放时取而代之的是一个同等宽高的占位元素 |
|
| blockSelector | null | 所有 element.matches(blockSelector)为 true 的元素都不会被录制,回放时取而代之的是一个同等宽高的占位元素 |
|
||||||
| ignoreClass | 'rr-ignore' | 字符串或正则表达式,可用于自定义忽略元素的类名,详见[“隐私”](#隐私)章节 |
|
| ignoreClass | 'rr-ignore' | 字符串或正则表达式,可用于自定义忽略元素的类名,详见[“隐私”](#隐私)章节 |
|
||||||
| maskTextClass | 'rr-mask' | 字符串或正则表达式,可用于自定义忽略元素 text 内容的类名,详见[“隐私”](#隐私)章节 |
|
| maskTextClass | 'rr-mask' | 字符串或正则表达式,可用于自定义忽略元素 text 内容的类名,详见[“隐私”](#隐私)章节 |
|
||||||
| maskTextSelector | null | 所有 element.matches(maskTextSelector)为 true 的元素及其子元素的 text 内容将会被屏蔽 |
|
| maskTextSelector | null | 所有 element.matches(maskTextSelector)为 true 的元素及其子元素的 text 内容将会被屏蔽 |
|
||||||
| maskAllInputs | false | 将所有输入内容记录为 \* |
|
| maskAllInputs | false | 将所有输入内容记录为 \* |
|
||||||
| maskInputOptions | { password: true } | 选择将特定类型的输入框内容记录为 \*<br />类型详见[列表](https://github.com/rrweb-io/rrweb-snapshot/blob/0bb95f1ee77fef03166a68f75b959ad997171442/src/types.ts#L77-L95) |
|
| maskInputOptions | { password: true } | 选择将特定类型的输入框内容记录为 \*<br />类型详见[列表](https://github.com/rrweb-io/rrweb-snapshot/blob/0bb95f1ee77fef03166a68f75b959ad997171442/src/types.ts#L77-L95) |
|
||||||
| maskInputFn | - | 自定义特定类型的输入框内容记录逻辑 |
|
| maskInputFn | - | 自定义特定类型的输入框内容记录逻辑 |
|
||||||
| maskTextFn | - | 自定义文字内容的记录逻辑 |
|
| maskTextFn | - | 自定义文字内容的记录逻辑 |
|
||||||
| slimDOMOptions | {} | 去除 DOM 中不必要的部分 <br />类型详见[列表](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L91) |
|
| slimDOMOptions | {} | 去除 DOM 中不必要的部分 <br />类型详见[列表](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L91) |
|
||||||
| inlineStylesheet | true | 是否将样式表内联 |
|
| inlineStylesheet | true | 是否将样式表内联 |
|
||||||
| hooks | {} | 各类事件的回调<br />类型详见[列表](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) |
|
| hooks | {} | 各类事件的回调<br />类型详见[列表](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) |
|
||||||
| packFn | - | 数据压缩函数,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) |
|
| packFn | - | 数据压缩函数,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) |
|
||||||
| sampling | - | 数据抽样策略,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) |
|
| sampling | - | 数据抽样策略,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) |
|
||||||
| recordCanvas | false | 是否记录 canvas 内容 |
|
| recordCanvas | false | 是否记录 canvas 内容 |
|
||||||
| collectFonts | false | 是否记录页面中的字体文件 |
|
| collectFonts | false | 是否记录页面中的字体文件 |
|
||||||
| recordLog | false | 是否记录 console 输出,详见[console 录制和播放](./docs/recipes/console.zh_CN.md) |
|
| recordLog | false | 是否记录 console 输出,详见[console 录制和播放](./docs/recipes/console.zh_CN.md) |
|
||||||
|
| userTriggeredOnInput | false | [什么是 `userTriggered`](https://github.com/rrweb-io/rrweb/pull/495) |
|
||||||
|
|
||||||
#### 隐私
|
#### 隐私
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ function record<T = eventWithTime>(
|
|||||||
sampling = {},
|
sampling = {},
|
||||||
mousemoveWait,
|
mousemoveWait,
|
||||||
recordCanvas = false,
|
recordCanvas = false,
|
||||||
|
userTriggeredOnInput = false,
|
||||||
collectFonts = false,
|
collectFonts = false,
|
||||||
plugins,
|
plugins,
|
||||||
keepIframeSrcFn = () => false,
|
keepIframeSrcFn = () => false,
|
||||||
@@ -379,6 +380,7 @@ function record<T = eventWithTime>(
|
|||||||
inlineStylesheet,
|
inlineStylesheet,
|
||||||
sampling,
|
sampling,
|
||||||
recordCanvas,
|
recordCanvas,
|
||||||
|
userTriggeredOnInput,
|
||||||
collectFonts,
|
collectFonts,
|
||||||
doc,
|
doc,
|
||||||
maskInputFn,
|
maskInputFn,
|
||||||
|
|||||||
@@ -337,6 +337,15 @@ function initViewportResizeObserver(
|
|||||||
return on('resize', updateDimension, window);
|
return on('resize', updateDimension, window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wrapEventWithUserTriggeredFlag(
|
||||||
|
v: inputValue,
|
||||||
|
enable: boolean,
|
||||||
|
): inputValue {
|
||||||
|
const value = { ...v };
|
||||||
|
if (!enable) delete value.userTriggered;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
export const INPUT_TAGS = ['INPUT', 'TEXTAREA', 'SELECT'];
|
export const INPUT_TAGS = ['INPUT', 'TEXTAREA', 'SELECT'];
|
||||||
const lastInputValueMap: WeakMap<EventTarget, inputValue> = new WeakMap();
|
const lastInputValueMap: WeakMap<EventTarget, inputValue> = new WeakMap();
|
||||||
function initInputObserver(
|
function initInputObserver(
|
||||||
@@ -348,9 +357,11 @@ function initInputObserver(
|
|||||||
maskInputOptions: MaskInputOptions,
|
maskInputOptions: MaskInputOptions,
|
||||||
maskInputFn: MaskInputFn | undefined,
|
maskInputFn: MaskInputFn | undefined,
|
||||||
sampling: SamplingStrategy,
|
sampling: SamplingStrategy,
|
||||||
|
userTriggeredOnInput: boolean,
|
||||||
): listenerHandler {
|
): listenerHandler {
|
||||||
function eventHandler(event: Event) {
|
function eventHandler(event: Event) {
|
||||||
const target = getEventTarget(event);
|
const target = getEventTarget(event);
|
||||||
|
const userTriggered = event.isTrusted;
|
||||||
if (
|
if (
|
||||||
!target ||
|
!target ||
|
||||||
!(target as Element).tagName ||
|
!(target as Element).tagName ||
|
||||||
@@ -381,7 +392,13 @@ function initInputObserver(
|
|||||||
maskInputFn,
|
maskInputFn,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
cbWithDedup(target, { text, isChecked });
|
cbWithDedup(
|
||||||
|
target,
|
||||||
|
wrapEventWithUserTriggeredFlag(
|
||||||
|
{ text, isChecked, userTriggered },
|
||||||
|
userTriggeredOnInput,
|
||||||
|
),
|
||||||
|
);
|
||||||
// if a radio was checked
|
// if a radio was checked
|
||||||
// the other radios with the same name attribute will be unchecked.
|
// the other radios with the same name attribute will be unchecked.
|
||||||
const name: string | undefined = (target as HTMLInputElement).name;
|
const name: string | undefined = (target as HTMLInputElement).name;
|
||||||
@@ -390,10 +407,17 @@ function initInputObserver(
|
|||||||
.querySelectorAll(`input[type="radio"][name="${name}"]`)
|
.querySelectorAll(`input[type="radio"][name="${name}"]`)
|
||||||
.forEach((el) => {
|
.forEach((el) => {
|
||||||
if (el !== target) {
|
if (el !== target) {
|
||||||
cbWithDedup(el, {
|
cbWithDedup(
|
||||||
text: (el as HTMLInputElement).value,
|
el,
|
||||||
isChecked: !isChecked,
|
wrapEventWithUserTriggeredFlag(
|
||||||
});
|
{
|
||||||
|
text: (el as HTMLInputElement).value,
|
||||||
|
isChecked: !isChecked,
|
||||||
|
userTriggered: false,
|
||||||
|
},
|
||||||
|
userTriggeredOnInput,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -763,6 +787,7 @@ export function initObservers(
|
|||||||
o.maskInputOptions,
|
o.maskInputOptions,
|
||||||
o.maskInputFn,
|
o.maskInputFn,
|
||||||
o.sampling,
|
o.sampling,
|
||||||
|
o.userTriggeredOnInput,
|
||||||
);
|
);
|
||||||
const mediaInteractionHandler = initMediaInteractionObserver(
|
const mediaInteractionHandler = initMediaInteractionObserver(
|
||||||
o.mediaInteractionCb,
|
o.mediaInteractionCb,
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ export class Replayer {
|
|||||||
triggerFocus: true,
|
triggerFocus: true,
|
||||||
UNSAFE_replayCanvas: false,
|
UNSAFE_replayCanvas: false,
|
||||||
pauseAnimation: true,
|
pauseAnimation: true,
|
||||||
|
userTriggeredOnInput: true,
|
||||||
mouseTail: defaultMouseTailConfig,
|
mouseTail: defaultMouseTailConfig,
|
||||||
};
|
};
|
||||||
this.config = Object.assign({}, defaultConfig, config);
|
this.config = Object.assign({}, defaultConfig, config);
|
||||||
@@ -501,10 +502,7 @@ export class Replayer {
|
|||||||
|
|
||||||
// events are kept sorted by timestamp, check if this is the last event
|
// events are kept sorted by timestamp, check if this is the last event
|
||||||
let last_index = this.service.state.context.events.length - 1;
|
let last_index = this.service.state.context.events.length - 1;
|
||||||
if (
|
if (event === this.service.state.context.events[last_index]) {
|
||||||
event ===
|
|
||||||
this.service.state.context.events[last_index]
|
|
||||||
) {
|
|
||||||
const finish = () => {
|
const finish = () => {
|
||||||
if (last_index < this.service.state.context.events.length - 1) {
|
if (last_index < this.service.state.context.events.length - 1) {
|
||||||
// more events have been added since the setTimeout
|
// more events have been added since the setTimeout
|
||||||
|
|||||||
@@ -218,6 +218,7 @@ export type recordOptions<T> = {
|
|||||||
packFn?: PackFn;
|
packFn?: PackFn;
|
||||||
sampling?: SamplingStrategy;
|
sampling?: SamplingStrategy;
|
||||||
recordCanvas?: boolean;
|
recordCanvas?: boolean;
|
||||||
|
userTriggeredOnInput?: boolean;
|
||||||
collectFonts?: boolean;
|
collectFonts?: boolean;
|
||||||
plugins?: RecordPlugin[];
|
plugins?: RecordPlugin[];
|
||||||
// departed, please use sampling options
|
// departed, please use sampling options
|
||||||
@@ -247,6 +248,7 @@ export type observerParam = {
|
|||||||
fontCb: fontCallback;
|
fontCb: fontCallback;
|
||||||
sampling: SamplingStrategy;
|
sampling: SamplingStrategy;
|
||||||
recordCanvas: boolean;
|
recordCanvas: boolean;
|
||||||
|
userTriggeredOnInput: boolean;
|
||||||
collectFonts: boolean;
|
collectFonts: boolean;
|
||||||
slimDOMOptions: SlimDOMOptions;
|
slimDOMOptions: SlimDOMOptions;
|
||||||
doc: Document;
|
doc: Document;
|
||||||
@@ -419,6 +421,12 @@ export type viewportResizeCallback = (d: viewportResizeDimension) => void;
|
|||||||
export type inputValue = {
|
export type inputValue = {
|
||||||
text: string;
|
text: string;
|
||||||
isChecked: boolean;
|
isChecked: boolean;
|
||||||
|
|
||||||
|
// `userTriggered` indicates if this event was triggered directly by user (userTriggered: true)
|
||||||
|
// or was triggered indirectly (userTriggered: false)
|
||||||
|
// Example of `userTriggered` in action:
|
||||||
|
// User clicks on radio element (userTriggered: true) which triggers the other radio element to change (userTriggered: false)
|
||||||
|
userTriggered?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type inputCallback = (v: inputValue & { id: number }) => void;
|
export type inputCallback = (v: inputValue & { id: number }) => void;
|
||||||
@@ -484,6 +492,7 @@ export type playerConfig = {
|
|||||||
triggerFocus: boolean;
|
triggerFocus: boolean;
|
||||||
UNSAFE_replayCanvas: boolean;
|
UNSAFE_replayCanvas: boolean;
|
||||||
pauseAnimation?: boolean;
|
pauseAnimation?: boolean;
|
||||||
|
userTriggeredOnInput: boolean;
|
||||||
mouseTail:
|
mouseTail:
|
||||||
| boolean
|
| boolean
|
||||||
| {
|
| {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,37 +1,38 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
||||||
|
<title>form fields</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
<head>
|
<body>
|
||||||
<meta charset="UTF-8">
|
<form>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<label for="text">
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
<input type="text" />
|
||||||
<title>form fields</title>
|
</label>
|
||||||
</head>
|
<label>
|
||||||
|
<input type="radio" name="toggle" value="on" />
|
||||||
<body>
|
</label>
|
||||||
<form>
|
<label>
|
||||||
<label for="text">
|
<input type="radio" name="toggle" value="off" checked />
|
||||||
<input type="text">
|
</label>
|
||||||
</label>
|
<label for="checkbox">
|
||||||
<label for="radio">
|
<input type="checkbox" />
|
||||||
<input type="radio">
|
</label>
|
||||||
</label>
|
<label for="textarea">
|
||||||
<label for="checkbox">
|
<textarea name="" id="" cols="30" rows="10"></textarea>
|
||||||
<input type="checkbox">
|
</label>
|
||||||
</label>
|
<label for="select">
|
||||||
<label for="textarea">
|
<select name="" id="">
|
||||||
<textarea name="" id="" cols="30" rows="10"></textarea>
|
<option value="1">1</option>
|
||||||
</label>
|
<option value="2">2</option>
|
||||||
<label for="select">
|
</select>
|
||||||
<select name="" id="">
|
</label>
|
||||||
<option value="1">1</option>
|
<label for="password">
|
||||||
<option value="2">2</option>
|
<input type="password" />
|
||||||
</select>
|
</label>
|
||||||
</label>
|
</form>
|
||||||
<label for="password">
|
</body>
|
||||||
<input type="password" />
|
|
||||||
</label>
|
|
||||||
</form>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ describe('record integration tests', function (this: ISuite) {
|
|||||||
maskTextSelector: ${JSON.stringify(options.maskTextSelector)},
|
maskTextSelector: ${JSON.stringify(options.maskTextSelector)},
|
||||||
maskAllInputs: ${options.maskAllInputs},
|
maskAllInputs: ${options.maskAllInputs},
|
||||||
maskInputOptions: ${JSON.stringify(options.maskAllInputs)},
|
maskInputOptions: ${JSON.stringify(options.maskAllInputs)},
|
||||||
|
userTriggeredOnInput: ${options.userTriggeredOnInput},
|
||||||
maskTextFn: ${options.maskTextFn},
|
maskTextFn: ${options.maskTextFn},
|
||||||
recordCanvas: ${options.recordCanvas},
|
recordCanvas: ${options.recordCanvas},
|
||||||
plugins: ${options.plugins}
|
plugins: ${options.plugins}
|
||||||
@@ -284,6 +285,24 @@ describe('record integration tests', function (this: ISuite) {
|
|||||||
assertSnapshot(snapshots, __filename, 'maskPassword');
|
assertSnapshot(snapshots, __filename, 'maskPassword');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should record input userTriggered values if userTriggeredOnInput is enabled', async () => {
|
||||||
|
const page: puppeteer.Page = await this.browser.newPage();
|
||||||
|
await page.goto('about:blank');
|
||||||
|
await page.setContent(
|
||||||
|
getHtml.call(this, 'form.html', { userTriggeredOnInput: true }),
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.type('input[type="text"]', 'test');
|
||||||
|
await page.click('input[type="radio"]');
|
||||||
|
await page.click('input[type="checkbox"]');
|
||||||
|
await page.type('input[type="password"]', 'password');
|
||||||
|
await page.type('textarea', 'textarea test');
|
||||||
|
await page.select('select', '1');
|
||||||
|
|
||||||
|
const snapshots = await page.evaluate('window.snapshots');
|
||||||
|
assertSnapshot(snapshots, __filename, 'userTriggered');
|
||||||
|
});
|
||||||
|
|
||||||
it('should not record blocked elements and its child nodes', async () => {
|
it('should not record blocked elements and its child nodes', async () => {
|
||||||
const page: puppeteer.Page = await this.browser.newPage();
|
const page: puppeteer.Page = await this.browser.newPage();
|
||||||
await page.goto('about:blank');
|
await page.goto('about:blank');
|
||||||
|
|||||||
Reference in New Issue
Block a user