testa_appium_driver 0.1.11 → 0.1.14

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +23 -0
  3. data/.gitignore +16 -16
  4. data/.rspec +3 -3
  5. data/.rubocop.yml +13 -13
  6. data/CHANGELOG.md +5 -5
  7. data/CODE_OF_CONDUCT.md +102 -102
  8. data/Gemfile +12 -12
  9. data/Gemfile.lock +74 -0
  10. data/LICENSE.txt +21 -21
  11. data/README.md +402 -378
  12. data/Rakefile +12 -12
  13. data/{testa_appium_driver.iml → appium-driver.iml} +71 -78
  14. data/bin/console +17 -17
  15. data/bin/setup +8 -8
  16. data/lib/testa_appium_driver/android/class_selectors.rb +437 -437
  17. data/lib/testa_appium_driver/android/driver.rb +70 -69
  18. data/lib/testa_appium_driver/android/locator/attributes.rb +117 -113
  19. data/lib/testa_appium_driver/android/locator.rb +141 -141
  20. data/lib/testa_appium_driver/android/scroll_actions/uiautomator_scroll_actions.rb +61 -61
  21. data/lib/testa_appium_driver/android/selenium_element.rb +11 -7
  22. data/lib/testa_appium_driver/common/bounds.rb +149 -149
  23. data/lib/testa_appium_driver/common/constants.rb +37 -36
  24. data/lib/testa_appium_driver/common/exceptions/strategy_mix_exception.rb +11 -11
  25. data/lib/testa_appium_driver/common/helpers.rb +270 -270
  26. data/lib/testa_appium_driver/common/locator/scroll_actions.rb +397 -397
  27. data/lib/testa_appium_driver/common/locator.rb +627 -610
  28. data/lib/testa_appium_driver/common/scroll_actions/json_wire_scroll_actions.rb +3 -3
  29. data/lib/testa_appium_driver/common/scroll_actions/w3c_scroll_actions.rb +304 -237
  30. data/lib/testa_appium_driver/common/scroll_actions.rb +253 -246
  31. data/lib/testa_appium_driver/common/selenium_element.rb +19 -19
  32. data/lib/testa_appium_driver/driver.rb +328 -312
  33. data/lib/testa_appium_driver/ios/driver.rb +48 -48
  34. data/lib/testa_appium_driver/ios/locator/attributes.rb +84 -80
  35. data/lib/testa_appium_driver/ios/locator.rb +71 -70
  36. data/lib/testa_appium_driver/ios/selenium_element.rb +6 -6
  37. data/lib/testa_appium_driver/ios/type_selectors.rb +187 -187
  38. data/lib/testa_appium_driver/version.rb +5 -5
  39. data/lib/testa_appium_driver.rb +6 -6
  40. data/testa_appium_driver.gemspec +41 -41
  41. metadata +9 -16
  42. data/.idea/deployment.xml +0 -22
  43. data/.idea/inspectionProfiles/Project_Default.xml +0 -9
  44. data/.idea/misc.xml +0 -6
  45. data/.idea/modules.xml +0 -8
  46. data/.idea/runConfigurations/Android_Test.xml +0 -42
  47. data/.idea/runConfigurations.xml +0 -10
  48. data/.idea/sshConfigs.xml +0 -13
  49. data/.idea/vcs.xml +0 -6
  50. data/.idea/webServers.xml +0 -21
