@aarwitz/tapp 0.17.0-rc.3 → 0.17.0-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -139,6 +139,13 @@ export function isAndroidAppSnapshot(snapshot, appId) {
|
|
|
139
139
|
return ownsActivity || ownsElements;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
function isSystemOverlayOverApp(snapshot, appId) {
|
|
143
|
+
if (!snapshot || !appId || !String(snapshot.activity || "").startsWith(`${appId}/`)) return false;
|
|
144
|
+
const packages = new Set((snapshot.elements || []).map((element) => element.package).filter(Boolean));
|
|
145
|
+
return packages.size > 0 && !packages.has(appId)
|
|
146
|
+
&& [...packages].every((name) => name === "android" || name.startsWith("com.android.systemui"));
|
|
147
|
+
}
|
|
148
|
+
|
|
142
149
|
export class AndroidDriver {
|
|
143
150
|
constructor({ adbPath = resolveAdbPath(), serial = "", appId = "" } = {}) {
|
|
144
151
|
if (!adbPath) throw new Error("Android testing needs adb. Install Android SDK platform-tools or set ANDROID_SDK_ROOT.");
|
|
@@ -205,7 +212,42 @@ export class AndroidDriver {
|
|
|
205
212
|
const r = await this.adb(["shell", "am", "start", "-W", "-n", component], { timeout: 30_000 });
|
|
206
213
|
if (r.code !== 0 || !/Status:\s*ok/i.test(String(r.stdout))) throw new Error((r.stderr || r.stdout || `Could not launch ${this.appId}`).trim());
|
|
207
214
|
await sleep(600);
|
|
208
|
-
return this.
|
|
215
|
+
return this.waitForOwnedSnapshot();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async closeSystemDialogs() {
|
|
219
|
+
// A crash dialog from the previous app can outlive that process and cover a
|
|
220
|
+
// newly launched app. The shell broadcast only closes system-owned surfaces;
|
|
221
|
+
// it does not clear crash history or interact with the app under test.
|
|
222
|
+
await this.adb(["shell", "am", "broadcast", "-a", "android.intent.action.CLOSE_SYSTEM_DIALOGS"]);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async waitForOwnedSnapshot(timeoutMs = 12_000) {
|
|
226
|
+
const deadline = Date.now() + timeoutMs;
|
|
227
|
+
let latest;
|
|
228
|
+
let consecutiveOwned = 0;
|
|
229
|
+
let overlayDismissed = false;
|
|
230
|
+
do {
|
|
231
|
+
latest = await this.snapshot();
|
|
232
|
+
if (isAndroidAppSnapshot(latest, this.appId)) {
|
|
233
|
+
consecutiveOwned += 1;
|
|
234
|
+
// One mixed dumpsys/UIAutomator sample caused cross-run contamination.
|
|
235
|
+
// Require agreement twice before handing the surface to exploration.
|
|
236
|
+
if (consecutiveOwned >= 2) return latest;
|
|
237
|
+
} else {
|
|
238
|
+
consecutiveOwned = 0;
|
|
239
|
+
if (!overlayDismissed && isSystemOverlayOverApp(latest, this.appId)) {
|
|
240
|
+
overlayDismissed = true;
|
|
241
|
+
await this.closeSystemDialogs();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (Date.now() >= deadline) break;
|
|
245
|
+
// Activity and UIAutomator are intentionally required to agree. During app
|
|
246
|
+
// launch they may momentarily describe opposite sides of the transition;
|
|
247
|
+
// retry that mixed snapshot instead of turning it into zero-screen evidence.
|
|
248
|
+
await sleep(150);
|
|
249
|
+
} while (Date.now() < deadline);
|
|
250
|
+
return latest;
|
|
209
251
|
}
|
|
210
252
|
|
|
211
253
|
async currentActivity() {
|
package/package.json
CHANGED