Support physical iOS and tvOS devices - #15
Open
DouweBos wants to merge 4 commits into
Open
Conversation
Studio no longer records case results: Qase is the system of record, so the local execution log, the manual run wizard, the pass/fail buttons, the record_case_result MCP tool and the matrix's verdict cells are gone. Around the cases it does read: - Resolve select-type custom fields to their option titles instead of showing Qase's option ids, and decode the HTML entities Qase stores prose with. - A step can name several page objects, each with its own env — steps regularly bundle actions — and a scaffold emits a runFlow per entry, in order. - Linking and scaffolding write the case's priority beside testCaseId. - Scaffolded comments stay commented on every line, so multi-line case prose no longer breaks the YAML. - Unlinking a flow whose file is already gone is a quiet no-op. - The open case lives in the URL, so leaving the screen and coming back reopens it; the nav rail returns each view to where it was. Editor and design system: Tab indents (and accepts a completion) instead of tabbing out, `properties` is offered as a flow header key, and a status pill grows with its text rather than spilling out of its background.
Qase nests suites and Studio only read their titles, so 250 cases arrived as one flat list under a row of dropdowns. Fetch the hierarchy, carry each case's suite path, and show it as the folder tree beside the matrix: same nesting, counts including everything below a folder, and picking one scopes the matrix to that subtree. The filter row starts as a search and nothing else — every other filter is added from an "Add filter" picker and removed with an ✕ — so a project with six custom fields no longer greets you with six dropdowns. The "group by" banding goes with it: the tree is the table of contents it stood in for.
Discover real iPhones, iPads, and Apple TVs through devicectl and drive them with the same XCTest driver used for simulators. Real hardware only runs code signed for the user's team, so the driver is built and signed locally on first use and cached per team. It binds all interfaces on device because the host reaches it over the LAN rather than a shared loopback, and app lifecycle routes through devicectl instead of simctl. Simulator-only features (set-location, open-link, clipboard, clear-keychain, add-media, recording, video stream, OS logs) fail with an explicit message. Also fixes tvOS inspection hanging on physical Apple TVs: querying HeadBoard for screen size, the status bar, and window origin never returns there, and none of it is needed on a platform without windowing or a status bar.
Three defects that only a physical device surfaces: Bonjour hostnames dashed apostrophes instead of dropping them, so "Douwe's iPhone" resolved to a name that doesn't exist. Candidates now also reuse devicectl's own sanitized hostnames, re-pointed at .local. Every tap killed the driver. A phone resting on a desk reports .faceUp, which simulators never do and orientationAwarePoint had no case for, so it hit fatalError. Flat orientations map to portrait, and an unmapped orientation now degrades to an untranslated point rather than taking the driver down. The first driver build for a team creates provisioning profiles as a side effect and Xcode regularly references one before it lands on disk, so retry once. Build failures now report xcodebuild's errors, not just an exit code.
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.
Adds support for driving real iPhones, iPads, and Apple TVs, alongside the existing simulator support.
Physical devices are discovered through
devicectl, show up inlist-deviceswith statusconnected, and are addressed by their CoreDevice identifier. Everything that runs through the XCTest driver —inspect,capture-ui,tap-on,press-key,swipe,launch-app, flows — behaves as it does on a simulator.How it works
Three things differ from a simulator, and each forced a design decision:
Signing. Real hardware only runs code signed for the user's team, and we can't ship a signed driver. The XCTest driver is therefore built and signed locally on first use (
xcodebuild build-for-testing -allowProvisioningUpdates), cached per team under~/.conductor/<platform>-driver-device/<team>/. Driver sources ship in the drivers tarball to make this possible.CONDUCTOR_TEAM_IDselects the team; when a Mac has several, conductor refuses to guess rather than sign with the wrong one.Reachability. The driver bound
127.0.0.1, which on a device is the device's loopback — unreachable from the host. ABIND_ALL=1env var (injected into the xctestrun) switches it to0.0.0.0, and the host connects over the LAN. Note that devicectl's*.coredevice.localhostnames only resolve inside Apple's tunnel, so the address is derived from the device's Bonjour name instead.App lifecycle.
simctlhas no device equivalent, so install/uninstall/launch/terminate route throughdevicectl.Not supported on device
These fail with an explicit message rather than misbehaving:
set-location,open-link, clipboard,clear-keychain,add-media, screen recording, and the live video stream (capture attaches to the Simulator framebuffer via SimulatorKit, which has no hardware counterpart). OS log collection is also simulator-only — devicectl has noconsolesubcommand — but Metro logs still stream, which is the useful source for React Native.clear-stateuninstalls without reinstalling.Driver fixes
Three bugs that only real hardware surfaces:
XCUIApplication("com.apple.HeadBoard")hangs indefinitely on a physical Apple TV. It backed screen size, status bars, and window origin, which brokedeviceInfoandviewHierarchy— the core inspect path. None of it applies on a platform with no windowing and no status bar, so all three are now#if os(tvOS)-guarded.deviceInfowent from a hang to 0.25s..faceUp, which simulators never do.orientationAwarePointhad no case for it and hitfatalError, so every tap killed the driver. Flat orientations now map to portrait, and an unmapped orientation logs and passes the point through instead of taking the driver down.Testing
Full suite passes (350 tests), including 8 new unit tests covering devicectl parsing, reachability classification, and hostname derivation.
Validated live on a physical Apple TV 4K and a physical iPhone 14 Pro: driver build/sign,
inspect,capture-ui,screenshot,list-apps,launch-app, and tap / press-key / swipe navigation, plus the streaming-input WebSocket. That validation was done against CLI 0.27.2; the branch has since been rebased onto 0.30.0 (which added Roku), so a repeat run on real hardware is worth doing before relying on it.