Skip to content

Commit 6c6cb2f

Browse files
committed
test(report): cover replay-all autoplay in playwright e2e
1 parent 2c4fcd3 commit 6c6cb2f

File tree

2 files changed

+171
-2
lines changed

2 files changed

+171
-2
lines changed

apps/report/src/components/store/index.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,10 @@ export const useExecutionDump = create<DumpStoreType>((set, get) => {
167167
deviceType: metaInfo.deviceType,
168168
});
169169

170-
// Default to first task static view instead of replay-all
171-
if (dump.executions[0].tasks.length > 0) {
170+
// Default to replay-all when available so opening a report starts playback.
171+
get().setReplayAllMode(true);
172+
173+
if (!get().replayAllMode && dump.executions[0].tasks.length > 0) {
172174
get().setActiveTask(dump.executions[0].tasks[0]);
173175
}
174176
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import { existsSync } from 'node:fs';
2+
import { join } from 'node:path';
3+
import { PlaywrightAgent } from '@/playwright';
4+
import { sleep } from '@midscene/core/utils';
5+
import { getMidsceneRunSubDir } from '@midscene/shared/common';
6+
import { expect, test } from '@playwright/test';
7+
8+
const REPLAY_ALL_SELECTOR = '.replay-all-mode-wrapper';
9+
const TIME_DISPLAY_SELECTOR = `${REPLAY_ALL_SELECTOR} .time-display`;
10+
const PLAYBACK_ADVANCE_TIMEOUT = 15_000;
11+
const PLAYBACK_COMPLETE_TIMEOUT = 30_000;
12+
const TEST_TIMEOUT = 15 * 60 * 1000;
13+
14+
function parseTimeText(text: string): number {
15+
const [minuteText, secondText] = text.trim().split(':');
16+
const minutes = Number(minuteText);
17+
const seconds = Number(secondText);
18+
19+
if (!Number.isFinite(minutes) || !Number.isFinite(seconds)) {
20+
throw new Error(`Unexpected playback time text: ${text}`);
21+
}
22+
23+
return minutes * 60 + seconds;
24+
}
25+
26+
function parsePlaybackTime(timeText: string): {
27+
currentSeconds: number;
28+
totalSeconds: number;
29+
} {
30+
const [currentTimeText = '', totalTimeText = ''] = timeText.split('/');
31+
32+
return {
33+
currentSeconds: parseTimeText(currentTimeText),
34+
totalSeconds: parseTimeText(totalTimeText),
35+
};
36+
}
37+
38+
function getReportPath(reportFileName: string): string {
39+
return join(getMidsceneRunSubDir('report'), `${reportFileName}.html`);
40+
}
41+
42+
async function waitForPlaybackToAdvance(
43+
readCurrentSeconds: () => Promise<number>,
44+
previousSeconds: number,
45+
): Promise<number> {
46+
const startTime = Date.now();
47+
48+
while (Date.now() - startTime < PLAYBACK_ADVANCE_TIMEOUT) {
49+
const currentSeconds = await readCurrentSeconds();
50+
if (currentSeconds > previousSeconds) {
51+
return currentSeconds;
52+
}
53+
await sleep(500);
54+
}
55+
56+
throw new Error(
57+
`Replay time did not advance within ${PLAYBACK_ADVANCE_TIMEOUT}ms`,
58+
);
59+
}
60+
61+
async function waitForPlaybackToReach(
62+
readCurrentSeconds: () => Promise<number>,
63+
targetSeconds: number,
64+
): Promise<number> {
65+
const startTime = Date.now();
66+
67+
while (Date.now() - startTime < PLAYBACK_COMPLETE_TIMEOUT) {
68+
const currentSeconds = await readCurrentSeconds();
69+
if (currentSeconds >= targetSeconds) {
70+
return currentSeconds;
71+
}
72+
await sleep(500);
73+
}
74+
75+
throw new Error(
76+
`Replay time did not reach ${targetSeconds}s within ${PLAYBACK_COMPLETE_TIMEOUT}ms`,
77+
);
78+
}
79+
80+
test.describe('report replay-all', () => {
81+
test.setTimeout(TEST_TIMEOUT);
82+
83+
test('should autoplay from the beginning instead of staying on the last step page', async ({
84+
page,
85+
}) => {
86+
const htmlPath = join(__dirname, '../../fixtures/search-engine.html');
87+
const reportId = `report-replay-all-${Date.now()}`;
88+
const validationReportId = `${reportId}-validation`;
89+
const validationReportPath = getReportPath(validationReportId);
90+
91+
await page.setViewportSize({ width: 1440, height: 900 });
92+
await page.goto(`file://${htmlPath}`);
93+
94+
const sourceAgent = new PlaywrightAgent(page, {
95+
cacheId: reportId,
96+
reportFileName: reportId,
97+
});
98+
99+
const reportPage = await page.context().newPage();
100+
const reportAgent = new PlaywrightAgent(reportPage, {
101+
cacheId: validationReportId,
102+
reportFileName: validationReportId,
103+
});
104+
105+
try {
106+
await sourceAgent.aiAct(
107+
'Type "Hello world" in the search box, then click the Search button.',
108+
);
109+
await sourceAgent.aiAssert(
110+
'The page is now showing search results about "Hello world".',
111+
);
112+
113+
const reportFile = sourceAgent.reportFile;
114+
expect(reportFile).toBeTruthy();
115+
116+
await reportPage.setViewportSize({ width: 1440, height: 900 });
117+
await reportPage.goto(`file://${reportFile}`);
118+
await reportPage.waitForSelector(REPLAY_ALL_SELECTOR, {
119+
timeout: 30_000,
120+
});
121+
await reportPage.waitForSelector(TIME_DISPLAY_SELECTOR, {
122+
timeout: 30_000,
123+
});
124+
125+
await reportAgent.aiAssert(
126+
'Only inspect the large replay player in the main content area. It is showing the initial search page with a centered large search box. It is not staying on the final "Hello world" search results page.',
127+
);
128+
129+
const initialTimeText = await reportPage
130+
.locator(TIME_DISPLAY_SELECTOR)
131+
.innerText();
132+
const { currentSeconds: initialSeconds, totalSeconds } =
133+
parsePlaybackTime(initialTimeText.trim());
134+
135+
const advancedSeconds = await waitForPlaybackToAdvance(async () => {
136+
const timeText = await reportPage
137+
.locator(TIME_DISPLAY_SELECTOR)
138+
.innerText();
139+
return parsePlaybackTime(timeText.trim()).currentSeconds;
140+
}, initialSeconds);
141+
142+
await waitForPlaybackToReach(
143+
async () => {
144+
const timeText = await reportPage
145+
.locator(TIME_DISPLAY_SELECTOR)
146+
.innerText();
147+
return parsePlaybackTime(timeText.trim()).currentSeconds;
148+
},
149+
Math.max(advancedSeconds + 1, totalSeconds),
150+
);
151+
152+
await sleep(1_000);
153+
154+
await reportAgent.aiAssert(
155+
'Only inspect the large replay player in the main content area. It is now showing the final search results page for "Hello world".',
156+
);
157+
158+
expect(existsSync(validationReportPath)).toBe(true);
159+
console.log('Source report file:', reportFile);
160+
console.log('Validation report file:', validationReportPath);
161+
} finally {
162+
await reportAgent.destroy();
163+
await sourceAgent.destroy();
164+
await reportPage.close();
165+
}
166+
});
167+
});

0 commit comments

Comments
 (0)