Skip to content

Commit f600fe6

Browse files
authored
refactor: separate some methods and modules (#651)
* separate some methods and modules * fix indents * separate method more
1 parent a542287 commit f600fe6

19 files changed

+378
-332
lines changed

ios_tests/lib/ios/specs/ios/command/pasteboard.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# rake ios[common/pasteboard]
22
describe 'ios/command/pasteboard' do
33
t 'pasteboard' do
4-
get_pasteboard.must_be_empty
4+
# set blank before testing because pasteboard is remaining during launching simulators
5+
set_pasteboard content: 'before'
6+
get_pasteboard.must_equal 'before'
57

68
set_pasteboard content: 'sample content'
79

ios_tests/lib/ios/specs/ios/element/alert.rb

+6-11
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,14 @@ def nav_once
66
automation_name_is_xcuitest? ? find_element(:name, 'Alerts').click : text('alerts').click
77
tag(ui_ios.navbar).name == 'Alerts' # wait for true
88
end
9-
10-
# redefine method as no-op after it's invoked once
11-
self.class.send :define_method, :nav_once, proc {}
129
end
1310

1411
def after_last
15-
alert_accept if exists do
16-
automation_name_is_xcuitest? ? find_elements(:name, 'UIActionSheet <title>') : text('UIActionSheet <title>')
17-
end
1812
back_click
1913
screen.must_equal catalog
2014
sleep 1
2115
end
2216

23-
before do
24-
nav_once
25-
open_alert
26-
end
27-
2817
def open_alert
2918
wait_true do
3019
if automation_name_is_xcuitest?
@@ -37,11 +26,17 @@ def open_alert
3726
end
3827
end
3928

29+
t 'before' do
30+
nav_once
31+
end
32+
4033
t 'alert_accept' do
34+
open_alert
4135
alert_accept
4236
end
4337

4438
t 'alert_dismiss' do
39+
open_alert
4540
alert_dismiss
4641
end
4742

ios_tests/lib/ios/specs/ios/patch.rb

+6-7
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,16 @@ def before_first
55
go_to_textfields
66
end
77

8-
def after_last
9-
leave_textfields
10-
end
11-
128
t 'before_first' do
139
before_first
1410
end
1511

1612
t 'label' do
17-
textfield('<enter text>').label.must_equal 'Rounded'
13+
textfields[1].label.must_equal 'Rounded'
1814
end
1915

2016
t 'type' do
21-
# nav to textfield
22-
text('textfields').click
17+
text('textfields').click # nav to textfield
2318

2419
ele = first_textfield
2520

@@ -28,6 +23,10 @@ def after_last
2823
ele.text.must_equal 'ok'
2924
end
3025

26+
def after_last
27+
leave_textfields
28+
end
29+
3130
t 'after_last' do
3231
after_last
3332
end

lib/appium_lib.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@
88
$driver = nil
99

1010
require_relative 'appium_lib/logger'
11-
require_relative 'appium_lib/driver'
11+
require_relative 'appium_lib/appium'

lib/appium_lib/android/android.rb

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
require_relative 'search_context'
2+
3+
require_relative 'common/helper'
4+
require_relative 'common/patch'
5+
require_relative 'common/client_xpath'
6+
require_relative 'common/device'
7+
8+
require_relative 'element/alert'
9+
require_relative 'element/button'
10+
require_relative 'element/generic'
11+
require_relative 'element/textfield'
12+
require_relative 'element/text'
13+
14+
# android - uiautomator2
15+
require_relative 'uiautomator2'
16+
17+
module Appium
18+
module Android
19+
# Android
20+
end
21+
end
File renamed without changes.
File renamed without changes.
File renamed without changes.

lib/appium_lib/android/mobile_methods.rb

-15
This file was deleted.
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module Appium
2+
module Android
3+
module SearchContext
4+
class << self
5+
# @!method uiautomator_find
6+
# find_element/s can be used with a [UISelector](http://developer.android.com/tools/help/uiautomator/UiSelector.html).
7+
#
8+
# ```ruby
9+
# find_elements :uiautomator, 'new UiSelector().clickable(true)'
10+
# ```
11+
def extended(_mod)
12+
::Appium::Driver::SearchContext::FINDERS[:uiautomator] = '-android uiautomator'
13+
end
14+
end
15+
end # class << self
16+
end # module Android
17+
end # module Appium

lib/appium_lib/appium.rb

+228
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
require 'rubygems'
2+
require 'ap'
3+
require 'selenium-webdriver'
4+
require 'nokogiri'
5+
6+
# base
7+
require_relative 'driver'
8+
require_relative 'capabilities'
9+
10+
# common
11+
require_relative 'common/helper'
12+
require_relative 'common/wait'
13+
require_relative 'common/patch'
14+
require_relative 'common/version'
15+
require_relative 'common/error'
16+
require_relative 'common/search_context'
17+
require_relative 'common/command'
18+
require_relative 'common/element/window'
19+
20+
# ios
21+
require_relative 'ios/ios'
22+
23+
# android
24+
require_relative 'android/android'
25+
26+
# device methods
27+
require_relative 'device/device'
28+
require_relative 'device/touch_actions'
29+
require_relative 'device/multi_touch'
30+
31+
module Appium
32+
# Load arbitrary text ([toml format](https://github.com/toml-lang/toml))
33+
# The toml is parsed by https://github.com/fbernier/tomlrb .
34+
#
35+
# ```
36+
# [caps]
37+
# app = "path/to/app"
38+
#
39+
# [appium_lib]
40+
# port = 8080
41+
# ```
42+
#
43+
# :app is expanded
44+
# :require is expanded
45+
# all keys are converted to symbols
46+
#
47+
# @param opts [Hash] file: '/path/to/appium.txt', verbose: true
48+
# @return [hash] the symbolized hash with updated :app and :require keys
49+
def self.load_settings(opts = {})
50+
raise 'opts must be a hash' unless opts.is_a? Hash
51+
raise 'opts must not be empty' if opts.empty?
52+
53+
toml = opts[:file]
54+
raise 'Must pass a capability file which has [caps] and [appium_lib]' unless toml
55+
verbose = opts.fetch :verbose, false
56+
57+
Appium::Logger.info "appium settings path: #{toml}" if verbose
58+
59+
toml_exists = File.exist? toml
60+
Appium::Logger.info "Exists? #{toml_exists}" if verbose
61+
62+
raise "toml doesn't exist #{toml}" unless toml_exists
63+
require 'tomlrb'
64+
Appium::Logger.info "Loading #{toml}" if verbose
65+
66+
data = Tomlrb.load_file(toml, symbolize_keys: true)
67+
if verbose
68+
Appium::Logger.ap_info data unless data.empty?
69+
end
70+
71+
if data && data[:caps] && data[:caps][:app] && !data[:caps][:app].empty?
72+
data[:caps][:app] = Appium::Driver.absolute_app_path data
73+
end
74+
75+
if data && data[:appium_lib] && data[:appium_lib][:require]
76+
parent_dir = File.dirname toml
77+
data[:appium_lib][:require] = expand_required_files(parent_dir, data[:appium_lib][:require])
78+
end
79+
80+
data
81+
end
82+
83+
class << self
84+
# rubocop:disable Style/Alias
85+
alias_method :load_appium_txt, :load_settings
86+
end
87+
88+
# @param [String] base_dir parent directory of loaded appium.txt (toml)
89+
# @param [String] file_paths
90+
# @return [Array] list of require files as an array, nil if require doesn't exist
91+
def self.expand_required_files(base_dir, file_paths)
92+
# ensure files are absolute
93+
Array(file_paths).map! do |f|
94+
file = File.exist?(f) ? f : File.join(base_dir, f)
95+
file = File.expand_path file
96+
97+
File.exist?(file) ? file : nil
98+
end
99+
file_paths.compact! # remove nils
100+
101+
files = []
102+
103+
# now expand dirs
104+
file_paths.each do |item|
105+
unless File.directory? item
106+
# save file
107+
files << item
108+
next # only look inside folders
109+
end
110+
Dir.glob(File.expand_path(File.join(item, '**', '*.rb'))) do |f|
111+
# do not add folders to the file list
112+
files << File.expand_path(f) unless File.directory? f
113+
end
114+
end
115+
116+
files
117+
end
118+
119+
# convert all keys (including nested) to symbols
120+
#
121+
# based on deep_symbolize_keys & deep_transform_keys from rails
122+
# https://github.com/rails/docrails/blob/a3b1105ada3da64acfa3843b164b14b734456a50/activesupport/lib/active_support/core_ext/hash/keys.rb#L84
123+
def self.symbolize_keys(hash)
124+
raise 'symbolize_keys requires a hash' unless hash.is_a? Hash
125+
result = {}
126+
hash.each do |key, value|
127+
key = key.to_sym rescue key # rubocop:disable Style/RescueModifier
128+
result[key] = value.is_a?(Hash) ? symbolize_keys(value) : value
129+
end
130+
result
131+
end
132+
133+
# This method is intended to work with page objects that share
134+
# a common module. For example, Page::HomePage, Page::SignIn
135+
# those could be promoted on with Appium.promote_singleton_appium_methods Page
136+
#
137+
# If you are promoting on an individual class then you should use
138+
# Appium.promote_appium_methods instead. The singleton method is intended
139+
# only for the shared module use case.
140+
#
141+
# if modules is a module instead of an array, then the constants of
142+
# that module are promoted on.
143+
# otherwise, the array of modules will be used as the promotion target.
144+
def self.promote_singleton_appium_methods(modules, driver = $driver)
145+
raise 'Global $driver is nil' if driver.nil?
146+
147+
target_modules = []
148+
149+
if modules.is_a? Module
150+
modules.constants.each do |sub_module|
151+
target_modules << modules.const_get(sub_module)
152+
end
153+
else
154+
raise 'modules must be a module or an array' unless modules.is_a? Array
155+
target_modules = modules
156+
end
157+
158+
target_modules.each do |const|
159+
# noinspection RubyResolve
160+
# rubocop:disable Style/MultilineIfModifier
161+
driver.public_methods(false).each do |m|
162+
const.send(:define_singleton_method, m) do |*args, &block|
163+
begin
164+
super(*args, &block) # promote.rb
165+
rescue NoMethodError, ArgumentError
166+
driver.send m, *args, &block if driver.respond_to?(m)
167+
end
168+
# override unless there's an existing method with matching arity
169+
end unless const.respond_to?(m) && const.method(m).arity == driver.method(m).arity
170+
end
171+
# rubocop:enable Style/MultilineIfModifier
172+
end
173+
end
174+
175+
##
176+
# Promote appium methods to class instance methods
177+
#
178+
# @param class_array [Array<Class>] An array of classes
179+
#
180+
# To promote methods to all classes:
181+
#
182+
# ```ruby
183+
# Appium.promote_appium_methods Object
184+
# ```
185+
#
186+
# It's better to promote on specific classes instead of Object
187+
#
188+
# ```ruby
189+
# # promote on rspec
190+
# Appium.promote_appium_methods RSpec::Core::ExampleGroup
191+
# ```
192+
#
193+
# ```ruby
194+
# # promote on minispec
195+
# Appium.promote_appium_methods Minitest::Spec
196+
# ```
197+
def self.promote_appium_methods(class_array, driver = $driver)
198+
raise 'Driver is nil' if driver.nil?
199+
# Wrap single class into an array
200+
class_array = [class_array] unless class_array.class == Array
201+
# Promote Appium driver methods to class instance methods.
202+
class_array.each do |klass|
203+
driver.public_methods(false).each do |m|
204+
klass.class_eval do
205+
define_method m do |*args, &block|
206+
begin
207+
# Prefer existing method.
208+
# super will invoke method missing on driver
209+
super(*args, &block)
210+
211+
# minitest also defines a name method,
212+
# so rescue argument error
213+
# and call the name method on $driver
214+
rescue NoMethodError, ArgumentError
215+
driver.send m, *args, &block if driver.respond_to?(m)
216+
end
217+
end
218+
end
219+
end
220+
end
221+
nil # return nil
222+
end
223+
224+
def self.selenium_webdriver_version_more?(version)
225+
require 'rubygems'
226+
Gem.loaded_specs['selenium-webdriver'].version >= Gem::Version.new(version)
227+
end
228+
end

0 commit comments

Comments
 (0)