rutl 0.1.3 → 0.1.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 91f41b74e68fe36acb46dd525f7f375f18d3939d5a7fc15c1972ff443af6125e
4
- data.tar.gz: 1a2d184998b8f3f18532ece3f353059cbd616e8f7a419c4331c8dfc10fc40d59
3
+ metadata.gz: 44ba7a05dd473a09ac45ecafc02d9e11f0a74e26d0e8eb5909e315226066e0ca
4
+ data.tar.gz: 6a272e4fc1afb2d1111b90604ceb3443cc89fd49c703ff417ab56fd9f609573b
5
5
  SHA512:
6
- metadata.gz: 4a91637e9662ede93d7991261f44dde9299d0b04482ab25ce8d952fc2f16e4df210452c120642f816a373f4c565287ee16aa57033d0d3c609b23a07f5a9cd17f
7
- data.tar.gz: 93a535c70dbd92121f461bbcea341c3dea2c9d23ac33ae6f5bdcd2658efe1dacf3feb048012412577d6a5726ecf5ca3ce535b76524a48789f92dffebcdf6e8ac
6
+ metadata.gz: 58227d7b2a1dd426ced4d42a826081ad71b638b6f212a16c5905ef57d0fb28ea4689b0240233dbfbb8d42701460c3bf91354cb088414693047836dcf30fb56ce
7
+ data.tar.gz: c4acf6148162ef28b3f7704ea6885a6acd81b9bfb0dbff26fb87a6294f943fa5a1e6bcdeb3aaa8668bf597fa0d452620a90587aadf6cd71ea3b4670f5f3281a1
data/.gitignore CHANGED
@@ -1,9 +1,10 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
1
+ /.bundle/
2
+ /.yardoc
3
+ /debug.log
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
data/.rubocop.yml CHANGED
@@ -6,7 +6,7 @@ require: rubocop-rspec
6
6
  # There are also a bunch of warnigs spewed about the RSpec cops listed
7
7
  # in the todo file so it looks like we're not playing well with rubocop-rspec
8
8
  # either.
9
- # Check this in for now and TODO: figure this out tomorrow.
9
+ # Check this in for now and TODO: figure this out.
10
10
  require: drewcoo-cops
11
11
 
12
12
  AllCops:
data/.rubocop_todo.yml CHANGED
@@ -1,45 +1,20 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2018-05-26 12:34:26 -0700 using RuboCop version 0.56.0.
3
+ # on 2018-06-03 15:06:48 -0700 using RuboCop version 0.56.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 1
10
- Metrics/AbcSize:
11
- Max: 16
12
-
13
9
  # Offense count: 3
14
10
  # Configuration parameters: CountComments, ExcludedMethods.
15
11
  Metrics/BlockLength:
16
- Max: 48
12
+ Max: 74
17
13
 
18
- # Offense count: 4
14
+ # Offense count: 3
19
15
  # Configuration parameters: CountComments.
20
16
  Metrics/MethodLength:
21
- Max: 19
22
-
23
- # Offense count: 1
24
- Naming/AccessorMethodName:
25
- Exclude:
26
- - 'lib/rutl/interface/null_interface.rb'
27
-
28
- # Offense count: 1
29
- RSpec/ExampleLength:
30
- Exclude:
31
- - 'spec/chrome_interface_spec.rb'
32
-
33
- # Offense count: 2
34
- RSpec/ExpectActual:
35
- Exclude:
36
- - 'spec/null_interface_spec.rb'
37
- - 'spec/rutl_spec.rb'
38
-
39
- # Offense count: 1
40
- RSpec/MultipleExpectations:
41
- Exclude:
42
- - 'spec/chrome_interface_spec.rb'
17
+ Max: 13
43
18
 
44
19
  # Offense count: 4
45
20
  # Cop supports --auto-correct.
@@ -50,39 +25,34 @@ Style/BracesAroundHashParameters:
50
25
  - 'spec/pages/internet_login_page.rb'
51
26
  - 'spec/pages/page1.rb'
52
27
 
53
- # Offense count: 1
28
+ # Offense count: 3
54
29
  Style/ClassVars:
55
30
  Exclude:
56
31
  - 'lib/rutl/base_page.rb'
32
+ - 'lib/rutl/driver/null_driver_page_element.rb'
57
33
 
58
34
  # Offense count: 4
