Skip to content

Commit e4af7a1

Browse files
committed
test(ios): cover WebDriver snapshot seams
1 parent 234f5a0 commit e4af7a1

3 files changed

Lines changed: 87 additions & 6 deletions

File tree

packages/provider-webdriver/src/webdriver-interactor.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,26 @@ test('iOS WebDriver interactor routes snapshots through the acquisition adapter'
269269
assert.equal(result.nodes?.[0]?.type, 'XCUIElementTypeApplication');
270270
});
271271

272+
test('Android WebDriver interactor keeps legacy-derived source facts at its call site', async () => {
273+
const source = vi.fn(
274+
async () =>
275+
'<hierarchy rotation="0"><android.widget.Button bounds="[0,0][100,40]" displayed="true" enabled="true" /></hierarchy>',
276+
);
277+
const interactor = createWebDriverInteractor({
278+
client: { source } as unknown as WebDriverClient,
279+
backend: 'android',
280+
capabilities: createCloudWebDriverCapabilities({ provider: 'test', platform: 'android' }),
281+
});
282+
283+
const result = await interactor.snapshot();
284+
285+
assert.equal(result.backend, 'android');
286+
assert.equal(result.nodes?.[0]?.type, 'hierarchy');
287+
assert.equal(result.nodes?.[1]?.type, 'android.widget.Button');
288+
assert.equal(result.nodes?.[1]?.hittable, true);
289+
assert.equal(source.mock.calls.length, 1);
290+
});
291+
272292
async function runFill(world: ReturnType<typeof createTextEntryWorld>) {
273293
vi.useFakeTimers();
274294
try {

packages/provider-webdriver/src/webdriver-ios-snapshot.test.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,17 @@ test('Appium iOS snapshots acquire facts and publish regular output through the
3333
assert.equal(result.nodes?.find((node) => node.label === 'Continue')?.hittable, false);
3434
assert.equal(source.mock.calls.length, 1);
3535
assert.ok(result.warnings?.some((warning) => warning.includes('hittability evidence')));
36-
assert.equal(JSON.stringify(result).includes('iosSnapshot'), false);
37-
assert.equal(JSON.stringify(result).includes('apple-runner'), false);
36+
assert.deepEqual(Object.keys(result).sort(), [
37+
'backend',
38+
'nodes',
39+
'producer',
40+
'truncated',
41+
'warnings',
42+
]);
43+
assert.equal(
44+
result.nodes?.every((node) => !('ref' in node)),
45+
true,
46+
);
3847
});
3948

4049
test('Appium iOS options become an engine plan and engine-owned projection', () => {
@@ -102,10 +111,11 @@ test('Appium iOS regular presentation fails typed when page source has no viewpo
102111
]);
103112
assert.throws(
104113
() => publishWebDriverIosSnapshot(acquired),
105-
(error: unknown) =>
106-
error instanceof AppError &&
107-
error.details?.reason === 'missing-viewport' &&
108-
!('iosSnapshotEngine' in (error.details ?? {})),
114+
(error: unknown) => {
115+
assert.ok(error instanceof AppError);
116+
assert.deepEqual(error.details, { reason: 'missing-viewport', field: 'viewport' });
117+
return true;
118+
},
109119
);
110120
});
111121

test/integration/provider-scenarios/cloud-webdriver-ios-text-entry.test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,55 @@ test('cloud iOS snapshot captures through the provider session after open', asyn
6262
});
6363
}, 15_000);
6464

65+
test('cloud iOS engine-presented snapshot survives daemon publication', async () => {
66+
await withProviderScenarioResource(createCloudIosWorld, async ({ daemon, server }) => {
67+
const lease = await openCloudIosSession(daemon);
68+
server.sourceOverride =
69+
'<XCUIElementTypeApplication name="Demo" x="0" y="0" width="390" height="844">' +
70+
'<XCUIElementTypeNavigationBar name="Team Standup" x="0" y="56" width="390" height="44">' +
71+
'<XCUIElementTypeStaticText label="Team Standup" x="219" y="58" width="85" height="40" />' +
72+
'<XCUIElementTypeButton label="Video Call" x="300" y="56" width="46" height="44" />' +
73+
'<XCUIElementTypeTextField label="Team Standup" value="Team Standup" name="DisplayNameTextField" enabled="false" x="100" y="67" width="113" height="22" />' +
74+
'<XCUIElementTypeImage name="RoomDetailsIconImageView" x="81" y="80" width="14" height="14" />' +
75+
'</XCUIElementTypeNavigationBar>' +
76+
'<XCUIElementTypeTable label="Team Standup" x="0" y="100" width="390" height="600">' +
77+
'<XCUIElementTypeCell label="Team Standup" x="0" y="100" width="390" height="297">' +
78+
'<XCUIElementTypeButton label="Team Standup" x="20" y="200" width="362" height="27" />' +
79+
'</XCUIElementTypeCell>' +
80+
'</XCUIElementTypeTable>' +
81+
'</XCUIElementTypeApplication>';
82+
83+
const response = await daemon.callCommand(
84+
'snapshot',
85+
[],
86+
{ ...leaseFlags(lease.leaseId), snapshotInteractiveOnly: true },
87+
{ meta: leaseMeta(lease.leaseId) },
88+
);
89+
90+
const data = assertRpcOk<{
91+
nodes?: Array<{ type?: string; label?: string; identifier?: string; enabled?: boolean }>;
92+
}>(response);
93+
assert.equal(
94+
data.nodes?.some(
95+
(node) =>
96+
node.type === 'Button' &&
97+
node.label === 'Team Standup' &&
98+
node.identifier === 'DisplayNameTextField' &&
99+
node.enabled === true,
100+
),
101+
true,
102+
);
103+
assert.equal(
104+
data.nodes?.some((node) => node.type === 'StaticText' && node.label === 'Team Standup'),
105+
false,
106+
);
107+
assert.equal(
108+
data.nodes?.some((node) => node.identifier === 'RoomDetailsIconImageView'),
109+
false,
110+
);
111+
});
112+
}, 15_000);
113+
65114
/**
66115
* #1658: `fill` tapped and sent its keys back-to-back. A WebView input takes
67116
* first responder asynchronously, so the keys arrived with nothing focused and
@@ -254,6 +303,7 @@ class FakeIosWebDriverServer extends CloudWebDriverTestServer {
254303
pollsUntilFocus = 1;
255304
fieldValues: Record<FieldName, string> = { email: '', password: '' };
256305
focused: FieldName | undefined;
306+
sourceOverride: string | undefined;
257307

258308
private pollsRemaining = Number.POSITIVE_INFINITY;
259309
private pendingFocus: FieldName | undefined;
@@ -373,6 +423,7 @@ class FakeIosWebDriverServer extends CloudWebDriverTestServer {
373423
}
374424

375425
private source(): string {
426+
if (this.sourceOverride !== undefined) return this.sourceOverride;
376427
const field = (name: FieldName, label: string) =>
377428
`<XCUIElementTypeTextField name="${name}" label="${label}" value="${this.fieldValues[name]}" ` +
378429
`x="${FIELDS[name].x}" y="${FIELDS[name].y}" width="${FIELDS[name].width}" ` +

0 commit comments

Comments
 (0)