Keybase E2E Flow Tests

SkillWeb & browsing

Guides your agent in writing and fixing end-to-end tests for the Keybase desktop and iOS apps.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Keybase E2E Flow Tests skill

About this capability

Use when writing, fixing, or adding e2e flow tests for the Keybase app — desktop (Playwright) or iOS (Appium + WebdriverIO). Covers testID conventions, navigation patterns, common pitfalls, and the two-harness structure.

What this skill tells your AI

The instructions your AI receives, as published by keybase/client in skill/keybase-e2e-tests/SKILL.md and read by ahel’s review.

Overview

Two harnesses, one shared testID registry. Always implement Electron + iOS for each bucket together (pairing rule in plans/flow-test.md).

Shared testID Registry

shared/tests/e2e/shared/test-ids.ts — single source of truth for all testID values.

Adding a testID to a component:

import * as TestIDs from '@/tests/e2e/shared/test-ids'
// ...
<Kb.ScrollView testID={TestIDs.SETTINGS_ADVANCED}>

Rule: Add testID prop to an already-existing element (input, scroll view, pre-existing wrapper). Never add a new container just to attach a testID.

Desktop — Playwright

WhatWhere
Test filesshared/tests/e2e/electron/flows/*.test.ts
Nav helpersshared/tests/e2e/electron/helpers/navigate.ts
Run allyarn test:e2e:desktop
Run branchyarn test:e2e:desktop:branch

Navigation helpers: navigateToChat, navigateToFiles, navigateToTeams, navigateToGit, navigateToSettings, navigateToPeople, navigateToCrypto, navigateToDevices

Common pitfall — hidden nav stack elements: React Navigation keeps prior screens mounted but hidden. getByTestId(X) may match 2+ elements (one hidden, one visible), causing Playwright strict-mode failures. Fix: use a selector unique to the destination screen rather than one shared with the source screen. Example: after navigating into a Files subfolder, the root screen's files-browser is still in the DOM but hidden — check for the Filter textbox instead, which only exists in subfolder views.

iOS — Appium + WebdriverIO (TypeScript)

WhatWhere
Flow filesshared/tests/e2e/ios-appium/flows/*.test.ts
Helpersshared/tests/e2e/ios-appium/helpers/ (elements, navigate, app)
Aggregate specshared/tests/e2e/ios-appium/all.test.ts (imports all flows → ONE session)
Configshared/tests/e2e/ios-appium/wdio.conf.ts
Run both devicesKB_SMOKE_USER=<user> yarn test:e2e:ios (iPhoneTest + iPadTest in PARALLEL; app installed on both)
Run one deviceKB_SMOKE_USER=<user> yarn test:e2e:ios:iphone / yarn test:e2e:ios:ipad (serial runner, shuts down other sims)
Reportyarn test:e2e:ios:report (reads ONLY tests/results/ios-appium-debug-{iphone,ipad}; each run overwrites its device's dir; per-image timestamps show when each device last ran)

Drives the already-installed app black-box (no rebuild). Selectors: ~<testID> (testID → iOS accessibilityIdentifier). Helpers: el/els/waitForTestID/byText/tab (elements), escapeToTabs/goBack/navigateTo*/scrollDownToText (navigate). escapeToTabs runs before every test (resets to the tab root).

Gotchas (hard-won — read before adding flows):

  • Native tab bar: tap tabs by label (tab('People')~People), NOT nav-tab-* testIDs — those don't reach the native UITabBar.
  • Container testIDs (a flex Kb.Box2 wrapping a list) report visible="false" to XCUITest even when on screen → use waitForTestID (it uses waitForExist, presence), never toBeDisplayed.
  • testIDs must be on the MOBILE-rendered element. Many components branch on isMobile/.desktop/.native; a desktop-only testID is invisible on iOS (see [[project_e2e_testid_mobile_branch]]). Put the testID on the clickable/leaf element (e.g. Kb.ListItem's testID, a ClickableBox), not a non-clickable wrapping Box2 — wdio .click() no-ops on a non-accessible container.
  • byText uses CONTAINS — tappable rows have merged accessibility labels (e.g. ", Crypto"), so exact match fails.
  • Kb.Tabs supports a per-tab testID (needed for icon-only tabs like the team Settings gear). The app remembers the last-selected team tab → select tabs by testID, don't assume the default.
  • Modals: dismiss via Done/Close/Cancel (escapeToTabs does this first, before back buttons — a modal's back button is a no-op that loops).
  • HMR applies testID/component changes to the running sim app — no manual reload needed when adding testIDs.
  • Wait for a real data row (not just the list container) before asserting/screenshotting, so shots show loaded content.

iOS tab structure: People & Teams are direct tabs; Chat & Files have their own nav helpers; Crypto/Devices/Git/Settings live under the More tab (navigateToMore).

Plan

plans/flow-test.md — bucket checklist ordered easiest-first. Work one bucket at a time, both platforms together.

Signals

GitHub stars
9k
Forks
1k
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
keybase-e2e-tests
Source
github.com/keybase/client