Add ability to mask passwords (#494)

* add ability to mask passwords

* remove duplicate mask snapshot

* make sure only one click happens at a time
This commit is contained in:
Justin Halsall
2021-06-22 13:47:30 +02:00
committed by GitHub
parent c2e042ca6c
commit fee48f084c
8 changed files with 359 additions and 556 deletions

View File

@@ -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<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 |
| 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 \*<br />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 <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 |
| 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) |
| 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<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 |
| 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 \*<br />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 <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 |
| 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) |
| 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 } |

View File

@@ -131,25 +131,25 @@ setInterval(save, 10 * 1000);
`rrweb.record(config)` 的 config 部分接受以下参数
| key | 默认值 | 功能 |
| ---------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| emit | 必填 | 获取当前录制的数据 |
| checkoutEveryNth | - | 每 N 次事件重新制作一次全量快照<br />详见[“重新制作快照”](#重新制作快照)章节 |
| checkoutEveryNms | - | 每 N 毫秒重新制作一次全量快照<br />详见[“重新制作快照”](#重新制作快照)章节 |
| blockClass | 'rr-block' | 字符串或正则表达式,可用于自定义屏蔽元素的类名,详见[“隐私”](#隐私)章节 |
| blockSelector | null | 字符串或正则表达式,可用于自定义屏蔽元素的选择器,详见[“隐私”](#隐私)章节 |
| ignoreClass | 'rr-ignore' | 字符串或正则表达式,可用于自定义忽略元素的类名,详见[“隐私”](#隐私)章节 |
| maskAllInputs | false | 将所有输入内容记录为 \* |
| maskInputOptions | {} | 选择将特定类型的输入框内容记录为 \*<br />类型详见[列表](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L72) |
| maskInputFn | - | 自定义特定类型的输入框内容记录逻辑 |
| slimDOMOptions | {} | 去除 DOM 中不必要的部分 <br />类型详见[列表](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L91) |
| inlineStylesheet | true | 是否将样式表内联 |
| hooks | {} | 各类事件的回调<br />类型详见[列表](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 次事件重新制作一次全量快照<br />详见[“重新制作快照”](#重新制作快照)章节 |
| checkoutEveryNms | - | 每 N 毫秒重新制作一次全量快照<br />详见[“重新制作快照”](#重新制作快照)章节 |
| blockClass | 'rr-block' | 字符串或正则表达式,可用于自定义屏蔽元素的类名,详见[“隐私”](#隐私)章节 |
| blockSelector | null | 字符串或正则表达式,可用于自定义屏蔽元素的选择器,详见[“隐私”](#隐私)章节 |
| ignoreClass | 'rr-ignore' | 字符串或正则表达式,可用于自定义忽略元素的类名,详见[“隐私”](#隐私)章节 |
| maskAllInputs | false | 将所有输入内容记录为 \* |
| maskInputOptions | { password: true } | 选择将特定类型的输入框内容记录为 \*<br />类型详见[列表](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L72) |
| maskInputFn | - | 自定义特定类型的输入框内容记录逻辑 |
| slimDOMOptions | {} | 去除 DOM 中不必要的部分 <br />类型详见[列表](https://github.com/rrweb-io/rrweb-snapshot/blob/6728d12b3cddd96951c86d948578f99ada5749ff/src/types.ts#L91) |
| inlineStylesheet | true | 是否将样式表内联 |
| hooks | {} | 各类事件的回调<br />类型详见[列表](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"]` 类型的密码输入框默认不会录制输入事件。
#### 重新制作快照

View File

@@ -88,10 +88,11 @@ function record<T = eventWithTime>(
week: true,
textarea: true,
select: true,
password: true,
}
: _maskInputOptions !== undefined
? _maskInputOptions
: {};
: { password: true };
const slimDOMOptions: SlimDOMOptions =
_slimDOMOptions === true || _slimDOMOptions === 'all'

View File

@@ -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;

View File

@@ -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,345 +2689,6 @@ 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`] = `
"[
{
\\"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\\": \\"Log record\\",
\\"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\\": \\"script\\",
\\"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\\",
\\"payload\\": [
\\"true\\",
\\"\\"assert\\"\\"
]
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"count\\",
\\"payload\\": [
\\"\\"count\\"\\"
]
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"countReset\\",
\\"payload\\": [
\\"\\"count\\"\\"
]
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"debug\\",
\\"payload\\": [
\\"\\"debug\\"\\"
]
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"dir\\",
\\"payload\\": [
\\"\\"dir\\"\\"
]
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"dirxml\\",
\\"payload\\": [
\\"\\"dirxml\\"\\"
]
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"group\\",
\\"payload\\": []
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"groupCollapsed\\",
\\"payload\\": []
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"info\\",
\\"payload\\": [
\\"\\"info\\"\\"
]
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"log\\",
\\"payload\\": [
\\"\\"log\\"\\"
]
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"table\\",
\\"payload\\": [
\\"\\"table\\"\\"
]
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"time\\",
\\"payload\\": []
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"timeEnd\\",
\\"payload\\": []
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"timeLog\\",
\\"payload\\": []
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"trace\\",
\\"payload\\": [
\\"\\"trace\\"\\"
]
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"warn\\",
\\"payload\\": [
\\"\\"warn\\"\\"
]
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 11,
\\"level\\": \\"clear\\",
\\"payload\\": []
}
}
]"
`;
@@ -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
@@ -3957,6 +3652,94 @@ exports[`mask 1`] = `
\\"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\\": 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\\": 2,
\\"type\\": 6,
\\"id\\": 54
}
},
{
\\"type\\": 3,
\\"data\\": {
@@ -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
}
}
]"

View File

@@ -28,6 +28,9 @@
<option value="2">2</option>
</select>
</label>
<label for="password">
<input type="password" />
</label>
</form>
</body>

View File

@@ -9,7 +9,6 @@
<body>
<form>
<label for="password"> <input type="password" /> </label>
<label for="ignore text"> <input type="text" class="rr-ignore" /> </label>
</form>
</body>

View File

@@ -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');