@@ -1,70 +1,71 @@
1
- require_relative 'class_selectors'
2
- require_relative 'locator'
3
- require_relative 'scroll_actions/uiautomator_scroll_actions'
4
- require_relative 'selenium_element'
5
-
6
- module TestaAppiumDriver
7
- class Driver
8
- include ClassSelectors
9
-
10
-
11
- # executes shell command
12
- # @param [String] command Shell command name to execute for example echo or rm
13
- # @param [Array<String>] args Array of command arguments, example: ['-f', '/sdcard/my_file.txt']
14
- # @param [Integer] timeout Command timeout in milliseconds. If the command blocks for longer than this timeout then an exception is going to be thrown. The default timeout is 20000 ms
15
- # @param [Boolean] includeStderr Whether to include stderr stream into the returned result.
16
- def shell(command, args: nil, timeout: nil, includeStderr: true)
17
- params = {
18
- command: command,
19
- includeStderr: includeStderr
20
- }
21
- params[:args] = args unless args.nil?
22
- params[:timeout] = timeout unless timeout.nil?
23
- @driver.execute_script("mobile: shell", params)
24
- end
25
-
26
-
27
- def scrollable
28
- locator = Locator.new(self, self, {single: true})
29
- locator.xpath_selector = "//androidx.recyclerview.widget.RecyclerView|//android.widget.ScrollView|//android.widget.ListView|//android.widget.HorizontalScrollView"
30
- locator.ui_selector = "new UiSelector().scrollable(true).instance(0)"
31
- locator
32
- end
33
-
34
-
35
- def scrollables
36
- locator = Locator.new(self, self, {single: false})
37
- locator.xpath_selector = "//androidx.recyclerview.widget.RecyclerView|//android.widget.ScrollView|//android.widget.ListView|//android.widget.HorizontalScrollView"
38
- locator.ui_selector = "new UiSelector().scrollable(true)"
39
- locator
40
- end
41
-
42
-
43
-
44
- private
45
- def handle_testa_opts
46
- if @testa_opts[:default_find_strategy].nil?
47
- @default_find_strategy = DEFAULT_ANDROID_FIND_STRATEGY
48
- else
49
- case @testa_opts[:default_find_strategy].to_sym
50
- when FIND_STRATEGY_UIAUTOMATOR, FIND_STRATEGY_XPATH
51
- @default_find_strategy = @testa_opts[:default_find_strategy].to_sym
52
- else
53
- raise "Default find strategy #{@testa_opts[:default_find_strategy]} not supported for Android"
54
- end
55
- end
56
-
57
-
58
- if @testa_opts[:default_scroll_strategy].nil?
59
- @default_scroll_strategy = DEFAULT_ANDROID_SCROLL_STRATEGY
60
- else
61
- case @testa_opts[:default_scroll_strategy].to_sym
62
- when SCROLL_STRATEGY_W3C, SCROLL_STRATEGY_UIAUTOMATOR
63
- @default_scroll_strategy = @testa_opts[:default_scroll_strategy].to_sym
64
- else
65
- raise "Default scroll strategy #{@testa_opts[:default_scroll_strategy]} not supported for Android"
66
- end
67
- end
68
- end
69
- end
1
+ require_relative 'class_selectors'
2
+ require_relative 'locator'
3
+ require_relative 'scroll_actions/uiautomator_scroll_actions'
4
+ require_relative 'selenium_element'
5
+
6
+ module TestaAppiumDriver
7
+ class Driver
8
+ include ClassSelectors
9
+
10
+
11
+
12
+ # executes shell command
13
+ # @param [String] command Shell command name to execute for example echo or rm
14
+ # @param [Array<String>] args Array of command arguments, example: ['-f', '/sdcard/my_file.txt']
15
+ # @param [Integer] timeout Command timeout in milliseconds. If the command blocks for longer than this timeout then an exception is going to be thrown. The default timeout is 20000 ms
16
+ # @param [Boolean] includeStderr Whether to include stderr stream into the returned result.
17
+ def shell(command, args: nil, timeout: nil, includeStderr: true)
18
+ params = {
19
+ command: command,
20
+ includeStderr: includeStderr
21
+ }
22
+ params[:args] = args unless args.nil?
23
+ params[:timeout] = timeout unless timeout.nil?
24
+ @driver.execute_script("mobile: shell", params)
25
+ end
26
+
27
+
28
+ def scrollable
29
+ locator = Locator.new(self, self, {single: true})
30
+ locator.xpath_selector = "//androidx.recyclerview.widget.RecyclerView|//android.widget.ScrollView|//android.widget.ListView|//android.widget.HorizontalScrollView"
31
+ locator.ui_selector = "new UiSelector().scrollable(true).instance(0)"
32
+ locator
33
+ end
34
+
35
+
36
+ def scrollables
37
+ locator = Locator.new(self, self, {single: false})
38
+ locator.xpath_selector = "//androidx.recyclerview.widget.RecyclerView|//android.widget.ScrollView|//android.widget.ListView|//android.widget.HorizontalScrollView"
39
+ locator.ui_selector = "new UiSelector().scrollable(true)"
40
+ locator
41
+ end
42
+
43
+
44
+
45
+ private
46
+ def handle_testa_opts
47
+ if @testa_opts[:default_find_strategy].nil?
48
+ @default_find_strategy = DEFAULT_ANDROID_FIND_STRATEGY
49
+ else
50
+ case @testa_opts[:default_find_strategy].to_sym
51
+ when FIND_STRATEGY_UIAUTOMATOR, FIND_STRATEGY_XPATH
52
+ @default_find_strategy = @testa_opts[:default_find_strategy].to_sym
53
+ else
54
+ raise "Default find strategy #{@testa_opts[:default_find_strategy]} not supported for Android"
55
+ end
56
+ end
57
+
58
+
59
+ if @testa_opts[:default_scroll_strategy].nil?
60
+ @default_scroll_strategy = DEFAULT_ANDROID_SCROLL_STRATEGY
61
+ else
62
+ case @testa_opts[:default_scroll_strategy].to_sym
63
+ when SCROLL_STRATEGY_W3C, SCROLL_STRATEGY_UIAUTOMATOR
64
+ @default_scroll_strategy = @testa_opts[:default_scroll_strategy].to_sym
65
+ else
66
+ raise "Default scroll strategy #{@testa_opts[:default_scroll_strategy]} not supported for Android"
67
+ end
68
+ end
69
+ end
70
+ end
70
71
  end
