add checkout config to recorder (#36)

* add checkout config to recorder

* add test cases for checkout feature and extract assertSnapshot method
This commit is contained in:
yz-yu
2026-04-01 12:00:00 +08:00
committed by GitHub
parent d72a15fd1b
commit 081781d1ca
9 changed files with 738 additions and 314 deletions

View File

@@ -4,13 +4,11 @@ exports[`attributes 1`] = `
"[
{
\\"type\\": 0,
\\"data\\": {},
\\"timestamp\\": 1542268800000
\\"data\\": {}
},
{
\\"type\\": 1,
\\"data\\": {},
\\"timestamp\\": 1542268800000
\\"data\\": {}
},
{
\\"type\\": 4,
@@ -18,8 +16,7 @@ exports[`attributes 1`] = `
\\"href\\": \\"about:blank\\",
\\"width\\": 1920,
\\"height\\": 1080
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 2,
@@ -128,8 +125,7 @@ exports[`attributes 1`] = `
\\"left\\": 0,
\\"top\\": 0
}
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -151,8 +147,7 @@ exports[`attributes 1`] = `
}
],
\\"adds\\": []
},
\\"timestamp\\": 1542268800000
}
}
]"
`;
@@ -161,13 +156,11 @@ exports[`block 1`] = `
"[
{
\\"type\\": 0,
\\"data\\": {},
\\"timestamp\\": 1542268800000
\\"data\\": {}
},
{
\\"type\\": 1,
\\"data\\": {},
\\"timestamp\\": 1542268800000
\\"data\\": {}
},
{
\\"type\\": 4,
@@ -175,8 +168,7 @@ exports[`block 1`] = `
\\"href\\": \\"about:blank\\",
\\"width\\": 1920,
\\"height\\": 1080
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 2,
@@ -335,8 +327,7 @@ exports[`block 1`] = `
\\"left\\": 0,
\\"top\\": 0
}
},
\\"timestamp\\": 1542268800000
}
}
]"
`;
@@ -345,13 +336,11 @@ exports[`character-data 1`] = `
"[
{
\\"type\\": 0,
\\"data\\": {},
\\"timestamp\\": 1542268800000
\\"data\\": {}
},
{
\\"type\\": 1,
\\"data\\": {},
\\"timestamp\\": 1542268800000
\\"data\\": {}
},
{
\\"type\\": 4,
@@ -359,8 +348,7 @@ exports[`character-data 1`] = `
\\"href\\": \\"about:blank\\",
\\"width\\": 1920,
\\"height\\": 1080
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 2,
@@ -469,8 +457,7 @@ exports[`character-data 1`] = `
\\"left\\": 0,
\\"top\\": 0
}
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -490,8 +477,7 @@ exports[`character-data 1`] = `
}
],
\\"adds\\": []
},
\\"timestamp\\": 1542268800000
}
}
]"
`;
@@ -500,13 +486,11 @@ exports[`child-list 1`] = `
"[
{
\\"type\\": 0,
\\"data\\": {},
\\"timestamp\\": 1542268800000
\\"data\\": {}
},
{
\\"type\\": 1,
\\"data\\": {},
\\"timestamp\\": 1542268800000
\\"data\\": {}
},
{
\\"type\\": 4,
@@ -514,8 +498,7 @@ exports[`child-list 1`] = `
\\"href\\": \\"about:blank\\",
\\"width\\": 1920,
\\"height\\": 1080
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 2,
@@ -624,8 +607,7 @@ exports[`child-list 1`] = `
\\"left\\": 0,
\\"top\\": 0
}
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -653,8 +635,7 @@ exports[`child-list 1`] = `
}
}
]
},
\\"timestamp\\": 1542268800000
}
}
]"
`;
@@ -663,13 +644,11 @@ exports[`form 1`] = `
"[
{
\\"type\\": 0,
\\"data\\": {},
\\"timestamp\\": 1542268800000
\\"data\\": {}
},
{
\\"type\\": 1,
\\"data\\": {},
\\"timestamp\\": 1542268800000
\\"data\\": {}
},
{
\\"type\\": 4,
@@ -677,8 +656,7 @@ exports[`form 1`] = `
\\"href\\": \\"about:blank\\",
\\"width\\": 1920,
\\"height\\": 1080
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 2,
@@ -1061,8 +1039,7 @@ exports[`form 1`] = `
\\"left\\": 0,
\\"top\\": 0
}
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1070,8 +1047,7 @@ exports[`form 1`] = `
\\"source\\": 2,
\\"type\\": 5,
\\"id\\": 22
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1080,8 +1056,7 @@ exports[`form 1`] = `
\\"text\\": \\"t\\",
\\"isChecked\\": false,
\\"id\\": 22
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1090,8 +1065,7 @@ exports[`form 1`] = `
\\"text\\": \\"te\\",
\\"isChecked\\": false,
\\"id\\": 22
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1100,8 +1074,7 @@ exports[`form 1`] = `
\\"text\\": \\"tes\\",
\\"isChecked\\": false,
\\"id\\": 22
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1110,8 +1083,7 @@ exports[`form 1`] = `
\\"text\\": \\"test\\",
\\"isChecked\\": false,
\\"id\\": 22
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1119,8 +1091,7 @@ exports[`form 1`] = `
\\"source\\": 2,
\\"type\\": 1,
\\"id\\": 27
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1128,8 +1099,7 @@ exports[`form 1`] = `
\\"source\\": 2,
\\"type\\": 6,
\\"id\\": 22
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1137,8 +1107,7 @@ exports[`form 1`] = `
\\"source\\": 2,
\\"type\\": 5,
\\"id\\": 27
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1146,8 +1115,7 @@ exports[`form 1`] = `
\\"source\\": 2,
\\"type\\": 0,
\\"id\\": 27
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1155,8 +1123,7 @@ exports[`form 1`] = `
\\"source\\": 2,
\\"type\\": 2,
\\"id\\": 27
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1165,8 +1132,7 @@ exports[`form 1`] = `
\\"text\\": \\"on\\",
\\"isChecked\\": true,
\\"id\\": 27
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1174,8 +1140,7 @@ exports[`form 1`] = `
\\"source\\": 2,
\\"type\\": 1,
\\"id\\": 32
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1183,8 +1148,7 @@ exports[`form 1`] = `
\\"source\\": 2,
\\"type\\": 6,
\\"id\\": 27
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1192,8 +1156,7 @@ exports[`form 1`] = `
\\"source\\": 2,
\\"type\\": 5,
\\"id\\": 32
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1201,8 +1164,7 @@ exports[`form 1`] = `
\\"source\\": 2,
\\"type\\": 0,
\\"id\\": 32
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1210,8 +1172,7 @@ exports[`form 1`] = `
\\"source\\": 2,
\\"type\\": 2,
\\"id\\": 32
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1220,8 +1181,7 @@ exports[`form 1`] = `
\\"text\\": \\"on\\",
\\"isChecked\\": true,
\\"id\\": 32
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1229,8 +1189,7 @@ exports[`form 1`] = `
\\"source\\": 2,
\\"type\\": 6,
\\"id\\": 32
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1238,8 +1197,7 @@ exports[`form 1`] = `
\\"source\\": 2,
\\"type\\": 5,
\\"id\\": 37
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1248,8 +1206,7 @@ exports[`form 1`] = `
\\"text\\": \\"t\\",
\\"isChecked\\": false,
\\"id\\": 37
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1258,8 +1215,7 @@ exports[`form 1`] = `
\\"text\\": \\"te\\",
\\"isChecked\\": false,
\\"id\\": 37
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1268,8 +1224,7 @@ exports[`form 1`] = `
\\"text\\": \\"tex\\",
\\"isChecked\\": false,
\\"id\\": 37
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1278,8 +1233,7 @@ exports[`form 1`] = `
\\"text\\": \\"text\\",
\\"isChecked\\": false,
\\"id\\": 37
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1288,8 +1242,7 @@ exports[`form 1`] = `
\\"text\\": \\"texta\\",
\\"isChecked\\": false,
\\"id\\": 37
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1298,8 +1251,7 @@ exports[`form 1`] = `
\\"text\\": \\"textar\\",
\\"isChecked\\": false,
\\"id\\": 37
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1308,8 +1260,7 @@ exports[`form 1`] = `
\\"text\\": \\"textare\\",
\\"isChecked\\": false,
\\"id\\": 37
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1318,8 +1269,7 @@ exports[`form 1`] = `
\\"text\\": \\"textarea\\",
\\"isChecked\\": false,
\\"id\\": 37
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1328,8 +1278,7 @@ exports[`form 1`] = `
\\"text\\": \\"textarea \\",
\\"isChecked\\": false,
\\"id\\": 37
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1338,8 +1287,7 @@ exports[`form 1`] = `
\\"text\\": \\"textarea t\\",
\\"isChecked\\": false,
\\"id\\": 37
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1348,8 +1296,7 @@ exports[`form 1`] = `
\\"text\\": \\"textarea te\\",
\\"isChecked\\": false,
\\"id\\": 37
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1358,8 +1305,7 @@ exports[`form 1`] = `
\\"text\\": \\"textarea tes\\",
\\"isChecked\\": false,
\\"id\\": 37
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1368,8 +1314,7 @@ exports[`form 1`] = `
\\"text\\": \\"textarea test\\",
\\"isChecked\\": false,
\\"id\\": 37
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1378,8 +1323,7 @@ exports[`form 1`] = `
\\"text\\": \\"1\\",
\\"isChecked\\": false,
\\"id\\": 42
},
\\"timestamp\\": 1542268800000
}
}
]"
`;
@@ -1388,13 +1332,11 @@ exports[`ignore 1`] = `
"[
{
\\"type\\": 0,
\\"data\\": {},
\\"timestamp\\": 1542268800000
\\"data\\": {}
},
{
\\"type\\": 1,
\\"data\\": {},
\\"timestamp\\": 1542268800000
\\"data\\": {}
},
{
\\"type\\": 4,
@@ -1402,8 +1344,7 @@ exports[`ignore 1`] = `
\\"href\\": \\"about:blank\\",
\\"width\\": 1920,
\\"height\\": 1080
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 2,
@@ -1633,8 +1574,7 @@ exports[`ignore 1`] = `
\\"left\\": 0,
\\"top\\": 0
}
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1642,8 +1582,7 @@ exports[`ignore 1`] = `
\\"source\\": 2,
\\"type\\": 5,
\\"id\\": 22
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1651,8 +1590,7 @@ exports[`ignore 1`] = `
\\"source\\": 2,
\\"type\\": 6,
\\"id\\": 22
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -1660,8 +1598,7 @@ exports[`ignore 1`] = `
\\"source\\": 2,
\\"type\\": 5,
\\"id\\": 27
},
\\"timestamp\\": 1542268800000
}
}
]"
`;
@@ -1670,13 +1607,11 @@ exports[`select2 1`] = `
"[
{
\\"type\\": 0,
\\"data\\": {},
\\"timestamp\\": 1542268800000
\\"data\\": {}
},
{
\\"type\\": 1,
\\"data\\": {},
\\"timestamp\\": 1542268800000
\\"data\\": {}
},
{
\\"type\\": 4,
@@ -1684,8 +1619,7 @@ exports[`select2 1`] = `
\\"href\\": \\"about:blank\\",
\\"width\\": 1920,
\\"height\\": 1080
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 2,
@@ -2189,8 +2123,7 @@ exports[`select2 1`] = `
\\"left\\": 0,
\\"top\\": 0
}
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -2198,8 +2131,7 @@ exports[`select2 1`] = `
\\"source\\": 2,
\\"type\\": 1,
\\"id\\": 26
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -2207,8 +2139,7 @@ exports[`select2 1`] = `
\\"source\\": 2,
\\"type\\": 5,
\\"id\\": 42
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -2647,8 +2578,7 @@ exports[`select2 1`] = `
}
}
]
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -2656,8 +2586,7 @@ exports[`select2 1`] = `
\\"source\\": 2,
\\"type\\": 0,
\\"id\\": 93
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -2666,8 +2595,7 @@ exports[`select2 1`] = `
\\"text\\": \\"\\",
\\"isChecked\\": false,
\\"id\\": 81
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -2676,8 +2604,7 @@ exports[`select2 1`] = `
\\"text\\": \\"\\",
\\"isChecked\\": false,
\\"id\\": 35
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -2685,8 +2612,7 @@ exports[`select2 1`] = `
\\"source\\": 2,
\\"type\\": 1,
\\"id\\": 93
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -2694,8 +2620,7 @@ exports[`select2 1`] = `
\\"source\\": 2,
\\"type\\": 6,
\\"id\\": 81
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -2703,8 +2628,7 @@ exports[`select2 1`] = `
\\"source\\": 2,
\\"type\\": 5,
\\"id\\": 35
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -2775,8 +2699,7 @@ exports[`select2 1`] = `
}
}
]
},
\\"timestamp\\": 1542268800000
}
},
{
\\"type\\": 3,
@@ -2784,8 +2707,7 @@ exports[`select2 1`] = `
\\"source\\": 2,
\\"type\\": 0,
\\"id\\": 67
},
\\"timestamp\\": 1542268800000
}
}
]"
`;

View File

@@ -0,0 +1,247 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`async-checkout 1`] = `
"[
{
\\"type\\": 4,
\\"data\\": {
\\"href\\": \\"about:blank\\",
\\"width\\": 800,
\\"height\\": 600
}
},
{
\\"type\\": 2,
\\"data\\": {
\\"node\\": {
\\"type\\": 0,
\\"childNodes\\": [
{
\\"type\\": 2,
\\"tagName\\": \\"html\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 2,
\\"tagName\\": \\"head\\",
\\"attributes\\": {},
\\"childNodes\\": [],
\\"id\\": 3
},
{
\\"type\\": 2,
\\"tagName\\": \\"body\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\",
\\"id\\": 5
},
{
\\"type\\": 2,
\\"tagName\\": \\"input\\",
\\"attributes\\": {
\\"type\\": \\"text\\"
},
\\"childNodes\\": [],
\\"id\\": 6
},
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\\\n \\\\n \\",
\\"id\\": 7
}
],
\\"id\\": 4
}
],
\\"id\\": 2
}
],
\\"id\\": 1
},
\\"initialOffset\\": {
\\"left\\": 0,
\\"top\\": 0
}
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 0,
\\"texts\\": [],
\\"attributes\\": [],
\\"removes\\": [
{
\\"parentId\\": 4,
\\"id\\": 6
}
],
\\"adds\\": [
{
\\"parentId\\": 4,
\\"previousId\\": 7,
\\"nextId\\": null,
\\"node\\": {
\\"type\\": 2,
\\"tagName\\": \\"p\\",
\\"attributes\\": {},
\\"childNodes\\": [],
\\"id\\": 8
}
},
{
\\"parentId\\": 8,
\\"previousId\\": null,
\\"nextId\\": null,
\\"node\\": {
\\"type\\": 2,
\\"tagName\\": \\"span\\",
\\"attributes\\": {},
\\"childNodes\\": [],
\\"id\\": 9
}
}
]
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 0,
\\"texts\\": [],
\\"attributes\\": [],
\\"removes\\": [],
\\"adds\\": [
{
\\"parentId\\": 9,
\\"previousId\\": null,
\\"nextId\\": null,
\\"node\\": {
\\"type\\": 3,
\\"textContent\\": \\"test\\",
\\"id\\": 10
}
}
]
}
},
{
\\"type\\": 4,
\\"data\\": {
\\"href\\": \\"about:blank\\",
\\"width\\": 800,
\\"height\\": 600
}
},
{
\\"type\\": 2,
\\"data\\": {
\\"node\\": {
\\"type\\": 0,
\\"childNodes\\": [
{
\\"type\\": 2,
\\"tagName\\": \\"html\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 2,
\\"tagName\\": \\"head\\",
\\"attributes\\": {},
\\"childNodes\\": [],
\\"id\\": 3
},
{
\\"type\\": 2,
\\"tagName\\": \\"body\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\",
\\"id\\": 5
},
{
\\"type\\": 3,
\\"textContent\\": \\"\\\\n \\\\n \\\\n \\",
\\"id\\": 6
},
{
\\"type\\": 2,
\\"tagName\\": \\"p\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 2,
\\"tagName\\": \\"span\\",
\\"attributes\\": {},
\\"childNodes\\": [
{
\\"type\\": 3,
\\"textContent\\": \\"test\\",
\\"id\\": 9
}
],
\\"id\\": 8
}
],
\\"id\\": 7
}
],
\\"id\\": 4
}
],
\\"id\\": 2
}
],
\\"id\\": 1
},
\\"initialOffset\\": {
\\"left\\": 0,
\\"top\\": 0
}
}
},
{
\\"type\\": 3,
\\"data\\": {
\\"source\\": 0,
\\"texts\\": [],
\\"attributes\\": [],
\\"removes\\": [
{
\\"parentId\\": 7,
\\"id\\": 8
}
],
\\"adds\\": [
{
\\"parentId\\": 4,
\\"previousId\\": 7,
\\"nextId\\": null,
\\"node\\": {
\\"type\\": 2,
\\"tagName\\": \\"span\\",
\\"attributes\\": {},
\\"childNodes\\": [],
\\"id\\": 10
}
},
{
\\"parentId\\": 10,
\\"previousId\\": null,
\\"nextId\\": null,
\\"node\\": {
\\"type\\": 3,
\\"textContent\\": \\"test\\",
\\"id\\": 11
}
}
]
}
}
]"
`;

