Skip to content

Commit ba3308e

Browse files
committed
test: cover maestro provider input coalescing
1 parent a14a013 commit ba3308e

3 files changed

Lines changed: 92 additions & 30 deletions

File tree

src/compat/maestro/__tests__/replay-flow.test.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ test('parseMaestroReplayFlow keeps focused inputText and pressKey Enter as separ
223223
assert.deepEqual(parsed.actionLines, [3, 4, 5]);
224224
});
225225

226-
test('parseMaestroReplayFlow marks tapOn before inputText for snapshot tap focus', () => {
226+
test('parseMaestroReplayFlow coalesces tapOn inputText without requiring Enter', () => {
227227
const parsed = parseMaestroReplayFlow(`appId: com.callstack.agentdevicelab
228228
---
229229
- tapOn:
@@ -234,11 +234,12 @@ test('parseMaestroReplayFlow marks tapOn before inputText for snapshot tap focus
234234
assert.deepEqual(
235235
parsed.actions.map((entry) => [entry.command, entry.positionals]),
236236
[
237-
['__maestroTapOn', ['id="editableNameInput"']],
238-
['type', ['Saved list']],
237+
['wait', ['id="editableNameInput"', '30000']],
238+
['fill', ['id="editableNameInput"', 'Saved list']],
239239
],
240240
);
241-
assert.equal(parsed.actions[0]?.flags?.maestro?.allowNonHittableCoordinateFallback, undefined);
241+
assert.deepEqual(parsed.actionLines, [3, 3]);
242+
assert.equal(parsed.actions[1]?.flags?.maestro?.allowNonHittableCoordinateFallback, true);
242243
});
243244

244245
test('parseMaestroReplayFlow coalesces tapOn inputText while preserving pressKey Enter submit', () => {
@@ -658,10 +659,10 @@ test('parseMaestroReplayFlow parses the test-app Maestro suite fixture', () => {
658659
'__maestroAssertVisible',
659660
'__maestroTapOn',
660661
'__maestroAssertVisible',
661-
'__maestroTapOn',
662-
'type',
663-
'__maestroTapOn',
664-
'type',
662+
'wait',
663+
'fill',
664+
'wait',
665+
'fill',
665666
'__maestroTapOn',
666667
'__maestroAssertVisible',
667668
'__maestroAssertVisible',

src/compat/maestro/replay-flow.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -113,27 +113,32 @@ function optimizeTypedAfterTap(
113113
if (!isLikelyTextEntrySelector(tapSelector)) {
114114
return { actions: [clearMaestroNonHittableTap(action)], actionLines: [line], consumed: 1 };
115115
}
116+
const fillActions: SessionAction[] = [
117+
{
118+
...action,
119+
command: 'wait',
120+
positionals: [tapSelector, '30000'],
121+
},
122+
{
123+
...nextAction,
124+
command: 'fill',
125+
positionals: [tapSelector, typedAfterTap],
126+
flags: action.flags,
127+
},
128+
];
129+
const fillActionLines = [line, line];
116130
const pressEnterAction = actions[index + 2];
117-
if (pressEnterAction?.command !== MAESTRO_RUNTIME_COMMAND.pressEnter) {
118-
return { actions: [clearMaestroNonHittableTap(action)], actionLines: [line], consumed: 1 };
131+
if (pressEnterAction?.command === MAESTRO_RUNTIME_COMMAND.pressEnter) {
132+
return {
133+
actions: [...fillActions, pressEnterAction],
134+
actionLines: [...fillActionLines, actionLines[index + 2] ?? line],
135+
consumed: 3,
136+
};
119137
}
120138
return {
121-
actions: [
122-
{
123-
...action,
124-
command: 'wait',
125-
positionals: [tapSelector, '30000'],
126-
},
127-
{
128-
...nextAction,
129-
command: 'fill',
130-
positionals: [tapSelector, typedAfterTap],
131-
flags: action.flags,
132-
},
133-
pressEnterAction,
134-
],
135-
actionLines: [line, line, actionLines[index + 2] ?? line],
136-
consumed: 3,
139+
actions: fillActions,
140+
actionLines: fillActionLines,
141+
consumed: 2,
137142
};
138143
}
139144

test/integration/provider-scenarios/android-test-suite.test.ts

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ test('Provider-backed integration Android Maestro replay uses fresh selector sna
121121
world.adbCalls.find((call) => call.slice(0, 3).join(' ') === 'shell input swipe'),
122122
['shell', 'input', 'swipe', '351', '390', '39', '390', '300'],
123123
);
124-
assert.equal(snapshots >= 2, true);
124+
assert.equal(snapshots, 2);
125125
},
126126
);
127127
});
@@ -167,18 +167,74 @@ test('Provider-backed integration Android Maestro replay test suite discovers YA
167167
world.adbCalls.find((call) => call.slice(0, 3).join(' ') === 'shell input tap'),
168168
['shell', 'input', 'tap', '180', '330'],
169169
);
170-
assert.equal(snapshots >= 2, true);
170+
assert.equal(snapshots, 2);
171171
},
172172
);
173173
});
174174

175-
test('Provider-backed integration Android Maestro coalesces tapOn inputText and pressKey Enter through native paths', async () => {
175+
test('Provider-backed integration Android Maestro fills tapOn inputText without trailing Enter', async () => {
176176
await withProviderScenarioResource(
177177
async () => await createAndroidSettingsWorld({ nativeTextInjection: true }),
178178
async (world) => {
179179
const client = world.daemon.client();
180180
const suiteRoot = path.join(world.tempRoot, 'suite-maestro-input');
181181
fs.mkdirSync(suiteRoot, { recursive: true });
182+
const flowPath = path.join(suiteRoot, 'input-only.yaml');
183+
fs.writeFileSync(
184+
flowPath,
185+
[
186+
'appId: com.android.settings',
187+
'---',
188+
'- launchApp',
189+
'- tapOn: Search',
190+
'- inputText: "Łódź café"',
191+
'',
192+
].join('\n'),
193+
);
194+
195+
const suite = await client.replay.test({
196+
paths: [flowPath],
197+
backend: 'maestro',
198+
artifactsDir: path.join(suiteRoot, 'artifacts'),
199+
timeoutMs: 30000,
200+
...world.selection,
201+
});
202+
203+
assert.equal(suite.total, 1, JSON.stringify(suite));
204+
assert.equal(suite.passed, 1, JSON.stringify(suite));
205+
assert.equal(suite.failed, 0, JSON.stringify(suite));
206+
assert.deepEqual(world.textInjectionCalls, [
207+
{
208+
action: 'fill',
209+
target: { x: 195, y: 52 },
210+
text: 'Łódź café',
211+
delayMs: 0,
212+
},
213+
]);
214+
assert.equal(
215+
world.adbCalls.some(
216+
(call) => call[0] === 'shell' && call[1] === 'input' && call[2] === 'text',
217+
),
218+
false,
219+
JSON.stringify(world.adbCalls),
220+
);
221+
assert.equal(
222+
world.adbCalls.some((call) => call.slice(0, 4).join(' ') === 'shell input keyevent ENTER'),
223+
false,
224+
JSON.stringify(world.adbCalls),
225+
);
226+
world.assertNoHostAdbCalls();
227+
},
228+
);
229+
});
230+
231+
test('Provider-backed integration Android Maestro preserves pressKey Enter after native fill', async () => {
232+
await withProviderScenarioResource(
233+
async () => await createAndroidSettingsWorld({ nativeTextInjection: true }),
234+
async (world) => {
235+
const client = world.daemon.client();
236+
const suiteRoot = path.join(world.tempRoot, 'suite-maestro-input-submit');
237+
fs.mkdirSync(suiteRoot, { recursive: true });
182238
const flowPath = path.join(suiteRoot, 'input-submit.yaml');
183239
fs.writeFileSync(
184240
flowPath,
@@ -277,7 +333,7 @@ test('Provider-backed integration Android Maestro executes runFlow conditions an
277333
world.adbCalls.find((call) => call.slice(0, 3).join(' ') === 'shell input tap'),
278334
['shell', 'input', 'tap', '180', '330'],
279335
);
280-
assert.equal(snapshots >= 3, true);
336+
assert.equal(snapshots, 3);
281337
},
282338
);
283339
});

0 commit comments

Comments
 (0)