Module: Calabash::Cucumber::UIA

Included in:
Core
Defined in:
lib/calabash-cucumber/uia.rb,
lib/calabash-cucumber/uia.rb

Overview

Low-level module for interacting directly with UIA See also https://developer.apple.com/library/ios/documentation/ToolsLanguages/Reference/UIAElementClassReference/UIAElement/UIAElement.html Typically used to interact with System or remote views.

Instance Method Summary collapse

Instance Method Details

#uia(command, options = {}) ⇒ Object

Executes raw JavaScript in the UIAutomation environment (using eval).

Parameters:

  • command (String)

    the JavaScript snippet to execute

Returns:

  • (Object)

    the result returned by the UIA process

Raises:

  • (ArgumentError)


14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/calabash-cucumber/uia.rb', line 14

def uia(command, options={})
  raise ArgumentError, "Please supply :command" unless command

  # UIA only makes sense if there is a run loop
  launcher = Calabash::Cucumber::Launcher.launcher_if_used
  run_loop = launcher && launcher.attached_to_automator? && launcher.run_loop

  # Automatically attach in the calabash console
  if !run_loop && defined?(IRB)
    RunLoop.log_debug("Attaching to current instruments process...")
    launcher = Calabash::Cucumber::Launcher.new
    Calabash::Cucumber::Launcher.attach
    run_loop = launcher.run_loop
    RunLoop.log_debug("Attached!")
  end

  strategy = run_loop[:uia_strategy]
  case strategy
    when :preferences, :shared_element
      path = strategy == :preferences ? 'uia' : 'uia-shared'
      res = http({:method => :post, :path => path}, {:command => command}.merge(options))

      begin
        res = JSON.parse(res)
      rescue TypeError, JSON::ParserError => _
        raise "Could not parse response '#{res}'; the app has probably crashed"
      end

      if res['outcome'] != 'SUCCESS'
        raise "uia action failed because: #{res['reason']}\n#{res['details']}"
      end
      res['results'].first
    when :host
      res = RunLoop.send_command(run_loop, command)
      status = res['status']
      case status
        when 'success'
          res
        when 'error'
          value = res['value']
          if value
            msg = "uia action failed because: #{res['value']}"
          else
            msg = 'uia action failed for an unknown reason'
          end
          raise msg
        else
          candidates = ['success', 'error']
          raise RuntimeError, "expected '#{status}' to be one of #{candidates}"
      end
    else
      candidates = [:preferences, :shared_element, :host]
      raise ArgumentError, "expected '#{run_loop[:uia_strategy]}' to be one of #{candidates}"
  end
end

#uia_call(args_arr, *opts) ⇒ Object

Advanced method used to invoke UIAutomation JavaScript methods on objects found via Calabash queries

Examples:

uia_call [:button, {marked:'New Post'}], :isVisible

Advanced example that chains calls and uses arguments

uia_call [:view, marked:'New Post'], {withName:"New Post"}, :toString, {charAt:0}

Parameters:

  • args_arr (Array)

    array describing the query, e.g., [:button, {marked:'foo'}]

  • opts (Array)

    optional arguments specifying a chained sequence of method calls (see example)

See Also:



133
134
135
# File 'lib/calabash-cucumber/uia.rb', line 133

def uia_call(args_arr, *opts)
  uia_call_method(:queryEl, [args_arr], *opts)
end

#uia_call_windows(args_arr, *opts) ⇒ Object

Similar to uia_call but searches all windows

See Also:



139
140
141
# File 'lib/calabash-cucumber/uia.rb', line 139

def uia_call_windows(args_arr, *opts)
  uia_call_method(:queryElWindows, [args_arr], *opts)
end

#uia_keyboard_visible?Boolean

Note:

IMPORTANT this should only be used when the app does not respond to keyboard_visible? and UIAutomation is being used.

Used for detecting keyboards that are not normally visible to calabash; e.g. the keyboard on the MFMailComposeViewController

Returns:

  • (Boolean)

Raises:

  • (RuntimeError)

    If the app was not launched with instruments

See Also:

  • #keyboard_visible?


320
321
322
323
# File 'lib/calabash-cucumber/uia.rb', line 320

def uia_keyboard_visible?
  res = uia_query_windows(:keyboard)
  res != ":nil"
end

#uia_names(*queryparts) ⇒ Array<String>

Invoke a Calabash query inside the UIAutomation Calabash engine - includes all UIAWindows. Note that this traverses the UIA (accessibility) hierarchy.

Examples:

uia equivalent of identifier "button"

uia_names :button
# returns
[
  "Browse",
  "UINavigationBarBackIndicatorDefault.png",
  "16h",
  "reader postaction comment blue",
  "16h",
  "52",
  "17h",
  "10",
  "Reader",
  "Notifications",
  "Me",
  "New Post"
]

Parameters:

  • queryparts (Array)

    array of segments in the query, e.g., :button, {marked:'Hello'}

