Skip to content

Commit 61ebace

Browse files
authored
feat: add a few new wait syntaxes (#344)
* feat: add a few new wait syntaxes * get back to macOS 10.15 since simulators on 11 seems unstable * fix element id in test
1 parent d6f73b1 commit 61ebace

File tree

12 files changed

+120
-49
lines changed

12 files changed

+120
-49
lines changed

.github/workflows/unittest.yml

+6-6
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ jobs:
2929
- name: Run tests
3030
run: |
3131
bundle exec rake rubocop
32-
bundle exec parallel_test test/unit/ -n 4
33-
AUTOMATION_NAME_DROID=espresso bundle exec parallel_test test/unit/android -n 4
34-
AUTOMATION_NAME_DROID=appium AUTOMATION_NAME_IOS=appium bundle exec parallel_test test/unit -n 4
32+
bundle exec rake test:unit
33+
AUTOMATION_NAME_DROID=espresso bundle exec rake test:unit:android
34+
AUTOMATION_NAME_DROID=appium AUTOMATION_NAME_IOS=appium bundle exec rake test:unit
3535
3636
test-win:
3737
strategy:
@@ -57,11 +57,11 @@ jobs:
5757
gem uninstall --force eventmachine && gem install eventmachine --platform ruby
5858
- name: Run tests
5959
run: |
60-
parallel_test test/unit/ -n 4
60+
rake test:unit
6161
6262
setx AUTOMATION_NAME_DROID espresso
63-
parallel_test test/unit/android -n 4
63+
rake test:unit:android
6464
6565
setx AUTOMATION_NAME_DROID appium
6666
setx AUTOMATION_NAME_IOS appium
67-
parallel_test test/unit -n 4
67+
rake test:unit

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ Read `release_notes.md` for commit level details.
2525
- Removed `forceMjsonwp` to send only MJSONWP capabilities since Selenium cleint v4 no longer supports MJSONWP
2626
- No longer set default `timeouts` as `0`. ruby_lib_core calls `/timeouts` endpoint only when `appium_lib: { wait: 5 }` is provided explicitly
2727
- Raises `::Appium::Core::Error::ArgumentError` instead of `ArgumentError` for this library specific argument errors
28+
- Add `driver#wait`, `driver#wait_until`, `driver#wait_true`, `driver#wait_until_true` syntaxes
29+
- Can give `driver` instance as its block variable
2830

2931
## [4.7.1] - 2021-09-26
3032

ci-jobs/functional_test.yml

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# jobs for functional test
22
parameters:
3-
vmImage: 'macOS-11'
4-
vmImageForIOS: 'macOS-11'
5-
xcodeForIOS: 12.5.1
6-
xcodeForTVOS: 12.5.1
3+
vmImage: 'macOS-10.15'
4+
vmImageForIOS: 'macOS-10.15'
5+
xcodeForIOS: 12.2
6+
xcodeForTVOS: 12.2
77
androidSDK: 29 # API Level 30 emulators are more unstable than 29
88
appiumVersion: 'beta'
99
ignoreVersionSkip: true

lib/appium_lib_core/common/base/driver.rb

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
require_relative 'remote_status'
2222
require_relative 'has_location'
2323
require_relative 'has_network_connection'
24+
require_relative '../wait'
2425

2526
module Appium
2627
module Core
@@ -38,6 +39,8 @@ class Driver < ::Selenium::WebDriver::Driver
3839
include ::Appium::Core::Base::HasLocation
3940
include ::Appium::Core::Base::HasNetworkConnection
4041

42+
include ::Appium::Core::Waitable
43+
4144
private
4245

4346
# Private API.

lib/appium_lib_core/common/wait.rb

+36-10
Original file line numberDiff line numberDiff line change
@@ -137,18 +137,31 @@ module Waitable
137137
# @param [String] message Exception message if timed out.
138138
# @param [Array, Exception] ignored Exceptions to ignore while polling (default: Exception)
139139
#
140-
# @example
140+
# @example With core instance
141141
#
142142
# @core.wait_true { @driver.find_element :accessibility_id, 'something' }
143143
# @core.wait_true(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
144144
#
145-
def wait_true(timeout: nil, interval: nil, message: nil, ignored: nil)
146-
Wait.until_true(timeout: timeout || @wait_timeout,
147-
interval: interval || @wait_interval,
145+
# @core.wait_until_true { @driver.find_element :accessibility_id, 'something' }
146+
# @core.wait_until_true(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
147+
#
148+
# @example With driver instance
149+
#
150+
# @driver.wait_true { |d| d.find_element :accessibility_id, 'something' }
151+
# @driver.wait_true(timeout: 30, interval: 2) { |d| driver.find_element :accessibility_id, 'something' }
152+
#
153+
# @driver.wait_until_true { |d| d.find_element :accessibility_id, 'something' }
154+
# @driver.wait_until_true(timeout: 30, interval: 2) { |d| driver.find_element :accessibility_id, 'something' }
155+
#
156+
def wait_until_true(timeout: nil, interval: nil, message: nil, ignored: nil, &block)
157+
Wait.until_true(timeout: timeout || @wait_timeout || ::Appium::Core::Wait::DEFAULT_TIMEOUT,
158+
interval: interval || @wait_interval || ::Appium::Core::Wait::DEFAULT_INTERVAL,
148159
message: message,
149160
ignored: ignored,
150-
object: self) { yield }
161+
object: self,
162+
&block)
151163
end
164+
alias wait_true wait_until_true
152165

153166
# Check every interval seconds to see if yield doesn't raise an exception.
154167
# Give up after timeout seconds.
@@ -160,18 +173,31 @@ def wait_true(timeout: nil, interval: nil, message: nil, ignored: nil)
160173
# @param [String] message Exception message if timed out.
161174
# @param [Array, Exception] ignored Exceptions to ignore while polling (default: Exception)
162175
#
163-
# @example
176+
# @example With core instance
164177
#
165178
# @core.wait { @driver.find_element :accessibility_id, 'something' }
166179
# @core.wait(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
167180
#
168-
def wait(timeout: nil, interval: nil, message: nil, ignored: nil)
169-
Wait.until(timeout: timeout || @wait_timeout,
170-
interval: interval || @wait_interval,
181+
# @core.wait_until { @driver.find_element :accessibility_id, 'something' }
182+
# @core.wait_until(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
183+
#
184+
# @example With driver instance
185+
#
186+
# @driver.wait { @driver.find_element :accessibility_id, 'something' }
187+
# @driver.wait(timeout: 30, interval: 2) { @driver.find_element :accessibility_id, 'something' }
188+
#
189+
# @driver.wait_until { |d| d.find_element :accessibility_id, 'something' }
190+
# @driver.wait_until(timeout: 30, interval: 2) { |d| d.find_element :accessibility_id, 'something' }
191+
#
192+
def wait_until(timeout: nil, interval: nil, message: nil, ignored: nil, &block)
193+
Wait.until(timeout: timeout || @wait_timeout || ::Appium::Core::Wait::DEFAULT_TIMEOUT,
194+
interval: interval || @wait_interval || ::Appium::Core::Wait::DEFAULT_INTERVAL,
171195
message: message,
172196
ignored: ignored,
173-
object: self) { yield }
197+
object: self,
198+
&block)
174199
end
200+
alias wait wait_until
175201
end
176202
end # module Core
177203
end # module Appium

lib/appium_lib_core/driver.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def initialize(appium_lib_opts)
5151

5252
@port = appium_lib_opts.fetch :port, Driver::DEFAULT_APPIUM_PORT
5353

54-
# timeout and interval used in ::Appium::Comm.wait/wait_true
54+
# timeout and interval used in ::Appium::Commn.wait/wait_true
5555
@wait_timeout = appium_lib_opts.fetch :wait_timeout, ::Appium::Core::Wait::DEFAULT_TIMEOUT
5656
@wait_interval = appium_lib_opts.fetch :wait_interval, ::Appium::Core::Wait::DEFAULT_INTERVAL
5757

test/functional/android/android/device_data_test.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def test_performance_related
7676
end
7777

7878
def test_take_element_screenshot
79-
e = @@core.wait { @driver.find_element :accessibility_id, 'App' }
79+
e = @driver.wait { |d| d.find_element :accessibility_id, 'App' }
8080
@driver.take_element_screenshot(e, 'take_element_screenshot.png')
8181

8282
assert File.exist? 'take_element_screenshot.png'

test/functional/android/android/device_test.rb

+23-23
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def test_close_and_launch_app
5555
end
5656
else
5757
@driver.close_app
58-
assert(@@core.wait { @driver.app_state('io.appium.android.apis') != :running_in_foreground })
58+
assert(@driver.wait_until { |d| d.app_state('io.appium.android.apis') != :running_in_foreground })
5959
end
6060

6161
if @@core.automation_name == :espresso
@@ -64,7 +64,7 @@ def test_close_and_launch_app
6464
end
6565
else
6666
@driver.launch_app
67-
e = @@core.wait { @driver.find_element :accessibility_id, 'App' }
67+
e = @driver.wait_until { |d| d.find_element :accessibility_id, 'App' }
6868
assert_equal 'App', e.text
6969
end
7070
end
@@ -74,13 +74,13 @@ def test_lock_unlock
7474
assert @driver.device_locked?
7575

7676
@driver.unlock
77-
@@core.wait { assert !@driver.device_locked? }
77+
@driver.wait_until { |d| assert !d.device_locked? }
7878
end
7979

8080
def test_background_reset
8181
@driver.background_app 3
8282

83-
e = @@core.wait { @driver.find_element :accessibility_id, 'App' }
83+
e = @driver.wait_until { |d| d.find_element :accessibility_id, 'App' }
8484
assert_equal 'App', e.text
8585

8686
@driver.background_app(-1)
@@ -90,7 +90,7 @@ def test_background_reset
9090
# Instrumentation process will crash in Espresso
9191
if @@core.automation_name == :espresso
9292
@driver.activate_app('io.appium.android.apis')
93-
@@core.wait { assert @driver.app_state('io.appium.android.apis') == :running_in_foreground }
93+
@driver.wait_until { |d| assert d.app_state('io.appium.android.apis') == :running_in_foreground }
9494
else
9595
error = assert_raises ::Selenium::WebDriver::Error::WebDriverError do
9696
@driver.find_element :accessibility_id, 'App'
@@ -100,7 +100,7 @@ def test_background_reset
100100
@driver.reset
101101
end
102102

103-
e = @@core.wait(timeout: 60) { @driver.find_element :accessibility_id, 'App' }
103+
e = @driver.wait(timeout: 60) { |d| d.find_element :accessibility_id, 'App' }
104104
assert_equal 'App', e.text
105105
end
106106

@@ -126,12 +126,12 @@ def test_context_related
126126
webview_context = contexts.detect { |e| e.start_with?('WEBVIEW') }
127127

128128
@driver.set_context webview_context
129-
@@core.wait { assert @driver.current_context.start_with? 'WEBVIEW' }
129+
@driver.wait { |d| assert d.current_context.start_with? 'WEBVIEW' }
130130

131131
webview_page = @driver.page_source
132132

133133
@driver.switch_to_default_context
134-
@@core.wait { assert_equal 'NATIVE_APP', @driver.current_context }
134+
@driver.wait { |d| assert_equal 'NATIVE_APP', d.current_context }
135135
assert native_page != webview_page
136136
end
137137

@@ -159,35 +159,35 @@ def test_app_management
159159
# Instrumentation process will crash in Espresso
160160
unless @@core.automation_name == :espresso
161161
assert @driver.terminate_app('io.appium.android.apis')
162-
@@core.wait { assert @driver.app_state('io.appium.android.apis') == :not_running }
162+
@driver.wait_until { |d| assert d.app_state('io.appium.android.apis') == :not_running }
163163
end
164164

165165
assert @driver.activate_app('io.appium.android.apis').nil?
166-
@@core.wait { assert @driver.app_state('io.appium.android.apis') == :running_in_foreground }
166+
@driver.wait_until { assert @driver.app_state('io.appium.android.apis') == :running_in_foreground }
167167
end
168168

169169
def test_start_activity
170-
e = @@core.wait { @driver.current_activity }
170+
e = @driver.wait_until(&:current_activity)
171171
assert_equal '.ApiDemos', e
172172

173173
@driver.start_activity app_package: 'io.appium.android.apis',
174174
app_activity: '.accessibility.AccessibilityNodeProviderActivity'
175-
e = @@core.wait { @driver.current_activity }
175+
e = @driver.wait_until(&:current_activity)
176176
assert true, e.include?('Node')
177177

178178
# Espresso cannot launch my root launched activity: https://github.com/appium/appium-espresso-driver/pull/378#discussion_r250034209
179179
return if @@core.automation_name == :espresso
180180

181181
@driver.start_activity app_package: 'com.android.settings', app_activity: '.Settings',
182182
app_wait_package: 'com.android.settings', app_wait_activity: '.Settings'
183-
e = @@core.wait { @driver.current_activity }
183+
e = @driver.wait_until(&:current_activity)
184184
assert true, e.include?('Settings')
185185

186186
@driver.start_activity app_package: 'io.appium.android.apis', app_activity: '.ApiDemos'
187187
end
188188

189189
def test_current_package
190-
e = @@core.wait { @driver.current_package }
190+
e = @driver.wait_until(&:current_package)
191191
assert_equal 'io.appium.android.apis', e
192192
end
193193

@@ -197,21 +197,21 @@ def test_touch_actions
197197
.release
198198
.perform
199199

200-
@@core.wait { @driver.find_element :accessibility_id, 'Action Bar' }
200+
@driver.wait_until { |d| d.find_element :accessibility_id, 'Action Bar' }
201201
@driver.back
202202
end
203203

204204
def test_swipe
205-
@@core.wait { @driver.find_element :accessibility_id, 'App' }.click
205+
@driver.wait_until { |d| d.find_element :accessibility_id, 'App' }.click
206206

207-
el = @@core.wait { @driver.find_element :accessibility_id, 'Fragment' }
207+
el = @driver.wait_until { |d| d.find_element :accessibility_id, 'Fragment' }
208208
rect = el.rect
209209

210210
Appium::Core::TouchAction.new(@driver)
211211
.swipe(start_x: 75, start_y: 500, end_x: 75, end_y: 500, duration: 500)
212212
.perform
213213
@driver.back # The above command become "tap" action since it doesn't move.
214-
el = @@core.wait { @driver.find_element :accessibility_id, 'Fragment' }
214+
el = @driver.wait_until { |d| d.find_element :accessibility_id, 'Fragment' }
215215
assert rect.x == el.rect.x
216216
assert rect.y == el.rect.y
217217

@@ -237,11 +237,11 @@ def test_swipe
237237
end
238238

239239
def test_hidekeyboard
240-
@@core.wait { @driver.find_element :accessibility_id, 'App' }.click
241-
@@core.wait { @driver.find_element :accessibility_id, 'Activity' }.click
242-
@@core.wait { @driver.find_element :accessibility_id, 'Custom Title' }.click
240+
@driver.wait_until { |d| d.find_element :accessibility_id, 'App' }.click
241+
@driver.wait_until { |d| d.find_element :accessibility_id, 'Activity' }.click
242+
@driver.wait_until { |d| d.find_element :accessibility_id, 'Custom Title' }.click
243243
# make sure to show keyboard
244-
@@core.wait { @driver.find_element :id, 'io.appium.android.apis:id/left_text_edit' }.click
244+
@driver.wait_until { |d| d.find_element :id, 'io.appium.android.apis:id/left_text_edit' }.click
245245

246246
latin_android = 'com.android.inputmethod.latin/.LatinIME'
247247
latin_google = 'com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME'
@@ -257,7 +257,7 @@ def test_hidekeyboard
257257

258258
assert @driver.keyboard_shown?
259259

260-
@@core.wait { @driver.hide_keyboard }
260+
@driver.wait(&:hide_keyboard)
261261
sleep 1 # wait animation
262262

263263
assert !@driver.is_keyboard_shown

test/functional/android/android/mobile_commands_test.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,9 @@ def test_backdoor
196196
e = @driver.find_elements :class, 'android.widget.TextView'
197197
assert_equal '0', e.last.text
198198

199-
type = @driver.execute_script('mobile: backdoor',
200-
{ target: :element, elementId: e.last.id, methods: [{ name: 'getTypeface' }] })
201-
assert type['mStyle']
199+
stype = @driver.execute_script('mobile: backdoor', { target: :element, elementId: e.last.id,
200+
methods: [{ name: 'getTypeface' }, { name: 'getStyle' }] })
201+
assert stype.zero?
202202
end
203203

204204
# @since Appium 1.12.0 (Espresso driver 1.8.0~)

test/functional/android/driver_test.rb

+20
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,26 @@ def test_wait
4949
assert_equal 'Content', e.text
5050
end
5151

52+
def test_wait_true_driver
53+
e = @driver.wait_true { |d| d.find_element :accessibility_id, 'Content' }
54+
assert e.text
55+
end
56+
57+
def test_wait_driver
58+
e = @driver.wait { |d| d.find_element :accessibility_id, 'Content' }
59+
assert_equal 'Content', e.text
60+
end
61+
62+
def test_wait_until_true_driver
63+
e = @driver.wait_until_true { |d| d.find_element :accessibility_id, 'Content' }
64+
assert e.text
65+
end
66+
67+
def test_wait_until_driver
68+
e = @driver.wait_until { |d| d.find_element :accessibility_id, 'Content' }
69+
assert_equal 'Content', e.text
70+
end
71+
5272
# @since Appium 1.10.0
5373
def test_mobile_perform_action
5474
skip_as_appium_version '1.10.0'

test/functional/ios/driver_test.rb

+20
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,26 @@ def test_wait
6060
assert_equal uicatalog, e.name
6161
end
6262

63+
def test_wait_true_driver
64+
e = @@driver.wait_true { |d| d.find_element :accessibility_id, uicatalog }
65+
assert e.name
66+
end
67+
68+
def test_wait_driver
69+
e = @@driver.wait { |d| d.find_element :accessibility_id, uicatalog }
70+
assert_equal uicatalog, e.name
71+
end
72+
73+
def test_wait_until_true_driver
74+
e = @@driver.wait_until_true { |d| d.find_element :accessibility_id, uicatalog }
75+
assert e.name
76+
end
77+
78+
def test_wait_until_driver
79+
e = @@driver.wait_until { |d| d.find_element :accessibility_id, uicatalog }
80+
assert_equal uicatalog, e.name
81+
end
82+
6383
def test_click_back
6484
skip_as_appium_version '1.8.0' # 1.7.2- have a bit different behaviour
6585

test/test_helper.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def self.mac2
121121

122122
# Require a simulator which OS version is 11.4, for example.
123123
def ios(platform_name = :ios)
124-
platform_version = '14.5'
124+
platform_version = '14.2'
125125
wda_port = wda_local_port
126126

127127
real_device = ENV['REAL'] ? true : false

0 commit comments

Comments
 (0)