26 May 2026 - 5 min read
Today's Qwordly work was about making the app easier to ship with confidence. There was no new game mechanic or visible screen redesign; the useful progress happened underneath: React Native 0.85.3, Expo 56.0.5, TypeScript 6.0.3, local Expo Doctor checks, cleaner build inputs, and a tougher Android QA matrix that can catch more UI regressions before a preview build goes out.
The Runtime Moved Forward
The main platform change was moving the app onto a cleaner Expo SDK 56 package set while also upgrading React Native to 0.85.3 and pinning TypeScript to 6.0.3. That sounds like package maintenance, but the real work was making sure the app, tests, Expo setup, and build tooling all agreed about the environment they were running in.
A few supporting changes mattered:
- React Native moved from
0.85.1to0.85.3. @react-native/jest-presetmoved to0.85.3.- Expo moved off the SDK 56 canary package set to
expo@56.0.5. - Direct Expo packages were aligned, including
@expo/cli@56.1.12,@expo/config@56.0.9,@expo/dom-webview@56.0.5,@expo/metro-runtime@56.0.13,expo-asset@56.0.15,expo-audio@56.0.11,expo-build-properties@56.0.15,expo-constants@56.0.16,expo-dev-client@56.0.16,expo-file-system@56.0.7,expo-font@56.0.5,expo-gl@56.0.5,expo-haptics@56.0.3,expo-status-bar@56.0.4, andexpo-system-ui@56.0.5. - Expo-managed support packages moved to
@react-native-community/netinfo@12.0.1,react-native-gesture-handler@~2.31.2,react-native-reanimated@4.3.1,react-native-safe-area-context@~5.7.0,react-native-screens@~4.25.2, andreact-native-worklets@0.8.3. jest-expomoved to56.0.4.- TypeScript is pinned exactly to
6.0.3. expo-fontandexpo-status-barwere added to the dynamic Expo config plugins.expo-doctoris now installed locally, and dotenv config loading was quieted so Expo's machine-readable checks can pass cleanly.
The mute button migration sits underneath that bigger package alignment. It moved from react-native-vector-icons to @expo/vector-icons, and Jest now has a mock for Expo vector icons so component tests can keep checking icon state without depending on native rendering.
That last point is small but important. A dependency migration is only done when the surrounding tests still describe the intended behaviour. In this case, the mute button still verifies the accessible labels, the icon names, and the toggle action.
EAS Builds Got Cleaner
The EAS build archive was tightened by expanding .easignore. The goal is simple: EAS should receive the project source and lockfile-driven dependency graph, not local caches, emulator evidence, generated builds, editor metadata, or machine-specific state.
The ignore list now excludes more of the files that commonly make mobile build archives heavier or noisier:
- Local Gradle, Kotlin, Metro, Expo, npm, and Yarn caches
- Generated Android and iOS project folders
- Emulator screenshots, logs, and QA output
- Coverage, debug logs, traces, profiles, and test artefacts
- Local environment files
- Editor, agent, and repository metadata
- Generated store and web build outputs
That reduces the chance of a preview build being affected by something that only exists on one development machine. It also makes build failures easier to reason about, because fewer irrelevant files are travelling into the build environment.
Local validation now passes with pnpm exec expo-doctor and pnpm exec expo install --check. The EAS profile skip flags are therefore about reducing build-environment noise, not about working around an unhealthy dependency graph.
Before turning back to Android QA, the package work was checked with pnpm install --frozen-lockfile, pnpm exec expo install --check, pnpm exec expo-doctor, pnpm run check:rn-upgrade, and pnpm run test:rn --runInBand. The React Native Jest suite passed with 173 suites and 3,571 tests.
Android QA Became More Defensive
The Android screenshot matrix is now more useful because it covers more real interface states and handles more emulator failure modes.
The QA surface inventory currently contains 51 pages, modals, modal variants, popups, and overlays. Today's work added coverage for states that are easy to miss manually, including the settings language picker and the upgrade-account flow when an existing account prompt appears.
The upgrade account modal also got a small behaviour change to support that test surface properly. Instead of showing the existing-account prompt immediately during initial state setup, it can now open after the parent modal is visible. That makes the QA harness closer to how the modal behaves in the real app, and a new unit test locks that behaviour in.
The emulator runner also became more resilient:
- It checks expected text for every QA surface.
- It fails if the expected-text list and the QA surface inventory drift apart.
- It dismisses Expo developer menus before capturing app evidence.
- It detects Android System UI ANR dialogs, taps "Wait", and retries.
- It retries when Android's UI automation bridge returns a null root.
- It saves screenshots, UI trees, logcat, and diagnostics when a surface fails.
The practical effect is better evidence. When the matrix fails, it should be clearer whether the problem is a real UI regression, an app launch issue, an emulator interruption, or a missing QA expectation.
The Useful Pattern
Today's work followed a pattern worth keeping:
- Treat runtime upgrades as system changes, not package edits.
- Keep build archives boring and reproducible.
- Turn awkward UI states into first-class QA surfaces.
- Make emulator failures produce evidence automatically.
- Mock external UI libraries at the boundary so tests still describe product behaviour.
That pattern matters for a mobile game because regressions often hide in the spaces between systems: native build tooling, modals, auth flows, emulator state, and dependency versions. The more those spaces are made explicit, the less each release depends on memory and manual checking.
What This Means for Qwordly
Qwordly is now in a better place for the next preview build. The app is on Expo 56.0.5, React Native 0.85.3, and TypeScript 6.0.3; the EAS upload surface is cleaner; and the Android matrix can inspect more of the interface with stronger failure reporting.
The decision rule for the next round is straightforward: every new user-facing state should either fit an existing QA surface or get a new deterministic one, with expected text and failure evidence built in. That keeps feature work moving without letting release risk quietly pile up.