Returns:

  • (Array<String>)

    “names” (accessibilityIdentifier) of UIAElements matching the query.



119
120
121
122
# File 'lib/calabash-cucumber/uia.rb', line 119

def uia_names(*queryparts)
  #TODO escape '\n etc in query
  uia_handle_command(:names, queryparts)
end

#uia_orientationString

Gets the current orientation of the device

Returns:

  • (String)

    the current orientation of the device one of {'portrait', 'portrait-upside-down', 'landscape-left', 'landscape-right', 'faceup', 'facedown' }



278
279
280
281
# File 'lib/calabash-cucumber/uia.rb', line 278

def uia_orientation
  o = uia_handle_command(:orientation).to_s
  o[1..o.length]
end

#uia_query(*queryparts) ⇒ Array<Hash>

Invoke a Calabash query inside the UIAutomation Calabash engine Note that this traverses the UIA (accessibility) hierarchy.

Examples:

uia query equivalent of “button marked:'Hello'”

uia_query :button, marked:'Hello'

Parameters:

  • queryparts (Array)

    array of segments in the query, e.g., :button, {marked:'Hello'}

Returns:

  • (Array<Hash>)

    UIAElements matching the query in serialized form.



76
77
78
79
# File 'lib/calabash-cucumber/uia.rb', line 76

def uia_query(*queryparts)
  #TODO escape '\n etc in query
  uia_handle_command(:query, queryparts)
end

#uia_query_windows(*queryparts) ⇒ Array<Hash>

Invoke a Calabash query inside the UIAutomation Calabash engine - includes all UIAWindows. Note that this traverses the UIA (accessibility) hierarchy.

Examples:

uia query equivalent of “button marked:'Hello'”

uia_query_windows :button

Parameters:

  • queryparts (Array)

    array of segments in the query, e.g., :button, {marked:'Hello'}

Returns:

  • (Array<Hash>)

    UIAElements matching the query in serialized form.



93
94
95
96
# File 'lib/calabash-cucumber/uia.rb', line 93

def uia_query_windows(*queryparts)
  #TODO escape '\n etc in query
  uia_handle_command(:queryWindows, queryparts)
end

#uia_rotate(dir) ⇒ Object

Simulates Rotation of the device

Parameters:

  • dir (String|Symbol)

    The position of the home button after the rotation. Can be one of {'clockwise' | 'counter-clockwise'| :left | :right}.



271
272
273
# File 'lib/calabash-cucumber/uia.rb', line 271

def uia_rotate(dir)
  uia_handle_command(:rotate, dir)
end

#uia_rotate_home_button_to(dir) ⇒ Object

Note:

Refer to Apple's documentation for clarification about left vs. right landscape orientations.

Rotates the home button position to the position indicated by dir.

Parameters:

  • dir (Symbol|String)

    The position of the home button after the rotation. Can be one of {:down, :left, :right, :up }.



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/calabash-cucumber/uia.rb', line 288

def uia_rotate_home_button_to(dir)
  dir = dir.to_sym
  if dir == :top
    dir = :up
  elsif dir == :bottom
    dir = :down
  end
  uia_orientation = case dir
                     when :left then
                       'UIA_DEVICE_ORIENTATION_LANDSCAPERIGHT'
                     when :right then
                       'UIA_DEVICE_ORIENTATION_LANDSCAPELEFT'
                     when :up then
                       'UIA_DEVICE_ORIENTATION_PORTRAIT_UPSIDEDOWN'
                     when :down then
                       'UIA_DEVICE_ORIENTATION_PORTRAIT'
                     else
                       raise "Unexpected direction #{dir}"
                    end
  uia("target.setDeviceOrientation(#{uia_orientation})")
end

#uia_set_responder_value(value) ⇒ Object

Advanced method used for fast keyboard entry by calling the setValue method on the input with current keyboard focus. This is an alternative to calling keyboard_enter_text

Parameters:

  • value (String)

    the value to set



148
149
150
# File 'lib/calabash-cucumber/uia.rb', line 148

def uia_set_responder_value(value)
  uia_call_method(:elementWithKeyboardFocus, [], setValue: value)
end

#uia_wait_for_keyboard(options = {}) ⇒ Object

Note:

IMPORTANT this should only be used when the app does not respond to keyboard_visible? and UIAutomation is being used.

Waits for a keyboard that is not normally visible to calabash; e.g. the keyboard on MFMailComposeViewController.

Raises:

  • (RuntimeError)

    if the app was not launched with instruments

See Also:

  • #keyboard_visible?


335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/calabash-cucumber/uia.rb', line 335

def uia_wait_for_keyboard(options={})
  default_opts = {
    :timeout => 10,
    :retry_frequency => 0.1,
    :post_timeout => 0.5,
    :timeout_message => "Keyboard did not appear"
  }

  options = default_opts.merge(options)

  wait_for(options) do
    uia_keyboard_visible?
  end
  true
end