feat: Ensure password inputs are masked when switching type (#1170)
* feat: Ensure password inputs are masked when switching type Apply formatting changes use data- attribute ref: Ensure type is always lowercased add changeset * extract into util * Apply formatting changes
This commit is contained in:
6
.changeset/new-snakes-call.md
Normal file
6
.changeset/new-snakes-call.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'rrweb-snapshot': minor
|
||||
'rrweb': minor
|
||||
---
|
||||
|
||||
feat: Ensure password inputs remain masked when switching input type
|
||||
@@ -674,8 +674,13 @@ function serializeElementNode(
|
||||
attributes.type !== 'button' &&
|
||||
value
|
||||
) {
|
||||
const type: string | null = n.hasAttribute('data-rr-is-password')
|
||||
? 'password'
|
||||
: typeof attributes.type === 'string'
|
||||
? attributes.type.toLowerCase()
|
||||
: null;
|
||||
attributes.value = maskInputValue({
|
||||
type: attributes.type,
|
||||
type,
|
||||
tagName,
|
||||
value,
|
||||
maskInputOptions,
|
||||
|
||||
@@ -162,14 +162,16 @@ export function maskInputValue({
|
||||
}: {
|
||||
maskInputOptions: MaskInputOptions;
|
||||
tagName: string;
|
||||
type: string | number | boolean | null;
|
||||
type: string | null;
|
||||
value: string | null;
|
||||
maskInputFn?: MaskInputFn;
|
||||
}): string {
|
||||
let text = value || '';
|
||||
const actualType = type && type.toLowerCase();
|
||||
|
||||
if (
|
||||
maskInputOptions[tagName.toLowerCase() as keyof MaskInputOptions] ||
|
||||
maskInputOptions[type as keyof MaskInputOptions]
|
||||
(actualType && maskInputOptions[actualType as keyof MaskInputOptions])
|
||||
) {
|
||||
if (maskInputFn) {
|
||||
text = maskInputFn(text);
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
isSerializedStylesheet,
|
||||
inDom,
|
||||
getShadowHost,
|
||||
getInputType,
|
||||
} from '../utils';
|
||||
|
||||
type DoubleLinkedListNode = {
|
||||
@@ -488,11 +489,14 @@ export default class MutationBuffer {
|
||||
const target = m.target as HTMLElement;
|
||||
let attributeName = m.attributeName as string;
|
||||
let value = (m.target as HTMLElement).getAttribute(attributeName);
|
||||
|
||||
if (attributeName === 'value') {
|
||||
const type = getInputType(target);
|
||||
|
||||
value = maskInputValue({
|
||||
maskInputOptions: this.maskInputOptions,
|
||||
tagName: (m.target as HTMLElement).tagName,
|
||||
type: (m.target as HTMLElement).getAttribute('type'),
|
||||
tagName: target.tagName,
|
||||
type,
|
||||
value,
|
||||
maskInputFn: this.maskInputFn,
|
||||
});
|
||||
@@ -527,6 +531,17 @@ export default class MutationBuffer {
|
||||
};
|
||||
this.attributes.push(item);
|
||||
}
|
||||
|
||||
// Keep this property on inputs that used to be password inputs
|
||||
// This is used to ensure we do not unmask value when using e.g. a "Show password" type button
|
||||
if (
|
||||
attributeName === 'type' &&
|
||||
target.tagName === 'INPUT' &&
|
||||
(m.oldValue || '').toLowerCase() === 'password'
|
||||
) {
|
||||
target.setAttribute('data-rr-is-password', 'true');
|
||||
}
|
||||
|
||||
if (attributeName === 'style') {
|
||||
const old = this.doc.createElement('span');
|
||||
if (m.oldValue) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
throttle,
|
||||
on,
|
||||
hookSetter,
|
||||
getInputType,
|
||||
getWindowScroll,
|
||||
getWindowHeight,
|
||||
getWindowWidth,
|
||||
@@ -338,39 +339,42 @@ function initInputObserver({
|
||||
userTriggeredOnInput,
|
||||
}: observerParam): listenerHandler {
|
||||
function eventHandler(event: Event) {
|
||||
let target = getEventTarget(event);
|
||||
let target = getEventTarget(event) as HTMLElement | null;
|
||||
const userTriggered = event.isTrusted;
|
||||
const tagName = target && target.tagName;
|
||||
|
||||
/**
|
||||
* If a site changes the value 'selected' of an option element, the value of its parent element, usually a select element, will be changed as well.
|
||||
* We can treat this change as a value change of the select element the current target belongs to.
|
||||
*/
|
||||
if (target && (target as Element).tagName === 'OPTION')
|
||||
target = (target as Element).parentElement;
|
||||
if (target && tagName === 'OPTION') {
|
||||
target = target.parentElement;
|
||||
}
|
||||
if (
|
||||
!target ||
|
||||
!(target as Element).tagName ||
|
||||
INPUT_TAGS.indexOf((target as Element).tagName) < 0 ||
|
||||
!tagName ||
|
||||
INPUT_TAGS.indexOf(tagName) < 0 ||
|
||||
isBlocked(target as Node, blockClass, blockSelector, true)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const type: string | undefined = (target as HTMLInputElement).type;
|
||||
if ((target as HTMLElement).classList.contains(ignoreClass)) {
|
||||
|
||||
if (target.classList.contains(ignoreClass)) {
|
||||
return;
|
||||
}
|
||||
let text = (target as HTMLInputElement).value;
|
||||
let isChecked = false;
|
||||
const type: Lowercase<string> = getInputType(target) || '';
|
||||
|
||||
if (type === 'radio' || type === 'checkbox') {
|
||||
isChecked = (target as HTMLInputElement).checked;
|
||||
} else if (
|
||||
maskInputOptions[
|
||||
(target as Element).tagName.toLowerCase() as keyof MaskInputOptions
|
||||
] ||
|
||||
maskInputOptions[tagName.toLowerCase() as keyof MaskInputOptions] ||
|
||||
maskInputOptions[type as keyof MaskInputOptions]
|
||||
) {
|
||||
text = maskInputValue({
|
||||
maskInputOptions,
|
||||
tagName: (target as HTMLElement).tagName,
|
||||
tagName,
|
||||
type,
|
||||
value: text,
|
||||
maskInputFn,
|
||||
|
||||
@@ -563,3 +563,17 @@ export function inDom(n: Node): boolean {
|
||||
if (!doc) return false;
|
||||
return doc.contains(n) || shadowHostInDom(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of an input element.
|
||||
* This takes care of the case where a password input is changed to a text input.
|
||||
* In this case, we continue to consider this of type password, in order to avoid leaking sensitive data
|
||||
* where passwords should be masked.
|
||||
*/
|
||||
export function getInputType(element: HTMLElement): Lowercase<string> | null {
|
||||
return element.hasAttribute('data-rr-is-password')
|
||||
? 'password'
|
||||
: element.hasAttribute('type')
|
||||
? (element.getAttribute('type')!.toLowerCase() as Lowercase<string>)
|
||||
: null;
|
||||
}
|
||||
|
||||
@@ -5102,6 +5102,451 @@ exports[`record integration tests should handle recursive console messages 1`] =
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`record integration tests should mask password value attribute with maskInputOptions 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\\": {
|
||||
\\"http-equiv\\": \\"X-UA-Compatible\\",
|
||||
\\"content\\": \\"IE=edge\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 8
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 9
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"meta\\",
|
||||
\\"attributes\\": {
|
||||
\\"name\\": \\"viewport\\",
|
||||
\\"content\\": \\"width=device-width, initial-scale=1.0\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 10
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 11
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"title\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"Document\\",
|
||||
\\"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\\": \\"input\\",
|
||||
\\"attributes\\": {
|
||||
\\"type\\": \\"password\\",
|
||||
\\"id\\": \\"password\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 18
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 19
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"button\\",
|
||||
\\"attributes\\": {
|
||||
\\"type\\": \\"button\\",
|
||||
\\"id\\": \\"show-password\\"
|
||||
},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"Toggle show password\\",
|
||||
\\"id\\": 21
|
||||
}
|
||||
],
|
||||
\\"id\\": 20
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 22
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"script\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
|
||||
\\"id\\": 24
|
||||
}
|
||||
],
|
||||
\\"id\\": 23
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\",
|
||||
\\"id\\": 25
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"script\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
|
||||
\\"id\\": 27
|
||||
}
|
||||
],
|
||||
\\"id\\": 26
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\",
|
||||
\\"id\\": 28
|
||||
}
|
||||
],
|
||||
\\"id\\": 16
|
||||
}
|
||||
],
|
||||
\\"id\\": 3
|
||||
}
|
||||
],
|
||||
\\"id\\": 1
|
||||
},
|
||||
\\"initialOffset\\": {
|
||||
\\"left\\": 0,
|
||||
\\"top\\": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 5,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"*\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"**\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"***\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"****\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"*****\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"******\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 1,
|
||||
\\"id\\": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 6,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 5,
|
||||
\\"id\\": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 0,
|
||||
\\"id\\": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 2,
|
||||
\\"id\\": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 18,
|
||||
\\"attributes\\": {
|
||||
\\"type\\": \\"text\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": []
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 18,
|
||||
\\"attributes\\": {
|
||||
\\"data-rr-is-password\\": \\"true\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": []
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 6,
|
||||
\\"id\\": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 5,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"*******\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"********\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 1,
|
||||
\\"id\\": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 6,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 5,
|
||||
\\"id\\": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 0,
|
||||
\\"id\\": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 2,
|
||||
\\"id\\": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 18,
|
||||
\\"attributes\\": {
|
||||
\\"type\\": \\"password\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": []
|
||||
}
|
||||
}
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`record integration tests should mask texts 1`] = `
|
||||
"[
|
||||
{
|
||||
@@ -5658,367 +6103,6 @@ exports[`record integration tests should mask texts using maskTextFn 1`] = `
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`record integration tests should mask value attribute with maskInputOptions 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\\": {
|
||||
\\"http-equiv\\": \\"X-UA-Compatible\\",
|
||||
\\"content\\": \\"IE=edge\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 8
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 9
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"meta\\",
|
||||
\\"attributes\\": {
|
||||
\\"name\\": \\"viewport\\",
|
||||
\\"content\\": \\"width=device-width, initial-scale=1.0\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 10
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 11
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"title\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"Document\\",
|
||||
\\"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\\": \\"input\\",
|
||||
\\"attributes\\": {
|
||||
\\"type\\": \\"password\\",
|
||||
\\"id\\": \\"password\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 18
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 19
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"script\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
|
||||
\\"id\\": 21
|
||||
}
|
||||
],
|
||||
\\"id\\": 20
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\",
|
||||
\\"id\\": 22
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"script\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
|
||||
\\"id\\": 24
|
||||
}
|
||||
],
|
||||
\\"id\\": 23
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\\\n\\\\n\\",
|
||||
\\"id\\": 25
|
||||
}
|
||||
],
|
||||
\\"id\\": 16
|
||||
}
|
||||
],
|
||||
\\"id\\": 3
|
||||
}
|
||||
],
|
||||
\\"id\\": 1
|
||||
},
|
||||
\\"initialOffset\\": {
|
||||
\\"left\\": 0,
|
||||
\\"top\\": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 5,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"*\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 18,
|
||||
\\"attributes\\": {
|
||||
\\"value\\": \\"*\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": []
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"**\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 18,
|
||||
\\"attributes\\": {
|
||||
\\"value\\": \\"**\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": []
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"***\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 18,
|
||||
\\"attributes\\": {
|
||||
\\"value\\": \\"***\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": []
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"****\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 18,
|
||||
\\"attributes\\": {
|
||||
\\"value\\": \\"****\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": []
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"*****\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 18,
|
||||
\\"attributes\\": {
|
||||
\\"value\\": \\"*****\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": []
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 5,
|
||||
\\"text\\": \\"******\\",
|
||||
\\"isChecked\\": false,
|
||||
\\"id\\": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 18,
|
||||
\\"attributes\\": {
|
||||
\\"value\\": \\"******\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": []
|
||||
}
|
||||
}
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`record integration tests should nest record iframe 1`] = `
|
||||
"[
|
||||
{
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
</head>
|
||||
<body>
|
||||
<input type="password" id="password" />
|
||||
<button type="button" id="show-password">Toggle show password</button>
|
||||
<script>
|
||||
const password = document.getElementById('password');
|
||||
password.addEventListener('keyup', (event) => {
|
||||
password.setAttribute('value', password.value);
|
||||
|
||||
document.getElementById('show-password').addEventListener('click', function() {
|
||||
password.setAttribute('type', password.getAttribute('type') === 'password' ? 'text' : 'password');
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -281,7 +281,7 @@ describe('record integration tests', function (this: ISuite) {
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
it('should mask value attribute with maskInputOptions', async () => {
|
||||
it('should mask password value attribute with maskInputOptions', async () => {
|
||||
const page: puppeteer.Page = await browser.newPage();
|
||||
await page.goto('about:blank');
|
||||
await page.setContent(
|
||||
@@ -292,7 +292,12 @@ describe('record integration tests', function (this: ISuite) {
|
||||
}),
|
||||
);
|
||||
|
||||
await page.type('input[type="password"]', 'secr3t');
|
||||
await page.type('#password', 'secr3t');
|
||||
|
||||
// Change type to text (simulate "show password")
|
||||
await page.click('#show-password');
|
||||
await page.type('#password', 'XY');
|
||||
await page.click('#show-password');
|
||||
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
|
||||
Reference in New Issue
Block a user