rutl 0.6.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.bundle/config +2 -0
  3. data/.gitignore +1 -1
  4. data/.rubocop.yml +1 -1
  5. data/.rubocop_todo.yml +37 -10
  6. data/.travis.yml +1 -0
  7. data/README.md +75 -38
  8. data/appveyor.yml +48 -0
  9. data/bin/ie.reg +0 -0
  10. data/bin/window_data.rb +27 -0
  11. data/lib/rspec/default_method_object_to_app.rb +28 -0
  12. data/lib/rspec/rutl_matchers.rb +7 -5
  13. data/lib/rutl.rb +26 -6
  14. data/lib/rutl/appium/appium_extension.rb +27 -0
  15. data/lib/rutl/appium/appium_server.rb +36 -0
  16. data/lib/rutl/appium/windows_test_app_wrapper.rb +40 -0
  17. data/lib/rutl/application.rb +70 -0
  18. data/lib/rutl/camera.rb +20 -4
  19. data/lib/rutl/element/click_to_change_state_mixin.rb +1 -1
  20. data/lib/rutl/element/element.rb +9 -10
  21. data/lib/rutl/element/element_context.rb +5 -4
  22. data/lib/rutl/element/string_reader_writer_mixin.rb +11 -7
  23. data/lib/rutl/interface/base.rb +30 -28
  24. data/lib/rutl/interface/browser/browser.rb +22 -0
  25. data/lib/rutl/interface/{chrome.rb → browser/chrome.rb} +3 -10
  26. data/lib/rutl/interface/{firefox.rb → browser/firefox.rb} +3 -10
  27. data/lib/rutl/interface/browser/internet_explorer.rb +23 -0
  28. data/lib/rutl/interface/browser/null.rb +36 -0
  29. data/lib/rutl/interface/windows/hello.rb +36 -0
  30. data/lib/rutl/interface/windows/notepad.rb +26 -0
  31. data/lib/rutl/interface/windows/windows_app.rb +35 -0
  32. data/lib/rutl/null_driver/null_driver.rb +4 -4
  33. data/lib/rutl/null_driver/null_element.rb +4 -4
  34. data/lib/rutl/version.rb +1 -1
  35. data/lib/rutl/{page.rb → view.rb} +37 -28
  36. data/lib/utilities/check_view.rb +12 -0
  37. data/lib/utilities/string.rb +12 -0
  38. data/lib/utilities/waiter.rb +23 -0
  39. data/rutl.gemspec +13 -0
  40. metadata +94 -10
  41. data/lib/rspec/default_rspec_to_browser.rb +0 -22
  42. data/lib/rutl/browser.rb +0 -70
  43. data/lib/rutl/interface/null.rb +0 -35
  44. data/lib/utilities.rb +0 -41
@@ -0,0 +1,22 @@
1
+ require 'selenium-webdriver'
2
+ require 'rutl/interface/base'
3
+
4
+ module RUTL
5
+ module Interface
6
+ #
7
+ # Small interface for Chrome browser.
8
+ #
9
+ class Browser < Base
10
+ def initialize
11
+ super
12
+ end
13
+
14
+ def current_view
15
+ url = @driver.current_url
16
+ view = find_view(url)
17
+ raise "NOT FOUND: #{url}, VIEWS: #{@views}" unless view
18
+ view
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,12 +1,12 @@
1
1
  require 'selenium-webdriver'
2
- require 'rutl/interface/base'
2
+ require 'rutl/interface/browser/browser'
3
3
 
4
4
  module RUTL
5
5
  module Interface
6
6
  #
7
7
  # Small interface for Chrome browser.
8
8
  #
9
- class Chrome < Base
9
+ class Chrome < Browser
10
10
  # rubocop:disable Metrics/MethodLength
11
11
  def initialize
12
12
  @logged_in = true
@@ -15,7 +15,7 @@ module RUTL
15
15
  options.add_argument('--disable-popup-blocking')
16
16
  options.add_argument('--disable-translate')
17
17
  # Run headless on TravisCI
18
- if ENV['TRAVIS'] == 'true'
18
+ if ENV['TRAVIS']
19
19
  options.add_argument('--disable-gpu')
20
20
  options.add_argument('--headless ')
21
21
  options.add_argument('--no-sandbox')
@@ -24,13 +24,6 @@ module RUTL
24
24
  super
25
25
  end
26
26
  # rubocop:enable Metrics/MethodLength
27
-
28
- def current_page
29
- url = @driver.current_url
30
- page = find_page(url)
31
- raise "PAGE NOT FOUND: #{url}, PAGES: #{@pages}" unless page
32
- page
33
- end
34
27
  end
35
28
  end
36
29
  end
