Skip to content

Commit 388e28c

Browse files
committed
fix: improve maestro test output
1 parent 096785b commit 388e28c

9 files changed

Lines changed: 430 additions & 29 deletions

File tree

src/__tests__/cli-network.test.ts

Lines changed: 119 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { runCliCapture } from './cli-capture.ts';
88
function makeFailedReplayResult() {
99
return {
1010
file: '/tmp/02-fail.ad',
11+
title: 'Checkout failure',
1112
session: 'default:test:suite:2',
1213
status: 'failed',
1314
durationMs: 5,
@@ -106,11 +107,14 @@ test('test command prints suite summary and exits non-zero on failures', async (
106107
assert.equal(result.calls[0]?.meta?.requestProgress, 'replay-test');
107108
assert.match(result.stderr, /Running replay suite\.\.\./);
108109
assert.doesNotMatch(result.stdout, /PASS \/tmp\/01-pass\.ad/);
109-
assert.match(result.stdout, /FAIL \/tmp\/02-fail\.ad after 2 attempts \(5ms\)/);
110+
assert.match(
111+
result.stdout,
112+
/FAIL "Checkout failure" in 02-fail\.ad after 2 attempts \(total 0\.005s\)/,
113+
);
110114
assert.match(result.stdout, /Replay failed at step 1 \(open Demo\): boom/);
111115
assert.match(result.stdout, /artifacts: \/tmp\/test-artifacts\/02-fail/);
112116
assert.doesNotMatch(result.stdout, /SKIP \/tmp\/03-skip\.ad/);
113-
assert.match(result.stdout, /Test summary: 1 passed, 1 failed in 25ms/);
117+
assert.match(result.stdout, /Test summary: 1 passed, 1 failed in 0\.025s/);
114118
});
115119

116120
test('test command --verbose prints all test statuses', async () => {
@@ -120,8 +124,8 @@ test('test command --verbose prints all test statuses', async () => {
120124

121125
assert.equal(result.code, 1);
122126
assert.match(result.stderr, /Running replay suite\.\.\./);
123-
assert.match(result.stdout, /PASS \/tmp\/01-pass\.ad \(10ms\)/);
124-
assert.match(result.stdout, /SKIP \/tmp\/03-skip\.ad/);
127+
assert.match(result.stdout, /PASS 01-pass\.ad \(0\.01s\)/);
128+
assert.match(result.stdout, /SKIP 03-skip\.ad/);
125129
});
126130

127131
test('test command reports flaky passed-on-retry cases in the default summary', async () => {
@@ -138,20 +142,127 @@ test('test command reports flaky passed-on-retry cases in the default summary',
138142
failures: [],
139143
tests: [
140144
{
141-
file: '/tmp/01-flaky.ad',
145+
file: '/tmp/auth-flow.yml',
146+
title: 'Authentication flow',
142147
session: 'default:test:suite:1',
143148
status: 'passed',
144-
durationMs: 10,
149+
durationMs: 112151,
150+
finalAttemptDurationMs: 17492,
145151
attempts: 2,
152+
attemptFailures: [
153+
{
154+
attempt: 1,
155+
message: 'Replay failed at step 3 (tapOn "Log in"): selector not found',
156+
durationMs: 94659,
157+
},
158+
],
146159
},
147160
],
148161
},
149162
}));
150163

151164
assert.equal(result.code, null);
152165
assert.match(result.stderr, /Running replay suite\.\.\./);
153-
assert.match(result.stdout, /FLAKY \/tmp\/01-flaky\.ad after 2 attempts \(10ms\)/);
154-
assert.match(result.stdout, /Test summary: 1 passed, 0 failed, 1 flaky in 25ms/);
166+
assert.doesNotMatch(result.stdout, /FLAKY/);
167+
assert.match(result.stdout, /Test summary: 1 passed, 0 failed, 1 flaky in 0\.025s/);
168+
assert.match(result.stdout, /Flaky tests:/);
169+
assert.match(
170+
result.stdout,
171+
/PASS "Authentication flow" after 2 attempts \(passed attempt 17\.5s, total 112\.2s\)/,
172+
);
173+
assert.match(
174+
result.stdout,
175+
/attempt 1 failed \(94\.7s\): Replay failed at step 3 \(tapOn "Log in"\): selector not found/,
176+
);
177+
});
178+
179+
test('test command prints failed attempt step telemetry when timing trace exists', async () => {
180+
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'agent-device-cli-test-steps-'));
181+
const artifactsDir = path.join(tmpDir, 'checkout-flow');
182+
const attemptDir = path.join(artifactsDir, 'attempt-2');
183+
await fs.mkdir(attemptDir, { recursive: true });
184+
await fs.writeFile(
185+
path.join(attemptDir, 'replay-timing.ndjson'),
186+
[
187+
{
188+
type: 'replay_action_start',
189+
step: 1,
190+
line: 3,
191+
command: 'open',
192+
positionals: ['Demo'],
193+
},
194+
{
195+
type: 'replay_action_stop',
196+
step: 1,
197+
line: 3,
198+
command: 'open',
199+
ok: true,
200+
durationMs: 125,
201+
resultTiming: { launchMs: 100 },
202+
},
203+
{
204+
type: 'replay_action_start',
205+
step: 2,
206+
line: 4,
207+
command: '__maestroTapOn',
208+
positionals: ['text="Pay"'],
209+
},
210+
{
211+
type: 'replay_action_stop',
212+
step: 2,
213+
line: 4,
214+
command: '__maestroTapOn',
215+
ok: false,
216+
durationMs: 1500,
217+
errorCode: 'ASSERTION_FAILED',
218+
},
219+
]
220+
.map((entry) => JSON.stringify(entry))
221+
.join('\n'),
222+
);
223+
224+
try {
225+
const failedReplayResult = {
226+
file: '/tmp/checkout-flow.yml',
227+
title: 'Checkout flow',
228+
session: 'default:test:suite:1',
229+
status: 'failed',
230+
durationMs: 2000,
231+
attempts: 2,
232+
artifactsDir,
233+
error: {
234+
code: 'ASSERTION_FAILED',
235+
message: 'Replay failed at step 2 (click "Pay"): selector not found',
236+
},
237+
};
238+
const result = await runCliCapture(['test', './suite'], async () => ({
239+
ok: true,
240+
data: {
241+
total: 1,
242+
executed: 1,
243+
passed: 0,
244+
failed: 1,
245+
skipped: 0,
246+
notRun: 0,
247+
durationMs: 2000,
248+
failures: [failedReplayResult],
249+
tests: [failedReplayResult],
250+
},
251+
}));
252+
253+
assert.equal(result.code, 1);
254+
assert.match(result.stdout, /steps \(attempt 2\):/);
255+
assert.match(
256+
result.stdout,
257+
/\[ok\] open "Demo" \(line 3, 0\.125s, timing \{"launchMs":100\}\)/,
258+
);
259+
assert.match(
260+
result.stdout,
261+
/\[FAIL\] tapOn "text=\\"Pay\\"" \(line 4, 1\.50s, ASSERTION_FAILED\)/,
262+
);
263+
} finally {
264+
await fs.rm(tmpDir, { recursive: true, force: true });
265+
}
155266
});
156267

157268
test('test --maestro forwards Maestro backend and platform for directory suites', async () => {

0 commit comments

Comments
 (0)