Files
rrweb/packages/rrweb/test/replay/dialog.test.ts
Justin Halsall 335639af9b Support top-layer <dialog> recording & replay (#1503)
* chore: its important to run `yarn build:all` before running `yarn dev`

* feat: trigger showModal from rrdom and rrweb

* feat: Add support for replaying modal and non modal dialog elements

* chore: Update dev script to remove CLEAR_DIST_DIR flag

* Get modal recording and replay working

* DRY up dialog test and dedupe snapshot images

* feat: Refactor dialog test to use updated attribute name

* feat: Update dialog test to include rr_open attribute

* chore: Add npm dependency happy-dom@14.12.0

* Add more test cases for dialog

* Clean up naming

* Refactor dialog open code

* Revert changed code that doesn't do anything

* Add documentation for unimplemented type

* chore: Remove unnecessary comments in dialog.test.ts

* rename rr_open to rr_openMode

* Replace todo with a skipped test

* Add better logging for CI

* Rename rr_openMode to rr_open_mode

rrdom downcases all attribute names which made `rr_openMode` tricky to deal with

* Remove unused images

* Move after iframe append based on @YunFeng0817's comment
https://github.com/rrweb-io/rrweb/pull/1503#discussion_r1666363931

* Remove redundant dialog handling from rrdom.

rrdom already handles dialog element creation it's self

* Rename variables for dialog handling in rrweb replay module

* Update packages/rrdom/src/document.ts

---------

Co-authored-by: Eoghan Murray <eoghan@getthere.ie>
2024-08-02 09:53:05 +02:00

160 lines
4.6 KiB
TypeScript

import * as fs from 'fs';
import { toMatchImageSnapshot } from 'jest-image-snapshot';
import * as path from 'path';
import { vi } from 'vitest';
import dialogPlaybackEvents, {
closedFullSnapshotTime,
showIncrementalAttributeTime,
closeIncrementalAttributeTime,
showModalIncrementalAttributeTime,
showFullSnapshotTime,
showModalFullSnapshotTime,
showModalIncrementalAddTime,
switchBetweenShowModalAndShowIncrementalAttributeTime,
switchBetweenShowAndShowModalIncrementalAttributeTime,
} from '../events/dialog-playback';
import {
fakeGoto,
getServerURL,
hideMouseAnimation,
ISuite,
launchPuppeteer,
startServer,
waitForRAF,
} from '../utils';
expect.extend({ toMatchImageSnapshot });
describe('dialog', () => {
vi.setConfig({ testTimeout: 100_000 });
let code: ISuite['code'];
let page: ISuite['page'];
let browser: ISuite['browser'];
let server: ISuite['server'];
let serverURL: ISuite['serverURL'];
beforeAll(async () => {
server = await startServer();
serverURL = getServerURL(server);
browser = await launchPuppeteer();
const bundlePath = path.resolve(__dirname, '../../dist/rrweb.umd.cjs');
code = fs.readFileSync(bundlePath, 'utf8');
});
afterEach(async () => {
await page.close();
});
afterAll(async () => {
await server.close();
await browser.close();
});
beforeEach(async () => {
page = await browser.newPage();
page.on('console', (msg) => {
console.log(msg.text());
});
await fakeGoto(page, `${serverURL}/html/dialog.html`);
await page.evaluate(code);
await waitForRAF(page);
await hideMouseAnimation(page);
});
[
{
name: 'show the dialog when open attribute gets added',
time: showIncrementalAttributeTime,
},
{
name: 'should close dialog again when open attribute gets removed',
time: closeIncrementalAttributeTime,
},
{
name: 'should open dialog with showModal',
time: showModalIncrementalAttributeTime,
},
{
name: 'should switch between showModal and show',
time: switchBetweenShowModalAndShowIncrementalAttributeTime,
},
{
name: 'should switch between show and showModal',
time: switchBetweenShowAndShowModalIncrementalAttributeTime,
},
{
name: 'should open dialog with show in full snapshot',
time: showFullSnapshotTime,
},
{
name: 'should open dialog with showModal in full snapshot',
time: showModalFullSnapshotTime,
},
{
name: 'should add an opened dialog with showModal in incremental snapshot',
time: showModalIncrementalAddTime,
},
{
name: 'should add an opened dialog with showModal in incremental snapshot alternative',
time: [showModalFullSnapshotTime, showModalIncrementalAddTime],
},
].forEach(({ name, time }) => {
[true, false].forEach((useVirtualDom) => {
it(`${name} (virtual dom: ${useVirtualDom})`, async () => {
await page.evaluate(
`let events = ${JSON.stringify(dialogPlaybackEvents)}`,
);
await page.evaluate(`
const { Replayer } = rrweb;
window.replayer = new Replayer(events, { useVirtualDom: ${useVirtualDom} });
`);
const timeArray = Array.isArray(time) ? time : [time];
for (let i = 0; i < timeArray.length; i++) {
await page.evaluate(`
window.replayer.pause(${timeArray[i]});
`);
await waitForRAF(page);
}
const frameImage = await page!.screenshot({
fullPage: false,
});
const defaultImageFilePrefix =
'dialog-test-ts-test-replay-dialog-test-ts-dialog';
const kebabCaseName = name
.replace(/ /g, '-')
.replace(/showModal/g, 'show-modal');
const imageFileName = `${defaultImageFilePrefix}-${kebabCaseName}`;
expect(frameImage).toMatchImageSnapshot({
customSnapshotIdentifier: imageFileName,
failureThreshold: 0.05,
failureThresholdType: 'percent',
dumpDiffToConsole: true,
storeReceivedOnFailure: true,
});
});
});
});
it('closed dialogs show nothing', async () => {
await page.evaluate(`let events = ${JSON.stringify(dialogPlaybackEvents)}`);
await page.evaluate(`
const { Replayer } = rrweb;
window.replayer = new Replayer(events);
`);
await waitForRAF(page);
const frameImage = await page!.screenshot();
expect(frameImage).toMatchImageSnapshot({
failureThreshold: 0.05,
failureThresholdType: 'percent',
});
});
// TODO: implement me in the future
it.skip('should trigger showModal on multiple dialogs in a specific order');
});