also looking at this
test(mobile-e2e): prove Android reports approximate location as granted
#214
5 files
+85
-5
| @@ -2,7 +2,7 @@ import type { LegOutcome, RunnerOptions } from './types' | ||
| 2 | 2 | import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs' |
| 3 | 3 | import { join } from 'node:path' |
| 4 | 4 | import { init } from '../../packages/android/src/index' |
| 5 | import { androidDeclines, awaitedNeeds, DISMISS_SHARE_MENU, evaluateRun, hasTerminated, shareMenuInFront } from './protocol' | |
| 5 | import { androidDeclines, awaitedNeeds, DISMISS_SHARE_MENU, evaluateRun, hasTerminated, runtimePermissionGranted, shareMenuInFront } from './protocol' | |
| 6 | 6 | import { command, driverPage, waitForFile } from './support' |
| 7 | 7 | |
| 8 | 8 | /** |
| @@ -15,11 +15,29 @@ import { command, driverPage, waitForFile } from './support' | ||
| 15 | 15 | * Clipboard is not a configurable capability on Android - CraftBridge serves |
| 16 | 16 | * clipboardRead/clipboardWrite unconditionally - so the success case needs no |
| 17 | 17 | * flag, and asking for one would imply a gate that is not there. |
| 18 | * | |
| 19 | * Geolocation on, because that is what puts the location permissions in the | |
| 20 | * manifest, and `pm grant` refuses a permission the manifest does not declare. | |
| 21 | * The Kotlin does not otherwise read the flag (#209). | |
| 18 | 22 | */ |
| 19 | 23 | const CONFIG = { |
| 20 | 24 | enablePushNotifications: false, |
| 25 | enableGeolocation: true, | |
| 21 | 26 | } |
| 22 | 27 | |
| 28 | /** | |
| 29 | * The one location permission the harness grants: approximate, and not | |
| 30 | * precise. | |
| 31 | * | |
| 32 | * #192 was a device with location granted that still reported something | |
| 33 | * else. Granting both permissions would pass against a bridge that required | |
| 34 | * either one, and that is not the contract. Android 12 onwards lets a person | |
| 35 | * choose approximate only, and CraftPermissionPolicy counts that as `granted` | |
| 36 | * (780e952). Coarse alone is the stricter of the two tests: anything this | |
| 37 | * passes, a device with both permissions granted passes too. | |
| 38 | */ | |
| 39 | const GRANTED_LOCATION_PERMISSION = 'android.permission.ACCESS_COARSE_LOCATION' | |
| 40 | ||
| 23 | 41 | const PACKAGE = 'dev.craft.e2e.probe' |
| 24 | 42 | const APP_NAME = 'CraftE2EProbe' |
| 25 | 43 | |
| @@ -142,6 +160,19 @@ async function runLeg(leg: Leg, options: RunnerOptions): Promise<LegOutcome> { | ||
| 142 | 160 | await adb(['uninstall', PACKAGE], { serial, allowFailure: true }) |
| 143 | 161 | await adb(['install', '-r', apk], { serial, logPath: join(evidence, 'adb.log') }) |
| 144 | 162 | |
| 163 | // Granted from the host before launch, so no dialog waits on a person. | |
| 164 | await adb(['shell', 'pm', 'grant', PACKAGE, GRANTED_LOCATION_PERMISSION], { serial, logPath: join(evidence, 'adb.log') }) | |
| 165 | ||
| 166 | // And read back, because the case is only as strict as the device state it | |
| 167 | // ran against: approximate granted, precise not. A device that had precise | |
| 168 | // granted as well would pass against a bridge requiring precise. | |
| 169 | const packageState = (await adb(['shell', 'dumpsys', 'package', PACKAGE], { serial })).stdout | |
| 170 | writeFileSync(join(evidence, 'package-permissions.txt'), packageState) | |
| 171 | if (runtimePermissionGranted(packageState, GRANTED_LOCATION_PERMISSION) !== true) | |
| 172 | failures.push(`${GRANTED_LOCATION_PERMISSION} is not granted after pm grant; see ${label}/package-permissions.txt`) | |
| 173 | if (runtimePermissionGranted(packageState, 'android.permission.ACCESS_FINE_LOCATION') === true) | |
| 174 | failures.push('ACCESS_FINE_LOCATION is granted too, so the location case cannot tell approximate-only from precise') | |
| 175 | ||
| 145 | 176 | // Not best-effort: the ring buffer outlives an uninstall, and a transcript |
| 146 | 177 | // left in it would otherwise be read as this run's. The plan event carries |
| 147 | 178 | // this run's nonce as a second guard. |