@@ -1,29 +1,22 @@
1
1
  require 'selenium-webdriver'
2
- require 'rutl/interface/base'
2
+ require 'rutl/interface/browser/browser'
3
3
 
4
4
  module RUTL
5
5
  module Interface
6
6
  #
7
7
  # Small interface for Chrome browser.
8
8
  #
9
- class Firefox < Base
9
+ class Firefox < Browser
10
10
  def initialize
11
11
  @logged_in = true
12
12
  options = Selenium::WebDriver::Firefox::Options.new
13
13
  options.add_argument('--ignore-certificate-errors')
14
14
  options.add_argument('--disable-popup-blocking')
15
15
  options.add_argument('--disable-translate')
16
- options.add_argument('--headless') if ENV['TRAVIS'] == 'true'
16
+ options.add_argument('--headless') if ENV['TRAVIS'] || ENV['CIRCLECI']
17
17
  @driver = Selenium::WebDriver.for :firefox, options: options
18
18
  super
19
19
  end
20
-
21
- def current_page
22
- url = @driver.current_url
23
- page = find_page(url)
24
- raise "PAGE NOT FOUND: #{url}, PAGES: #{@pages}" unless page
25
- page
26
- end
27
20
  end
28
21
  end
29
22
  end
@@ -0,0 +1,23 @@
1
+ require 'selenium-webdriver'
2
+ require 'rutl/interface/browser/browser'
3
+
4
+ module RUTL
5
+ module Interface
6
+ #
7
+ # Small interface for Chrome browser.
8
+ #
9
+ class InternetExplorer < Browser
10
+ def initialize
11
+ @logged_in = true
12
+ options = Selenium::WebDriver::IE::Options.new
13
+ options.add_argument('--ignore-certificate-errors')
14
+ options.add_argument('--disable-popup-blocking')
15
+ options.add_argument('--disable-translate')
16
+ options.ignore_zoom_level = true
17
+ options.initial_browser_url = 'https://www.google.com/'
18
+ @driver = Selenium::WebDriver.for :internet_explorer, options: options
19
+ super
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ require 'rutl/interface/browser/browser'
2
+
3
+ module RUTL
4
+ module Interface
5
+ #
6
+ # Interface-level code for fake application.
7
+ #
8
+ class Null < Browser
9
+ def initialize
10
+ context = RUTL::Element::ElementContext.new(destinations: nil,
11
+ interface: self,
12
+ selectors: [])
13
+ @driver = NullDriver.new(context)
14
+ super
15
+ end
16
+
17
+ # The null driver needs to talk to the null interface.
18
+ # Other driver/interface relations are not like this.
19
+ attr_writer :current_view
20
+
21
+ def current_view
22
+ # Default to @view.first if not set?
23
+ # An application can always check its current URL but
24
+ # the null driver can't.
25
+ @current_view ||= @views.first
26
+ end
27
+
28
+ def wait_for_transition(destinations)
29
+ # TODO: Setting @current view didn't do it beacause that set
30
+ # context.interface.current_view and we wanted this in the application.
31
+ @current_view = destinations.first.new(self)
32
+ $application.current_view = @current_view
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,36 @@
1
+ require 'selenium-webdriver'
2
+ require 'rutl/interface/windows/windows_app'
3
+
4
+ module RUTL
5
+ module Interface
6
+ #
7
+ # The hello world app with an exit button.
8
+ #
9
+ class Hello < WindowsApp
10
+ def file_name
11
+ File.expand_path('../../../../spec/hello.rb', __dir__)
12
+ end
13
+
14
+ def initialize
15
+ @app = WindowsTestApp.new(name: "ruby #{file_name}",
16
+ title: /hello world/i)
17
+ @app.start
18
+ driver_opts = base_opts
19
+ # Have to start app then attach winappdriver because these both fail:
20
+ # 1. passing hello.rb path as [:caps][:app]
21
+ # 2. passing ruby.exe path as [:caps][:app] and passing hello.rb
22
+ # path as [:caps][:appArguments]
23
+ driver_opts[:caps][:appTopLevelWindow] = @app.window_handle_string
24
+ @driver = Appium::Driver.new(driver_opts, false)
25
+ @driver.start
26
+ super
27
+ end
28
+
29
+ def current_view
30
+ # This only works because I only have one view.
31
+ # Should I? What about dialogs?
32
+ @views.first
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,26 @@
1
+ require 'selenium-webdriver'
2
+ require 'rutl/interface/windows/windows_app'
3
+
4
+ module RUTL
5
+ module Interface
6
+ #
7
+ # Notepad.
8
+ #
9
+ class Notepad < WindowsApp
10
+ def initialize
11
+ @app_name = 'notepad.exe'
12
+ driver_opts = base_opts
13
+ driver_opts[:caps][:app] = 'C:\Windows\System32\notepad.exe'
14
+ @driver = Appium::Driver.new(driver_opts, false)
15
+ @driver.start
16
+ super
17
+ end
18
+
19
+ def current_view
20
+ # This only works because I only have one view.
21
+ # Should I? What about dialogs?
22
+ @views.first
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ require 'selenium-webdriver'
2
+ require 'rutl/interface/base'
3
+
4
+ module RUTL
5
+ module Interface
6
+ #
7
+ # Parent class for all Windows apps.
8
+ #
9
+ class WindowsApp < Base
10
+ def base_opts
11
+ { caps: { platformName: 'WINDOWS',
12
+ platform: 'WINDOWS',
13
+ deviceName: 'WindowsPC' },
14
+ appium_lib: { wait_timeout: 2,
15
+ wait_interval: 0.01 } }
16
+ end
17
+
18
+ def kill
19
+ system "taskkill /f /im #{@app_name} /t 1>nul 2>&1"
20
+ end
21
+
22
+ def open?
23
+ @driver.find_elements(:id, 0)
24
+ true
25
+ rescue Selenium::WebDriver::Error::NoSuchWindowError
26
+ false
27
+ end
28
+
29
+ def quit
30
+ @driver.driver_quit
31
+ kill
32
+ end
33
+ end
34
+ end
35
+ end
@@ -2,7 +2,7 @@ require 'rutl/null_driver/null_element'
2
2
 
