Skip to content

Commit 4375981

Browse files
committed
test(app): fix Samsung permission dialog handling
1 parent 3bd1bbc commit 4375981

File tree

1 file changed

+22
-6
lines changed

1 file changed

+22
-6
lines changed

appium/tests/helpers/app.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,15 +160,20 @@ export async function acceptSystemAlert(timeoutMs = 10_000): Promise<string | nu
160160
}
161161

162162
// Try resource-id first (most robust across OEMs), then fall back to
163-
// text match. "Don't allow" contains "Allow" as a substring, so we
164-
// must use exact `text()` rather than `textContains()`.
163+
// clickable text/description. "Don't allow" contains "Allow" as a
164+
// substring, so we must use exact match rather than `textContains()`.
165+
// Samsung One UI wraps the label in a clickable container, so we also
166+
// target the clickable ancestor by text or content-description.
165167
const allowSelectors = [
166168
'new UiSelector().resourceIdMatches(".*:id/permission_allow_button")',
167169
'new UiSelector().resourceIdMatches(".*:id/permission_allow_foreground_only_button")',
168170
'new UiSelector().resourceIdMatches(".*:id/permission_allow_one_time_button")',
169-
'new UiSelector().text("Allow")',
170-
'new UiSelector().text("ALLOW")',
171-
'new UiSelector().text("While using the app")',
171+
'new UiSelector().clickable(true).textMatches("(?i)^allow$")',
172+
'new UiSelector().clickable(true).descriptionMatches("(?i)^allow$")',
173+
'new UiSelector().clickable(true).textMatches("(?i)^while using the app$")',
174+
'new UiSelector().textMatches("(?i)^allow$")',
175+
'new UiSelector().descriptionMatches("(?i)^allow$")',
176+
'new UiSelector().textMatches("(?i)^while using the app$")',
172177
];
173178

174179
let allowBtn: Awaited<ReturnType<typeof $>> | null = null;
@@ -198,6 +203,17 @@ export async function acceptSystemAlert(timeoutMs = 10_000): Promise<string | nu
198203
} catch {
199204
/* best-effort */
200205
}
206+
// If we matched a non-clickable text node (common on Samsung where the
207+
// label sits inside a clickable container), tap the nearest clickable
208+
// ancestor so the button actually fires.
209+
if ((await allowBtn.getAttribute('clickable').catch(() => 'true')) !== 'true') {
210+
const clickableAncestor = await $(
211+
'//*[@clickable="true" and .//*[(@text="Allow" or @content-desc="Allow")]]',
212+
);
213+
if (await clickableAncestor.isDisplayed().catch(() => false)) {
214+
allowBtn = clickableAncestor;
215+
}
216+
}
201217
await allowBtn.click();
202218
return text;
203219
} catch {
@@ -224,7 +240,7 @@ export async function waitForAppReady(opts: { skipLogin?: boolean } = {}) {
224240
const alertHandled = await browser.sharedStore.get('alertHandled');
225241
if (!alertHandled) {
226242
// Accept permission dialogs until the app UI is visible.
227-
await acceptSystemAlerts(5_000);
243+
await acceptSystemAlerts(10_000);
228244
await browser.sharedStore.set('alertHandled', true);
229245
}
230246

0 commit comments

Comments
 (0)