mirror of
https://github.com/yattee/yattee.git
synced 2026-07-19 05:42:04 +00:00
Run AXe against an Xcode that ships SimulatorKit
AXe dlopens the private SimulatorKit.framework from $DEVELOPER_DIR/Library/PrivateFrameworks. Xcode 27 relocated that framework to Contents/SharedFrameworks, so under a selected Xcode 27 every axe command fails with "Failed to load essential private frameworks" and all UI assertions fail uniformly. Detect when the active Xcode lacks SimulatorKit at the path AXe expects and inject DEVELOPER_DIR pointing at the first installed Xcode that has it, for every axe subprocess. No global xcode-select switch required. Claude-Session: https://claude.ai/code/session_01Aqq5bSGrYfjQNvkTEzWcva
This commit is contained in:
@@ -155,7 +155,7 @@ module UITest
|
||||
# Type text
|
||||
# @param text [String] Text to type
|
||||
def type(text)
|
||||
output, status = Open3.capture2e('axe', 'type', '--stdin', '--udid', @udid, stdin_data: text)
|
||||
output, status = Open3.capture2e(self.class.axe_env, 'axe', 'type', '--stdin', '--udid', @udid, stdin_data: text)
|
||||
output = output.dup.force_encoding('UTF-8') if output.is_a?(String)
|
||||
raise AxeError, "type failed: #{output}" unless status.success?
|
||||
end
|
||||
@@ -202,11 +202,53 @@ module UITest
|
||||
private
|
||||
|
||||
def run_axe(*)
|
||||
output, status = Open3.capture2e('axe', *, '--udid', @udid)
|
||||
output, status = Open3.capture2e(self.class.axe_env, 'axe', *, '--udid', @udid)
|
||||
output = output.dup.force_encoding('UTF-8') if output.is_a?(String)
|
||||
[output, status]
|
||||
end
|
||||
|
||||
class << self
|
||||
# Environment overrides for every `axe` invocation.
|
||||
#
|
||||
# AXe loads Xcode's private SimulatorKit.framework at runtime. Some Xcode
|
||||
# betas ship without it (e.g. Xcode 27 beta), which makes every `axe`
|
||||
# command fail with "Failed to load essential private frameworks". When
|
||||
# the active Xcode is missing SimulatorKit, fall back to any installed
|
||||
# Xcode that has it by overriding DEVELOPER_DIR for the axe subprocess.
|
||||
# @return [Hash] Environment hash to pass to Open3
|
||||
def axe_env
|
||||
return @axe_env if defined?(@axe_env)
|
||||
|
||||
@axe_env = simulatorkit?(active_developer_dir) ? {} : fallback_env
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fallback_env
|
||||
dir = usable_developer_dir
|
||||
dir ? { 'DEVELOPER_DIR' => dir } : {}
|
||||
end
|
||||
|
||||
# Path to the active Xcode's Developer directory (xcode-select -p)
|
||||
def active_developer_dir
|
||||
@active_developer_dir ||= `xcode-select -p 2>/dev/null`.strip
|
||||
end
|
||||
|
||||
# First installed Xcode Developer directory that ships SimulatorKit
|
||||
def usable_developer_dir
|
||||
Dir.glob('/Applications/Xcode*.app/Contents/Developer').find do |dir|
|
||||
simulatorkit?(dir)
|
||||
end
|
||||
end
|
||||
|
||||
# Whether a Developer directory ships the SimulatorKit private framework
|
||||
def simulatorkit?(developer_dir)
|
||||
return false if developer_dir.nil? || developer_dir.empty?
|
||||
|
||||
File.exist?(File.join(developer_dir, 'Library', 'PrivateFrameworks', 'SimulatorKit.framework'))
|
||||
end
|
||||
end
|
||||
|
||||
# Wait for a file to exist and have non-zero size
|
||||
# Helps avoid race conditions where screenshot isn't fully written
|
||||
# @param path [String] Path to the file
|
||||
|
||||
Reference in New Issue
Block a user