3
3
  module RUTL
4
4
  #
5
- # This is at a peer level to the webdrivers but it's for a fake brwoser.
5
+ # This is at a peer level to the webdrivers but it's for a fake application.
6
6
  #
7
7
  class NullDriver
8
8
  attr_accessor :context
@@ -19,7 +19,7 @@ module RUTL
19
19
  RUTL::Element::NullElement.new(context, type, location)
20
20
  end
21
21
 
22
- # Cheap way to handle browser.navigate.to(url)
22
+ # Cheap way to handle application.navigate.to(url)
23
23
  # TODO: Until I care about the url and then I should ????
24
24
  def navigate
25
25
  context = RUTL::Element::ElementContext.new(interface: @context.interface)
@@ -28,8 +28,8 @@ module RUTL
28
28
 
29
29
  # Cheap second part to naviate.to(url) calls to look like real drivers.
30
30
  def to(url)
31
- result = @context.interface.find_page(url)
32
- @context.interface.current_page = result
31
+ result = @context.interface.find_view(url)
32
+ @context.interface.current_view = result
33
33
  result.url
34
34
  end
35
35
 
@@ -3,7 +3,7 @@ require 'rutl/element/element_context'
3
3
  module RUTL
4
4
  module Element
5
5
  #
6
- # This fakes all page elements when used with the null driver.
6
+ # This fakes all view elements when used with the null driver.
7
7
  # It's a dirty way to avoid modeling all of what a driver talks to.
8
8
  #
9
9
  class NullElement
@@ -21,8 +21,8 @@ module RUTL
21
21
 
22
22
  # @@string is a class variable because this framework creates new
23
23
  # instances of each element every time it accesses them. This is good
24
- # behavior by default because pages could change underneath us.
25
- # For text fields in the null browser, though, we want to preserve the
24
+ # behavior by default because views could change underneath us.
25
+ # For text fields in the null application, though, we want to preserve the
26
26
  # values across calls, letting us write and then read.
27
27
  def send_keys(string)
28
28
  init = @@variables[@location] || ''
@@ -42,7 +42,7 @@ module RUTL
42
42
  @@variables[@location] = ''
43
43
  end
44
44
 
45
- def this_css
45
+ def find_element
46
46
  self
47
47
  end
48
48
 
data/lib/rutl/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RUTL
2
- VERSION = '0.6.0'.freeze
2
+ VERSION = '0.8.0'.freeze
3
3
  end
@@ -3,13 +3,16 @@ require 'rutl/null_driver/null_driver'
3
3
 
4
4
  module RUTL
5
5
  #
6
- # Base page class. It's used to call the magical method_messing
7
- # stuff to parse the page object files into actual objects.
6
+ # Base view class. It's used to call the magical method_messing
7
+ # stuff to parse the view object files into actual objects.
8
8
  #
9
- class Page
9
+ class View
10
+ #
11
+ # BUGBUG #1: Some view in a generic app should not have URL.
12
+ #
10
13
  # BUGBUG: Kludgy. What do I really want to do here?
11
- # Make it easy to define a page's default url and
12
- # also matchers for page urls for pages with variable urls?
14
+ # Make it easy to define a view's default url and
15
+ # also matchers for view urls for views with variable urls?
13
16
  # rubocop:disable Style/TrivialAccessors
