appom 1.4.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +170 -42
- data/lib/appom/configuration.rb +490 -0
- data/lib/appom/element_cache.rb +372 -0
- data/lib/appom/element_container.rb +257 -244
- data/lib/appom/element_finder.rb +142 -138
- data/lib/appom/element_state.rb +458 -0
- data/lib/appom/element_validation.rb +138 -0
- data/lib/appom/exceptions.rb +130 -0
- data/lib/appom/helpers.rb +328 -0
- data/lib/appom/logging.rb +106 -0
- data/lib/appom/page.rb +19 -10
- data/lib/appom/performance.rb +394 -0
- data/lib/appom/retry.rb +178 -0
- data/lib/appom/screenshot.rb +371 -0
- data/lib/appom/section.rb +24 -21
- data/lib/appom/smart_wait.rb +455 -0
- data/lib/appom/version.rb +4 -1
- data/lib/appom/visual.rb +600 -0
- data/lib/appom/wait.rb +96 -35
- data/lib/appom.rb +191 -20
- metadata +35 -19
|
@@ -1,313 +1,326 @@
|
|
|
1
|
-
|
|
2
|
-
module ElementContainer
|
|
3
|
-
def self.included(klass)
|
|
4
|
-
klass.extend ClassMethods
|
|
5
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
# Element container module for Appom automation framework
|
|
4
|
+
# Provides DSL methods for defining page elements, sections, and element collections
|
|
5
|
+
module Appom::ElementContainer
|
|
6
|
+
include Appom::Logging
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
def self.included(klass)
|
|
9
|
+
klass.extend ClassMethods
|
|
10
|
+
end
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
# Raise if contain a block
|
|
13
|
+
def raise_if_block(obj, name, has_block, type)
|
|
14
|
+
return unless has_block
|
|
16
15
|
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
# them such that there is only one hash passed as a final argument
|
|
20
|
-
# to Appium.
|
|
21
|
-
#
|
|
22
|
-
def merge_args(find_args, runtime_args={})
|
|
23
|
-
find_args = find_args.dup.flatten
|
|
24
|
-
runtime_args = runtime_args.dup
|
|
16
|
+
raise Appom::UnsupportedBlockError.new(name, "#{obj.class}##{type}")
|
|
17
|
+
end
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
##
|
|
20
|
+
# Options re-combiner. This takes the original inputs and combines
|
|
21
|
+
# them such that there is only one hash passed as a final argument
|
|
22
|
+
# to Appium.
|
|
23
|
+
#
|
|
24
|
+
def merge_args(find_args, runtime_args = {})
|
|
25
|
+
find_args = find_args.dup.flatten
|
|
26
|
+
runtime_args = runtime_args.dup
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
##
|
|
33
|
-
#
|
|
34
|
-
# Declare an element with name and args to find it
|
|
35
|
-
#
|
|
36
|
-
# element :email, :accessibility_id, 'email_text_field'
|
|
37
|
-
#
|
|
38
|
-
# @param name Element name
|
|
39
|
-
# @param *find_args An array contain information to find the element. It contains locator strategy and search target
|
|
40
|
-
# http://appium.io/docs/en/commands/element/find-element/
|
|
41
|
-
#
|
|
42
|
-
# Element doesn't support block so that will raise if pass a block when declare
|
|
43
|
-
#
|
|
44
|
-
def element(name, *find_args)
|
|
45
|
-
build_element(name, *find_args) do
|
|
46
|
-
define_method(name) do |*runtime_args, &block|
|
|
47
|
-
raise_if_block(self, name, !block.nil?, :element)
|
|
48
|
-
_find(*merge_args(find_args, runtime_args))
|
|
49
|
-
end
|
|
28
|
+
[*find_args, *runtime_args]
|
|
29
|
+
end
|
|
50
30
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
31
|
+
# Class methods for defining page elements and sections
|
|
32
|
+
module ClassMethods
|
|
33
|
+
attr_reader :mapped_items
|
|
54
34
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
define_method(name) do |*runtime_args, &block|
|
|
70
|
-
raise_if_block(self, name, !block.nil?, :elements)
|
|
71
|
-
_all(*merge_args(find_args, runtime_args))
|
|
72
|
-
end
|
|
35
|
+
##
|
|
36
|
+
#
|
|
37
|
+
# Declare an element with name and args to find it
|
|
38
|
+
#
|
|
39
|
+
# element :email, :accessibility_id, 'email_text_field'
|
|
40
|
+
#
|
|
41
|
+
# @param name Element name
|
|
42
|
+
# @param *find_args An array contain information to find the element. It contains locator strategy and search target
|
|
43
|
+
# http://appium.io/docs/en/commands/element/find-element/
|
|
44
|
+
#
|
|
45
|
+
# Element doesn't support block so that will raise if pass a block when declare
|
|
46
|
+
#
|
|
47
|
+
def element(name, *find_args)
|
|
48
|
+
Appom::ElementValidation.validate_element_args(name, *find_args)
|
|
73
49
|
|
|
74
|
-
|
|
50
|
+
build_element(name, *find_args) do
|
|
51
|
+
define_method(name) do |*runtime_args, &block|
|
|
52
|
+
raise_if_block(self, name, !block.nil?, :element)
|
|
53
|
+
_find(*merge_args(find_args, runtime_args))
|
|
75
54
|
end
|
|
55
|
+
|
|
56
|
+
create_get_element_params(name, find_args)
|
|
76
57
|
end
|
|
58
|
+
end
|
|
77
59
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
60
|
+
##
|
|
61
|
+
#
|
|
62
|
+
# Declare an elements with name and args to find it
|
|
63
|
+
#
|
|
64
|
+
# elements :contact_cell, :accessibility_id, 'contact_cell'
|
|
65
|
+
#
|
|
66
|
+
# @param name Element name
|
|
67
|
+
# @param *find_args An array contain information to find the elements. It contains locator strategy and search target
|
|
68
|
+
# http://appium.io/docs/en/commands/element/find-element/
|
|
69
|
+
#
|
|
70
|
+
# Elements doesn't support block so that will raise if pass a block when declare
|
|
71
|
+
#
|
|
72
|
+
def elements(name, *find_args)
|
|
73
|
+
Appom::ElementValidation.validate_element_args(name, *find_args)
|
|
85
74
|
|
|
86
|
-
|
|
75
|
+
build_elements(name, *find_args) do
|
|
76
|
+
define_method(name) do |*runtime_args, &block|
|
|
77
|
+
raise_if_block(self, name, !block.nil?, :elements)
|
|
78
|
+
_all(*merge_args(find_args, runtime_args))
|
|
87
79
|
end
|
|
80
|
+
|
|
81
|
+
create_get_element_params(name, find_args)
|
|
88
82
|
end
|
|
83
|
+
end
|
|
89
84
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
build_sections(section_class, name, *find_args) do
|
|
93
|
-
define_method(name) do |*runtime_args, &block|
|
|
94
|
-
raise_if_block(self, name, !block.nil?, :sections)
|
|
95
|
-
_all(*merge_args(find_args, runtime_args)).map do |element|
|
|
96
|
-
section_class.new(self, element)
|
|
97
|
-
end
|
|
98
|
-
end
|
|
85
|
+
def section(name, *args, &)
|
|
86
|
+
Appom::ElementValidation.validate_section_args(name, *args)
|
|
99
87
|
|
|
100
|
-
|
|
88
|
+
section_class, find_args = extract_section_options(args, &)
|
|
89
|
+
build_element(name, *find_args) do
|
|
90
|
+
define_method(name) do |*runtime_args|
|
|
91
|
+
section_element = _find(*merge_args(find_args, runtime_args))
|
|
92
|
+
section_class.new(self, section_element)
|
|
101
93
|
end
|
|
102
|
-
end
|
|
103
94
|
|
|
104
|
-
|
|
105
|
-
# Add item to @mapped_items array
|
|
106
|
-
#
|
|
107
|
-
# @param item Item need to add
|
|
108
|
-
#
|
|
109
|
-
def add_to_mapped_items(item)
|
|
110
|
-
@mapped_items ||= []
|
|
111
|
-
@mapped_items << item
|
|
95
|
+
create_get_element_params(name, find_args)
|
|
112
96
|
end
|
|
97
|
+
end
|
|
113
98
|
|
|
114
|
-
|
|
99
|
+
def sections(name, *args, &block)
|
|
100
|
+
Appom::ElementValidation.validate_section_args(name, *args)
|
|
115
101
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
102
|
+
section_class, find_args = extract_section_options(args, &block)
|
|
103
|
+
build_sections(section_class, name, *find_args) do
|
|
104
|
+
define_method(name) do |*runtime_args, &block|
|
|
105
|
+
raise_if_block(self, name, !block.nil?, :sections)
|
|
106
|
+
_all(*merge_args(find_args, runtime_args)).map do |element|
|
|
107
|
+
section_class.new(self, element)
|
|
108
|
+
end
|
|
123
109
|
end
|
|
124
110
|
|
|
125
|
-
|
|
126
|
-
create_nonexistence_checker(name, *find_args)
|
|
127
|
-
create_enable_checker(name, *find_args)
|
|
128
|
-
create_disable_checker(name, *find_args)
|
|
111
|
+
create_get_element_params(name, find_args)
|
|
129
112
|
end
|
|
113
|
+
end
|
|
130
114
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
115
|
+
##
|
|
116
|
+
# Add item to @mapped_items array
|
|
117
|
+
#
|
|
118
|
+
# @param item Item need to add
|
|
119
|
+
#
|
|
120
|
+
def add_to_mapped_items(item)
|
|
121
|
+
@mapped_items ||= []
|
|
122
|
+
@mapped_items << item
|
|
123
|
+
end
|
|
139
124
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
125
|
+
private
|
|
126
|
+
|
|
127
|
+
# Add item to @mapped_items or define method for element and section
|
|
128
|
+
def build_element(name, *find_args)
|
|
129
|
+
if find_args.empty?
|
|
130
|
+
create_error_method(name)
|
|
131
|
+
else
|
|
132
|
+
add_to_mapped_items(name)
|
|
133
|
+
yield
|
|
143
134
|
end
|
|
144
135
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
add_to_mapped_items(name)
|
|
151
|
-
yield
|
|
152
|
-
end
|
|
136
|
+
create_existence_checker(name, *find_args)
|
|
137
|
+
create_nonexistence_checker(name, *find_args)
|
|
138
|
+
create_enable_checker(name, *find_args)
|
|
139
|
+
create_disable_checker(name, *find_args)
|
|
140
|
+
end
|
|
153
141
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
142
|
+
# Add item to @mapped_items or define method for elements
|
|
143
|
+
def build_elements(name, *find_args)
|
|
144
|
+
if find_args.empty?
|
|
145
|
+
create_error_method(name)
|
|
146
|
+
else
|
|
147
|
+
add_to_mapped_items(name)
|
|
148
|
+
yield
|
|
157
149
|
end
|
|
158
150
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
151
|
+
create_existence_checker(name, *find_args)
|
|
152
|
+
create_nonexistence_checker(name, *find_args)
|
|
153
|
+
create_get_all_elements(name, *find_args)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Add item to @mapped_items or define method for elements
|
|
157
|
+
def build_sections(section_class, name, *find_args)
|
|
158
|
+
if find_args.empty?
|
|
159
|
+
create_error_method(name)
|
|
160
|
+
else
|
|
161
|
+
add_to_mapped_items(name)
|
|
162
|
+
yield
|
|
164
163
|
end
|
|
165
164
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
165
|
+
create_existence_checker(name, *find_args)
|
|
166
|
+
create_nonexistence_checker(name, *find_args)
|
|
167
|
+
create_get_all_sections(section_class, name, *find_args)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Define method to notify that we can't find item without args
|
|
171
|
+
def create_error_method(name)
|
|
172
|
+
define_method(name) do
|
|
173
|
+
raise Appom::InvalidElementError, name
|
|
172
174
|
end
|
|
175
|
+
end
|
|
173
176
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
create_helper_method(method_name, *find_args) do
|
|
180
|
-
define_method(method_name) do |*runtime_args|
|
|
181
|
-
args = merge_args(find_args, runtime_args)
|
|
182
|
-
wait_until_get_not_empty(*args)
|
|
183
|
-
end
|
|
184
|
-
end
|
|
177
|
+
def create_helper_method(proposed_method_name, *find_args)
|
|
178
|
+
if find_args.empty?
|
|
179
|
+
create_error_method(proposed_method_name)
|
|
180
|
+
else
|
|
181
|
+
yield
|
|
185
182
|
end
|
|
183
|
+
end
|
|
186
184
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
section_class.new(self, element)
|
|
197
|
-
end
|
|
198
|
-
end
|
|
185
|
+
##
|
|
186
|
+
# Try to get all elements until not get empty array
|
|
187
|
+
#
|
|
188
|
+
def create_get_all_elements(element_name, *find_args)
|
|
189
|
+
method_name = "get_all_#{element_name}"
|
|
190
|
+
create_helper_method(method_name, *find_args) do
|
|
191
|
+
define_method(method_name) do |*runtime_args|
|
|
192
|
+
args = merge_args(find_args, runtime_args)
|
|
193
|
+
wait_until_get_not_empty(*args)
|
|
199
194
|
end
|
|
200
195
|
end
|
|
196
|
+
end
|
|
201
197
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
#
|
|
207
|
-
|
|
208
|
-
method_name
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
wait_until('at least one element exists', *args)
|
|
198
|
+
##
|
|
199
|
+
# Try to get all sections until not get empty array
|
|
200
|
+
#
|
|
201
|
+
def create_get_all_sections(section_class, element_name, *find_args)
|
|
202
|
+
method_name = "get_all_#{element_name}"
|
|
203
|
+
create_helper_method(method_name, *find_args) do
|
|
204
|
+
define_method(method_name) do |*runtime_args|
|
|
205
|
+
args = merge_args(find_args, runtime_args)
|
|
206
|
+
wait_until_get_not_empty(*args).map do |element|
|
|
207
|
+
section_class.new(self, element)
|
|
213
208
|
end
|
|
214
209
|
end
|
|
215
210
|
end
|
|
211
|
+
end
|
|
216
212
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
end
|
|
213
|
+
##
|
|
214
|
+
# Check element exist
|
|
215
|
+
# We will try to find all elements with *find_args
|
|
216
|
+
# Condition is pass when response is not empty
|
|
217
|
+
#
|
|
218
|
+
def create_existence_checker(element_name, *find_args)
|
|
219
|
+
method_name = "has_#{element_name}"
|
|
220
|
+
create_helper_method(method_name, *find_args) do
|
|
221
|
+
define_method(method_name) do |*runtime_args|
|
|
222
|
+
args = merge_args(find_args, runtime_args)
|
|
223
|
+
wait_until('at least one element exists', *args)
|
|
229
224
|
end
|
|
230
225
|
end
|
|
226
|
+
end
|
|
231
227
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
228
|
+
##
|
|
229
|
+
# Check element non-existent
|
|
230
|
+
# We will try to find all elements with *find_args
|
|
231
|
+
# Condition is pass when response is empty
|
|
232
|
+
#
|
|
233
|
+
def create_nonexistence_checker(element_name, *find_args)
|
|
234
|
+
method_name = "has_no_#{element_name}"
|
|
235
|
+
create_helper_method(method_name, *find_args) do
|
|
236
|
+
define_method(method_name) do |*runtime_args|
|
|
237
|
+
args = merge_args(find_args, runtime_args)
|
|
238
|
+
wait_until('no element exists', *args)
|
|
242
239
|
end
|
|
243
240
|
end
|
|
241
|
+
end
|
|
244
242
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
end
|
|
243
|
+
##
|
|
244
|
+
# Wait until element will be enable
|
|
245
|
+
#
|
|
246
|
+
def create_enable_checker(element_name, *find_args)
|
|
247
|
+
method_name = "#{element_name}_enable"
|
|
248
|
+
create_helper_method(method_name, *find_args) do
|
|
249
|
+
define_method(method_name) do |*runtime_args|
|
|
250
|
+
args = merge_args(find_args, runtime_args)
|
|
251
|
+
wait_until('element enable', *args)
|
|
255
252
|
end
|
|
256
253
|
end
|
|
254
|
+
end
|
|
257
255
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
256
|
+
##
|
|
257
|
+
# Wait until an element will be disable
|
|
258
|
+
#
|
|
259
|
+
def create_disable_checker(element_name, *find_args)
|
|
260
|
+
method_name = "#{element_name}_disable"
|
|
261
|
+
create_helper_method(method_name, *find_args) do
|
|
262
|
+
define_method(method_name) do |*runtime_args|
|
|
263
|
+
args = merge_args(find_args, runtime_args)
|
|
264
|
+
wait_until('element disable', *args)
|
|
267
265
|
end
|
|
268
266
|
end
|
|
267
|
+
end
|
|
269
268
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
269
|
+
##
|
|
270
|
+
# Get parameter is passed when declared element
|
|
271
|
+
#
|
|
272
|
+
def create_get_element_params(element_name, *find_args)
|
|
273
|
+
method_name = "#{element_name}_params"
|
|
274
|
+
create_helper_method(method_name, *find_args) do
|
|
275
|
+
define_method(method_name) do
|
|
276
|
+
merge_args(find_args)
|
|
278
277
|
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
279
280
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
281
|
+
##
|
|
282
|
+
# Extract section options
|
|
283
|
+
# @return section class name and the remaining parameters
|
|
284
|
+
#
|
|
285
|
+
def extract_section_options(args, &)
|
|
286
|
+
if args.first.is_a?(Class)
|
|
287
|
+
klass = args.shift
|
|
288
|
+
section_class = klass if klass.ancestors.include?(Appom::Section)
|
|
283
289
|
end
|
|
284
290
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
klass = base_class
|
|
291
|
+
section_class = deduce_section_class(section_class, &)
|
|
292
|
+
arguments = deduce_search_arguments(section_class, args)
|
|
293
|
+
[section_class, arguments]
|
|
294
|
+
end
|
|
290
295
|
|
|
291
|
-
|
|
296
|
+
##
|
|
297
|
+
# Deduce section class
|
|
298
|
+
#
|
|
299
|
+
def deduce_section_class(base_class, &)
|
|
300
|
+
klass = base_class
|
|
292
301
|
|
|
293
|
-
|
|
294
|
-
raise ArgumentError, 'You should provide descendant of Appom::Section class or/and a block as the second argument.'
|
|
295
|
-
end
|
|
296
|
-
klass
|
|
297
|
-
end
|
|
302
|
+
klass = Class.new(klass || Appom::Section, &) if block_given?
|
|
298
303
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
def deduce_search_arguments(section_class, args)
|
|
303
|
-
extract_search_arguments(args) ||
|
|
304
|
-
extract_search_arguments(section_class.default_search_arguments) ||
|
|
305
|
-
raise(ArgumentError, 'You should provide search arguments in section creation or set_default_search_arguments within section class')
|
|
304
|
+
unless klass
|
|
305
|
+
raise Appom::InvalidSectionError,
|
|
306
|
+
'You should provide descendant of Appom::Section class or/and a block as the second argument.'
|
|
306
307
|
end
|
|
307
308
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
309
|
+
klass
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
##
|
|
313
|
+
# Deduce search parameters
|
|
314
|
+
#
|
|
315
|
+
def deduce_search_arguments(section_class, args)
|
|
316
|
+
extract_search_arguments(args) ||
|
|
317
|
+
extract_search_arguments(section_class.respond_to?(:default_search_arguments) ? section_class.default_search_arguments : nil) ||
|
|
318
|
+
raise(Appom::InvalidSectionError,
|
|
319
|
+
'You should provide search arguments in section creation or set_default_search_arguments within section class',)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def extract_search_arguments(args)
|
|
323
|
+
args if args && !args.empty?
|
|
311
324
|
end
|
|
312
325
|
end
|
|
313
326
|
end
|