testa_appium_driver 0.1.11 → 0.1.14

Sign up to get free protection for your applications and to get access to all the features.
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