59
- Style/MethodMissingSuper:
60
- Exclude:
61
- - 'lib/rutl/base_page.rb'
62
- - 'lib/rutl/browser.rb'
63
- - 'lib/rutl/interface/base_interface.rb'
64
- - 'spec/spec_helper.rb'
65
-
66
- # Offense count: 1
67
- # Cop supports --auto-correct.
68
- Style/PerlBackrefs:
35
+ # Configuration parameters: AllowedVariables.
36
+ Style/GlobalVars:
69
37
  Exclude:
70
38
  - 'lib/rutl/browser.rb'
39
+ - 'lib/rutl/interface/elements/base_element.rb'
40
+ - 'lib/rutl/interface/elements/element_context.rb'
41
+ - 'lib/rutl/interface/null_interface.rb'
71
42
 
72
43
  # Offense count: 1
73
44
  # Cop supports --auto-correct.
74
- # Configuration parameters: EnforcedStyle.
75
- # SupportedStyles: implicit, explicit
76
- Style/RescueStandardError:
45
+ Style/IfUnlessModifier:
77
46
  Exclude:
78
- - 'lib/rutl/utilities.rb'
47
+ - 'lib/rutl/interface/elements/element_context.rb'
79
48
 
80
- # Offense count: 3
81
- # Cop supports --auto-correct.
82
- # Configuration parameters: AllowIfMethodIsEmpty.
83
- Style/SingleLineMethods:
49
+ # Offense count: 4
50
+ Style/MethodMissingSuper:
84
51
  Exclude:
85
52
  - 'lib/rutl/base_page.rb'
53
+ - 'lib/rutl/browser.rb'
54
+ - 'lib/rutl/interface/base_interface.rb'
55
+ - 'spec/spec_helper.rb'
86
56
 
87
57
  # Offense count: 1
88
58
  # Cop supports --auto-correct.
@@ -91,3 +61,9 @@ Style/SingleLineMethods:
91
61
  Style/TrivialAccessors:
92
62
  Exclude:
93
63
  - 'lib/rutl/base_page.rb'
