From fee48f084c9614f88d2a5a81e598f0918bd9988e Mon Sep 17 00:00:00 2001 From: Justin Halsall Date: Tue, 22 Jun 2021 13:47:30 +0200 Subject: [PATCH] Add ability to mask passwords (#494) * add ability to mask passwords * remove duplicate mask snapshot * make sure only one click happens at a time --- guide.md | 54 +- guide.zh_CN.md | 40 +- src/record/index.ts | 3 +- src/record/observer.ts | 5 +- test/__snapshots__/integration.test.ts.snap | 994 ++++++++------------ test/html/form.html | 3 + test/html/ignore.html | 1 - test/integration.test.ts | 7 +- 8 files changed, 455 insertions(+), 652 deletions(-) diff --git a/guide.md b/guide.md index 4cd3403c29..ecb35fcd1f 100644 --- a/guide.md +++ b/guide.md @@ -135,28 +135,28 @@ setInterval(save, 10 * 1000); The parameter of `rrweb.record` accepts the following options. -| key | default | description | -| ---------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| emit | required | the callback function to get emitted events | -| checkoutEveryNth | - | take a full snapshot after every N events
refer to the [checkout](#checkout) chapter | -| checkoutEveryNms | - | take a full snapshot after every N ms
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 | -| 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 | -| blockSelector | null | Use a string to configure which selector should be blocked, 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 \* | -| maskInputOptions | {} | mask some kinds of input \*
refer to the [list](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L72) | -| maskInputFn | - | customize mask input content recording logic | -| maskTextFn | - | customize mask text content recording logic | -| slimDOMOptions | {} | remove unnecessary parts of the DOM
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 | -| hooks | {} | hooks for events
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) | -| sampling | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) | -| recordCanvas | false | whether to record the canvas element | -| 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) | +| key | default | description | +| ---------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| emit | required | the callback function to get emitted events | +| checkoutEveryNth | - | take a full snapshot after every N events
refer to the [checkout](#checkout) chapter | +| checkoutEveryNms | - | take a full snapshot after every N ms
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 | +| 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 | +| blockSelector | null | Use a string to configure which selector should be blocked, 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 \* | +| maskInputOptions | { password: true } | mask some kinds of input \*
refer to the [list](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L72) | +| maskInputFn | - | customize mask input content recording logic | +| maskTextFn | - | customize mask text content recording logic | +| slimDOMOptions | {} | remove unnecessary parts of the DOM
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 | +| hooks | {} | hooks for events
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) | +| sampling | - | refer to the [storage optimization recipe](./docs/recipes/optimize-storage.md) | +| recordCanvas | false | whether to record the canvas element | +| 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) | #### Privacy @@ -164,9 +164,9 @@ 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. -- `input[type="password"]` will be ignored as default. - Mask options to mask the content in input elements. -- A text of elements with the class name `.rr-mask` and its children will be masked. +- `input[type="password"]` will be masked by default. +- All text of elements with the class name `.rr-mask` and their children will be masked. #### Checkout @@ -294,7 +294,7 @@ The replayer accepts options as its constructor's second parameter, and it has t | showDebug | false | whether to print debug messages during replay | | blockClass | 'rr-block' | element with the class name will display as a blocked area | | liveMode | false | whether to enable live mode | -| insertStyleRules | [] | accepts multiple CSS rule string, which will be injected into the replay iframe | +| insertStyleRules | [] | accepts multiple CSS rule string, which will be injected into the replay iframe | | triggerFocus | true | whether to trigger focus during replay | | UNSAFE_replayCanvas | false | whether to replay the canvas element. **Enable this will remove the sandbox, which is unsafe.** | | mouseTail | true | whether to show mouse tail during replay. Set to false to disable mouse tail. A complete config can be found in this [type](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L407) | @@ -380,8 +380,8 @@ The rrweb-replayer also re-expose the event listener via a `component.addEventLi And there are three rrweb-replayer event will be emitted in the same way: -| Event | Description | Value | -| ---------------------- | -------------------------------- | ----------------------- | +| Event | Description | Value | +| ---------------------- | -------------------------------- | ----------- | | ui-update-current-time | current time has changed | { payload } | | ui-update-player-state | current player state has changed | { payload } | | ui-update-progress | current progress has changed | { payload } | diff --git a/guide.zh_CN.md b/guide.zh_CN.md index 9a54488d0f..e85e0a92bb 100644 --- a/guide.zh_CN.md +++ b/guide.zh_CN.md @@ -131,25 +131,25 @@ setInterval(save, 10 * 1000); `rrweb.record(config)` 的 config 部分接受以下参数 -| key | 默认值 | 功能 | -| ---------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| emit | 必填 | 获取当前录制的数据 | -| checkoutEveryNth | - | 每 N 次事件重新制作一次全量快照
详见[“重新制作快照”](#重新制作快照)章节 | -| checkoutEveryNms | - | 每 N 毫秒重新制作一次全量快照
详见[“重新制作快照”](#重新制作快照)章节 | -| blockClass | 'rr-block' | 字符串或正则表达式,可用于自定义屏蔽元素的类名,详见[“隐私”](#隐私)章节 | -| blockSelector | null | 字符串或正则表达式,可用于自定义屏蔽元素的选择器,详见[“隐私”](#隐私)章节 | -| ignoreClass | 'rr-ignore' | 字符串或正则表达式,可用于自定义忽略元素的类名,详见[“隐私”](#隐私)章节 | -| maskAllInputs | false | 将所有输入内容记录为 \* | -| maskInputOptions | {} | 选择将特定类型的输入框内容记录为 \*
类型详见[列表](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L72) | -| maskInputFn | - | 自定义特定类型的输入框内容记录逻辑 | -| slimDOMOptions | {} | 去除 DOM 中不必要的部分
类型详见[列表](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L91) | -| inlineStylesheet | true | 是否将样式表内联 | -| hooks | {} | 各类事件的回调
类型详见[列表](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) | -| packFn | - | 数据压缩函数,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | -| sampling | - | 数据抽样策略,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | -| recordCanvas | false | 是否记录 canvas 内容 | -| collectFonts | false | 是否记录页面中的字体文件 | -| recordLog | false | 是否记录 console 输出,详见[console 录制和播放](./docs/recipes/console.zh_CN.md) | +| key | 默认值 | 功能 | +| ---------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| emit | 必填 | 获取当前录制的数据 | +| checkoutEveryNth | - | 每 N 次事件重新制作一次全量快照
详见[“重新制作快照”](#重新制作快照)章节 | +| checkoutEveryNms | - | 每 N 毫秒重新制作一次全量快照
详见[“重新制作快照”](#重新制作快照)章节 | +| blockClass | 'rr-block' | 字符串或正则表达式,可用于自定义屏蔽元素的类名,详见[“隐私”](#隐私)章节 | +| blockSelector | null | 字符串或正则表达式,可用于自定义屏蔽元素的选择器,详见[“隐私”](#隐私)章节 | +| ignoreClass | 'rr-ignore' | 字符串或正则表达式,可用于自定义忽略元素的类名,详见[“隐私”](#隐私)章节 | +| maskAllInputs | false | 将所有输入内容记录为 \* | +| maskInputOptions | { password: true } | 选择将特定类型的输入框内容记录为 \*
类型详见[列表](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L72) | +| maskInputFn | - | 自定义特定类型的输入框内容记录逻辑 | +| slimDOMOptions | {} | 去除 DOM 中不必要的部分
类型详见[列表](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L91) | +| inlineStylesheet | true | 是否将样式表内联 | +| hooks | {} | 各类事件的回调
类型详见[列表](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) | +| packFn | - | 数据压缩函数,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | +| sampling | - | 数据抽样策略,详见[优化存储策略](./docs/recipes/optimize-storage.zh_CN.md) | +| recordCanvas | false | 是否记录 canvas 内容 | +| collectFonts | false | 是否记录页面中的字体文件 | +| recordLog | false | 是否记录 console 输出,详见[console 录制和播放](./docs/recipes/console.zh_CN.md) | #### 隐私 @@ -157,8 +157,8 @@ setInterval(save, 10 * 1000); - 在 HTML 元素中添加类名 `.rr-block` 将会避免该元素及其子元素被录制,回放时取而代之的是一个同等宽高的占位元素。 - 在 HTML 元素中添加类名 `.rr-ignore` 将会避免录制该元素的输入事件。 -- `input[type="password"]` 类型的密码输入框默认不会录制输入事件。 - 配置中还有更为丰富的隐私保护选项。 +- `input[type="password"]` 类型的密码输入框默认不会录制输入事件。 #### 重新制作快照 diff --git a/src/record/index.ts b/src/record/index.ts index 4738a14496..68f2625c23 100644 --- a/src/record/index.ts +++ b/src/record/index.ts @@ -88,10 +88,11 @@ function record( week: true, textarea: true, select: true, + password: true, } : _maskInputOptions !== undefined ? _maskInputOptions - : {}; + : { password: true }; const slimDOMOptions: SlimDOMOptions = _slimDOMOptions === true || _slimDOMOptions === 'all' diff --git a/src/record/observer.ts b/src/record/observer.ts index 7ffffd4859..30db7f836e 100644 --- a/src/record/observer.ts +++ b/src/record/observer.ts @@ -359,10 +359,7 @@ function initInputObserver( return; } const type: string | undefined = (target as HTMLInputElement).type; - if ( - type === 'password' || - (target as HTMLElement).classList.contains(ignoreClass) - ) { + if ((target as HTMLElement).classList.contains(ignoreClass)) { return; } let text = (target as HTMLInputElement).value; diff --git a/test/__snapshots__/integration.test.ts.snap b/test/__snapshots__/integration.test.ts.snap index e2f17250a2..e74967e4fc 100644 --- a/test/__snapshots__/integration.test.ts.snap +++ b/test/__snapshots__/integration.test.ts.snap @@ -1229,8 +1229,42 @@ exports[`form 1`] = ` }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", \\"id\\": 51 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"password\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 53 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"password\\" + }, + \\"childNodes\\": [], + \\"id\\": 54 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 55 + } + ], + \\"id\\": 52 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 56 } ], \\"id\\": 18 @@ -1238,7 +1272,7 @@ exports[`form 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"\\\\n\\\\n \\", - \\"id\\": 52 + \\"id\\": 57 }, { \\"type\\": 2, @@ -1248,15 +1282,15 @@ exports[`form 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 54 + \\"id\\": 59 } ], - \\"id\\": 53 + \\"id\\": 58 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\\\n\\", - \\"id\\": 55 + \\"id\\": 60 } ], \\"id\\": 16 @@ -2576,7 +2610,7 @@ exports[`ignore 1`] = ` \\"type\\": 2, \\"tagName\\": \\"label\\", \\"attributes\\": { - \\"for\\": \\"password\\" + \\"for\\": \\"ignore text\\" }, \\"childNodes\\": [ { @@ -2588,7 +2622,8 @@ exports[`ignore 1`] = ` \\"type\\": 2, \\"tagName\\": \\"input\\", \\"attributes\\": { - \\"type\\": \\"password\\" + \\"type\\": \\"text\\", + \\"class\\": \\"rr-ignore\\" }, \\"childNodes\\": [], \\"id\\": 22 @@ -2601,45 +2636,10 @@ exports[`ignore 1`] = ` ], \\"id\\": 20 }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", - \\"id\\": 24 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"label\\", - \\"attributes\\": { - \\"for\\": \\"ignore text\\" - }, - \\"childNodes\\": [ - { - \\"type\\": 3, - \\"textContent\\": \\" \\", - \\"id\\": 26 - }, - { - \\"type\\": 2, - \\"tagName\\": \\"input\\", - \\"attributes\\": { - \\"type\\": \\"text\\", - \\"class\\": \\"rr-ignore\\" - }, - \\"childNodes\\": [], - \\"id\\": 27 - }, - { - \\"type\\": 3, - \\"textContent\\": \\" \\", - \\"id\\": 28 - } - ], - \\"id\\": 25 - }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\", - \\"id\\": 29 + \\"id\\": 24 } ], \\"id\\": 18 @@ -2647,7 +2647,7 @@ exports[`ignore 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\", - \\"id\\": 30 + \\"id\\": 25 }, { \\"type\\": 2, @@ -2657,15 +2657,15 @@ exports[`ignore 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 32 + \\"id\\": 27 } ], - \\"id\\": 31 + \\"id\\": 26 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", - \\"id\\": 33 + \\"id\\": 28 } ], \\"id\\": 16 @@ -2689,27 +2689,11 @@ exports[`ignore 1`] = ` \\"type\\": 5, \\"id\\": 22 } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 6, - \\"id\\": 22 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 5, - \\"id\\": 27 - } } ]" `; -exports[`log`] = ` +exports[`log 1`] = ` "[ { \\"type\\": 0, @@ -2875,9 +2859,12 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"assert\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:2:37\\" + ], \\"payload\\": [ \\"true\\", - \\"\\"assert\\"\\" + \\"\\\\\\"assert\\\\\\"\\" ] } }, @@ -2886,8 +2873,11 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"count\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:3:37\\" + ], \\"payload\\": [ - \\"\\"count\\"\\" + \\"\\\\\\"count\\\\\\"\\" ] } }, @@ -2896,8 +2886,11 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"countReset\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:4:37\\" + ], \\"payload\\": [ - \\"\\"count\\"\\" + \\"\\\\\\"count\\\\\\"\\" ] } }, @@ -2906,8 +2899,11 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"debug\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:5:37\\" + ], \\"payload\\": [ - \\"\\"debug\\"\\" + \\"\\\\\\"debug\\\\\\"\\" ] } }, @@ -2916,8 +2912,11 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"dir\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:6:37\\" + ], \\"payload\\": [ - \\"\\"dir\\"\\" + \\"\\\\\\"dir\\\\\\"\\" ] } }, @@ -2926,8 +2925,11 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"dirxml\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:7:37\\" + ], \\"payload\\": [ - \\"\\"dirxml\\"\\" + \\"\\\\\\"dirxml\\\\\\"\\" ] } }, @@ -2936,6 +2938,9 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"group\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:8:37\\" + ], \\"payload\\": [] } }, @@ -2944,6 +2949,9 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"groupCollapsed\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:9:37\\" + ], \\"payload\\": [] } }, @@ -2952,8 +2960,11 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"info\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:10:37\\" + ], \\"payload\\": [ - \\"\\"info\\"\\" + \\"\\\\\\"info\\\\\\"\\" ] } }, @@ -2962,8 +2973,11 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"log\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:11:37\\" + ], \\"payload\\": [ - \\"\\"log\\"\\" + \\"\\\\\\"log\\\\\\"\\" ] } }, @@ -2972,8 +2986,11 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"table\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:12:37\\" + ], \\"payload\\": [ - \\"\\"table\\"\\" + \\"\\\\\\"table\\\\\\"\\" ] } }, @@ -2982,6 +2999,9 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"time\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:13:37\\" + ], \\"payload\\": [] } }, @@ -2990,6 +3010,9 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"timeEnd\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:14:37\\" + ], \\"payload\\": [] } }, @@ -2998,6 +3021,9 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"timeLog\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:15:37\\" + ], \\"payload\\": [] } }, @@ -3006,8 +3032,11 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"trace\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:16:37\\" + ], \\"payload\\": [ - \\"\\"trace\\"\\" + \\"\\\\\\"trace\\\\\\"\\" ] } }, @@ -3016,8 +3045,11 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"warn\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:17:37\\" + ], \\"payload\\": [ - \\"\\"warn\\"\\" + \\"\\\\\\"warn\\\\\\"\\" ] } }, @@ -3026,13 +3058,16 @@ exports[`log`] = ` \\"data\\": { \\"source\\": 11, \\"level\\": \\"clear\\", + \\"trace\\": [ + \\"__puppeteer_evaluation_script__:18:37\\" + ], \\"payload\\": [] } } ]" `; -exports[`log 1`] = ` +exports[`mask 1`] = ` "[ { \\"type\\": 0, @@ -3077,7 +3112,7 @@ exports[`log 1`] = ` \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", \\"id\\": 5 }, { @@ -3091,7 +3126,7 @@ exports[`log 1`] = ` }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", \\"id\\": 7 }, { @@ -3106,7 +3141,7 @@ exports[`log 1`] = ` }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", \\"id\\": 9 }, { @@ -3121,7 +3156,7 @@ exports[`log 1`] = ` }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", \\"id\\": 11 }, { @@ -3131,7 +3166,7 @@ exports[`log 1`] = ` \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\"Log record\\", + \\"textContent\\": \\"form fields\\", \\"id\\": 13 } ], @@ -3139,7 +3174,7 @@ exports[`log 1`] = ` }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n\\", \\"id\\": 14 } ], @@ -3147,7 +3182,7 @@ exports[`log 1`] = ` }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n\\\\n\\", \\"id\\": 15 }, { @@ -3157,391 +3192,17 @@ exports[`log 1`] = ` \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", \\"id\\": 17 }, { \\"type\\": 2, - \\"tagName\\": \\"script\\", + \\"tagName\\": \\"form\\", \\"attributes\\": {}, \\"childNodes\\": [ { \\"type\\": 3, - \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 19 - } - ], - \\"id\\": 18 - }, - { - \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\", - \\"id\\": 20 - } - ], - \\"id\\": 16 - } - ], - \\"id\\": 3 - } - ], - \\"id\\": 1 - }, - \\"initialOffset\\": { - \\"left\\": 0, - \\"top\\": 0 - } - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"assert\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:2:37\\" - ], - \\"payload\\": [ - \\"true\\", - \\"\\\\\\"assert\\\\\\"\\" - ] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"count\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:3:37\\" - ], - \\"payload\\": [ - \\"\\\\\\"count\\\\\\"\\" - ] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"countReset\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:4:37\\" - ], - \\"payload\\": [ - \\"\\\\\\"count\\\\\\"\\" - ] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"debug\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:5:37\\" - ], - \\"payload\\": [ - \\"\\\\\\"debug\\\\\\"\\" - ] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"dir\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:6:37\\" - ], - \\"payload\\": [ - \\"\\\\\\"dir\\\\\\"\\" - ] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"dirxml\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:7:37\\" - ], - \\"payload\\": [ - \\"\\\\\\"dirxml\\\\\\"\\" - ] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"group\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:8:37\\" - ], - \\"payload\\": [] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"groupCollapsed\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:9:37\\" - ], - \\"payload\\": [] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"info\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:10:37\\" - ], - \\"payload\\": [ - \\"\\\\\\"info\\\\\\"\\" - ] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"log\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:11:37\\" - ], - \\"payload\\": [ - \\"\\\\\\"log\\\\\\"\\" - ] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"table\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:12:37\\" - ], - \\"payload\\": [ - \\"\\\\\\"table\\\\\\"\\" - ] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"time\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:13:37\\" - ], - \\"payload\\": [] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"timeEnd\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:14:37\\" - ], - \\"payload\\": [] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"timeLog\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:15:37\\" - ], - \\"payload\\": [] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"trace\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:16:37\\" - ], - \\"payload\\": [ - \\"\\\\\\"trace\\\\\\"\\" - ] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"warn\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:17:37\\" - ], - \\"payload\\": [ - \\"\\\\\\"warn\\\\\\"\\" - ] - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 11, - \\"level\\": \\"clear\\", - \\"trace\\": [ - \\"__puppeteer_evaluation_script__:18:37\\" - ], - \\"payload\\": [] - } - } -]" -`; - -exports[`mask 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 \\", + \\"textContent\\": \\"\\\\n \\", \\"id\\": 19 }, { @@ -3763,8 +3424,42 @@ exports[`mask 1`] = ` }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", \\"id\\": 51 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"password\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 53 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"password\\" + }, + \\"childNodes\\": [], + \\"id\\": 54 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 55 + } + ], + \\"id\\": 52 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 56 } ], \\"id\\": 18 @@ -3772,7 +3467,7 @@ exports[`mask 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"\\\\n\\\\n \\", - \\"id\\": 52 + \\"id\\": 57 }, { \\"type\\": 2, @@ -3782,15 +3477,15 @@ exports[`mask 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 54 + \\"id\\": 59 } ], - \\"id\\": 53 + \\"id\\": 58 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\\\n\\", - \\"id\\": 55 + \\"id\\": 60 } ], \\"id\\": 16 @@ -3895,58 +3590,146 @@ exports[`mask 1`] = ` \\"type\\": 3, \\"data\\": { \\"source\\": 5, - \\"text\\": \\"on\\", - \\"isChecked\\": true, - \\"id\\": 27 + \\"text\\": \\"on\\", + \\"isChecked\\": true, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 1, + \\"id\\": 32 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 27 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 32 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 0, + \\"id\\": 32 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 2, + \\"id\\": 32 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"on\\", + \\"isChecked\\": true, + \\"id\\": 32 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 32 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"**\\", + \\"isChecked\\": false, + \\"id\\": 54 } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 2, - \\"type\\": 1, - \\"id\\": 32 + \\"source\\": 5, + \\"text\\": \\"***\\", + \\"isChecked\\": false, + \\"id\\": 54 } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 2, - \\"type\\": 6, - \\"id\\": 27 + \\"source\\": 5, + \\"text\\": \\"****\\", + \\"isChecked\\": false, + \\"id\\": 54 } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 2, - \\"type\\": 5, - \\"id\\": 32 + \\"source\\": 5, + \\"text\\": \\"*****\\", + \\"isChecked\\": false, + \\"id\\": 54 } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 2, - \\"type\\": 0, - \\"id\\": 32 + \\"source\\": 5, + \\"text\\": \\"******\\", + \\"isChecked\\": false, + \\"id\\": 54 } }, { \\"type\\": 3, \\"data\\": { - \\"source\\": 2, - \\"type\\": 2, - \\"id\\": 32 + \\"source\\": 5, + \\"text\\": \\"*******\\", + \\"isChecked\\": false, + \\"id\\": 54 } }, { \\"type\\": 3, \\"data\\": { \\"source\\": 5, - \\"text\\": \\"on\\", - \\"isChecked\\": true, - \\"id\\": 32 + \\"text\\": \\"********\\", + \\"isChecked\\": false, + \\"id\\": 54 } }, { @@ -3954,7 +3737,7 @@ exports[`mask 1`] = ` \\"data\\": { \\"source\\": 2, \\"type\\": 6, - \\"id\\": 32 + \\"id\\": 54 } }, { @@ -5191,8 +4974,42 @@ exports[`maskInputOptions 1`] = ` }, { \\"type\\": 3, - \\"textContent\\": \\"\\\\n \\", + \\"textContent\\": \\"\\\\n \\", \\"id\\": 51 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"label\\", + \\"attributes\\": { + \\"for\\": \\"password\\" + }, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 53 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"input\\", + \\"attributes\\": { + \\"type\\": \\"password\\" + }, + \\"childNodes\\": [], + \\"id\\": 54 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 55 + } + ], + \\"id\\": 52 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"id\\": 56 } ], \\"id\\": 18 @@ -5200,7 +5017,7 @@ exports[`maskInputOptions 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"\\\\n\\\\n \\", - \\"id\\": 52 + \\"id\\": 57 }, { \\"type\\": 2, @@ -5210,15 +5027,15 @@ exports[`maskInputOptions 1`] = ` { \\"type\\": 3, \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", - \\"id\\": 54 + \\"id\\": 59 } ], - \\"id\\": 53 + \\"id\\": 58 }, { \\"type\\": 3, \\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\\\n\\", - \\"id\\": 55 + \\"id\\": 60 } ], \\"id\\": 16 @@ -5510,6 +5327,94 @@ exports[`maskInputOptions 1`] = ` \\"id\\": 37 } }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 6, + \\"id\\": 37 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 2, + \\"type\\": 5, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"**\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"***\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"****\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*****\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"******\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"*******\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 5, + \\"text\\": \\"********\\", + \\"isChecked\\": false, + \\"id\\": 54 + } + }, { \\"type\\": 3, \\"data\\": { @@ -7400,14 +7305,6 @@ exports[`select2 1`] = ` ] } }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 0, - \\"id\\": 70 - } - }, { \\"type\\": 3, \\"data\\": { @@ -7426,107 +7323,12 @@ exports[`select2 1`] = ` \\"id\\": 35 } }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 1, - \\"id\\": 70 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 6, - \\"id\\": 42 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 2, - \\"type\\": 5, - \\"id\\": 35 - } - }, - { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 70, - \\"attributes\\": { - \\"style\\": \\"display: none;\\" - } - }, - { - \\"id\\": 36, - \\"attributes\\": { - \\"id\\": null, - \\"style\\": \\"left: Npx; width: Npx; top: Npx; bottom: auto; display: none;\\" - } - }, - { - \\"id\\": 25, - \\"attributes\\": { - \\"class\\": \\"select2-container select2-container-active\\" - } - }, - { - \\"id\\": 35, - \\"attributes\\": { - \\"disabled\\": null - } - }, - { - \\"id\\": 42, - \\"attributes\\": { - \\"class\\": \\"select2-input\\" - } - } - ], - \\"removes\\": [ - { - \\"parentId\\": 18, - \\"id\\": 70 - }, - { - \\"parentId\\": 45, - \\"id\\": 72 - }, - { - \\"parentId\\": 45, - \\"id\\": 67 - } - ], - \\"adds\\": [ - { - \\"parentId\\": 18, - \\"nextId\\": 36, - \\"node\\": { - \\"type\\": 2, - \\"tagName\\": \\"div\\", - \\"attributes\\": { - \\"id\\": \\"select2-drop-mask\\", - \\"class\\": \\"select2-drop-mask\\", - \\"style\\": \\"display: none;\\" - }, - \\"childNodes\\": [], - \\"id\\": 70 - } - } - ] - } - }, { \\"type\\": 3, \\"data\\": { \\"source\\": 2, \\"type\\": 0, - \\"id\\": 26 + \\"id\\": 70 } } ]" diff --git a/test/html/form.html b/test/html/form.html index 14120fd30e..9d4a0c7116 100644 --- a/test/html/form.html +++ b/test/html/form.html @@ -28,6 +28,9 @@ + diff --git a/test/html/ignore.html b/test/html/ignore.html index 91e0652d9e..f46c2efd00 100644 --- a/test/html/ignore.html +++ b/test/html/ignore.html @@ -9,7 +9,6 @@
-
diff --git a/test/integration.test.ts b/test/integration.test.ts index 6d5234e7b3..01bef4297d 100644 --- a/test/integration.test.ts +++ b/test/integration.test.ts @@ -177,8 +177,7 @@ describe('record integration tests', function (this: ISuite) { }); // toggle the select box - await page.click('.select2-container'); - await page.click('.select2-container'); + await page.click('.select2-container', { clickCount: 2, delay: 100 }); const snapshots = await page.evaluate('window.snapshots'); assertSnapshot(snapshots, __filename, 'select2'); @@ -215,7 +214,6 @@ describe('record integration tests', function (this: ISuite) { await page.goto('about:blank'); await page.setContent(getHtml.call(this, 'ignore.html')); - await page.type('input[type="password"]', 'password'); await page.type('.rr-ignore', 'secret'); const snapshots = await page.evaluate('window.snapshots'); @@ -232,6 +230,7 @@ describe('record integration tests', function (this: ISuite) { 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'); @@ -247,6 +246,7 @@ describe('record integration tests', function (this: ISuite) { maskInputOptions: { text: false, textarea: false, + password: true, }, }), ); @@ -255,6 +255,7 @@ describe('record integration tests', function (this: ISuite) { await page.click('input[type="radio"]'); await page.click('input[type="checkbox"]'); await page.type('textarea', 'textarea test'); + await page.type('input[type="password"]', 'password'); await page.select('select', '1'); const snapshots = await page.evaluate('window.snapshots');