Add native Android test runtime support#21
Merged
joyzoursky merged 95 commits intomainfrom Feb 23, 2026
Merged
Conversation
- Add emulator pool config (maxInstances, timeouts, adb, apk, ports) - Add features.androidEmulator feature flag - Add TargetType, BrowserTargetConfig, AndroidTargetConfig, TargetConfig - Add AndroidDevice and AndroidAgent interfaces (placeholder until @midscene/android) - Re-export android types from src/types/index.ts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- shell() with per-command timeout and configurable retries - Classifies ADB errors (DEVICE_OFFLINE, CONNECTION_RESET, COMMAND_TIMEOUT, etc.) - Retries on transient errors, fails fast on unrecoverable ones - Doubles timeout on COMMAND_TIMEOUT then gives up after one retry - installApk() with configurable install timeout - healthCheck() checks device online, boot_completed, screen responsive - reconnect() via adb disconnect/connect cycle Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
State machine: BOOTING → IDLE → ACQUIRED → CLEANING → IDLE/DEAD - acquire() finds idle emulator, boots new one if under limit, or queues - release() cleans device (APK uninstall, home screen, kill-all), returns to IDLE - Idle timeout: auto-stops emulators idle beyond idleTimeoutMs - Force-reclaim: kills emulators stuck in ACQUIRED beyond maxDuration + 60s grace - Periodic health checks on IDLE emulators; reconnect or stop on failure - AbortSignal support in acquire() wait queue for run cancellation - cleanupOrphans() on initialize() kills stale emulator processes from prior server runs - setEmulatorDevice/Agent() hooks for Phase 2 midscene integration Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- setupExecutionTargets() replaces setupBrowserInstances(); handles browser and android targets in a single pass - executeSteps() dispatches to AndroidAgent for android targets; guards playwright-code steps on android with a clear error - Browser-specific logic (URL wait, page.screenshot) is gated on isAndroid - cleanupTargets() closes the browser AND releases emulators back to the pool - URL substitution in resolvedBrowserConfig skips android configs (no url field) - BrowserConfig | TargetConfig union used throughout RunTestOptions + TestData Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AvdProfile: stores server-managed AVD configs (name, displayName, apiLevel, enabled) - ProjectApk: stores uploaded APK files per project with metadata (packageName, activityName, versionName); cascade-deletes with project - Add apks relation to Project model - Add getApkUploadPath/getApkFilePath helpers to file-security Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GET /api/avd-profiles: list enabled AVD profiles (authenticated) - GET /api/projects/[id]/apks: list project APKs (ownership checked) - POST /api/projects/[id]/apks: upload APK with validation (size, extension, per-project count limit); extracts packageName/activityName/versionName via aapt2 if available, otherwise stores empty metadata gracefully - DELETE /api/projects/[id]/apks/[apkId]: delete APK and remove file from disk Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- BrowserEntry.config now accepts BrowserConfig | TargetConfig across all components - ConfigurationsSection: renders android entries with AVD/APK dropdowns, fetches avd-profiles and project APKs, adds "Add Android Target" button when AVD profiles available - SortableStepItem: shows 🌐/📱 icon per target in step selector, disables Code mode for android targets - BuilderForm/TestForm: updated BrowserEntry type + buildBrowsers/buildCurrentData to preserve android configs - testCaseExcel: filters out android targets from Excel export (browser-only format) - i18n: android target keys added to all 3 locales (en, zh-Hant, zh-Hans)
- TimelineEvent: android screenshots (browserId starting with android_) use portrait 1080x2400 ratio and max-w-[280px] frame with 📱 badge - GET /api/admin/emulators: returns EmulatorPoolStatus (gated by FEATURE_ANDROID_EMULATOR) - POST /api/admin/emulators: stop action for idle/booting emulators - /admin/emulators page: real-time pool dashboard with 5s polling, state badges, uptime, memory, stop button
- Mark stale RUNNING/QUEUED test runs as FAIL on server restart via queue.startup() - Initialize emulator pool on server start via Next.js instrumentation hook - Gate android target execution behind FEATURE_ANDROID_EMULATOR flag in test runner - Gate APK upload/list APIs behind feature flag (return 404 when disabled)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- useApkUpload hook for file input + upload flow - ApkManager component: list, upload, delete with confirmation modal - EmulatorStatusPanel: pool status, boot/stop controls, 15s visibility-aware polling - Project page: androidEnabled feature flag, apks/emulators tabs, replace inline active-run checks with isActiveRunStatus() - i18n: apk.*, emulator.panel.*, feature.android.disabled, project.tab.apks/emulators (en/zh-Hant/zh-Hans)
Reuses useApkUpload hook; on success auto-selects new APK and closes dropdown. i18n: configs.android.apk.upload (en/zh-Hant/zh-Hans)
- queue.ts: include PREPARING in startup stale-run cleanup and orphan cancel guard - events route: forward PREPARING status changes via SSE - projects routes: include PREPARING in active-run guards (delete project, active badge) - run/page.tsx: PREPARING in TestResult type, SSE connect check, isRunInProgress, stop button - history/page.tsx: PREPARING in delete modal and row active-run checks - history/[runId]/page.tsx: expand TestRun.status type; allow TargetConfig in browserConfig - ResultStatus.tsx: add PREPARING spinner panel - i18n: status.preparing, status.preparing.detail (en/zh-Hant/zh-Hans)
- ResultViewer: derive targetTypeMap from config.browserConfig; pass targetType to TimelineEvent
- TimelineEvent: add targetType prop; replace all startsWith('android_') checks; add 📱/🌐 icon on log events
- RunTestOptions: add optional onPreparing callback - test-runner.ts: call onPreparing before setupExecutionTargets when Android target present - queue.ts: pass onPreparing to runTest; sets run + test case to PREPARING and publishes project event
Android support is now gated solely by the user-level androidEnabled field. The FEATURE_ANDROID_EMULATOR env var, config.features block, and server-side guard in test-runner are no longer needed. EmulatorPool now always initializes at startup.
- TimelineEvent: remove 📱/🌐 from log events; replace 📱 Android badge with plain text - SortableStepItem: remove 📱/🌐 from target selector button and dropdown - ConfigurationsSection: remove 📱 from Android target header, 🌐 from browser target header, 📱 from add-Android button - EmulatorStatusPanel, admin emulators page: remove 📱 from emulator list rows - i18n: rename Configurations tab to Variables (en/zh-Hant/zh-Hans)
AVD profiles are now scoped to projects instead of being global. Adds CRUD API endpoints, an AvdProfileManager UI component on the project Emulators tab, and updates all consumers (test-runner, admin emulators, ConfigurationsSection) to use the project-scoped lookup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace native <select> with custom button-dropdown and move the boot controls from the header into a centered footer row inside the emulator list card. Includes click-outside-to-close behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move /api/admin/emulators to /api/emulators since there is no admin role — the route only checks androidEnabled. Delete the unused standalone /admin/emulators page that duplicated EmulatorStatusPanel. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 3 oversized stat cards with inline summary text next to the title. Move AVD dropdown and Boot button to the header row (right side) to match the AVD Profiles section pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Accept browserConfig-only payloads when saving a test case so Android targets no longer require a URL.
- add server Android runtime capability detection and effective feature gating\n- block Android-only API paths and runs when the host lacks Android tooling\n- hide Android UI entry points on non-Android servers\n- document optional Android server mode for operators and maintainers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Android runs.