@@ -1,114 +1,118 @@
1
- module TestaAppiumDriver
2
- module Attributes
3
-
4
- #noinspection RubyNilAnalysis
5
- def attribute(name, *args)
6
- elements = execute(*args)
7
-
8
- @driver = get_driver if self.instance_of?(Selenium::WebDriver::Element)
9
-
10
- if elements.kind_of?(Selenium::WebDriver::Element)
11
- r = elements.send(:attribute, name.to_s)
12
- r = TestaAppiumDriver::Bounds.from_android(r, @driver) if name.to_s == "bounds"
13
- else
14
- r = elements.map { |e| e.send(:attribute, name.to_s) }
15
- r.map! { |b| TestaAppiumDriver::Bounds.from_android(b, @driver) } if name.to_s == "bounds"
16
- end
17
- r
18
- end
19
-
20
- def text(*args)
21
- attribute("text", *args)
22
- end
23
-
24
- def package(*args)
25
- attribute("package", *args)
26
- end
27
-
28
- def class_name(*args)
29
- attribute("className", *args)
30
- end
31
-
32
- def checkable?(*args)
33
- attribute("checkable", *args).to_s == "true"
34
- end
35
-
36
- def checked?(*args)
37
- attribute("checked", *args).to_s == "true"
38
- end
39
-
40
- def clickable?(*args)
41
- attribute("clickable", *args).to_s == "true"
42
- end
43
-
44
- def desc(*args)
45
- attribute("contentDescription", *args)
46
- end
47
-
48
- def enabled?(*args)
49
- attribute("enabled", *args).to_s == "true"
50
- end
51
-
52
- def focusable?(*args)
53
- attribute("focusable", *args).to_s == "true"
54
- end
55
-
56
- def focused?(*args)
57
- attribute("focused", *args).to_s == "true"
58
- end
59
-
60
- def long_clickable?(*args)
61
- attribute("longClickable", *args).to_s == "true"
62
- end
63
-
64
- def password?(*args)
65
- attribute("password", *args).to_s == "true"
66
- end
67
-
68
- def id(*args)
69
- attribute("resourceId", *args)
70
- end
71
-
72
- def scrollable?(*args)
73
- attribute("scrollable", *args).to_s == "true"
74
- end
75
-
76
- def selected?(*args)
77
- attribute("selected", *args).to_s == "true"
78
- end
79
-
80
- def displayed?(*args)
81
- attribute("displayed", *args).to_s == "true"
82
- end
83
-
84
- def selection_start(*args)
85
- attribute("selection-start", *args)
86
- end
87
-
88
- def selection_end(*args)
89
- attribute("selection-end", *args)
90
- end
91
-
92
- def bounds(*args)
93
- attribute("bounds", *args)
94
- end
95
-
96
- end
97
-
98
- class Locator
99
- include TestaAppiumDriver::Attributes
100
-
101
-
102
- # element index in parent element, starts from 0
103
- #noinspection RubyNilAnalysis,RubyYardReturnMatch
104
- # @return [Integer, nil] index of element
105
- def index(*args)
106
- raise "Index not supported for uiautomator strategy" if @strategy == FIND_STRATEGY_UIAUTOMATOR
107
- this = execute(*args)
108
- children = self.dup.parent.children.execute
109
- index = children.index(this)
110
- raise "Index not found" if index.nil?
111
- index.to_i
112
- end
113
- end
1
+ module TestaAppiumDriver
2
+ module Attributes
3
+
4
+ #noinspection RubyNilAnalysis
5
+ def testa_attribute(name, *args)
6
+ if self.instance_of?(::Selenium::WebDriver::Element) || self.instance_of?(::Appium::Core::Element)
7
+ @driver = get_driver # does not get correct driver
8
+ elements = self
9
+ else
10
+ elements = execute(*args)
11
+ raise "Element not found" if elements.nil?
12
+ end
13
+
14
+
15
+
16
+ if elements.kind_of?(::Selenium::WebDriver::Element) || elements.kind_of?(::Appium::Core::Element)
17
+ r = elements.send(:attribute, name.to_s)
18
+ r = TestaAppiumDriver::Bounds.from_android(r, @driver) if name.to_s == "bounds"
19
+ else
20
+ r = elements.map { |e| e.send(:attribute, name.to_s) }
21
+ r.map! { |b| TestaAppiumDriver::Bounds.from_android(b, @driver) } if name.to_s == "bounds"
22
+ end
23
+ r
24
+ end
25
+
26
+ def text(*args)
27
+ testa_attribute("text", *args)
28
+ end
29
+
30
+ def package(*args)
31
+ testa_attribute("package", *args)
32
+ end
33
+
34
+ def class_name(*args)
35
+ testa_attribute("className", *args)
36
+ end
37
+
38
+ def checkable?(*args)
39
+ testa_attribute("checkable", *args).to_s == "true"
40
+ end
41
+
42
+ def checked?(*args)
43
+ testa_attribute("checked", *args).to_s == "true"
44
+ end
45
+
46
+ def clickable?(*args)
47
+ testa_attribute("clickable", *args).to_s == "true"
48
+ end
49
+
50
+ def desc(*args)
51
+ testa_attribute("contentDescription", *args)
52
+ end
53
+
54
+ def enabled?(*args)
55
+ testa_attribute("enabled", *args).to_s == "true"
56
+ end
57
+
58
+ def focusable?(*args)
59
+ testa_attribute("focusable", *args).to_s == "true"
60
+ end
61
+
62
+ def focused?(*args)
63
+ testa_attribute("focused", *args).to_s == "true"
64
+ end
65
+
66
+ def long_clickable?(*args)
67
+ testa_attribute("longClickable", *args).to_s == "true"
68
+ end
69
+
70
+ def password?(*args)
71
+ testa_attribute("password", *args).to_s == "true"
72
+ end
73
+
74
+ def id(*args)
75
+ testa_attribute("resourceId", *args)
76
+ end
77
+
78
+ def scrollable?(*args)
79
+ testa_attribute("scrollable", *args).to_s == "true"
80
+ end
81
+
82
+ def selected?(*args)
83
+ testa_attribute("selected", *args).to_s == "true"
84
+ end
85
+
86
+ def displayed?(*args)
87
+ testa_attribute("displayed", *args).to_s == "true"
88
+ end
89
+
90
+ def selection_start(*args)
91
+ testa_attribute("selection-start", *args)
92
+ end
93
+
94
+ def selection_end(*args)
95
+ testa_attribute("selection-end", *args)
96
+ end
97
+
98
+ def bounds(*args)
99
+ testa_attribute("bounds", *args)
100
+ end
101
+
102
+ end
103
+
104
+ class Locator
105
+
106
+ # element index in parent element, starts from 0
107
+ #noinspection RubyNilAnalysis,RubyYardReturnMatch
108
+ # @return [Integer, nil] index of element
109
+ def index(*args)
110
+ raise "Index not supported for uiautomator strategy" if @strategy == FIND_STRATEGY_UIAUTOMATOR
111
+ this = execute(*args)
112
+ children = self.dup.parent.children.execute
113
+ index = children.index(this)
114
+ raise "Index not found" if index.nil?
115
+ index.to_i
116
+ end
117
+ end
114
118
  end