View File

@@ -1,84 +1,7 @@
import * as fs from 'fs';
import * as path from 'path';
import * as puppeteer from 'puppeteer';
import { assert } from 'chai';
import { SnapshotState, toMatchSnapshot } from 'jest-snapshot';
import { EventType, IncrementalSource, eventWithTime } from '../src/types';
import { NodeType } from 'rrweb-snapshot';
function matchSnapshot(actual: string, testFile: string, testTitle: string) {
const snapshotState = new SnapshotState(testFile, {
updateSnapshot: process.env.SNAPSHOT_UPDATE ? 'all' : 'new',
});
const matcher = toMatchSnapshot.bind({
snapshotState,
currentTestName: testTitle,
});
const result = matcher(actual);
snapshotState.save();
return result;
}
/**
* Puppeteer may cast random mouse move which make our tests flaky.
* So we only do snapshot test with filtered events.
* @param snapshots incrementalSnapshotEvent[]
*/
function stringifySnapshots(snapshots: eventWithTime[]): string {
return JSON.stringify(
snapshots
.filter(s => {
if (
s.type === EventType.IncrementalSnapshot &&
s.data.source === IncrementalSource.MouseMove
) {
return false;
}
return true;
})
.map(s => {
if (s.type === EventType.Meta) {
s.data.href = 'about:blank';
}
// FIXME: travis coordinates seems different with my laptop
const coordinatesReg = /(bottom|top|left|right)/;
if (
s.type === EventType.IncrementalSnapshot &&
s.data.source === IncrementalSource.MouseInteraction
) {
delete s.data.x;
delete s.data.y;
}
if (
s.type === EventType.IncrementalSnapshot &&
s.data.source === IncrementalSource.Mutation
) {
s.data.attributes.forEach(a => {
if (
'style' in a.attributes &&
coordinatesReg.test(a.attributes.style!)
) {
delete a.attributes.style;
}
});
s.data.adds.forEach(add => {
if (
add.node.type === NodeType.Element &&
'style' in add.node.attributes &&
typeof add.node.attributes.style === 'string' &&
coordinatesReg.test(add.node.attributes.style)
) {
delete add.node.attributes.style;
}
});
}
return s;
}),
null,
2,
);
}
import { assertSnapshot } from './utils';
describe('record integration tests', () => {
function getHtml(fileName: string): string {
@@ -133,12 +56,7 @@ describe('record integration tests', () => {
await page.select('select', '1');
const snapshots = await page.evaluate('window.snapshots');
const result = matchSnapshot(
stringifySnapshots(snapshots),
__filename,
'form',
);
assert(result.pass, result.pass ? '' : result.report());
assertSnapshot(snapshots, __filename, 'form');
}).timeout(5000);
it('can record childList mutations', async () => {
@@ -156,12 +74,7 @@ describe('record integration tests', () => {
});
const snapshots = await page.evaluate('window.snapshots');
const result = matchSnapshot(
stringifySnapshots(snapshots),
__filename,
'child-list',
);
assert(result.pass, result.pass ? '' : result.report());
assertSnapshot(snapshots, __filename, 'child-list');
}).timeout(5000);
it('can record character data muatations', async () => {
@@ -181,12 +94,7 @@ describe('record integration tests', () => {
});
const snapshots = await page.evaluate('window.snapshots');
const result = matchSnapshot(
stringifySnapshots(snapshots),
__filename,
'character-data',
);
assert(result.pass, result.pass ? '' : result.report());
assertSnapshot(snapshots, __filename, 'character-data');
});
it('can record attribute mutation', async () => {
@@ -204,12 +112,7 @@ describe('record integration tests', () => {
});
const snapshots = await page.evaluate('window.snapshots');
const result = matchSnapshot(
stringifySnapshots(snapshots),
__filename,
'attributes',
);
assert(result.pass, result.pass ? '' : result.report());
assertSnapshot(snapshots, __filename, 'attributes');
});
it('can record node mutations', async () => {
@@ -228,12 +131,7 @@ describe('record integration tests', () => {
await page.click('.select2-container');
const snapshots = await page.evaluate('window.snapshots');
const result = matchSnapshot(
stringifySnapshots(snapshots),
__filename,
'select2',
);
assert(result.pass, result.pass ? '' : result.report());
assertSnapshot(snapshots, __filename, 'select2');
}).timeout(10000);
it('should not record input events on ignored elements', async () => {
@@ -245,12 +143,7 @@ describe('record integration tests', () => {
await page.type('.rr-ignore', 'secret');
const snapshots = await page.evaluate('window.snapshots');
const result = matchSnapshot(
stringifySnapshots(snapshots),
__filename,
'ignore',
);
assert(result.pass, result.pass ? '' : result.report());
assertSnapshot(snapshots, __filename, 'ignore');
});
it('should not record blocked elements and its child nodes', async () => {
@@ -263,11 +156,6 @@ describe('record integration tests', () => {
await page.click('#text');
const snapshots = await page.evaluate('window.snapshots');
const result = matchSnapshot(
stringifySnapshots(snapshots),
__filename,
'block',
);
assert(result.pass, result.pass ? '' : result.report());
assertSnapshot(snapshots, __filename, 'block');
});
});

175
test/record.test.ts Normal file
View File

@@ -0,0 +1,175 @@
/* tslint:disable no-console */
import * as fs from 'fs';
import * as path from 'path';
import * as puppeteer from 'puppeteer';
import { expect } from 'chai';
import {
recordOptions,
listenerHandler,
eventWithTime,
EventType,
} from '../src/types';
import { assertSnapshot } from './utils';
interface IWindow extends Window {
rrweb: {
record: (options: recordOptions) => listenerHandler | undefined;
};
emit: (e: eventWithTime) => undefined;
}
describe('record', () => {
before(async () => {
this.browser = await puppeteer.launch({
headless: false,
args: ['--no-sandbox'],
});
const bundlePath = path.resolve(__dirname, '../dist/rrweb.min.js');
this.code = fs.readFileSync(bundlePath, 'utf8');
});
beforeEach(async () => {
const page: puppeteer.Page = await this.browser.newPage();
await page.goto('about:blank');
await page.setContent(`
<html>
<body>
<input type="text" />
</body>
</html>
`);
await page.evaluate(this.code);
this.page = page;
this.events = [];
await this.page.exposeFunction('emit', (e: eventWithTime) => {
if (e.type === EventType.DomContentLoaded || e.type === EventType.Load) {
return;
}
this.events.push(e);
});
page.on('console', msg => console.log('PAGE LOG:', msg.text()));
});
afterEach(async () => {
await this.page.close();
});
after(async () => {
await this.browser.close();
});
it('will only have one full snapshot without checkout config', async () => {
await this.page.evaluate(() => {
const { record } = (window as IWindow).rrweb;
record({
emit: (window as IWindow).emit,
});
});
let count = 30;
while (count--) {
await this.page.type('input', 'a');
}
await this.page.waitFor(10);
expect(this.events.length).to.equal(33);
expect(
this.events.filter(
(event: eventWithTime) => event.type === EventType.Meta,
).length,
).to.equal(1);
expect(
this.events.filter(
(event: eventWithTime) => event.type === EventType.FullSnapshot,
).length,
).to.equal(1);
});
it('can checkout full snapshot by count', async () => {
await this.page.evaluate(() => {
const { record } = (window as IWindow).rrweb;
record({
emit: (window as IWindow).emit,
checkoutEveryNth: 10,
});
});
let count = 30;
while (count--) {
await this.page.type('input', 'a');
}
await this.page.waitFor(10);
expect(this.events.length).to.equal(39);
expect(
this.events.filter(
(event: eventWithTime) => event.type === EventType.Meta,
).length,
).to.equal(4);
expect(
this.events.filter(
(event: eventWithTime) => event.type === EventType.FullSnapshot,
).length,
).to.equal(4);
expect(this.events[1].type).to.equal(EventType.FullSnapshot);
expect(this.events[13].type).to.equal(EventType.FullSnapshot);
expect(this.events[25].type).to.equal(EventType.FullSnapshot);
expect(this.events[37].type).to.equal(EventType.FullSnapshot);
});
it('can checkout full snapshot by time', async () => {
await this.page.evaluate(() => {
const { record } = (window as IWindow).rrweb;
record({
emit: (window as IWindow).emit,
checkoutEveryNms: 500,
});
});
let count = 30;
while (count--) {
await this.page.type('input', 'a');
}
await this.page.waitFor(500);
expect(this.events.length).to.equal(33);
await this.page.type('input', 'a');
await this.page.waitFor(10);
expect(this.events.length).to.equal(36);
expect(
this.events.filter(
(event: eventWithTime) => event.type === EventType.Meta,
).length,
).to.equal(2);
expect(
this.events.filter(
(event: eventWithTime) => event.type === EventType.FullSnapshot,
).length,
).to.equal(2);
expect(this.events[1].type).to.equal(EventType.FullSnapshot);
expect(this.events[35].type).to.equal(EventType.FullSnapshot);
});
it('is safe to checkout during async callbacks', async () => {
await this.page.evaluate(() => {
const { record } = (window as IWindow).rrweb;
record({
emit: (window as IWindow).emit,
checkoutEveryNth: 2,
});
const p = document.createElement('p');
const span = document.createElement('span');
setTimeout(() => {
document.body.appendChild(p);
p.appendChild(span);
document.body.removeChild(document.querySelector('input')!);
}, 0);
setTimeout(() => {
span.innerText = 'test';
}, 10);
setTimeout(() => {
p.removeChild(span);
document.body.appendChild(span);
}, 10);
});
await this.page.waitFor(50);
assertSnapshot(this.events, __filename, 'async-checkout');
});
});

View File

@@ -1,4 +1,4 @@
/* tslint:disable no-string-literal */
/* tslint:disable no-string-literal no-console */
import * as fs from 'fs';
import * as path from 'path';

89
test/utils.ts Normal file
View File

@@ -0,0 +1,89 @@
import { SnapshotState, toMatchSnapshot } from 'jest-snapshot';
import { NodeType } from 'rrweb-snapshot';
import { assert } from 'chai';
import { EventType, IncrementalSource, eventWithTime } from '../src/types';
function matchSnapshot(actual: string, testFile: string, testTitle: string) {
const snapshotState = new SnapshotState(testFile, {
updateSnapshot: process.env.SNAPSHOT_UPDATE ? 'all' : 'new',
});
const matcher = toMatchSnapshot.bind({
snapshotState,
currentTestName: testTitle,
});
const result = matcher(actual);
snapshotState.save();
return result;
}
/**
* Puppeteer may cast random mouse move which make our tests flaky.
* So we only do snapshot test with filtered events.
* Also remove timestamp from event.
* @param snapshots incrementalSnapshotEvent[]
*/
function stringifySnapshots(snapshots: eventWithTime[]): string {
return JSON.stringify(
snapshots
.filter(s => {
if (
s.type === EventType.IncrementalSnapshot &&
s.data.source === IncrementalSource.MouseMove
) {
return false;
}
return true;
})
.map(s => {
if (s.type === EventType.Meta) {
s.data.href = 'about:blank';
}
// FIXME: travis coordinates seems different with my laptop
const coordinatesReg = /(bottom|top|left|right)/;
if (
s.type === EventType.IncrementalSnapshot &&
s.data.source === IncrementalSource.MouseInteraction
) {
delete s.data.x;
delete s.data.y;
}
if (
s.type === EventType.IncrementalSnapshot &&
s.data.source === IncrementalSource.Mutation
) {
s.data.attributes.forEach(a => {
if (
'style' in a.attributes &&
coordinatesReg.test(a.attributes.style!)
) {
delete a.attributes.style;
}
});
s.data.adds.forEach(add => {
if (
add.node.type === NodeType.Element &&
'style' in add.node.attributes &&
typeof add.node.attributes.style === 'string' &&
coordinatesReg.test(add.node.attributes.style)
) {
delete add.node.attributes.style;
}
});
}
delete s.timestamp;
return s;
}),
null,
2,
);
}
export function assertSnapshot(
snapshots: eventWithTime[],
filename: string,
name: string,
) {
const result = matchSnapshot(stringifySnapshots(snapshots), filename, name);
assert(result.pass, result.pass ? '' : result.report());
}