skip to content

Why is a permission prompt drawn by the operating system not just another dialog in your application?

level: juniorimportance: should knowfreq 48%

answer

  1. Two owners share one screen
  2. The platform writes that wording, not you
  3. It may not appear at all
  4. A stored decision suppresses the panel
  5. Arrange across it, assert on your own screens

basics

~20 s

The prompt belongs to the platform, not the product. Its wording and layout are decided outside the application, it may not appear at all when a decision is already stored, and it takes focus away from your own screens.

solid answer

~40 s

It is a second owner drawing on the same screen, and three things follow for case design. **Its content is not yours**: the headline, the button labels and the translation are the platform's, revised between releases, so asserting them makes the case fail for reasons that have nothing to do with your product. **Its appearance is conditional**: if a decision is already stored on the device, no prompt is drawn at all, so a step that waits for one is a timeout waiting to happen. **It interrupts your flow**: while it is up, your own screen is not receiving input, so anything the case does between the request and the answer is unreliable. The repair is to make the stored decision an explicit arrange step, and to keep every assertion on your own screens.

code

pseudocode · 14 lines
pseudocode
# fragile: assumes the platform panel is always drawn
open_capture_screen()
wait_for(system_approval_panel)     # never appears once a decision is stored
tap(system_approval_panel.approve)
expect_visible(live_preview)

# deterministic: the stored decision is arranged, the assertion stays local
arrange:
  set_stored_decision(capability = "camera", value = GRANTED)
  launch_app()
act:
  open_capture_screen()
assert:
  expect_visible(live_preview)      # your screen, not the platform's panel

go deeper

for a junior

Recall that the approval panel is drawn by the operating system rather than by the application, so its words and buttons are not part of the product under test. Never assert on that text.

for a middle

Explain why its appearance is conditional: a decision already stored on the device suppresses it. Describe how a case becomes deterministic by establishing that stored decision instead of waiting for something that may never be drawn.

for a senior

Show how suites stay stable across repeated runs on shared devices — arranging the stored decision explicitly, guarding any step that answers the panel, and keeping every assertion on product-owned surfaces.

for a principal

Own the convention: one agreed way for all teams to establish capability preconditions, so squads do not each grow a fragile panel-tapping helper and the shared device pool stays predictable.

## Two owners, one screen When a capability-gated screen opens, the panel asking for approval is drawn by the operating system, not by your application. It sits above your interface, it takes input focus, and your process cannot change a word of it. For a case, that means the screen is no longer a single artefact with a single owner: part of what is visible belongs to the product under test and part belongs to the platform hosting it. This sounds like a technicality and is in fact the source of most flaky capability cases. Almost every bad habit in this area comes from treating that panel as though it were one of the product's own dialogs. ## Three consequences for how you write the case 1. **You cannot assert its content.** The headline, the explanatory line, the button labels and their order are chosen by the platform, translated into the device's language and revised between releases. A case that matches that text is measuring the platform, and it will turn red on an update while the product is perfectly healthy. 2. **You cannot assume it appears.** Whether it is drawn depends entirely on the decision already stored for that capability. On a freshly installed build it appears; on the second run of the same suite on the same device it does not, because the first run answered it. A step that waits for it therefore passes once and then times out forever. 3. **You cannot treat answering it as an incidental tap.** While the panel is up your own screen is not receiving input, so anything sequenced between the request and the answer lands somewhere unpredictable. Answering belongs in the arrange step, deliberately, before assertions start. ## What belongs to whom | Surface | Owner | What a case may do with it | |---|---|---| | The approval panel's wording, layout and buttons | The platform | Answer it as setup; never assert on it, never screenshot it as evidence | | The moment the request is made | Your product | Assert that it happens at the right point in the flow, and only then | | The screen after approval | Your product | Assert freely — this is the actual subject of the case | | The screen after refusal | Your product | Assert freely — the degraded path is product behaviour | | The stored decision on the device | The platform, on the person's behalf | Establish it as a precondition; restore it afterwards | The line is simple to hold once stated: **arrange across the platform's surface, assert only on your own.** ## Making the prompt's presence deterministic The stable form of these cases removes the guesswork entirely. The case declares the stored decision it needs, sets it before launch, and then never mentions the panel again — it asserts on the product screen that the decision produces. Where the panel genuinely must be answered through the interface, because no state-setting seam exists, three habits keep that step from becoming the flakiest line in the suite: - Guard it. Check whether the panel is present before trying to answer it, rather than assuming, so the step is a no-op on a device that already holds a decision. - Identify its controls by something stable rather than by their visible wording, which is translated and revised. - Keep it in setup. An answer buried inside an assertion step means a red result cannot distinguish "the fallback did not render" from "the panel was never answered". Note that driving that panel is a capability of whichever automation tooling operates the device, and the tooling's specifics are somebody else's subject. The case-design point stands regardless of tooling: the outcome must be deterministic before the assertions begin. ## The flakiness pattern this produces when ignored The classic report is "it passes on a clean device and fails on the second run". The sequence is always the same: - Run one starts with no decision stored, so the panel appears; the case waits for it, answers it, and passes. - Run one leaves a decision stored on the device. - Run two opens the same screen, no panel is drawn, and the wait expires — or worse, the tap intended for the panel lands on whatever the product is now showing, and the case fails several steps later with a message that points at the wrong thing. Teams often chase this as a timing problem and raise the wait, which cannot help, since the thing being waited for will never exist. The diagnosis is a precondition problem, and the fix is to own the precondition. ## The one thing worth asserting near the prompt There is a product assertion hiding here, and it is worth keeping: **that the request is made at the right moment**. Asking for a sensitive capability the instant the application opens, before the person has met the feature that needs it, drives refusals up sharply and is a genuine product defect. A case can assert that the request is not made on the first screen, and is made when the relevant feature is first used. That is your product's behaviour, not the platform's, and it is a fair thing to hold in a suite.

  • If a case must answer the panel through the interface, how do you keep that step stable?
    Make it an explicit arrange step with its own wait, guarded by a check that the panel is actually present so it becomes a no-op on a device that already holds a decision. Identify its controls by something stable rather than by their visible wording, which is translated and revised. Then assert only on your product's screen afterwards, so a red result points at the product rather than at the setup.
  • Why can a case that passes on a freshly installed build fail on its second run on the same device?
    Because the first run stored a decision. The panel the case waited for on run one is not drawn on run two, so the wait expires, or a tap aimed at it lands on the product's own screen and the case fails several steps later pointing at the wrong thing. Make the stored decision part of the arrange step so both runs start from the same place.
  • Is there anything about the request itself worth asserting?
    Yes — when it happens. Asking for a sensitive capability on the very first screen, before the person has met the feature that needs it, pushes refusals up and is a real product defect. A case can assert the request is not made at launch and is made when the relevant feature is first used, which is product behaviour rather than platform behaviour.

It is like the visitor form at the front desk of a building you rent space in: you can warn people it is coming, but you cannot reword it, and on their second visit they may be waved straight through without ever seeing it.

saying these in an interview costs you the question

  • Asserts the exact text of a system-drawn approval panel
  • Assumes the panel appears whenever the screen opens
  • Answers the panel inside an assertion rather than in setup
  • Captures the panel as evidence the feature works
  • Raises the wait when the panel is simply never drawn
  • Blames the automation tooling for an unowned precondition