Fix serialization and mutation of <textarea> elements (#1351)
* Fix serialization and mutation of <textarea> elements taking account the duality that the value can be set in either the child node, or in the value _parameter_ (not attribute)
* Backwards compatibility: Bug fix and regression test for #112
- this is to fix up 'historical' recordings, as duplicate textarea content should no longer be being created at record time
- new test shows what the snapshot generated by previous versions of rrweb used to look like, hence 'bad'
- original 0efe23f04a fix either didn't work or no longer works due to childNodes being appended subsequent to this part of the code
- during review, we also verified that the `_cssText` case should still be handled okay, as there's currently no scenario where csstext is present with css child nodes of a <style>
* Masking: Fix that textarea values were being missed by the masking system if the value was recorded as a child node
- I didn't notice that form.html was used in other tests, so lucky that I noticed that those tests also should have the 'pre value' masked out
* Simplify by always storing the textarea value in the `.value` attribute (from it's DOM property) and not as a childNode. It should still be rebuilt as a childNode rather than a property
---------
Authored-by: eoghanmurray <eoghan@getthere.ie>
This commit is contained in:
@@ -247,6 +247,7 @@ exports[`integration tests [html file]: form-fields.html 1`] = `
|
||||
</label>
|
||||
<label for=\\"textarea\\">
|
||||
<textarea name=\\"\\" id=\\"\\" cols=\\"30\\" rows=\\"10\\">1234</textarea>
|
||||
<textarea name=\\"\\" id=\\"\\" cols=\\"30\\" rows=\\"10\\">1234</textarea>
|
||||
</label>
|
||||
<label for=\\"select\\">
|
||||
<select name=\\"\\" id=\\"\\" value=\\"2\\">
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
</label>
|
||||
<label for="textarea">
|
||||
<textarea name="" id="" cols="30" rows="10"></textarea>
|
||||
<textarea name="" id="" cols="30" rows="10">-1</textarea>
|
||||
</label>
|
||||
<label for="select">
|
||||
<select name="" id="">
|
||||
@@ -36,7 +37,8 @@
|
||||
document.querySelector('input[type="text"]').value = '1';
|
||||
document.querySelector('input[type="radio"]').checked = true;
|
||||
document.querySelector('input[type="checkbox"]').checked = true;
|
||||
document.querySelector('textarea').value = '1234';
|
||||
document.querySelector('textarea:empty').value = '1234';
|
||||
document.querySelector('textarea:not(:empty)').value = '1234';
|
||||
document.querySelector('select').value = '2';
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
serializeNodeWithId,
|
||||
_isBlockedElement,
|
||||
} from '../src/snapshot';
|
||||
import { serializedNodeWithId } from '../src/types';
|
||||
import { serializedNodeWithId, elementNode } from '../src/types';
|
||||
import { Mirror } from '../src/utils';
|
||||
|
||||
describe('absolute url to stylesheet', () => {
|
||||
@@ -218,3 +218,42 @@ describe('scrollTop/scrollLeft', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('form', () => {
|
||||
const serializeNode = (node: Node): serializedNodeWithId | null => {
|
||||
return serializeNodeWithId(node, {
|
||||
doc: document,
|
||||
mirror: new Mirror(),
|
||||
blockClass: 'blockblock',
|
||||
blockSelector: null,
|
||||
maskTextClass: 'maskmask',
|
||||
maskTextSelector: null,
|
||||
skipChild: false,
|
||||
inlineStylesheet: true,
|
||||
maskTextFn: undefined,
|
||||
maskInputFn: undefined,
|
||||
slimDOMOptions: {},
|
||||
newlyAddedElement: false,
|
||||
});
|
||||
};
|
||||
|
||||
const render = (html: string): HTMLTextAreaElement => {
|
||||
document.write(html);
|
||||
return document.querySelector('textarea')!;
|
||||
};
|
||||
|
||||
it('should record textarea values once', () => {
|
||||
const el = render(`<textarea>Lorem ipsum</textarea>`);
|
||||
const sel = serializeNode(el) as elementNode;
|
||||
|
||||
// we serialize according to where the DOM stores the value, not how
|
||||
// the HTML stores it (this is so that maskInputValue can work over
|
||||
// inputs/textareas/selects in a uniform way)
|
||||
expect(sel).toMatchObject({
|
||||
attributes: {
|
||||
value: 'Lorem ipsum',
|
||||
},
|
||||
});
|
||||
expect(sel?.childNodes).toEqual([]); // shouldn't be stored in childNodes while in transit
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user