Cross origin iframe support (#1035)
* Add `recordCrossOriginIframe` setting * Set up messaging between iframes * should emit full snapshot event from iframe as mutation event * this.mirror was dropped on attachIframe * should use unique id for child of iframe * Cross origin iframe recording in `yarn live-stream` * Root iframe check thats supported by firefox * Live stream: Inject script in all frames * Record same origin and cross origin iframes differently * Should map Input events correctly * Turn on other tests * Fix compatibility with newer puppeteer * puppeteer vs 12 seems stable without to many changes needed * normalize port numbers in snapshots * Handle scroll and ViewportResize events in cross origin iframe * Correctly map cross origin mutations * Map selection events for cross origin iframes * Map canvas mutations for cross origin iframes * Update snapshot to include canvas events * Skip all meta events * Support custom events as best we can in cross origin iframes * Use earliest version of puppeteer that works with cross origin live-stream * Map mouse/touch interaction events * Update snapshots for correctly mapped click events * Tweak tests for new puppeteer version * Map MediaInteraction correctly for cross origin iframes * Make tests consistent between high and low dpi devices * Make test less flaky * Make test less flaky * Make test less flaky * Make test less flaky * Add support for styles in cross origin iframes * Map traditional stylesheet mutations on cross origin iframes * Add todo * Add iframe mirror * Get iframe manager to use iframe mirrors internally * Rename `IframeMirror` to `CrossOriginIframeMirror` * Setup basic cross origin canvas webrtc streaming * Clean up removed canvas elements * reset style mirror on new full snapshot * Fix cross origin canvas webrtc streaming * Make emit optional * Run tests on github actions * Upload image artifacts from failed tests * Use newer github actions * Test: hopefully adding more wait will fix it * add extra wait * Fix image snapshot tests * Make tests run with new puppeteer version * upgrade eslint-plugin-jest * Chore: Remove travis ci as ci's running on github actions * Chore: Support recording cross origin iframe in repl * Force developers to update the cross origin iframe mapping when adding new events https://github.com/rrweb-io/rrweb/pull/1035#discussion_r1012516277 * Document cross origin iframe recording * Docs: cross origin iframes recording methods * Docs: AI translated, cross origin iframe recording * rename getParentId to getId * Migrate to @rrweb/types * Run on pull request * doc: improve Chinese doc * Rename `parentId` to `Id` Co-authored-by: Mark-Fenng <f18846188605@gmail.com>
This commit is contained in:
@@ -4014,8 +4014,27 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n\\\\n\\",
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 7
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"style\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"#b-class, #b-class-2 { height: 33px; width: 200px; }\\",
|
||||
\\"isStyle\\": true,
|
||||
\\"id\\": 9
|
||||
}
|
||||
],
|
||||
\\"id\\": 8
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n\\",
|
||||
\\"id\\": 10
|
||||
}
|
||||
],
|
||||
\\"id\\": 3
|
||||
@@ -4023,7 +4042,7 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n\\\\n\\",
|
||||
\\"id\\": 8
|
||||
\\"id\\": 11
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
@@ -4033,7 +4052,7 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 10
|
||||
\\"id\\": 13
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
@@ -4043,40 +4062,22 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
|
||||
\\"id\\": 12
|
||||
\\"id\\": 15
|
||||
}
|
||||
],
|
||||
\\"id\\": 11
|
||||
\\"id\\": 14
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 13
|
||||
\\"id\\": 16
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 14
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 15
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"h1\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n Verify that block class bugs are fixed\\\\n \\",
|
||||
\\"id\\": 17
|
||||
}
|
||||
],
|
||||
\\"id\\": 16
|
||||
\\"id\\": 17
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
@@ -4085,15 +4086,33 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"tagName\\": \\"h1\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"Verify that block class bugs are fixed\\",
|
||||
\\"id\\": 20
|
||||
}
|
||||
],
|
||||
\\"id\\": 19
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 20
|
||||
\\"id\\": 21
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 22
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 23
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
@@ -4105,7 +4124,7 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 22
|
||||
\\"id\\": 25
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
@@ -4117,7 +4136,7 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 24
|
||||
\\"id\\": 27
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
@@ -4127,91 +4146,91 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"VISIBLE\\",
|
||||
\\"id\\": 26
|
||||
\\"id\\": 29
|
||||
}
|
||||
],
|
||||
\\"id\\": 25
|
||||
\\"id\\": 28
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 27
|
||||
\\"id\\": 30
|
||||
}
|
||||
],
|
||||
\\"id\\": 23
|
||||
\\"id\\": 26
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 28
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 29
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 30
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 31
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 32
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 33
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 34
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 32
|
||||
\\"id\\": 35
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {
|
||||
\\"class\\": \\"rr-block\\",
|
||||
\\"rr_width\\": \\"1904px\\",
|
||||
\\"rr_height\\": \\"21px\\"
|
||||
\\"rr_width\\": \\"200px\\",
|
||||
\\"rr_height\\": \\"33px\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 33
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 34
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 35
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 36
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 37
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 37
|
||||
\\"id\\": 38
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 39
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 40
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 38
|
||||
\\"id\\": 41
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
@@ -4223,49 +4242,49 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"MUTATE\\",
|
||||
\\"id\\": 40
|
||||
\\"id\\": 43
|
||||
}
|
||||
],
|
||||
\\"id\\": 39
|
||||
\\"id\\": 42
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 41
|
||||
\\"id\\": 44
|
||||
}
|
||||
],
|
||||
\\"id\\": 21
|
||||
\\"id\\": 24
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 42
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 43
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 44
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 45
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 46
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 47
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 48
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 46
|
||||
\\"id\\": 49
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
@@ -4277,7 +4296,7 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 48
|
||||
\\"id\\": 51
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
@@ -4289,7 +4308,7 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 50
|
||||
\\"id\\": 53
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
@@ -4299,91 +4318,91 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"VISIBLE\\",
|
||||
\\"id\\": 52
|
||||
\\"id\\": 55
|
||||
}
|
||||
],
|
||||
\\"id\\": 51
|
||||
\\"id\\": 54
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 53
|
||||
\\"id\\": 56
|
||||
}
|
||||
],
|
||||
\\"id\\": 49
|
||||
\\"id\\": 52
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 54
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 55
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 56
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 57
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 58
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 59
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 60
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 58
|
||||
\\"id\\": 61
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {
|
||||
\\"class\\": \\"rr-block\\",
|
||||
\\"rr_width\\": \\"1904px\\",
|
||||
\\"rr_height\\": \\"21px\\"
|
||||
\\"rr_width\\": \\"200px\\",
|
||||
\\"rr_height\\": \\"33px\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 59
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 60
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 61
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 62
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 63
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 63
|
||||
\\"id\\": 64
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 65
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"br\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 66
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 64
|
||||
\\"id\\": 67
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
@@ -4395,23 +4414,23 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"MUTATE\\",
|
||||
\\"id\\": 66
|
||||
\\"id\\": 69
|
||||
}
|
||||
],
|
||||
\\"id\\": 65
|
||||
\\"id\\": 68
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 67
|
||||
\\"id\\": 70
|
||||
}
|
||||
],
|
||||
\\"id\\": 47
|
||||
\\"id\\": 50
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n\\\\n \\",
|
||||
\\"id\\": 68
|
||||
\\"id\\": 71
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
@@ -4421,18 +4440,18 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"SCRIPT_PLACEHOLDER\\",
|
||||
\\"id\\": 70
|
||||
\\"id\\": 73
|
||||
}
|
||||
],
|
||||
\\"id\\": 69
|
||||
\\"id\\": 72
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\\\n\\",
|
||||
\\"id\\": 71
|
||||
\\"id\\": 74
|
||||
}
|
||||
],
|
||||
\\"id\\": 9
|
||||
\\"id\\": 12
|
||||
}
|
||||
],
|
||||
\\"id\\": 2
|
||||
@@ -4452,7 +4471,7 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 1,
|
||||
\\"id\\": 39
|
||||
\\"id\\": 42
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -4460,7 +4479,7 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 5,
|
||||
\\"id\\": 39
|
||||
\\"id\\": 42
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -4468,7 +4487,7 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 0,
|
||||
\\"id\\": 39
|
||||
\\"id\\": 42
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -4476,7 +4495,7 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 2,
|
||||
\\"id\\": 39
|
||||
\\"id\\": 42
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -4486,7 +4505,7 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 33,
|
||||
\\"id\\": 36,
|
||||
\\"attributes\\": {
|
||||
\\"class\\": \\"notB\\"
|
||||
}
|
||||
@@ -4495,50 +4514,19 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": [
|
||||
{
|
||||
\\"parentId\\": 23,
|
||||
\\"parentId\\": 26,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 72
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 72,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 73
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 73,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"button\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 74
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 74,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"I1I2 VISIBLE\\",
|
||||
\\"id\\": 75
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 72,
|
||||
\\"nextId\\": 73,
|
||||
\\"parentId\\": 75,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
@@ -4563,71 +4551,13 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"I1I1 VISIBLE\\",
|
||||
\\"textContent\\": \\"I1I2 VISIBLE\\",
|
||||
\\"id\\": 78
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 1,
|
||||
\\"id\\": 65
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 6,
|
||||
\\"id\\": 39
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 5,
|
||||
\\"id\\": 65
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 0,
|
||||
\\"id\\": 65
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 2,
|
||||
\\"id\\": 65
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
},
|
||||
{
|
||||
\\"id\\": 59,
|
||||
\\"attributes\\": {
|
||||
\\"class\\": \\"notB\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": [
|
||||
{
|
||||
\\"parentId\\": 49,
|
||||
\\"nextId\\": null,
|
||||
\\"parentId\\": 75,
|
||||
\\"nextId\\": 76,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
@@ -4641,7 +4571,7 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"tagName\\": \\"button\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 80
|
||||
@@ -4651,25 +4581,83 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
\\"parentId\\": 80,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"button\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"I1I1 VISIBLE\\",
|
||||
\\"id\\": 81
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 1,
|
||||
\\"id\\": 68
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 6,
|
||||
\\"id\\": 42
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 5,
|
||||
\\"id\\": 68
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 0,
|
||||
\\"id\\": 68
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 2,
|
||||
\\"type\\": 2,
|
||||
\\"id\\": 68
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"parentId\\": 81,
|
||||
\\"id\\": 62,
|
||||
\\"attributes\\": {
|
||||
\\"class\\": \\"notB\\"
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": [
|
||||
{
|
||||
\\"parentId\\": 52,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"I1I2 VISIBLE\\",
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 82
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 79,
|
||||
\\"nextId\\": 80,
|
||||
\\"parentId\\": 82,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
@@ -4694,9 +4682,40 @@ exports[`record integration tests mutations should work when blocked class is un
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"I1I1 VISIBLE\\",
|
||||
\\"textContent\\": \\"I1I2 VISIBLE\\",
|
||||
\\"id\\": 85
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 82,
|
||||
\\"nextId\\": 83,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"div\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 86
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 86,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"button\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 87
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"parentId\\": 87,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"I1I1 VISIBLE\\",
|
||||
\\"id\\": 88
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1905,168 +1905,6 @@ exports[`record captures stylesheet rules 1`] = `
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`record captures stylesheets in iframes that are still loading 1`] = `
|
||||
"[
|
||||
{
|
||||
\\"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\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"head\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 4
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"body\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 6
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"input\\",
|
||||
\\"attributes\\": {
|
||||
\\"type\\": \\"text\\",
|
||||
\\"size\\": \\"40\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 7
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\\\n \\",
|
||||
\\"id\\": 8
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"iframe\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 9
|
||||
}
|
||||
],
|
||||
\\"id\\": 5
|
||||
}
|
||||
],
|
||||
\\"id\\": 3
|
||||
}
|
||||
],
|
||||
\\"id\\": 1
|
||||
},
|
||||
\\"initialOffset\\": {
|
||||
\\"left\\": 0,
|
||||
\\"top\\": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"adds\\": [
|
||||
{
|
||||
\\"parentId\\": 9,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 0,
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"html\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"head\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"link\\",
|
||||
\\"attributes\\": {
|
||||
\\"rel\\": \\"stylesheet\\",
|
||||
\\"href\\": \\"blob:null\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"rootId\\": 10,
|
||||
\\"id\\": 13
|
||||
}
|
||||
],
|
||||
\\"rootId\\": 10,
|
||||
\\"id\\": 12
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"body\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"rootId\\": 10,
|
||||
\\"id\\": 14
|
||||
}
|
||||
],
|
||||
\\"rootId\\": 10,
|
||||
\\"id\\": 11
|
||||
}
|
||||
],
|
||||
\\"compatMode\\": \\"BackCompat\\",
|
||||
\\"id\\": 10
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [],
|
||||
\\"isAttachIframe\\": true
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"adds\\": [],
|
||||
\\"removes\\": [],
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 13,
|
||||
\\"attributes\\": {
|
||||
\\"_cssText\\": \\"body { color: pink; }\\"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`record captures stylesheets in iframes with \`blob:\` url 1`] = `
|
||||
"[
|
||||
{
|
||||
@@ -2211,126 +2049,6 @@ exports[`record captures stylesheets in iframes with \`blob:\` url 1`] = `
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`record captures stylesheets that are still loading 1`] = `
|
||||
"[
|
||||
{
|
||||
\\"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\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"head\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 4
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"body\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"id\\": 6
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"input\\",
|
||||
\\"attributes\\": {
|
||||
\\"type\\": \\"text\\",
|
||||
\\"size\\": \\"40\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 7
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\\\n \\\\n \\",
|
||||
\\"id\\": 8
|
||||
}
|
||||
],
|
||||
\\"id\\": 5
|
||||
}
|
||||
],
|
||||
\\"id\\": 3
|
||||
}
|
||||
],
|
||||
\\"id\\": 1
|
||||
},
|
||||
\\"initialOffset\\": {
|
||||
\\"left\\": 0,
|
||||
\\"top\\": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": [
|
||||
{
|
||||
\\"parentId\\": 4,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"link\\",
|
||||
\\"attributes\\": {
|
||||
\\"rel\\": \\"stylesheet\\",
|
||||
\\"href\\": \\"blob:null\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 9
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"adds\\": [],
|
||||
\\"removes\\": [],
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 9,
|
||||
\\"attributes\\": {
|
||||
\\"_cssText\\": \\"body { color: pink; }\\"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`record captures stylesheets with \`blob:\` url 1`] = `
|
||||
"[
|
||||
{
|
||||
@@ -2910,6 +2628,522 @@ exports[`record is safe to checkout during async callbacks 1`] = `
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`record loading stylesheets captures stylesheets in iframes that are still loading 1`] = `
|
||||
"[
|
||||
{
|
||||
\\"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\\": \\"Hello World!\\",
|
||||
\\"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 Hello world!\\\\n \\\\n\\\\n\\",
|
||||
\\"id\\": 17
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"iframe\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 18
|
||||
}
|
||||
],
|
||||
\\"id\\": 16
|
||||
}
|
||||
],
|
||||
\\"id\\": 3
|
||||
}
|
||||
],
|
||||
\\"id\\": 1
|
||||
},
|
||||
\\"initialOffset\\": {
|
||||
\\"left\\": 0,
|
||||
\\"top\\": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"adds\\": [
|
||||
{
|
||||
\\"parentId\\": 18,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 0,
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 1,
|
||||
\\"name\\": \\"html\\",
|
||||
\\"publicId\\": \\"\\",
|
||||
\\"systemId\\": \\"\\",
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 20
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"html\\",
|
||||
\\"attributes\\": {
|
||||
\\"lang\\": \\"en\\"
|
||||
},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"head\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 23
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"meta\\",
|
||||
\\"attributes\\": {
|
||||
\\"charset\\": \\"UTF-8\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 24
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 25
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"meta\\",
|
||||
\\"attributes\\": {
|
||||
\\"http-equiv\\": \\"X-UA-Compatible\\",
|
||||
\\"content\\": \\"IE=edge\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 26
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 27
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"meta\\",
|
||||
\\"attributes\\": {
|
||||
\\"name\\": \\"viewport\\",
|
||||
\\"content\\": \\"width=device-width, initial-scale=1.0\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 28
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 29
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"title\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"Hello World!\\",
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 31
|
||||
}
|
||||
],
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 30
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 32
|
||||
}
|
||||
],
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 22
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n \\",
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 33
|
||||
},
|
||||
{
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"body\\",
|
||||
\\"attributes\\": {},
|
||||
\\"childNodes\\": [
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"textContent\\": \\"\\\\n Hello world!\\\\n \\\\n\\\\n\\",
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 35
|
||||
}
|
||||
],
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 34
|
||||
}
|
||||
],
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 21
|
||||
}
|
||||
],
|
||||
\\"id\\": 19
|
||||
}
|
||||
}
|
||||
],
|
||||
\\"removes\\": [],
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [],
|
||||
\\"isAttachIframe\\": true
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": [
|
||||
{
|
||||
\\"parentId\\": 22,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"link\\",
|
||||
\\"attributes\\": {
|
||||
\\"rel\\": \\"stylesheet\\",
|
||||
\\"href\\": \\"http://localhost:3030/html/assets/style.css\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"rootId\\": 19,
|
||||
\\"id\\": 36
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"adds\\": [],
|
||||
\\"removes\\": [],
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 36,
|
||||
\\"attributes\\": {
|
||||
\\"_cssText\\": \\"body { color: pink; }\\"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`record loading stylesheets captures stylesheets that are still loading 1`] = `
|
||||
"[
|
||||
{
|
||||
\\"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\\": \\"Hello World!\\",
|
||||
\\"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 Hello world!\\\\n \\\\n\\\\n\\",
|
||||
\\"id\\": 17
|
||||
}
|
||||
],
|
||||
\\"id\\": 16
|
||||
}
|
||||
],
|
||||
\\"id\\": 3
|
||||
}
|
||||
],
|
||||
\\"id\\": 1
|
||||
},
|
||||
\\"initialOffset\\": {
|
||||
\\"left\\": 0,
|
||||
\\"top\\": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [],
|
||||
\\"removes\\": [],
|
||||
\\"adds\\": [
|
||||
{
|
||||
\\"parentId\\": 4,
|
||||
\\"nextId\\": null,
|
||||
\\"node\\": {
|
||||
\\"type\\": 2,
|
||||
\\"tagName\\": \\"link\\",
|
||||
\\"attributes\\": {
|
||||
\\"rel\\": \\"stylesheet\\",
|
||||
\\"href\\": \\"http://localhost:3030/html/assets/style.css\\"
|
||||
},
|
||||
\\"childNodes\\": [],
|
||||
\\"id\\": 18
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
\\"type\\": 3,
|
||||
\\"data\\": {
|
||||
\\"source\\": 0,
|
||||
\\"adds\\": [],
|
||||
\\"removes\\": [],
|
||||
\\"texts\\": [],
|
||||
\\"attributes\\": [
|
||||
{
|
||||
\\"id\\": 18,
|
||||
\\"attributes\\": {
|
||||
\\"_cssText\\": \\"body { color: pink; }\\"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]"
|
||||
`;
|
||||
|
||||
exports[`record should record scroll position 1`] = `
|
||||
"[
|
||||
{
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
@@ -76,7 +76,9 @@ describe('e2e webgl', () => {
|
||||
|
||||
const hideMouseAnimation = async (p: puppeteer.Page) => {
|
||||
await p.addStyleTag({
|
||||
content: '.replayer-mouse-tail{display: none !important;}',
|
||||
content: `.replayer-mouse-tail{display: none !important;}
|
||||
html, body { margin: 0; padding: 0; }
|
||||
iframe { border: none; }`,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -90,7 +92,9 @@ describe('e2e webgl', () => {
|
||||
|
||||
await waitForRAF(page);
|
||||
|
||||
const snapshots: eventWithTime[] = await page.evaluate('window.snapshots');
|
||||
const snapshots: eventWithTime[] = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
|
||||
page = await browser.newPage();
|
||||
|
||||
@@ -108,9 +112,8 @@ describe('e2e webgl', () => {
|
||||
`);
|
||||
await waitForRAF(page);
|
||||
|
||||
const element = await page.$('iframe');
|
||||
const frameImage = await element!.screenshot();
|
||||
|
||||
const frameImage = await page!.screenshot();
|
||||
await waitForRAF(page);
|
||||
expect(frameImage).toMatchImageSnapshot();
|
||||
});
|
||||
|
||||
@@ -122,8 +125,11 @@ describe('e2e webgl', () => {
|
||||
getHtml.call(this, 'canvas-webgl-image.html', { recordCanvas: true }),
|
||||
);
|
||||
|
||||
await waitForRAF(page);
|
||||
await page.waitForTimeout(100);
|
||||
const snapshots: eventWithTime[] = await page.evaluate('window.snapshots');
|
||||
const snapshots: eventWithTime[] = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
|
||||
page = await browser.newPage();
|
||||
|
||||
@@ -143,9 +149,7 @@ describe('e2e webgl', () => {
|
||||
await page.evaluate(`replayer.play(500);`);
|
||||
await waitForRAF(page);
|
||||
|
||||
const element = await page.$('iframe');
|
||||
const frameImage = await element!.screenshot();
|
||||
|
||||
const frameImage = await page!.screenshot();
|
||||
expect(frameImage).toMatchImageSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
BIN
packages/rrweb/test/html/assets/1-minute-of-silence.mp3
Normal file
BIN
packages/rrweb/test/html/assets/1-minute-of-silence.mp3
Normal file
Binary file not shown.
3
packages/rrweb/test/html/assets/style.css
Normal file
3
packages/rrweb/test/html/assets/style.css
Normal file
@@ -0,0 +1,3 @@
|
||||
body {
|
||||
color: pink;
|
||||
}
|
||||
16
packages/rrweb/test/html/audio.html
Normal file
16
packages/rrweb/test/html/audio.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Audio</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>1 minute of silence</h1>
|
||||
<audio controls>
|
||||
<source src="assets/1-minute-of-silence.mp3" type="audio/mpeg" />
|
||||
Your browser does not support the audio element.
|
||||
</audio>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,88 +1,92 @@
|
||||
<head>
|
||||
<title>Uber Application for Codegen Testing</title>
|
||||
|
||||
<style>
|
||||
#b-class,
|
||||
#b-class-2 {
|
||||
height: 33px;
|
||||
width: 200px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
function mutate1() {
|
||||
const bClassDiv = document.getElementById("b-class");
|
||||
bClassDiv.className = "notB";
|
||||
function mutate1() {
|
||||
const bClassDiv = document.getElementById('b-class');
|
||||
bClassDiv.className = 'notB';
|
||||
|
||||
const removeBlockedButton = document.getElementById("remove");
|
||||
removeBlockedButton.remove();
|
||||
const removeBlockedButton = document.getElementById('remove');
|
||||
removeBlockedButton.remove();
|
||||
|
||||
const visibleCollection = document.getElementsByClassName("visible");
|
||||
const i1Div = document.createElement("div");
|
||||
const i1i1Div = document.createElement("div");
|
||||
const i1i2Div = document.createElement("div");
|
||||
const visibleCollection = document.getElementsByClassName('visible');
|
||||
const i1Div = document.createElement('div');
|
||||
const i1i1Div = document.createElement('div');
|
||||
const i1i2Div = document.createElement('div');
|
||||
|
||||
const i1i1Button = document.createElement("button");
|
||||
i1i1Button.innerHTML = "I1I1 VISIBLE";
|
||||
i1i1Div.appendChild(i1i1Button);
|
||||
const i1i1Button = document.createElement('button');
|
||||
i1i1Button.innerHTML = 'I1I1 VISIBLE';
|
||||
i1i1Div.appendChild(i1i1Button);
|
||||
|
||||
const i1i2Button = document.createElement("button");
|
||||
i1i2Button.innerHTML = "I1I2 VISIBLE";
|
||||
i1i2Div.appendChild(i1i2Button);
|
||||
const i1i2Button = document.createElement('button');
|
||||
i1i2Button.innerHTML = 'I1I2 VISIBLE';
|
||||
i1i2Div.appendChild(i1i2Button);
|
||||
|
||||
i1Div.appendChild(i1i1Div);
|
||||
i1Div.appendChild(i1i2Div);
|
||||
visibleCollection[0].appendChild(i1Div);
|
||||
}
|
||||
function mutate2() {
|
||||
const bClassDiv = document.getElementById("b-class-2");
|
||||
bClassDiv.className = "notB";
|
||||
i1Div.appendChild(i1i1Div);
|
||||
i1Div.appendChild(i1i2Div);
|
||||
visibleCollection[0].appendChild(i1Div);
|
||||
}
|
||||
function mutate2() {
|
||||
const bClassDiv = document.getElementById('b-class-2');
|
||||
bClassDiv.className = 'notB';
|
||||
|
||||
const removeBlockedButton = document.getElementById("remove2");
|
||||
const innerButton = document.createElement("button");
|
||||
innerButton.innerHTML = "INNER BLOCKED";
|
||||
removeBlockedButton.appendChild(innerButton)
|
||||
removeBlockedButton.remove();
|
||||
const removeBlockedButton = document.getElementById('remove2');
|
||||
const innerButton = document.createElement('button');
|
||||
innerButton.innerHTML = 'INNER BLOCKED';
|
||||
removeBlockedButton.appendChild(innerButton);
|
||||
removeBlockedButton.remove();
|
||||
|
||||
const visibleCollection = document.getElementsByClassName("visible2");
|
||||
const i1Div = document.createElement("div");
|
||||
const i1i1Div = document.createElement("div");
|
||||
const i1i2Div = document.createElement("div");
|
||||
const visibleCollection = document.getElementsByClassName('visible2');
|
||||
const i1Div = document.createElement('div');
|
||||
const i1i1Div = document.createElement('div');
|
||||
const i1i2Div = document.createElement('div');
|
||||
|
||||
const i1i1Button = document.createElement("button");
|
||||
i1i1Button.innerHTML = "I1I1 VISIBLE";
|
||||
i1i1Div.appendChild(i1i1Button);
|
||||
const i1i1Button = document.createElement('button');
|
||||
i1i1Button.innerHTML = 'I1I1 VISIBLE';
|
||||
i1i1Div.appendChild(i1i1Button);
|
||||
|
||||
const i1i2Button = document.createElement("button");
|
||||
i1i2Button.innerHTML = "I1I2 VISIBLE";
|
||||
i1i2Div.appendChild(i1i2Button);
|
||||
const i1i2Button = document.createElement('button');
|
||||
i1i2Button.innerHTML = 'I1I2 VISIBLE';
|
||||
i1i2Div.appendChild(i1i2Button);
|
||||
|
||||
i1Div.appendChild(i1i1Div);
|
||||
i1Div.appendChild(i1i2Div);
|
||||
visibleCollection[0].appendChild(i1Div);
|
||||
}
|
||||
i1Div.appendChild(i1i1Div);
|
||||
i1Div.appendChild(i1i2Div);
|
||||
visibleCollection[0].appendChild(i1Div);
|
||||
}
|
||||
</script>
|
||||
<br/>
|
||||
<h1>
|
||||
Verify that block class bugs are fixed
|
||||
</h1>
|
||||
<br/>
|
||||
<br />
|
||||
<h1>Verify that block class bugs are fixed</h1>
|
||||
<br />
|
||||
<div class="first">
|
||||
<div class="visible">
|
||||
<button>VISIBLE</button>
|
||||
</div>
|
||||
<br/><br/><br/>
|
||||
<br /><br /><br />
|
||||
<div class="rr-block" id="b-class">
|
||||
<button id="remove">BLOCKED</button>
|
||||
</div>
|
||||
<br/><br/><br/>
|
||||
<br /><br /><br />
|
||||
<button onclick="mutate1()">MUTATE</button>
|
||||
</div>
|
||||
<br/><br/><br/>
|
||||
<br /><br /><br />
|
||||
<div class="second">
|
||||
<div class="visible2">
|
||||
<button>VISIBLE</button>
|
||||
</div>
|
||||
<br/><br/><br/>
|
||||
<br /><br /><br />
|
||||
<div class="rr-block" id="b-class-2">
|
||||
<button id="remove2">BLOCKED</button>
|
||||
</div>
|
||||
<br/><br/><br/>
|
||||
<br /><br /><br />
|
||||
<button onclick="mutate2()">MUTATE</button>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
12
packages/rrweb/test/html/hello-world.html
Normal file
12
packages/rrweb/test/html/hello-world.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Hello World!</title>
|
||||
</head>
|
||||
<body>
|
||||
Hello world!
|
||||
</body>
|
||||
</html>
|
||||
@@ -73,7 +73,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
await page.type('textarea', 'textarea test');
|
||||
await page.select('select', '1');
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -91,7 +93,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
p.appendChild(document.createElement('span'));
|
||||
});
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -111,7 +115,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
p.innerText = 'mutated';
|
||||
});
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -129,7 +135,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
document.body.setAttribute('test', 'true');
|
||||
});
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -146,7 +154,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
await page.evaluate(
|
||||
'document.getElementById("select2-drop").setAttribute("style", document.getElementById("select2-drop").style.cssText + "color:black !important")',
|
||||
);
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -180,7 +190,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
|
||||
await waitForRAF(page);
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -191,7 +203,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
|
||||
await page.type('.rr-ignore', 'secret');
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -209,7 +223,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
await page.type('textarea', 'textarea test');
|
||||
await page.select('select', '1');
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -233,7 +249,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
await page.type('input[type="password"]', 'password');
|
||||
await page.select('select', '1');
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -250,7 +268,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
|
||||
await page.type('input[type="password"]', 'secr3t');
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -268,7 +288,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
await page.type('textarea', 'textarea test');
|
||||
await page.select('select', '1');
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -281,7 +303,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
await page.evaluate(`document.getElementById('text').innerText = '1'`);
|
||||
await page.click('#text');
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -301,7 +325,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
nextElement.parentNode!.insertBefore(el, nextElement);
|
||||
});
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -310,13 +336,19 @@ describe('record integration tests', function (this: ISuite) {
|
||||
await page.goto('about: blank');
|
||||
await page.setContent(getHtml.call(this, 'blocked-unblocked.html'));
|
||||
|
||||
const elements1 = await page.$x('/html/body/div[1]/button');
|
||||
const elements1 = (await page.$x(
|
||||
'/html/body/div[1]/button',
|
||||
)) as puppeteer.ElementHandle<HTMLButtonElement>[];
|
||||
await elements1[0].click();
|
||||
|
||||
const elements2 = await page.$x('/html/body/div[2]/button');
|
||||
const elements2 = (await page.$x(
|
||||
'/html/body/div[2]/button',
|
||||
)) as puppeteer.ElementHandle<HTMLButtonElement>[];
|
||||
await elements2[0].click();
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -334,7 +366,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
p.removeChild(span);
|
||||
div.appendChild(span);
|
||||
});
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -349,7 +383,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
document.body.appendChild(div);
|
||||
div.appendChild(span);
|
||||
});
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -358,7 +394,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
await page.goto('about:blank');
|
||||
await page.setContent(getHtml.call(this, 'react-styled-components.html'));
|
||||
await page.click('.toggle');
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -371,7 +409,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
}),
|
||||
);
|
||||
await waitForRAF(page);
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
for (const event of snapshots) {
|
||||
if (event.type === EventType.FullSnapshot) {
|
||||
visitSnapshot(event.data.node, (n) => {
|
||||
@@ -393,7 +433,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
}),
|
||||
);
|
||||
await page.waitForTimeout(50);
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -406,7 +448,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
}),
|
||||
);
|
||||
await waitForRAF(page);
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -425,7 +469,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
}
|
||||
});
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -483,11 +529,15 @@ describe('record integration tests', function (this: ISuite) {
|
||||
document.body.appendChild(iframe);
|
||||
});
|
||||
|
||||
await waitForRAF(page);
|
||||
await page.frames()[1].evaluate(() => {
|
||||
console.log('from iframe');
|
||||
});
|
||||
await waitForRAF(page);
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -504,7 +554,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
|
||||
await page.waitForTimeout(50);
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -519,7 +571,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
await page.waitForSelector('img'); // wait for image to get added
|
||||
await waitForRAF(page); // wait for image to be captured
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -534,7 +588,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
await page.waitForTimeout(50); // wait for image to get added
|
||||
await waitForRAF(page); // wait for image to be captured
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -555,7 +611,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
await page.waitForTimeout(50); // wait for image to get added
|
||||
await waitForRAF(page); // wait for image to be captured
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -603,7 +661,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
});
|
||||
await page.waitForTimeout(50);
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -646,7 +706,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
});
|
||||
await waitForRAF(page); // wait till browser sent snapshots
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -674,7 +736,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
});
|
||||
|
||||
await waitForRAF(page); // wait for snapshot to be updated
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -706,7 +770,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
});
|
||||
await waitForRAF(page); // wait till browser sent snapshots
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -746,7 +812,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
});
|
||||
await waitForRAF(page); // wait till browser sent snapshots
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -759,7 +827,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
}),
|
||||
);
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -773,7 +843,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
}),
|
||||
);
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
@@ -794,7 +866,9 @@ describe('record integration tests', function (this: ISuite) {
|
||||
p.innerText = 'mutated';
|
||||
});
|
||||
|
||||
const snapshots = await page.evaluate('window.snapshots');
|
||||
const snapshots = (await page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,14 @@ import {
|
||||
styleSheetRuleData,
|
||||
selectionData,
|
||||
} from '@rrweb/types';
|
||||
import { assertSnapshot, launchPuppeteer, waitForRAF } from './utils';
|
||||
import {
|
||||
assertSnapshot,
|
||||
getServerURL,
|
||||
launchPuppeteer,
|
||||
startServer,
|
||||
waitForRAF,
|
||||
} from './utils';
|
||||
import type { Server } from 'http';
|
||||
|
||||
interface ISuite {
|
||||
code: string;
|
||||
@@ -465,54 +472,63 @@ describe('record', function (this: ISuite) {
|
||||
|
||||
it('captures mutations on adopted stylesheets', async () => {
|
||||
await ctx.page.evaluate(() => {
|
||||
document.body.innerHTML = `
|
||||
return new Promise((resolve) => {
|
||||
document.body.innerHTML = `
|
||||
<div>div in outermost document</div>
|
||||
<iframe></iframe>
|
||||
`;
|
||||
|
||||
const sheet = new CSSStyleSheet();
|
||||
// Add stylesheet to a document.
|
||||
const sheet = new CSSStyleSheet();
|
||||
// Add stylesheet to a document.
|
||||
|
||||
document.adoptedStyleSheets = [sheet];
|
||||
document.adoptedStyleSheets = [sheet];
|
||||
|
||||
const iframe = document.querySelector('iframe');
|
||||
const sheet2 = new (iframe!.contentWindow! as Window &
|
||||
typeof globalThis).CSSStyleSheet();
|
||||
const iframe = document.querySelector('iframe');
|
||||
const sheet2 = new (iframe!.contentWindow! as Window &
|
||||
typeof globalThis).CSSStyleSheet();
|
||||
|
||||
// Add stylesheet to an IFrame document.
|
||||
iframe!.contentDocument!.adoptedStyleSheets = [sheet2];
|
||||
iframe!.contentDocument!.body.innerHTML = '<h1>h1 in iframe</h1>';
|
||||
// Add stylesheet to an IFrame document.
|
||||
iframe!.contentDocument!.adoptedStyleSheets = [sheet2];
|
||||
iframe!.contentDocument!.body.innerHTML = '<h1>h1 in iframe</h1>';
|
||||
|
||||
const { record } = ((window as unknown) as IWindow).rrweb;
|
||||
record({
|
||||
emit: ((window as unknown) as IWindow).emit,
|
||||
const { record } = ((window as unknown) as IWindow).rrweb;
|
||||
record({
|
||||
emit: ((window as unknown) as IWindow).emit,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
sheet.replace!('div { color: yellow; }');
|
||||
sheet2.replace!('h1 { color: blue; }');
|
||||
}, 0);
|
||||
|
||||
setTimeout(() => {
|
||||
sheet.replaceSync!('div { display: inline ; }');
|
||||
sheet2.replaceSync!('h1 { font-size: large; }');
|
||||
}, 5);
|
||||
|
||||
setTimeout(() => {
|
||||
(sheet.cssRules[0] as CSSStyleRule).style.setProperty(
|
||||
'color',
|
||||
'green',
|
||||
);
|
||||
(sheet.cssRules[0] as CSSStyleRule).style.removeProperty('display');
|
||||
(sheet2.cssRules[0] as CSSStyleRule).style.setProperty(
|
||||
'font-size',
|
||||
'medium',
|
||||
'important',
|
||||
);
|
||||
sheet2.insertRule('h2 { color: red; }');
|
||||
}, 10);
|
||||
|
||||
setTimeout(() => {
|
||||
sheet.insertRule('body { border: 2px solid blue; }', 1);
|
||||
sheet2.deleteRule(0);
|
||||
}, 15);
|
||||
|
||||
setTimeout(() => {
|
||||
resolve(undefined);
|
||||
}, 20);
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
sheet.replace!('div { color: yellow; }');
|
||||
sheet2.replace!('h1 { color: blue; }');
|
||||
}, 0);
|
||||
|
||||
setTimeout(() => {
|
||||
sheet.replaceSync!('div { display: inline ; }');
|
||||
sheet2.replaceSync!('h1 { font-size: large; }');
|
||||
}, 5);
|
||||
|
||||
setTimeout(() => {
|
||||
(sheet.cssRules[0] as CSSStyleRule).style.setProperty('color', 'green');
|
||||
(sheet.cssRules[0] as CSSStyleRule).style.removeProperty('display');
|
||||
(sheet2.cssRules[0] as CSSStyleRule).style.setProperty(
|
||||
'font-size',
|
||||
'medium',
|
||||
'important',
|
||||
);
|
||||
sheet2.insertRule('h2 { color: red; }');
|
||||
}, 10);
|
||||
|
||||
setTimeout(() => {
|
||||
sheet.insertRule('body { border: 2px solid blue; }', 1);
|
||||
sheet2.deleteRule(0);
|
||||
}, 15);
|
||||
});
|
||||
await waitForRAF(ctx.page);
|
||||
assertSnapshot(ctx.events);
|
||||
@@ -602,70 +618,91 @@ describe('record', function (this: ISuite) {
|
||||
assertSnapshot(ctx.events);
|
||||
});
|
||||
|
||||
it('captures stylesheets that are still loading', async () => {
|
||||
await ctx.page.evaluate(() => {
|
||||
const { record } = ((window as unknown) as IWindow).rrweb;
|
||||
describe('loading stylesheets', () => {
|
||||
let server: Server;
|
||||
let serverURL: string;
|
||||
|
||||
record({
|
||||
inlineStylesheet: true,
|
||||
emit: ((window as unknown) as IWindow).emit,
|
||||
});
|
||||
|
||||
const link1 = document.createElement('link');
|
||||
link1.setAttribute('rel', 'stylesheet');
|
||||
link1.setAttribute(
|
||||
'href',
|
||||
URL.createObjectURL(
|
||||
new Blob(['body { color: pink; }'], {
|
||||
type: 'text/css',
|
||||
}),
|
||||
),
|
||||
);
|
||||
document.head.appendChild(link1);
|
||||
beforeAll(async () => {
|
||||
server = await startServer();
|
||||
serverURL = getServerURL(server);
|
||||
});
|
||||
|
||||
// `blob:` URLs are not available immediately, so we need to wait for the browser to load them
|
||||
await waitForRAF(ctx.page);
|
||||
// 'blob' URL is different in every execution so we need to remove it from the snapshot.
|
||||
const filteredEvents = JSON.parse(
|
||||
JSON.stringify(ctx.events).replace(/blob\:[\w\d-/]+"/, 'blob:null"'),
|
||||
);
|
||||
assertSnapshot(filteredEvents);
|
||||
});
|
||||
|
||||
it('captures stylesheets in iframes that are still loading', async () => {
|
||||
await ctx.page.evaluate(() => {
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('src', 'about:blank');
|
||||
document.body.appendChild(iframe);
|
||||
const iframeDoc = iframe.contentDocument!;
|
||||
|
||||
const { record } = ((window as unknown) as IWindow).rrweb;
|
||||
|
||||
record({
|
||||
inlineStylesheet: true,
|
||||
emit: ((window as unknown) as IWindow).emit,
|
||||
beforeEach(async () => {
|
||||
ctx.page = await ctx.browser.newPage();
|
||||
await ctx.page.goto(`${serverURL}/html/hello-world.html`);
|
||||
await ctx.page.evaluate(ctx.code);
|
||||
ctx.events = [];
|
||||
await ctx.page.exposeFunction('emit', (e: eventWithTime) => {
|
||||
if (
|
||||
e.type === EventType.DomContentLoaded ||
|
||||
e.type === EventType.Load
|
||||
) {
|
||||
return;
|
||||
}
|
||||
ctx.events.push(e);
|
||||
});
|
||||
|
||||
const linkEl = document.createElement('link');
|
||||
linkEl.setAttribute('rel', 'stylesheet');
|
||||
linkEl.setAttribute(
|
||||
'href',
|
||||
URL.createObjectURL(
|
||||
new Blob(['body { color: pink; }'], {
|
||||
type: 'text/css',
|
||||
}),
|
||||
),
|
||||
);
|
||||
iframeDoc.head.appendChild(linkEl);
|
||||
ctx.page.on('console', (msg) => console.log('PAGE LOG:', msg.text()));
|
||||
});
|
||||
|
||||
// `blob:` URLs are not available immediately, so we need to wait for the browser to load them
|
||||
await waitForRAF(ctx.page);
|
||||
const filteredEvents = JSON.parse(
|
||||
JSON.stringify(ctx.events).replace(/blob\:[\w\d-/]+"/, 'blob:null"'),
|
||||
);
|
||||
assertSnapshot(filteredEvents);
|
||||
afterAll(async () => {
|
||||
await server.close();
|
||||
});
|
||||
|
||||
it('captures stylesheets that are still loading', async () => {
|
||||
ctx.page.evaluate((serverURL) => {
|
||||
const { record } = ((window as unknown) as IWindow).rrweb;
|
||||
|
||||
record({
|
||||
inlineStylesheet: true,
|
||||
emit: ((window as unknown) as IWindow).emit,
|
||||
});
|
||||
|
||||
const link1 = document.createElement('link');
|
||||
link1.setAttribute('rel', 'stylesheet');
|
||||
link1.setAttribute('href', `${serverURL}/html/assets/style.css`);
|
||||
document.head.appendChild(link1);
|
||||
}, serverURL);
|
||||
|
||||
await ctx.page.waitForResponse(`${serverURL}/html/assets/style.css`);
|
||||
await waitForRAF(ctx.page);
|
||||
|
||||
assertSnapshot(ctx.events);
|
||||
});
|
||||
|
||||
it('captures stylesheets in iframes that are still loading', async () => {
|
||||
ctx.page.evaluate(() => {
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('src', `/html/hello-world.html?2`);
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
const { record } = ((window as unknown) as IWindow).rrweb;
|
||||
|
||||
record({
|
||||
inlineStylesheet: true,
|
||||
emit: ((window as unknown) as IWindow).emit,
|
||||
});
|
||||
});
|
||||
|
||||
await ctx.page.waitForResponse(`${serverURL}/html/hello-world.html?2`);
|
||||
|
||||
await waitForRAF(ctx.page);
|
||||
|
||||
ctx.page.evaluate(() => {
|
||||
const iframe = document.querySelector('iframe')!;
|
||||
const iframeDoc = iframe.contentDocument!;
|
||||
const linkEl = document.createElement('link');
|
||||
linkEl.setAttribute('rel', 'stylesheet');
|
||||
linkEl.setAttribute('href', `/html/assets/style.css`);
|
||||
iframeDoc.head.appendChild(linkEl);
|
||||
});
|
||||
|
||||
await ctx.page.waitForResponse(`${serverURL}/html/assets/style.css`);
|
||||
|
||||
await waitForRAF(ctx.page);
|
||||
|
||||
assertSnapshot(ctx.events);
|
||||
});
|
||||
});
|
||||
|
||||
it('captures CORS stylesheets that are still loading', async () => {
|
||||
@@ -695,65 +732,71 @@ describe('record', function (this: ISuite) {
|
||||
|
||||
it('captures adopted stylesheets in shadow doms and iframe', async () => {
|
||||
await ctx.page.evaluate(() => {
|
||||
document.body.innerHTML = `
|
||||
return new Promise((resolve) => {
|
||||
document.body.innerHTML = `
|
||||
<div>div in outermost document</div>
|
||||
<div id="shadow-host1"></div>
|
||||
<div id="shadow-host2"></div>
|
||||
<iframe></iframe>
|
||||
`;
|
||||
|
||||
const sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync!(
|
||||
'div { color: yellow; } h2 { color: orange; } h3 { font-size: larger;}',
|
||||
);
|
||||
// Add stylesheet to a document.
|
||||
const sheet = new CSSStyleSheet();
|
||||
sheet.replaceSync!(
|
||||
'div { color: yellow; } h2 { color: orange; } h3 { font-size: larger;}',
|
||||
);
|
||||
// Add stylesheet to a document.
|
||||
|
||||
document.adoptedStyleSheets = [sheet];
|
||||
document.adoptedStyleSheets = [sheet];
|
||||
|
||||
// Add stylesheet to a shadow host.
|
||||
const host = document.querySelector('#shadow-host1');
|
||||
const shadow = host!.attachShadow({ mode: 'open' });
|
||||
shadow.innerHTML =
|
||||
'<div>div in shadow dom 1</div><span>span in shadow dom 1</span>';
|
||||
const sheet2 = new CSSStyleSheet();
|
||||
|
||||
sheet2.replaceSync!('span { color: red; }');
|
||||
|
||||
shadow.adoptedStyleSheets = [sheet, sheet2];
|
||||
|
||||
// Add stylesheet to an IFrame document.
|
||||
const iframe = document.querySelector('iframe');
|
||||
const sheet3 = new (iframe!.contentWindow! as IWindow &
|
||||
typeof globalThis).CSSStyleSheet();
|
||||
sheet3.replaceSync!('h1 { color: blue; }');
|
||||
|
||||
iframe!.contentDocument!.adoptedStyleSheets = [sheet3];
|
||||
|
||||
const ele = iframe!.contentDocument!.createElement('h1');
|
||||
ele.innerText = 'h1 in iframe';
|
||||
iframe!.contentDocument!.body.appendChild(ele);
|
||||
|
||||
((window as unknown) as IWindow).rrweb.record({
|
||||
emit: ((window.top as unknown) as IWindow).emit,
|
||||
});
|
||||
|
||||
// Make incremental changes to shadow dom.
|
||||
setTimeout(() => {
|
||||
const host = document.querySelector('#shadow-host2');
|
||||
// Add stylesheet to a shadow host.
|
||||
const host = document.querySelector('#shadow-host1');
|
||||
const shadow = host!.attachShadow({ mode: 'open' });
|
||||
shadow.innerHTML =
|
||||
'<div>div in shadow dom 2</div><span>span in shadow dom 2</span>';
|
||||
const sheet4 = new CSSStyleSheet();
|
||||
sheet4.replaceSync!('span { color: green; }');
|
||||
shadow.adoptedStyleSheets = [sheet, sheet4];
|
||||
'<div>div in shadow dom 1</div><span>span in shadow dom 1</span>';
|
||||
const sheet2 = new CSSStyleSheet();
|
||||
|
||||
document.adoptedStyleSheets = [sheet4, sheet, sheet2];
|
||||
sheet2.replaceSync!('span { color: red; }');
|
||||
|
||||
const sheet5 = new (iframe!.contentWindow! as IWindow &
|
||||
shadow.adoptedStyleSheets = [sheet, sheet2];
|
||||
|
||||
// Add stylesheet to an IFrame document.
|
||||
const iframe = document.querySelector('iframe');
|
||||
const sheet3 = new (iframe!.contentWindow! as IWindow &
|
||||
typeof globalThis).CSSStyleSheet();
|
||||
sheet5.replaceSync!('h2 { color: purple; }');
|
||||
iframe!.contentDocument!.adoptedStyleSheets = [sheet5, sheet3];
|
||||
}, 10);
|
||||
sheet3.replaceSync!('h1 { color: blue; }');
|
||||
|
||||
iframe!.contentDocument!.adoptedStyleSheets = [sheet3];
|
||||
|
||||
const ele = iframe!.contentDocument!.createElement('h1');
|
||||
ele.innerText = 'h1 in iframe';
|
||||
iframe!.contentDocument!.body.appendChild(ele);
|
||||
|
||||
((window as unknown) as IWindow).rrweb.record({
|
||||
emit: ((window.top as unknown) as IWindow).emit,
|
||||
});
|
||||
|
||||
// Make incremental changes to shadow dom.
|
||||
setTimeout(() => {
|
||||
const host = document.querySelector('#shadow-host2');
|
||||
const shadow = host!.attachShadow({ mode: 'open' });
|
||||
shadow.innerHTML =
|
||||
'<div>div in shadow dom 2</div><span>span in shadow dom 2</span>';
|
||||
const sheet4 = new CSSStyleSheet();
|
||||
sheet4.replaceSync!('span { color: green; }');
|
||||
shadow.adoptedStyleSheets = [sheet, sheet4];
|
||||
|
||||
document.adoptedStyleSheets = [sheet4, sheet, sheet2];
|
||||
|
||||
const sheet5 = new (iframe!.contentWindow! as IWindow &
|
||||
typeof globalThis).CSSStyleSheet();
|
||||
sheet5.replaceSync!('h2 { color: purple; }');
|
||||
iframe!.contentDocument!.adoptedStyleSheets = [sheet5, sheet3];
|
||||
}, 10);
|
||||
|
||||
setTimeout(() => {
|
||||
resolve(null);
|
||||
}, 20);
|
||||
});
|
||||
});
|
||||
await waitForRAF(ctx.page); // wait till events get sent
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
514
packages/rrweb/test/record/cross-origin-iframes.test.ts
Normal file
514
packages/rrweb/test/record/cross-origin-iframes.test.ts
Normal file
@@ -0,0 +1,514 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import type * as puppeteer from 'puppeteer';
|
||||
import type { recordOptions } from '../../src/types';
|
||||
import type {
|
||||
listenerHandler,
|
||||
eventWithTime,
|
||||
mutationData,
|
||||
} from '@rrweb/types';
|
||||
import { EventType, IncrementalSource } from '@rrweb/types';
|
||||
import {
|
||||
assertSnapshot,
|
||||
getServerURL,
|
||||
launchPuppeteer,
|
||||
startServer,
|
||||
stripBase64,
|
||||
waitForRAF,
|
||||
} from '../utils';
|
||||
import type * as http from 'http';
|
||||
|
||||
interface ISuite {
|
||||
code: string;
|
||||
browser: puppeteer.Browser;
|
||||
page: puppeteer.Page;
|
||||
events: eventWithTime[];
|
||||
server: http.Server;
|
||||
serverURL: string;
|
||||
}
|
||||
|
||||
interface IWindow extends Window {
|
||||
rrweb: {
|
||||
record: (
|
||||
options: recordOptions<eventWithTime>,
|
||||
) => listenerHandler | undefined;
|
||||
addCustomEvent<T>(tag: string, payload: T): void;
|
||||
};
|
||||
emit: (e: eventWithTime) => undefined;
|
||||
snapshots: eventWithTime[];
|
||||
}
|
||||
|
||||
async function injectRecordScript(frame: puppeteer.Frame) {
|
||||
await frame.addScriptTag({
|
||||
path: path.resolve(__dirname, '../../dist/rrweb.js'),
|
||||
});
|
||||
await frame.evaluate(() => {
|
||||
((window as unknown) as IWindow).snapshots = [];
|
||||
const { record } = ((window as unknown) as IWindow).rrweb;
|
||||
record({
|
||||
recordCrossOriginIframes: true,
|
||||
recordCanvas: true,
|
||||
emit(event) {
|
||||
((window as unknown) as IWindow).snapshots.push(event);
|
||||
((window as unknown) as IWindow).emit(event);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
for (const child of frame.childFrames()) {
|
||||
await injectRecordScript(child);
|
||||
}
|
||||
}
|
||||
|
||||
const setup = function (this: ISuite, content: string): ISuite {
|
||||
const ctx = {} as ISuite;
|
||||
|
||||
beforeAll(async () => {
|
||||
ctx.browser = await launchPuppeteer();
|
||||
ctx.server = await startServer();
|
||||
ctx.serverURL = getServerURL(ctx.server);
|
||||
// ctx.serverB = await startServer();
|
||||
// ctx.serverBURL = getServerURL(ctx.serverB);
|
||||
|
||||
const bundlePath = path.resolve(__dirname, '../../dist/rrweb.js');
|
||||
ctx.code = fs.readFileSync(bundlePath, 'utf8');
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
ctx.page = await ctx.browser.newPage();
|
||||
await ctx.page.goto('about:blank');
|
||||
await ctx.page.setContent(
|
||||
content.replace(/\{SERVER_URL\}/g, ctx.serverURL),
|
||||
);
|
||||
// await ctx.page.evaluate(ctx.code);
|
||||
ctx.events = [];
|
||||
await ctx.page.exposeFunction('emit', (e: eventWithTime) => {
|
||||
if (e.type === EventType.DomContentLoaded || e.type === EventType.Load) {
|
||||
return;
|
||||
}
|
||||
ctx.events.push(e);
|
||||
});
|
||||
|
||||
ctx.page.on('console', (msg) => console.log('PAGE LOG:', msg.text()));
|
||||
await injectRecordScript(ctx.page.mainFrame());
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await ctx.page.close();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await ctx.browser.close();
|
||||
ctx.server.close();
|
||||
// ctx.serverB.close();
|
||||
});
|
||||
|
||||
return ctx;
|
||||
};
|
||||
|
||||
describe('cross origin iframes', function (this: ISuite) {
|
||||
jest.setTimeout(100_000);
|
||||
|
||||
describe('form.html', function (this: ISuite) {
|
||||
const ctx: ISuite = setup.call(
|
||||
this,
|
||||
`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<iframe src="{SERVER_URL}/html/form.html" style="width: 400px; height: 400px;"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
);
|
||||
|
||||
it("won't emit events if it isn't in the top level iframe", async () => {
|
||||
const el = (await ctx.page.$(
|
||||
'body > iframe',
|
||||
)) as puppeteer.ElementHandle<Element>;
|
||||
|
||||
const frame = await el.contentFrame();
|
||||
const events = await frame?.evaluate(
|
||||
() => ((window as unknown) as IWindow).snapshots,
|
||||
);
|
||||
expect(events).toMatchObject([]);
|
||||
});
|
||||
|
||||
it('will emit events if it is in the top level iframe', async () => {
|
||||
const events = await ctx.page.evaluate(
|
||||
() => ((window as unknown) as IWindow).snapshots,
|
||||
);
|
||||
expect(events.length).not.toBe(0);
|
||||
});
|
||||
|
||||
it('should emit contents of iframe', async () => {
|
||||
const events = await ctx.page.evaluate(
|
||||
() => ((window as unknown) as IWindow).snapshots,
|
||||
);
|
||||
await waitForRAF(ctx.page);
|
||||
// two events (full snapshot + meta) from main frame, and one full snapshot from iframe
|
||||
expect(events.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should emit full snapshot event from iframe as mutation event', async () => {
|
||||
const events = await ctx.page.evaluate(
|
||||
() => ((window as unknown) as IWindow).snapshots,
|
||||
);
|
||||
await waitForRAF(ctx.page);
|
||||
// two events from main frame, and two from iframe
|
||||
expect(events[events.length - 1]).toMatchObject({
|
||||
type: EventType.IncrementalSnapshot,
|
||||
data: {
|
||||
source: IncrementalSource.Mutation,
|
||||
adds: [
|
||||
{
|
||||
parentId: expect.any(Number),
|
||||
node: {
|
||||
id: expect.any(Number),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should use unique id for child of iframes', async () => {
|
||||
const events: eventWithTime[] = await ctx.page.evaluate(
|
||||
() => ((window as unknown) as IWindow).snapshots,
|
||||
);
|
||||
await waitForRAF(ctx.page);
|
||||
expect(
|
||||
(events[events.length - 1].data as mutationData).adds[0].node.id,
|
||||
).not.toBe(1);
|
||||
});
|
||||
|
||||
it('should replace the existing DOM nodes on iframe navigation with `isAttachIframe`', async () => {
|
||||
await ctx.page.evaluate((url) => {
|
||||
const iframe = document.querySelector('iframe') as HTMLIFrameElement;
|
||||
iframe.src = `${url}/html/form.html?2`;
|
||||
}, ctx.serverURL);
|
||||
await waitForRAF(ctx.page); // loads iframe
|
||||
|
||||
await injectRecordScript(ctx.page.mainFrame().childFrames()[0]); // injects script into new iframe
|
||||
|
||||
const events: eventWithTime[] = await ctx.page.evaluate(
|
||||
() => ((window as unknown) as IWindow).snapshots,
|
||||
);
|
||||
expect(
|
||||
(events[events.length - 1].data as mutationData).removes,
|
||||
).toMatchObject([]);
|
||||
expect(
|
||||
(events[events.length - 1].data as mutationData).isAttachIframe,
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should map input events correctly', async () => {
|
||||
const frame = ctx.page.mainFrame().childFrames()[0];
|
||||
await frame.type('input[type="text"]', 'test');
|
||||
await frame.click('input[type="radio"]');
|
||||
await frame.click('input[type="checkbox"]');
|
||||
await frame.type('input[type="password"]', 'password');
|
||||
await frame.type('textarea', 'textarea test');
|
||||
await frame.select('select', '1');
|
||||
|
||||
const snapshots = (await ctx.page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
it('should map scroll events correctly', async () => {
|
||||
// force scrollbars in iframe
|
||||
ctx.page.evaluate(() => {
|
||||
const iframe = document.querySelector('iframe') as HTMLIFrameElement;
|
||||
iframe.style.width = '50px';
|
||||
iframe.style.height = '50px';
|
||||
});
|
||||
|
||||
await waitForRAF(ctx.page);
|
||||
const frame = ctx.page.mainFrame().childFrames()[0];
|
||||
|
||||
// scroll a little
|
||||
frame.evaluate(() => {
|
||||
window.scrollTo(0, 10);
|
||||
});
|
||||
await waitForRAF(ctx.page);
|
||||
|
||||
const snapshots = (await ctx.page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
});
|
||||
|
||||
describe('move-node.html', function (this: ISuite) {
|
||||
const ctx: ISuite = setup.call(
|
||||
this,
|
||||
`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<iframe src="{SERVER_URL}/html/move-node.html"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
);
|
||||
|
||||
it('should record DOM node movement', async () => {
|
||||
const frame = ctx.page.mainFrame().childFrames()[0];
|
||||
await frame.evaluate(() => {
|
||||
const div = document.createElement('div');
|
||||
const span = document.querySelector('span')!;
|
||||
document.body.appendChild(div);
|
||||
div.appendChild(span);
|
||||
});
|
||||
const snapshots = (await ctx.page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
it('should record DOM node removal', async () => {
|
||||
const frame = ctx.page.mainFrame().childFrames()[0];
|
||||
await frame.evaluate(() => {
|
||||
const span = document.querySelector('span')!;
|
||||
span.remove();
|
||||
});
|
||||
const snapshots = (await ctx.page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
it('should record DOM attribute changes', async () => {
|
||||
const frame = ctx.page.mainFrame().childFrames()[0];
|
||||
await frame.evaluate(() => {
|
||||
const span = document.querySelector('span')!;
|
||||
span.className = 'added-class-name';
|
||||
});
|
||||
const snapshots = (await ctx.page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
it('should record DOM text changes', async () => {
|
||||
const frame = ctx.page.mainFrame().childFrames()[0];
|
||||
await frame.evaluate(() => {
|
||||
const b = document.querySelector('b')!;
|
||||
b.childNodes[0].textContent = 'replaced text';
|
||||
});
|
||||
const snapshots = (await ctx.page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
it('should record canvas elements', async () => {
|
||||
const frame = ctx.page.mainFrame().childFrames()[0];
|
||||
await frame.evaluate(() => {
|
||||
var canvas = document.createElement('canvas');
|
||||
var gl = canvas.getContext('webgl')!;
|
||||
var program = gl.createProgram()!;
|
||||
gl.linkProgram(program);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
document.body.appendChild(canvas);
|
||||
});
|
||||
await waitForRAF(ctx.page);
|
||||
const snapshots = (await ctx.page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
it('should record custom events', async () => {
|
||||
const frame = ctx.page.mainFrame().childFrames()[0];
|
||||
await frame.evaluate(() => {
|
||||
((window as unknown) as IWindow).rrweb.addCustomEvent('test', {
|
||||
id: 1,
|
||||
parentId: 1,
|
||||
nextId: 2,
|
||||
});
|
||||
});
|
||||
await waitForRAF(ctx.page);
|
||||
const snapshots = (await ctx.page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
it('captures mutations on adopted stylesheets', async () => {
|
||||
const frame = ctx.page.mainFrame().childFrames()[0];
|
||||
await ctx.page.evaluate(() => {
|
||||
const sheet = new CSSStyleSheet();
|
||||
// Add stylesheet to a document.
|
||||
document.adoptedStyleSheets = [sheet];
|
||||
});
|
||||
await frame.evaluate(() => {
|
||||
const sheet = new CSSStyleSheet();
|
||||
// Add stylesheet to a document.
|
||||
document.adoptedStyleSheets = [sheet];
|
||||
});
|
||||
await waitForRAF(ctx.page);
|
||||
await ctx.page.evaluate(() => {
|
||||
document.adoptedStyleSheets![0].replace!('div { color: yellow; }');
|
||||
});
|
||||
await frame.evaluate(() => {
|
||||
document.adoptedStyleSheets![0].replace!('h1 { color: blue; }');
|
||||
});
|
||||
await waitForRAF(ctx.page);
|
||||
await ctx.page.evaluate(() => {
|
||||
document.adoptedStyleSheets![0].replaceSync!(
|
||||
'div { display: inline ; }',
|
||||
);
|
||||
});
|
||||
await frame.evaluate(() => {
|
||||
document.adoptedStyleSheets![0].replaceSync!(
|
||||
'h1 { font-size: large; }',
|
||||
);
|
||||
});
|
||||
await waitForRAF(ctx.page);
|
||||
await ctx.page.evaluate(() => {
|
||||
(document.adoptedStyleSheets![0]
|
||||
.cssRules[0] as CSSStyleRule).style.setProperty('color', 'green');
|
||||
(document.adoptedStyleSheets![0]
|
||||
.cssRules[0] as CSSStyleRule).style.removeProperty('display');
|
||||
});
|
||||
await frame.evaluate(() => {
|
||||
(document.adoptedStyleSheets![0]
|
||||
.cssRules[0] as CSSStyleRule).style.setProperty(
|
||||
'font-size',
|
||||
'medium',
|
||||
'important',
|
||||
);
|
||||
document.adoptedStyleSheets![0].insertRule('h2 { color: red; }');
|
||||
});
|
||||
await waitForRAF(ctx.page);
|
||||
await ctx.page.evaluate(() => {
|
||||
document.adoptedStyleSheets![0].insertRule(
|
||||
'body { border: 2px solid blue; }',
|
||||
1,
|
||||
);
|
||||
});
|
||||
await frame.evaluate(() => {
|
||||
document.adoptedStyleSheets![0].deleteRule(0);
|
||||
});
|
||||
await waitForRAF(ctx.page);
|
||||
|
||||
const snapshots = (await ctx.page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
|
||||
it('captures mutations on stylesheets', async () => {
|
||||
const frame = ctx.page.mainFrame().childFrames()[0];
|
||||
await ctx.page.evaluate(() => {
|
||||
// Add stylesheet to a document.
|
||||
const style = document.createElement('style');
|
||||
document.head.appendChild(style);
|
||||
});
|
||||
await frame.evaluate(() => {
|
||||
// Add stylesheet to a document.
|
||||
const style = document.createElement('style');
|
||||
document.head.appendChild(style);
|
||||
});
|
||||
await waitForRAF(ctx.page);
|
||||
await ctx.page.evaluate(() => {
|
||||
document.styleSheets[0].insertRule('div { color: yellow; }');
|
||||
});
|
||||
await frame.evaluate(() => {
|
||||
document.styleSheets[0].insertRule('h1 { color: blue; }');
|
||||
});
|
||||
await waitForRAF(ctx.page);
|
||||
await ctx.page.evaluate(() => {
|
||||
(document.styleSheets[0].cssRules[0] as CSSStyleRule).style.setProperty(
|
||||
'color',
|
||||
'green',
|
||||
);
|
||||
(document.styleSheets[0]
|
||||
.cssRules[0] as CSSStyleRule).style.removeProperty('display');
|
||||
});
|
||||
await frame.evaluate(() => {
|
||||
(document.styleSheets[0].cssRules[0] as CSSStyleRule).style.setProperty(
|
||||
'font-size',
|
||||
'medium',
|
||||
'important',
|
||||
);
|
||||
document.styleSheets[0].insertRule('h2 { color: red; }');
|
||||
});
|
||||
await waitForRAF(ctx.page);
|
||||
await ctx.page.evaluate(() => {
|
||||
document.styleSheets[0].insertRule(
|
||||
'body { border: 2px solid blue; }',
|
||||
1,
|
||||
);
|
||||
});
|
||||
await frame.evaluate(() => {
|
||||
document.styleSheets[0].deleteRule(0);
|
||||
});
|
||||
await waitForRAF(ctx.page);
|
||||
|
||||
const snapshots = (await ctx.page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
});
|
||||
|
||||
describe('audio.html', function (this: ISuite) {
|
||||
jest.setTimeout(100_000);
|
||||
|
||||
const ctx: ISuite = setup.call(
|
||||
this,
|
||||
`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<iframe src="{SERVER_URL}/html/audio.html"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
);
|
||||
|
||||
it('should emit contents of iframe once', async () => {
|
||||
const frame = ctx.page.mainFrame().childFrames()[0];
|
||||
await frame.evaluate(() => {
|
||||
const el = document.querySelector('audio')!;
|
||||
el.play();
|
||||
});
|
||||
await waitForRAF(ctx.page);
|
||||
const snapshots = (await ctx.page.evaluate(
|
||||
'window.snapshots',
|
||||
)) as eventWithTime[];
|
||||
assertSnapshot(snapshots);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('same origin iframes', function (this: ISuite) {
|
||||
jest.setTimeout(100_000);
|
||||
|
||||
const ctx: ISuite = setup.call(
|
||||
this,
|
||||
`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<iframe src="about:blank"></iframe>
|
||||
</body>
|
||||
</html>
|
||||
`,
|
||||
);
|
||||
|
||||
it('should emit contents of iframe once', async () => {
|
||||
const events = await ctx.page.evaluate(
|
||||
() => ((window as unknown) as IWindow).snapshots,
|
||||
);
|
||||
await waitForRAF(ctx.page);
|
||||
// two events (full snapshot + meta) from main frame,
|
||||
// and two (full snapshot + mutation) from iframe
|
||||
expect(events.length).toBe(4);
|
||||
assertSnapshot(events);
|
||||
});
|
||||
});
|
||||
@@ -191,6 +191,11 @@ function stringifySnapshots(snapshots: eventWithTime[]): string {
|
||||
}),
|
||||
null,
|
||||
2,
|
||||
).replace(
|
||||
// servers might get run on a random port,
|
||||
// so we need to normalize the port number
|
||||
/http:\/\/localhost:\d+/g,
|
||||
'http://localhost:3030',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user