64
+
65
+ # Offense count: 15
66
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
67
+ # URISchemes: http, https
68
+ Metrics/LineLength:
69
+ Max: 223
data/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![CircleCI](https://circleci.com/gh/drewcoo/rutl.svg?style=shield)](https://circleci.com/gh/drewcoo/rutl)
5
5
  [![Coverage Status](https://coveralls.io/repos/github/drewcoo/rutl/badge.svg?branch=master)](https://coveralls.io/github/drewcoo/rutl?branch=master)
6
6
  [![Gem Version](https://badge.fury.io/rb/rutl.svg)](https://badge.fury.io/rb/rutl)
7
+ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/f95d69f6bdd149e697cf63e842f71600)](https://www.codacy.com/app/drewcoo/rutl?utm_source=github.com&utm_medium=referral&utm_content=drewcoo/rutl&utm_campaign=Badge_Grade)
7
8
 
8
9
  This is the Ruby Ui Test Library, or RUTL. Not to be confused with [The Rutles](https://www.rutles.org/).
9
10
 
data/Rakefile CHANGED
@@ -1,6 +1,17 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
3
4
 
4
- RSpec::Core::RakeTask.new(:spec)
5
+ RuboCop::RakeTask.new
5
6
 
6
- task default: :spec
7
+ task :fast_first do
8
+ Rake::Task['spec'].invoke('fast')
9
+ Rake::Task['spec'].reenable
10
+ Rake::Task['spec'].invoke('slow')
11
+ end
12
+
13
+ RSpec::Core::RakeTask.new(:spec, :tag) do |t, args|
14
+ t.rspec_opts = ["--tag #{args[:tag]}"]
15
+ end
16
+
17
+ task default: :fast_first
@@ -9,73 +9,68 @@ class BasePage
9
9
  # BUGBUG: Kludgy. What do I really want to do here?
10
10
  # Make it easy to define a page's default url and
11
11
  # also matchers for page urls for pages with variable urls?
12
- def self.url; @url; end
13
-
14
- def url; self.class.url; end
12
+ def self.url
13
+ @url
14
+ end
15
15
 
16
- @@children = []
17
- def self.children; @@children; end
16
+ def url
17
+ self.class.url
18
+ end
18
19
 
19
- attr_accessor :driver
20
+ @@loaded_pages = []
20
21
 
21
- # TODO: DO I REALLY WANT TO PASS IN DRIVER LIKE THAT?
22
- def initialize
23
- # Call a class's layout method the first time it's loaded
24
- # and put the class name in a list of children, which is a
25
- # list of all actual page objects in this case.
26
- return if @@children.include?(self.class)
22
+ def initialize(interface)
23
+ @interface = interface
24
+ # Dirty trick because we're loading all of page classes from files and then
25
+ # initializing them, calling their layout methods to do magic.
26
+ # The base_page class knows what pages are loaded.
27
+ return if @@loaded_pages.include?(self.class)
27
28
  layout
28
- @@children << self.class
29
+ @@loaded_pages << self.class
29
30
  end
30
31
 
31
- def loaded?
32
- @url = @driver.current_url
32
+ # Written by Browser and only used internally.
33
+ attr_writer :interface
34
+
35
+ def loaded?(driver)
36
+ url == driver.current_url
33
37
  end
34
38
 
35
- # BUGBUG: This creates a new element instance whenever it's called.
36
- # Because of that we can't keep state in any element objects.
37
- # Is that actually a good thing, maybe?
39
+ # Dynamically add a method, :<name> (or :<name>= if setter)
40
+ # to the current class where that method creates an instance
41
+ # of klass.
42
+ # context is an ElementContext
43
+ def add_method(context:, klass:, name:, setter: false)
44
+ name = "#{name}_#{klass.downcase}"
45
+ constant = Module.const_get(klass.capitalize)
46
+ self.class.class_exec do
47
+ if setter
48
+ define_method("#{name}=") do |value|
49
+ constant.new(context, value)
50
+ end
51
+ else
52
+ define_method(name) do
53
+ constant.new(context)
54
+ end
55
+ end
56
+ end
57
+ end
38
58
 
59
+ # This creates a new element instance whenever it's called.
60
+ # Because of that we can't keep state in any element objects.
61
+ # That seems like a good thing, actually.
39
62
  # Called by layout method on pages.
40
63
  def method_missing(element, *args, &_block)
41
- name, selector, rest = args
42
- name = "#{name}_#{element.downcase}"
43
-
64
+ name, selectors, rest = args
65
+ context = ElementContext.new(destinations: rest,
66
+ interface: @interface,
67
+ selectors: selectors)
44
68
  case element
45
69
  when /button/, /checkbox/, /link/
46
- # self.class.class_exec do
47
- # define_method(name) do
48
- # Module.const_get(element.capitalize).new(selector, rest)
49
- # end
50
- # end
51
- self.class.class_exec do
52
- foo = Module.const_get(element.capitalize).new(selector, rest)
53
- define_method(name) do
54
- foo
55
- end
56
- end
70
+ add_method(name: name, context: context, klass: element)
57
71
  when /text/
58
- self.class.class_exec do
59
- # foo = Module.const_get(element.capitalize).new(selector, rest)
60
- # define_method("_#{name}") do
61
- # foo.get
62
- # end
63
- define_method(name) do
64
- Module.const_get(element.capitalize).new(selector, rest)
65
- end
66
- # define_method(name.to_s) do
67
- # foo.get
68
- # end
69
- # define_method((name + '=').to_s) do
70
- # foo.set
71
- # end
72
- # foo.define_method(:get) do
73
- # foo.get
74
- # end
75
- # foo.define_method(:set) do
76
- # foo.set
77
- # end
78
- end
72
+ add_method(name: name, context: context, klass: element)
73
+ add_method(name: name, context: context, klass: element, setter: true)
79
74
  else
80
75
  # TODO: replace with a super call. This is useful for debugging for now.
81
76
  raise "#{element} NOT FOUND WITH ARGS #{args}!!!"
@@ -93,7 +88,7 @@ class BasePage
93
88
  else
94
89
  # I think it's good to raise but change the message.
95
90
  raise 'Drew, you hit this most often when checking current page ' \
96
- 'rather than current page class'
91
+ "rather than current page class:\n\n #{args}"
97
92
  # I think I want to raise instead of returningn false.
98
93
  end
99
94
  end
data/lib/rutl/browser.rb CHANGED
@@ -9,26 +9,30 @@ require 'rutl/base_page'
9
9
  class Browser
10
10
  include Utilities
11
11
 
12
- def initialize(interface_type: :none, page_object_dir: 'spec/pages')
13
- @pages = load_pages(page_object_dir: page_object_dir)
14
- load_interface(type: interface_type)
12
+ attr_reader :interface
13
+
14
+ def initialize(interface_type:, page_object_dir: 'spec/pages')
15
+ # This is kind of evil. Figure out how to ditch the $ variable.
16
+ $browser = self
17
+ @interface = nil
18
+ @interface = load_interface(interface_type)
19
+ @interface.pages = load_pages(dir: page_object_dir)
15
20
  end
16
21
 
17
- def load_interface(type: :none)
18
- pages = @pages
22
+ def load_interface(type)
19
23
  require "rutl/interface/#{type}_interface"
20
24
  klass = "#{type.to_s.capitalize}Interface"
21
- @interface = Object.const_get(klass).new(pages: pages)
22
- @interface.interface = @interface
25
+ Object.const_get(klass).new
23
26
  end
24
27
 
25
28
  # Ugly. Requires files for page objects. Returns array of class names to load.
26
- def require_pages(page_object_dir: 'spec/pages')
29
+ def require_pages(dir: 'spec/pages')
27
30
  names = []
28
- Dir["#{page_object_dir}/*"].each do |file|
31
+ Dir["#{dir}/*"].each do |file|
29
32
  require "rutl/../../#{file}"
30
33
  File.open(file).each do |line|
31
- names << $1 if line =~ /class (.*) < BasePage/
34
+ bingo = line.match(/class (.*) < BasePage/)
35
+ names << bingo[1] if bingo && bingo[1]
32
36
  end
33
37
  end
34
38
  names
@@ -36,26 +40,19 @@ class Browser
36
40
 
37
41
  def load_pages(*)
38
42
  pages = []
39
- names = require_pages
40
- names.each do |klass|
43
+ require_pages.each do |klass|
41
44
  # Don't have @interface set yet.
42
45
  # That would have been the param to new, :interface.
43
- pages << Object.const_get(klass).new
46
+ pages << Object.const_get(klass).new(@interface)
44
47
  end
45
48
  pages
46
49
  end
47
50
 
48
51
  def method_missing(method, *args, &block)
49
- result = if args.empty?
50
- @interface.send(method)
51
- else
52
- @interface.send(method, *args, &block)
53
- end
54
- if result.class == Array && (result[0].class.ancestors.include?(BasePage) ||
55
- result[0].class == Exception)
56
- wait_for_transition(result)
52
+ if args.empty?
53
+ @interface.send(method)
57
54
  else
58
- result
55
+ @interface.send(method, *args, &block)
59
56
  end
60
57
  end
61
58
 
@@ -4,31 +4,35 @@ require 'rutl/driver/null_driver_page_element'
4
4
  # This is at a peer level to the webdrivers but it's for a fake brwoser.
5
5
  #
6
6
  class NullDriver
7
- attr_accessor :interface
7
+ attr_accessor :context
8
+
9
+ def initialize(context)
10
+ raise 'no context' unless context.is_a?(ElementContext)
11
+ @context = context
12
+ end
8
13
 
9
14
  def find_element(type, location)
10
15
  # Return a new one of these so that it can be clicked ar written
11
16
  # to or whatever.
12
- element = NullDriverPageElement.new(type, location)
13
- element.interface = @interface
14
- element
17
+ context = ElementContext.new(interface: @context.interface)
18
+ NullDriverPageElement.new(context, type, location)
15
19
  end
16
20
 
17
21
  # Cheap way to handle browser.navigate.to(url)
18
22
  # TODO: Until I care about the url and then I should ????
19
23
  def navigate
20
- result = NullDriver.new
21
- result.interface = @interface
22
- result
24
+ context = ElementContext.new(interface: @context.interface)
25
+ NullDriver.new(context)
23
26
  end
24
27
 
25
28
  def to(url)
26
- result = @interface.find_page(url)
27
- @interface.set_current_page result
29
+ result = @context.interface.find_page(url)
30
+ @context.interface.current_page = result
28
31
  result.url
29
32
  end
30
33
 
31
34
  def quit
32
- 'quit'
35
+ # Clean out the @@variables.
36
+ NullDriverPageElement.clear_variables
33
37
  end
34
38
  end
@@ -1,40 +1,51 @@
1
+ require 'rutl/interface/elements/element_context'
2
+
1
3
  #
2
4
  # This fakes all page elements when used with the null driver.
3
5
  # It's a dirty way to avoid modeling all of what a driver talks to.
4
6
  #
5
7
  class NullDriverPageElement
6
- attr_accessor :string
7
- attr_reader :selector_type, :selector
8
+ attr_accessor :context
8
9
 
9
- attr_accessor :interface
10
- attr_accessor :destinations
10
+ def self.clear_variables
11
+ @@variables = {}
12
+ end
11
13
 
12
- def initialize(selector_type, selector)
13
- # :css, selector
14
- @selector_type = selector_type
15
- @selector = selector
14
+ def initialize(context, _type, location)
15
+ @@variables ||= {}
16
+ @context = context
17
+ @location = location
16
18
  end
17
19
 
20
+ # @@string is a class variable because this framework creates new instances
21
+ # of each element every time it accesses them. This is good behavior by
22
+ # default because pages could change underneath us.
23
+ # For text fields in the null browser, though, we want to preserve the values
24
+ # across calls, letting us write and then read.
18
25
  def send_keys(string)
19
- @string = string
26
+ init = @@variables[@location] || ''
27
+ @@variables[@location] = init + string
20
28
  end
21
29
 
22
30
  def attribute(attr)
23
31
  case attr.to_sym
24
32
  when :value
25
- @string
33
+ @@variables[@location]
26
34
  else
27
35
  raise ArgumentError, "Attribute unknown: #{attr}"
28
36
  end
29
37
  end
30
38
 
31
- # Return simple strings for checks against the NullDriver instead of
32
- # having to use some heavyweight UI.
33
39
  def clear
34
- 'clear'
40
+ @@variables[@location] = ''
41
+ end
42
+
43
+ def this_css
44
+ self
35
45
  end
36
46
 
37
47
  def click
38
- 'click'
48
+ # nop
49
+ # Called by ClickToChangeStateMixin like Selenium driver.click
39
50
  end
40
51
  end
@@ -7,46 +7,27 @@ require 'rutl/utilities'
7
7
  class BaseInterface
8
8
  include Utilities
9
9
 
10
- def current_url
11
- current_page.url
12
- end
13
-
14
- attr_reader :pages
15
10
  attr_accessor :driver
16
- attr_accessor :interface
11
+ attr_accessor :pages
17
12
 
18
- def initialize(pages:)
19
- @pages = pages
20
- raise 'Child classes must implement @driver.' unless defined? @driver
21
- @pages.each { |p| p.driver = @driver }
13
+ def initialize
14
+ raise 'Child interface class must set @driver.' if @driver.nil?
22
15
  end
23
16
 
24
- def goto(input)
25
- # TODO: KLUDGY. Fix. Modifier if usage bombs here. *shrug*
26
- @driver.interface = @interface if @interface.class.to_s == 'NullInterface'
27
- input = find_page(input) unless input.methods.include?(:url)
28
-
29
- @driver.navigate.to input.url
17
+ def goto(page)
18
+ @driver.navigate.to find_page(page).url
30
19
  end
31
20
 
32
21
  def current_page
33
- raise 'OVERRIDE IN CHILDREN'
22
+ raise 'define in child classes'
34
23
  end
35
24
 
36
25
  def method_missing(method, *args, &block)
37
- result = if args.empty?
38
- current_page.send(method)
39
- else
40
- current_page.send(method, *args, &block)
41
- end
42
- raise interface.to_s if interface.nil?
43
- raise result.to_s unless defined? result # result.interface
44
- begin
45
- result.interface = interface
46
- rescue NoMethodError
47
- raise NoMethodError, "METHOD NOT FOUND: #{method}"
26
+ if args.empty?
27
+ current_page.send(method)
28
+ else
29
+ current_page.send(method, *args, &block)
48
30
  end
49
- result
50
31
  end
51
32
 
52
33
  # TODO: Is this needed? I not only find the page but also make sure the
@@ -54,21 +35,18 @@ class BaseInterface
54
35
  def find_state(target_states)
55
36
  target_states.each do |state|
56
37
  next unless state.url == current_page.url
57
- page = find_page(state, true)
58
- return page if page.loaded?
38
+ page = find_page(state)
39
+ return page if page.loaded?(@driver)
59
40
  end
60
- raise current_page.to_s if current_page.class == InternetLoggedInPage
61
41
  false
62
42
  end
63
43
 
64
- def find_page(page, raise_on_fail = false)
44
+ def find_page(page)
65
45
  @pages.each do |p|
66
- # page is a Page class
67
46
  return p if page?(page) && p.class == page
68
- # or a String, possibly URL
69
47
  return p if String == page.class && page == p.url
70
48
  end
71
- raise "Page \"#{page}\" not found in pages #{@pages}" if raise_on_fail
49
+ raise "Page \"#{page}\" not found in pages #{@pages}"
72
50
  end
73
51
 
74
52
  def wait_for_transition(target_states)
@@ -86,6 +64,7 @@ class BaseInterface
86
64
 
87
65
  def quit
88
66
  @driver.quit
89
- @pages = []
67
+ # Maybe I'm reusing pages from @pages?
68
+ # @pages = []
90
69
  end
91
70
  end
@@ -4,10 +4,8 @@ require 'rutl/interface/base_interface'
4
4
  #
5
5
  # Small interface for Chrome browser.
6
6
  #
7
- # TODO: Probably the current_page() implementation should move up to base.
8
- #
9
7
  class ChromeInterface < BaseInterface
10
- def initialize(pages:)
8
+ def initialize
11
9
  @logged_in = true
12
10
  options = Selenium::WebDriver::Chrome::Options.new
13
11
  options.add_argument('--ignore-certificate-errors')
@@ -25,7 +23,7 @@ class ChromeInterface < BaseInterface
25
23
 
26
24
  def current_page
27
25
  url = @driver.current_url
28
- page = find_page(url, true)
26
+ page = find_page(url)
29
27
  raise "PAGE NOT FOUND: #{url}, PAGES: #{@pages}" unless page
30
28
  page
31
29
  end
@@ -2,27 +2,23 @@
2
2
  # Page elements. Base class.
3
3
  #
4
4
  class BaseElement
5
- attr_accessor :interface
6
- def driver
7
- @interface.driver
8
- end
9
- attr_accessor :selectors
10
- attr_accessor :destinations
11
-
12
- def initialize(selectors = {}, destinations = [])
13
- @selectors = selectors
14
- @destinations = destinations
15
- end
5
+ attr_accessor :context
16
6
 
17
- def find_css(selectors)
18
- result = driver.find_element(:css, selectors[:css])
19
- return result unless NullDriver == result.class
20
- result.destinations = @destinations
21
- result.interface = @interface
22
- result
7
+ def initialize(element_context)
8
+ raise element_context.to_s unless element_context.is_a? ElementContext
9
+ @context = element_context
10
+ # Not sure why, but I'm seeing Chrome fail becase the context interface
11
+ # passed in isn't the same as the browser's interface.
12
+ # This only happens with click test cases, before the click, and
13
+ # only if that case isn't run first.
14
+ # The context we're passed is also an instance from as ChromeInterface,
15
+ # but a different instance.
16
+ #
17
+ # Here's the kludge workaround line:
18
+ @context.interface = $browser.interface
23
19
  end
24
20
 
25
21
  def this_css
26
- find_css(@selectors)
22
+ @context.find_element(:css)
27
23
  end
28
24
  end
@@ -6,8 +6,9 @@
6
6
  module ClickToChangeStateMixin
7
7
  def click
8
8
  this_css.click
9
- interface.wait_for_transition(@destinations)
10
- # TODO: Return what?
11
- @destinations
9
+
10
+ # TODO: Is this part of the instance-stamping problem???
11
+ # returns the page it found
12
+ @context.interface.wait_for_transition(@context.destinations)
12
13
  end
13
14
  end
@@ -0,0 +1,29 @@
1
+ #
2
+ # The context passed around to all elements.
3
+ # What they need to know outside of themselves to function.
4
+ #
5
+ class ElementContext
6
+ attr_accessor :destinations
7
+ attr_accessor :interface
8
+ attr_accessor :selectors
9
+
10
+ def initialize(destinations: nil, interface: nil, selectors: [])
11
+ unless destinations.nil? || destinations.class == Array
12
+ # Should check each destination to make sure it's a Page or a _____, too.
13
+ raise 'destination must be an Array of destinations or nil.'
14
+ end
15
+ @destinations = destinations || []
16
+ unless interface.nil? || interface.class.ancestors.include?(BaseInterface)
17
+ raise "#{interface.class}: #{interface} must be a *Interface class."
18
+ end
19
+ @interface = interface
20
+ @selectors = selectors
21
+ end
22
+
23
+ def find_element(type)
24
+ # @interface.driver.find_element(type, @selectors[type])
25
+ # Should be this, but apparently @interface.driver is being overwritten
26
+ # (or not written to) and it doesn't work. Using $browser does. :-(
27
+ $browser.interface.driver.find_element(type, @selectors[type])
28
+ end
29
+ end
@@ -0,0 +1,66 @@
1
+ #
2
+ # Implement String stuff in a mixin.
3
+ # TODO: Not finished yet. Must be able to
4
+ #
5
+ module StringReaderWriterMixin
6
+ # Override BaseElement's normal initialize method.
7
+ def initialize(element_context, input_value = nil)
8
+ raise element_context.to_s unless element_context.is_a? ElementContext
9
+ @context = element_context
10
+ set input_value unless input_value.nil?
11
+ end
12
+
13
+ # I could cut set() and only foo_text= if I change this.
14
+ # The problem I'm running into is not having the driver in
15
+ # base element to do this_css calls. So I have to change the way
16
+ # drivers are passed into everything or initially have them everywhere,
17
+ # which means rewriting chosen drivers or changing page load.
18
+ # Ick.
19
+ def set(string)
20
+ clear
21
+ this_css.send_keys(string)
22
+ end
23
+ alias text= set
24
+ alias value= set
25
+
26
+ def get
27
+ this_css.attribute(:value)
28
+ end
29
+ alias text get
30
+ alias value get
31
+ alias to_s get
32
+
33
+ def clear
34
+ this_css.clear
35
+ get
36
+ end
37
+
38
+ def eql?(other)
39
+ other == get
40
+ end
41
+ alias == eql?
42
+
43
+ def send_keys(string)
44
+ this_css.send_keys(string)
45
+ get
46
+ end
47
+
48
+ def method_missing(method, *args, &block)
49
+ # RuboCop complains unless I fall back to super here
50
+ # even though that's pretty meaningless. Oh, well, it's harmless.
51
+ super unless get.respond_to?(method)
52
+ if args.empty?
53
+ get.send(method)
54
+ else
55
+ get.send(method, *args, &block)
56
+ end
57
+ end
58
+
59
+ def respond_to_missing?(method, flag)
60
+ get.respond_to?(method, flag)
61
+ end
62
+
63
+ #
64
+ # TODO: Fall through to String methods?
65
+ #
66
+ end
@@ -1,30 +1,10 @@
1
1
  require 'rutl/interface/elements/base_element'
2
+ require 'rutl/interface/elements/string_reader_writer_mixin.rb'
2
3
 
3
4
  #
4
5
  # I'm using the text element for all text-like things. Passowrds, too.
6
+ # TODO: Also have a reader only class with StringReaderMixin for labels?
5
7
  #
6
8
  class Text < BaseElement
7
- def initialize(selectors = {}, destinations = [])
8
- super
9
- end
10
-
11
- def clear
12
- this_css.clear
13
- end
14
-
15
- def text
16
- get
17
- end
18
-
19
- def get
20
- this_css.attribute(:value)
21
- end
22
-
23
- def text=(string)
24
- set(string)
25
- end
26
-
27
- def set(string)
28
- this_css.send_keys(string)
29
- end
9
+ include StringReaderWriterMixin
30
10
  end
@@ -2,3 +2,4 @@ require 'rutl/interface/elements/button'
2
2
  require 'rutl/interface/elements/checkbox'
3
3
  require 'rutl/interface/elements/link'
4
4
  require 'rutl/interface/elements/text'
5
+ require 'rutl/interface/elements/element_context'
@@ -4,10 +4,8 @@ require 'rutl/interface/base_interface'
4
4
  #
5
5
  # Small interface for Chrome browser.
6
6
  #
7
- # TODO: Probably the current_page() implementation should move up to base.
8
- #
9
7
  class FirefoxInterface < BaseInterface
10
- def initialize(pages:)
8
+ def initialize
11
9
  @logged_in = true
12
10
  options = Selenium::WebDriver::Firefox::Options.new
13
11
  options.add_argument('--ignore-certificate-errors')
@@ -20,7 +18,7 @@ class FirefoxInterface < BaseInterface
20
18
 
21
19
  def current_page
22
20
  url = @driver.current_url
23
- page = find_page(url, true)
21
+ page = find_page(url)
24
22
  raise "PAGE NOT FOUND: #{url}, PAGES: #{@pages}" unless page
25
23
  page
26
24
  end
@@ -4,26 +4,28 @@ require 'rutl/interface/base_interface'
4
4
  # Interface-level code for fake browser.
5
5
  #
6
6
  class NullInterface < BaseInterface
7
- @variables = []
8
- attr_accessor :variables
9
-
10
- def initialize(pages:)
11
- @driver = NullDriver.new
12
- @driver.interface = @interface
7
+ def initialize
8
+ context = ElementContext.new(destinations: nil,
9
+ interface: self,
10
+ selectors: [])
11
+ @driver = NullDriver.new(context)
13
12
  super
14
13
  end
15
14
 
16
- def set_current_page(page)
17
- @current_page = page
18
- end
15
+ # The null driver needs to talk to the null interface.
16
+ # Other driver/interface relations are not like this.
17
+ attr_writer :current_page
19
18
 
20
19
  def current_page
21
20
  # Default to @pages.first if not set?
22
- # A browser can alwasy check its current URL but the null driver can't.
23
- @current_page || @pages.first
21
+ # A browser can always check its current URL but the null driver can't.
22
+ @current_page ||= @pages.first
24
23
  end
25
24
 
26
25
  def wait_for_transition(destinations)
27
- @current_page = find_page(destinations.first)
26
+ # TODO: Setting @current page didn't do it beacause that set
27
+ # context.interface.current_page and we wanted this in the browser.
28
+ @current_page = destinations.first.new(self)
29
+ $browser.current_page = @current_page
28
30
  end
29
31
  end
@@ -34,7 +34,8 @@ module Utilities
34
34
 
35
35
  def page?(checkme)
36
36
  checkme.ancestors.include?(BasePage)
37
- rescue # BUGBUG: Didn't have time to find all the things to rescue yet.
37
+ rescue NoMethodError
38
+ # This isn't a even a class. It's no page!
38
39
  false
39
40
  end
40
41
 
data/lib/rutl/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RUTL
2
- VERSION = '0.1.3'.freeze
2
+ VERSION = '0.1.4'.freeze
3
3
  end
data/rutl.gemspec CHANGED
@@ -30,14 +30,14 @@ Gem::Specification.new do |spec|
30
30
  spec.require_paths = ['lib']
31
31
 
32
32
  spec.add_development_dependency 'bundler', '~> 1.15'
33
- spec.add_development_dependency 'coveralls'
34
- spec.add_development_dependency 'drewcoo-cops'
35
- spec.add_development_dependency 'gem-release'
33
+ spec.add_development_dependency 'coveralls', '~> 0.8'
34
+ spec.add_development_dependency 'drewcoo-cops', '~> 0.1'
35
+ spec.add_development_dependency 'gem-release', '~> 1.0'
36
36
  spec.add_development_dependency 'rake', '~> 12.3'
37
37
  spec.add_development_dependency 'rspec', '~> 3.0'
38
38
  spec.add_development_dependency 'rspec_junit_formatter', '~> 0.3'
39
39
  spec.add_development_dependency 'rubocop', '~> 0.55'
40
- spec.add_development_dependency 'rubocop-rspec'
40
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.25'
41
41
  spec.add_development_dependency 'selenium-webdriver', '~> 3.12'
42
42
  spec.add_development_dependency 'webdrivers', '~> 3.0'
43
43
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rutl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Drew Cooper
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-05-27 00:00:00.000000000 Z
11
+ date: 2018-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -28,44 +28,44 @@ dependencies:
28
28
  name: coveralls
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '0.8'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '0.8'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: drewcoo-cops
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '0.1'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '0.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: gem-release
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '1.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '1.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rake
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -126,16 +126,16 @@ dependencies:
126
126
  name: rubocop-rspec
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - ">="
129
+ - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: '0'
131
+ version: '1.25'
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - ">="
136
+ - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: '0'
138
+ version: '1.25'
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: selenium-webdriver
141
141
  requirement: !ruby/object:Gem::Requirement
@@ -197,7 +197,9 @@ files:
197
197
  - lib/rutl/interface/elements/button.rb
198
198
  - lib/rutl/interface/elements/checkbox.rb
199
199
  - lib/rutl/interface/elements/click_to_change_state_mixin.rb
200
+ - lib/rutl/interface/elements/element_context.rb
200
201
  - lib/rutl/interface/elements/link.rb
202
+ - lib/rutl/interface/elements/string_reader_writer_mixin.rb
201
203
  - lib/rutl/interface/elements/text.rb
202
204
  - lib/rutl/interface/firefox_interface.rb
203
205
  - lib/rutl/interface/null_interface.rb