@@ -1,142 +1,142 @@
1
- require_relative 'locator/attributes'
2
-
3
- module TestaAppiumDriver
4
- #noinspection RubyTooManyInstanceVariablesInspection
5
- class Locator
6
- attr_accessor :closing_parenthesis
7
- include ClassSelectors
8
-
9
- def init(params, selectors, single)
10
- @closing_parenthesis = 0
11
-
12
-
13
- @ui_selector = hash_to_uiautomator(selectors, single)
14
-
15
-
16
- if is_scrollable_selector?(selectors, single)
17
- if selectors[:class] == "android.widget.HorizontalScrollView"
18
- @scroll_orientation = :horizontal
19
- else
20
- @scroll_orientation = :vertical
21
- end
22
-
23
- if !params[:top].nil? || !params[:bottom].nil? || !params[:right].nil? || !params[:left].nil?
24
- @scroll_deadzone = {}
25
- @scroll_deadzone[:top] = params[:top].to_f unless params[:top].nil?
26
- @scroll_deadzone[:bottom] = params[:bottom].to_f unless params[:bottom].nil?
27
- @scroll_deadzone[:right] = params[:right].to_f unless params[:right].nil?
28
- @scroll_deadzone[:left] = params[:left].to_f unless params[:left].nil?
29
- end
30
-
31
- params[:scrollable_locator] = self.dup
32
- end
33
-
34
- @scrollable_locator = params[:scrollable_locator] if params[:scrollable_locator]
35
- end
36
-
37
-
38
- # resolve selector which will be used for finding element
39
- def strategies_and_selectors
40
- ss = []
41
- if @can_use_id_strategy
42
- ss.push({"#{FIND_STRATEGY_ID}": @can_use_id_strategy})
43
- end
44
- if @strategy.nil? || @strategy == FIND_STRATEGY_UIAUTOMATOR
45
- ss.push({"#{FIND_STRATEGY_UIAUTOMATOR}": ui_selector})
46
- end
47
-
48
- if @strategy.nil? || @strategy == FIND_STRATEGY_XPATH
49
- ss.push({"#{FIND_STRATEGY_XPATH}": @xpath_selector})
50
- end
51
-
52
- if @strategy == FIND_STRATEGY_IMAGE
53
- ss.push({"#{FIND_STRATEGY_IMAGE}": @image_selector})
54
- end
55
- ss
56
- end
57
-
58
-
59
- # @param [Boolean] include_semicolon should the semicolon be included at the end
60
- # @return ui_selector for uiautomator find strategy
61
- def ui_selector(include_semicolon = true)
62
- @ui_selector + ")" * @closing_parenthesis + (include_semicolon ? ";" : "");
63
- end
64
-
65
- def ui_selector=(value)
66
- @ui_selector = value
67
- end
68
-
69
-
70
-
71
-
72
- # @return [TestaAppiumDriver::Locator]
73
- def from_parent(selectors = {})
74
- raise "Cannot add from_parent selector to array" unless @single
75
- raise StrategyMixException.new(@strategy, @strategy_reason, FIND_STRATEGY_UIAUTOMATOR, "from_parent") if @strategy != FIND_STRATEGY_UIAUTOMATOR
76
-
77
- locator = self.dup
78
- locator.strategy = FIND_STRATEGY_UIAUTOMATOR
79
- locator.strategy_reason = "from_parent"
80
- locator.closing_parenthesis += 1
81
- locator.ui_selector = "#{locator.ui_selector}.fromParent(#{hash_to_uiautomator(selectors)}"
82
- locator
83
- end
84
-
85
-
86
- # @return [Locator] new child locator element
87
- def add_child_selector(params)
88
- params, selectors = extract_selectors_from_params(params)
89
- single = params[:single]
90
- raise "Cannot add child selector to Array" if single && !@single
91
-
92
- locator = self.dup
93
- locator.can_use_id_strategy = false
94
- if (@strategy.nil? && !single) || @strategy == FIND_STRATEGY_XPATH
95
- locator.strategy = FIND_STRATEGY_XPATH
96
- locator.strategy_reason = "multiple child selector"
97
- add_xpath_child_selectors(locator, selectors, single)
98
- elsif @strategy == FIND_STRATEGY_UIAUTOMATOR
99
- locator = add_uiautomator_child_selector(locator, selectors, single)
100
- elsif @strategy == FIND_STRATEGY_IMAGE
101
- locator = add_image_child_selector(locator, selectors, single)
102
- else
103
- # both paths are valid
104
- add_xpath_child_selectors(locator, selectors, single)
105
- locator = add_uiautomator_child_selector(locator, selectors, single)
106
- end
107
-
108
- if is_scrollable_selector?(selectors, single)
109
- locator.scrollable_locator = locator
110
- if selectors[:class] == "android.widget.HorizontalScrollView"
111
- locator.scrollable_locator.scroll_orientation = :horizontal
112
- else
113
- locator.scrollable_locator.scroll_orientation = :vertical
114
- end
115
- end
116
-
117
- locator.last_selector_adjacent = false
118
- locator
119
- end
120
-
121
-
122
- private
123
- def add_uiautomator_child_selector(locator, selectors, single)
124
- if locator.single && !single
125
- # current locator stays single, the child locator looks for multiple
126
- params = selectors.merge({single: single, scrollable_locator: locator.scrollable_locator})
127
- params[:default_find_strategy] = locator.default_find_strategy
128
- params[:default_scroll_strategy] = locator.default_scroll_strategy
129
- Locator.new(@driver, self, params)
130
- else
131
- locator.single = true
132
- locator.ui_selector = "#{locator.ui_selector(false)}.childSelector(#{hash_to_uiautomator(selectors, single)})"
133
- locator
134
- end
135
- end
136
-
137
- def add_image_child_selector(locator, selectors, single)
138
- params = selectors.merge({single: single, scrollable_locator: locator.scrollable_locator})
139
- Locator.new(@driver, self, params)
140
- end
141
- end
1
+ require_relative 'locator/attributes'
2
+
3
+ module TestaAppiumDriver
4
+ #noinspection RubyTooManyInstanceVariablesInspection
5
+ class Locator
6
+ attr_accessor :closing_parenthesis
7
+ include ClassSelectors
8
+ include Attributes
9
+
10
+ def init(params, selectors, single)
11
+ @closing_parenthesis = 0
12
+
13
+
14
+ @ui_selector = hash_to_uiautomator(selectors, single)
15
+
16
+ if is_scrollable_selector?(selectors, single)
17
+ if selectors[:class] == "android.widget.HorizontalScrollView"
18
+ @scroll_orientation = :horizontal
19
+ else
20
+ @scroll_orientation = :vertical
21
+ end
22
+
23
+ if !params[:top].nil? || !params[:bottom].nil? || !params[:right].nil? || !params[:left].nil?
24
+ @scroll_deadzone = {}
25
+ @scroll_deadzone[:top] = params[:top].to_f unless params[:top].nil?
26
+ @scroll_deadzone[:bottom] = params[:bottom].to_f unless params[:bottom].nil?
27
+ @scroll_deadzone[:right] = params[:right].to_f unless params[:right].nil?
28
+ @scroll_deadzone[:left] = params[:left].to_f unless params[:left].nil?
29
+ end
30
+
31
+ params[:scrollable_locator] = self.dup
32
+ end
33
+
34
+ @scrollable_locator = params[:scrollable_locator] if params[:scrollable_locator]
35
+ end
36
+
37
+
38
+ # resolve selector which will be used for finding element
39
+ def strategies_and_selectors
40
+ ss = []
41
+ if @can_use_id_strategy
42
+ ss.push({"#{FIND_STRATEGY_ID}": @can_use_id_strategy})
43
+ end
44
+ if @strategy.nil? || @strategy == FIND_STRATEGY_UIAUTOMATOR
45
+ ss.push({"#{FIND_STRATEGY_UIAUTOMATOR}": ui_selector})
46
+ end
47
+
48
+ if @strategy.nil? || @strategy == FIND_STRATEGY_XPATH
49
+ ss.push({"#{FIND_STRATEGY_XPATH}": @xpath_selector})
50
+ end
51
+
52
+ if @strategy == FIND_STRATEGY_IMAGE
53
+ ss.push({"#{FIND_STRATEGY_IMAGE}": @image_selector})
54
+ end
55
+ ss
56
+ end
57
+
58
+
59
+ # @param [Boolean] include_semicolon should the semicolon be included at the end
60
+ # @return ui_selector for uiautomator find strategy
61
+ def ui_selector(include_semicolon = true)
62
+ @ui_selector + ")" * @closing_parenthesis + (include_semicolon ? ";" : "");
63
+ end
64
+
65
+ def ui_selector=(value)
66
+ @ui_selector = value
67
+ end
68
+
69
+
70
+
71
+
72
+ # @return [TestaAppiumDriver::Locator]
73
+ def from_parent(selectors = {})
74
+ raise "Cannot add from_parent selector to array" unless @single
75
+ raise StrategyMixException.new(@strategy, @strategy_reason, FIND_STRATEGY_UIAUTOMATOR, "from_parent") if @strategy != FIND_STRATEGY_UIAUTOMATOR
76
+
77
+ locator = self.dup
78
+ locator.strategy = FIND_STRATEGY_UIAUTOMATOR
79
+ locator.strategy_reason = "from_parent"
80
+ locator.closing_parenthesis += 1
81
+ locator.ui_selector = "#{locator.ui_selector}.fromParent(#{hash_to_uiautomator(selectors)}"
82
+ locator
83
+ end
84
+
85
+
86
+ # @return [Locator] new child locator element
87
+ def add_child_selector(params)
88
+ params, selectors = extract_selectors_from_params(params)
89
+ single = params[:single]
90
+ raise "Cannot add child selector to Array" if single && !@single
91
+
92
+ locator = self.dup
93
+ locator.can_use_id_strategy = false
94
+ if (@strategy.nil? && !single) || @strategy == FIND_STRATEGY_XPATH
95
+ locator.strategy = FIND_STRATEGY_XPATH
96
+ locator.strategy_reason = "multiple child selector"
97
+ add_xpath_child_selectors(locator, selectors, single)
98
+ elsif @strategy == FIND_STRATEGY_UIAUTOMATOR
99
+ locator = add_uiautomator_child_selector(locator, selectors, single)
100
+ elsif @strategy == FIND_STRATEGY_IMAGE
101
+ locator = add_image_child_selector(locator, selectors, single)
102
+ else
103
+ # both paths are valid
104
+ add_xpath_child_selectors(locator, selectors, single)
105
+ locator = add_uiautomator_child_selector(locator, selectors, single)
106
+ end
107
+
108
+ if is_scrollable_selector?(selectors, single)
109
+ locator.scrollable_locator = locator
110
+ if selectors[:class] == "android.widget.HorizontalScrollView"
111
+ locator.scrollable_locator.scroll_orientation = :horizontal
112
+ else
113
+ locator.scrollable_locator.scroll_orientation = :vertical
114
+ end
115
+ end
116
+
117
+ locator.last_selector_adjacent = false
118
+ locator
119
+ end
120
+
121
+
122
+ private
123
+ def add_uiautomator_child_selector(locator, selectors, single)
124
+ if locator.single && !single
125
+ # current locator stays single, the child locator looks for multiple
126
+ params = selectors.merge({single: single, scrollable_locator: locator.scrollable_locator})
127
+ params[:default_find_strategy] = locator.default_find_strategy
128
+ params[:default_scroll_strategy] = locator.default_scroll_strategy
129
+ Locator.new(@driver, self, params)
130
+ else
131
+ locator.single = true
132
+ locator.ui_selector = "#{locator.ui_selector(false)}.childSelector(#{hash_to_uiautomator(selectors, single)})"
133
+ locator
134
+ end
135
+ end
136
+
137
+ def add_image_child_selector(locator, selectors, single)
138
+ params = selectors.merge({single: single, scrollable_locator: locator.scrollable_locator})
139
+ Locator.new(@driver, self, params)
140
+ end
141
+ end
142
142
  end