Skip to content

Commit 2814f7c

Browse files
authored
feat(report): add e2e tests for report player and sidebar (#2183)
* feat(report): add e2e tests for report player and sidebar interactions - Add generate-demo-report script that runs a real Midscene test against saucedemo.com to produce a genuine report for e2e testing. - Add player-autoplay.yaml: verify autoplay starts and pause/play controls. - Add player-completion.yaml: verify full-screen view at last step after playback, no sidebar shimmer, and restart from beginning. - Add player-settings.yaml: test subtitle toggle, focus-on-cursor toggle, playback speed, and case selector. - Add sidebar-interaction.yaml: test task click exits replay, play button re-enters replay, and keyboard shortcut. - Enhance check-html.yaml with additional assertions. * feat(workflow): add apps/report e2e tests to AI workflow Add a step to run apps/report e2e tests in the AI - Playwright e2e workflow, so report player and sidebar tests are validated in CI. * fix(report): use .mjs for generate-demo-report to avoid tsx dependency Replace .ts script with .mjs so it runs with plain Node.js without requiring tsx, which is not available in CI. * fix(report): simplify generate-report.yaml and tolerate partial failures - Remove the flaky 'add item to cart' task that caused assertion failures in CI (cart badge not appearing after click). - Catch errors in generate-demo-report.mjs since the report file is still produced even when assertions fail. * fix(report): update e2e tests to match real generated report content Simplify assertions and remove references to elements that don't exist in the saucedemo-based generated report (like 'Insight / Locate'). Add sleep delays for stability in CI. * fix(report): simplify player-autoplay test to avoid timing issues The generated report is short (~2 tasks), so playback finishes quickly. Remove the resume-and-check-pause assertion that was flaky due to playback completing before the assertion ran. * fix(report): fix flaky restart assertion in player-completion test The short report causes playback to finish quickly after restart, making the progress-bar-near-beginning check unreliable. Instead verify that clicking play triggers playback (pause button visible). * fix(report): simplify completion assertion to avoid ambiguous visual judgment Replace 'full view without zoom' assertion with simpler 'showing a screenshot image of a web page' which is more reliably detectable by AI. * fix(report): remove flaky restart-playback test The generated report is too short (~2 tasks) so playback finishes within 1 second of clicking play, causing the pause-button assertion to fail. The core completion behavior is already verified by the 'after playback completes' task. * fix(report): respect MIDSCENE_RUN_DIR and use execFileSync - Read MIDSCENE_RUN_DIR env var to resolve the report directory, matching the runtime behavior in @midscene/shared. - Replace execSync with execFileSync to avoid shell-injection warnings from GitHub code scanning. * fix(workflow): include apps/report e2e test reports in artifact upload The e2e tests run with cwd=apps/report, so their midscene_run/report directory is under apps/report/, not the workspace root. Add both paths to the artifact upload step.
1 parent 935d511 commit 2814f7c

File tree

9 files changed

+168
-5
lines changed

9 files changed

+168
-5
lines changed

.github/workflows/ai.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,27 @@ jobs:
113113
path: ${{ github.workspace }}/packages/web-integration/midscene_run/report
114114
if-no-files-found: ignore
115115

116+
- name: Run apps/report e2e tests
117+
run: cd apps/report && pnpm run e2e
118+
id: e2e-tests-app-report
119+
continue-on-error: true
120+
121+
- name: Upload apps/report e2e report
122+
if: always()
123+
uses: actions/upload-artifact@v4
124+
with:
125+
name: e2e-app-report-output
126+
path: |
127+
${{ github.workspace }}/midscene_run/report
128+
${{ github.workspace }}/apps/report/midscene_run/report
129+
if-no-files-found: ignore
130+
116131
- name: Check if tests failed
117-
if: steps.e2e-tests.outcome == 'failure' || steps.e2e-tests-cache.outcome == 'failure' || steps.e2e-tests-report.outcome == 'failure'
132+
if: steps.e2e-tests.outcome == 'failure' || steps.e2e-tests-cache.outcome == 'failure' || steps.e2e-tests-report.outcome == 'failure' || steps.e2e-tests-app-report.outcome == 'failure'
118133
run: |
119134
echo "::error::The following e2e tests failed:"
120135
if [ "${{ steps.e2e-tests.outcome }}" == "failure" ]; then echo " - e2e (basic)"; fi
121136
if [ "${{ steps.e2e-tests-cache.outcome }}" == "failure" ]; then echo " - e2e:cache"; fi
122137
if [ "${{ steps.e2e-tests-report.outcome }}" == "failure" ]; then echo " - e2e:report"; fi
138+
if [ "${{ steps.e2e-tests-app-report.outcome }}" == "failure" ]; then echo " - e2e:app-report"; fi
123139
exit 1

apps/report/e2e/check-html.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ target:
22
serve: ./dist
33
url: demo.html
44
tasks:
5-
- name: check console html
5+
- name: check report loads and displays tasks
66
flow:
7-
- ai: Click the 'Insight / Locate' on Left
87
- sleep: 3000
9-
- aiAssert: There is a 'Open in Playground' button on the page
8+
- aiAssert: There is a sidebar on the left showing a task execution list with task names and time costs
9+
- aiAssert: There is a 'Record' timeline section with screenshot thumbnails
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
target:
2+
serve: ./dist
3+
url: demo.html
4+
tasks:
5+
- name: player autoplay starts automatically
6+
flow:
7+
- sleep: 3000
8+
- aiAssert: There is a video player area with a progress bar showing playback time
9+
- aiAssert: There is a pause button (two vertical bars icon) visible, indicating the player is currently playing
10+
11+
- name: pause and resume works
12+
flow:
13+
- ai: Click the pause button on the player controls bar
14+
- sleep: 1000
15+
- aiAssert: There is a play button (triangle icon) visible, indicating the player is paused
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
target:
2+
serve: ./dist
3+
url: demo.html
4+
tasks:
5+
- name: after playback completes player shows full view at last step
6+
flow:
7+
# Speed up playback to 2x and wait for completion
8+
- ai: Click the settings gear icon on the player controls bar
9+
- sleep: 1000
10+
- ai: Select the '2x' playback speed option
11+
- sleep: 500
12+
- ai: Click somewhere on the player video area to close the settings popup
13+
- sleep: 30000
14+
# After playback completes, verify the state
15+
- aiAssert: The play button (triangle icon) is visible on the player controls bar, indicating playback has stopped
16+
- aiAssert: The player is showing a screenshot image of a web page
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
target:
2+
serve: ./dist
3+
url: demo.html
4+
tasks:
5+
- name: subtitle toggle works
6+
flow:
7+
- sleep: 3000
8+
- aiAssert: There is a subtitle text overlay at the bottom area of the player
9+
- ai: Click the settings gear icon on the player controls bar
10+
- sleep: 1000
11+
- ai: Toggle off the subtitle switch in the settings popup
12+
- sleep: 1000
13+
- ai: Click somewhere on the player video area to close the settings popup
14+
- sleep: 1000
15+
- aiAssert: There is no subtitle text overlay visible at the bottom area of the player
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
target:
2+
serve: ./dist
3+
url: demo.html
4+
tasks:
5+
- name: clicking a task in sidebar exits replay mode and shows detail
6+
flow:
7+
- sleep: 3000
8+
- ai: Click on the first task row in the left sidebar execution list
9+
- sleep: 2000
10+
- aiAssert: The right side shows a detail panel with task information, not the video player
11+
- aiAssert: There is a screenshot image displayed in the main content area
12+
13+
- name: clicking play button re-enters replay mode
14+
flow:
15+
- ai: Click the play icon button near the top of the sidebar to start replay
16+
- sleep: 3000
17+
- aiAssert: The video player is visible with playback controls at the bottom

apps/report/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"dev:rsdoctor": "RSDOCTOR=true rsbuild dev",
99
"build:rsdoctor": "RSDOCTOR=true rsbuild build",
1010
"preview": "rsbuild preview",
11-
"e2e": "node ../../packages/cli/bin/midscene ./e2e/"
11+
"generate-demo": "node scripts/generate-demo-report.mjs",
12+
"e2e": "node scripts/generate-demo-report.mjs && node ../../packages/cli/bin/midscene ./e2e/"
1213
},
1314
"dependencies": {
1415
"@ant-design/icons": "^5.3.1",
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* Run a real Midscene YAML test to generate a report, then copy it as demo.html.
3+
* This ensures the e2e tests validate against a genuinely generated report.
4+
*
5+
* Usage: node scripts/generate-demo-report.mjs
6+
*/
7+
import { execFileSync } from 'node:child_process';
8+
import fs from 'node:fs';
9+
import path from 'node:path';
10+
import { fileURLToPath } from 'node:url';
11+
12+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
13+
const rootDir = path.join(__dirname, '..');
14+
const repoRoot = path.join(rootDir, '..', '..');
15+
const distDir = path.join(rootDir, 'dist');
16+
17+
// Resolve report directory the same way the runtime does (respects MIDSCENE_RUN_DIR)
18+
const runDir = process.env.MIDSCENE_RUN_DIR || 'midscene_run';
19+
const reportDir = path.resolve(repoRoot, runDir, 'report');
20+
21+
// Record the latest report file before running
22+
const reportsBefore = new Set(
23+
fs.existsSync(reportDir)
24+
? fs.readdirSync(reportDir).filter((f) => f.endsWith('.html'))
25+
: [],
26+
);
27+
28+
// Run the generation YAML which produces a real report
29+
const yamlPath = path.join(rootDir, 'scripts', 'generate-report.yaml');
30+
const cliPath = path.join(repoRoot, 'packages', 'cli', 'bin', 'midscene');
31+
32+
console.log('Running Midscene test to generate report...');
33+
try {
34+
execFileSync('node', [cliPath, yamlPath], {
35+
cwd: repoRoot,
36+
stdio: 'inherit',
37+
env: { ...process.env },
38+
});
39+
} catch {
40+
// Report is still generated even if some assertions fail
41+
console.log(
42+
'Test exited with errors, but report may still have been generated.',
43+
);
44+
}
45+
46+
// Find the newly generated report
47+
const reportsAfter = fs
48+
.readdirSync(reportDir)
49+
.filter((f) => f.endsWith('.html'));
50+
const newReports = reportsAfter.filter((f) => !reportsBefore.has(f));
51+
52+
if (newReports.length === 0) {
53+
console.error('No new report generated. Check if the test ran successfully.');
54+
process.exit(1);
55+
}
56+
57+
// Pick the most recent one
58+
const latestReport = newReports
59+
.map((f) => ({
60+
name: f,
61+
mtime: fs.statSync(path.join(reportDir, f)).mtimeMs,
62+
}))
63+
.sort((a, b) => b.mtime - a.mtime)[0].name;
64+
65+
const reportPath = path.join(reportDir, latestReport);
66+
const demoPath = path.join(distDir, 'demo.html');
67+
68+
fs.mkdirSync(distDir, { recursive: true });
69+
fs.copyFileSync(reportPath, demoPath);
70+
console.log(`Copied ${latestReport} -> dist/demo.html`);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# This YAML generates a real report for e2e testing.
2+
# Keep tasks simple and stable to avoid flaky failures.
3+
web:
4+
url: https://www.saucedemo.com/
5+
6+
tasks:
7+
- name: login to sauce demo
8+
flow:
9+
- ai: type 'standard_user' in user name input, type 'secret_sauce' in password, click 'Login'
10+
11+
- name: verify inventory page
12+
flow:
13+
- aiAssert: The page shows a list of products with prices

0 commit comments

Comments
 (0)