14
17
  def self.url
15
18
  @url
@@ -20,34 +23,40 @@ module RUTL
20
23
  end
21
24
  # rubocop:enable Style/TrivialAccessors
22
25
 
23
- # Intentially use a class variable to hald pages. Once they're all loaded
24
- # they are all loaded for everyone.
26
+ def go_to_here
27
+ # Ovveride this in base view to have something more
28
+ # complicated than this.
29
+ @interface.driver.navigate.to(url)
30
+ end
31
+
32
+ def loaded?
33
+ # Default to only checking url to see if view loaded.
34
+ url == @interface.driver.current_url
35
+ end
36
+
37
+ # Intentionally use a class variable to hald views. Once they're
38
+ # all loaded they are all loaded for everyone.
25
39
  # rubocop:disable Style/ClassVars
26
- @@loaded_pages = []
40
+ @@loaded_views = []
27
41
  # rubocop:enable Style/ClassVars
28
42
 
29
43
  def initialize(interface)
30
44
  @interface = interface
31
- # Dirty trick because we're loading all of page classes from files and
45
+ # Dirty trick because we're loading all of view classes from files and
32
46
  # then initializing them, calling their layout methods to do magic.
33
- # The Page class knows what pages are loaded.
34
- return if @@loaded_pages.include?(self.class)
47
+ # The view class knows what views are loaded.
48
+ return if @@loaded_views.include?(self.class)
35
49
  layout
36
- @@loaded_pages << self.class
50
+ @@loaded_views << self.class
37
51
  end
38
52
 
39
- def go_to_here
40
- # Ovveride this in base page to have something more
41
- # complicated than this.
42
- @interface.driver.navigate.to(url)
43
- end
44
-
45
- # Written by Browser and only used internally.
53
+ # Written by Application and only used internally.
46
54
  attr_writer :interface
47
55
 
48
- def loaded?
49
- url == @interface.driver.current_url
50
- end
56
+ # def loaded?
57
+ # # In case I try calling this without defining it first.
58
+ # raise 'No #loaded? method defined.'
59
+ # end
51
60
 
52
61
  # Dynamically add a method, :<name> (or :<name>= if setter)
53
62
  # to the current class where that method creates an instance
@@ -57,7 +66,7 @@ module RUTL
57
66
  # As it is, this seems silly to break into pieces for Rubocop.
58
67
  # rubocop:disable Metrics/MethodLength
59
68
  def add_method(context:, klass:, name:, setter: false)
60
- name = "#{name}_#{klass.downcase}"
69
+ name = "#{name}_#{klass.downcase}" if RUTL::HUNGARIAN
61
70
  constant = Module.const_get("RUTL::Element::#{klass.capitalize}")
62
71
  self.class.class_exec do
63
72
  if setter
@@ -77,7 +86,7 @@ module RUTL
77
86
  # This creates a new element instance whenever it's called.
78
87
  # Because of that we can't keep state in any element objects.
79
88
  # That seems like a good thing, actually.
80
- # Called by layout method on pages.
89
+ # Called by layout method on views.
81
90
  #
82
91
  # Hard to make shorter.
83
92
  # rubocop:disable Metrics/MethodLength
@@ -103,15 +112,15 @@ module RUTL
103
112
  # Is this right at all???
104
113
  case args[0].to_s
105
114
  when /button/, /checkbox/, /element/, /link/, /text/,
106
- 'driver', 'url', 'children', 'loaded?'
115
+ 'driver', 'children', 'loaded?'
107
116
  true
108
117
  when 'ok_link'
109
- raise 'OK LINK WAY DOWN HERE IN BASE PAGE!!!'
118
+ raise 'OK LINK WAY DOWN HERE IN BASE VIEW!!!'
110
119
  else
111
120
  # I think it's good to raise but change the message.
112
121
  raise 'TODO: BETTER ERROR MESSAGE, PLEASE. I AM SHOUTING!!!\n' \
113
- 'Drew, you hit this most often when checking current page ' \
114
- "rather than current page class:\n\n #{args}"
122
+ 'Drew, you hit this most often when checking current view ' \
123
+ "rather than current view class:\n\n #{args}"
115
124
  # I think I want to raise instead of returningn false.
116
125
  end
117
126
  end
@@ -0,0 +1,12 @@
1
+ #
2
+ # #view? is used in interface/base.rb and in rspec/rutl_matchers.rb
3
+ # so the method lives over in this lonely place.
4
+ #
5
+ module CheckView
6
+ def view?(checkme)
7
+ checkme.ancestors.include?(RUTL::View)
8
+ rescue NoMethodError
9
+ false
10
+ end
11
+ alias page? view?
12
+ end