rutl 0.6.0 → 0.8.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.
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