capybara-rc 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.2.3
5
+ before_install: gem install bundler -v 1.10.6
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ * 0.5.1
2
+
3
+ * Add homepage to gemspec (rubygems.org won't let you publish without it)
4
+
5
+ # 0.5.0
6
+
7
+ * Initial version
data/DIFFERENCES.md ADDED
@@ -0,0 +1,34 @@
1
+ # Behavior differences between Selenium-RC and Capybara
2
+
3
+ * Selenium-RC can locate elements which are not visible. Capybara only returns
4
+ visible elements. Among other things, this means that `is_element_present` and
5
+ `is_visible` behave differently in this adapter than in Selenium-RC.
6
+ * Capybara always waits for conditions to become true, so
7
+ `wait_for_page_to_load` is a no-op. Capybara does not expose a way to do what
8
+ Selenium-RC's `wait_for_page_to_load` does (wait until the next time a page
9
+ is loaded for any reason). The Capybara way to do this is to specify a
10
+ condition that will be satisfied once the new page is loaded. Capybara will
11
+ wait for that to become true. See also `Capybara.use_wait_time` if Capybara's
12
+ default wait time is too short for the page load you are waiting on.
13
+ * The Capybara-backed implementation of `is_visible` waits for an element to
14
+ show up even if what you're trying to test is that the element is _not_
15
+ present. The adapter provides a method `capybara_has_no_locator?` which allows
16
+ you to hook into Capybara's negated matchers and avoid this delay.
17
+ * Support for `mouse_out` is not available, but a workaround is calling
18
+ `mouse_over` on another element.
19
+ * Methods which take JavaScript snippets as arguments (such as `get_eval`,
20
+ `run_script`, and `wait_for_condition`) behave differently. Because Capybara
21
+ uses Selenium-Webdriver and not RC, the JavaScript environment is different:
22
+ the execution context is the active window, not the `selenium` object and the
23
+ Selenium-RC API is not present (`browserbot`, etc.). Details for specific
24
+ adapted methods:
25
+ * `get_eval` is translated to Capybara's `execute_script`. While `get_eval`
26
+ returns the value of the last expression automatically, `execute_script`
27
+ only returns a value if the script snippet has an explicit return.
28
+ * `run_script` is also translated to Capybara's `execute_script`. Unlike
29
+ Selenium-RC, this will not necessarily be implemented by creating a script
30
+ tag with the provided JavaScript source.
31
+ * `wait_for_condition`'s condition is evaluated using `evaluate_script`. The
32
+ script must only contain a single expression.
33
+ * Capybara-RC does not support switching to a window based on a handle
34
+ referenced as a local variable in the current page.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in capybara-rc.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Collaborative Drug Discovery.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # Capybara::Rc
2
+
3
+ This gem provides an adapter that allows Selenium-RC-based code to execute using
4
+ [Capybara](http://jnicklas.github.io/capybara/) instead. This is useful because:
5
+
6
+ * Selenium-RC has been deprecated for a while
7
+ * Capybara allows your tests to run against different backends (not just selenium)
8
+ * Porting an existing test suite to a new library is dangerous — you can wind up
9
+ modifying your tests in such a way that they still pass but are no longer
10
+ actually testing what you want.
11
+
12
+ ### Caveats
13
+
14
+ * This is not a full adapter yet — it only adapts the methods that we are using
15
+ at CDD.
16
+ * Some things that were possible with Selenium-RC are not possible in Capybara.
17
+ See DIFFERENCES.md for a list of some of these.
18
+
19
+ ## Installation
20
+
21
+ Add this line to your application's Gemfile:
22
+
23
+ ```ruby
24
+ gem 'capybara-rc'
25
+ ```
26
+
27
+ And then execute:
28
+
29
+ $ bundle
30
+
31
+ Or install it yourself as:
32
+
33
+ $ gem install capybara-rc
34
+
35
+ ## Usage
36
+
37
+ * Configure Capybara as is appropriate for your application
38
+ * Replace your Selenium-RC initialization with something like this:
39
+
40
+ ```ruby
41
+ require 'capybara/rc'
42
+ # If @browser was your Selenium-RC browser before …
43
+ @browser = Capybara::Rc::Adapter.new(Capybara.current_session)
44
+ ```
45
+
46
+ ### Additions
47
+
48
+ The ideal with this adapter is that you'd be able to drop it in and run your
49
+ existing test suite with no changes. The reality is that you probably use some
50
+ features of Selenium-RC that can't be adapted to Capybara (`get_eval` and the
51
+ Selenium-RC in-browser JavaScript API are likely sources of this).
52
+
53
+ In order to limit the changes you need to make to your test suite, the `Adapter`
54
+ class provides some lower-level translation methods:
55
+
56
+ * `capybara_find_by_locator(locator)`: returns a Capybara element corresponding
57
+ to the given Selenium-RC locator. You can use the Capybara API to do further
58
+ querying/testing on the returned element.
59
+ * `capybara_has_no_locator?(locator)`: allows you to bypass Capybara's wait
60
+ when you are trying to test that something isn't present. Maps from a
61
+ Selenium-RC locator to the appropriate Capybara `has_no_*` method
62
+ (`has_no_css`, `has_no_xpath`, etc.).
63
+ * `capybara_has_locator?(locator)`: The positive version of the previous method.
64
+ Similarly maps to the appropriate Capybara `has_*` method.
65
+
66
+ ## Development
67
+
68
+ ### Project infrastructure
69
+
70
+ * [Source on GitHub](https://github.com/cdd/capybara-rc)
71
+ * [![Build Status](https://travis-ci.org/cdd/capybara-rc.svg?branch=master)](https://travis-ci.org/cdd/capybara-rc)
72
+ [Continuous Integration on Travis-CI](https://travis-ci.org/cdd/capybara-rc)
73
+
74
+ ## Contributing
75
+
76
+ Bug reports and pull requests are welcome on GitHub at https://github.com/cdd/capybara-rc.
77
+
78
+
79
+ ## License
80
+
81
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/rspec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'rspec')
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'capybara/rc/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "capybara-rc"
8
+ spec.version = Capybara::Rc::VERSION
9
+ spec.authors = ["Rhett Sutphin"]
10
+ spec.email = ["rhett@collaborativedrug.com"]
11
+
12
+ spec.summary = %q{Provides the Selenium-RC API on top of Capybara}
13
+ spec.description = %q{
14
+ Provides a wrapper for a Capybara session which exposes the ancient Selenium-RC API.
15
+ Allows gradually porting a legacy Selenium suite to newer technologies.
16
+ }
17
+ spec.homepage = "https://github.com/cdd/capybara-rc"
18
+ spec.license = "MIT"
19
+
20
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.10"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rspec", '~> 3.3'
28
+ spec.add_development_dependency "capybara"
29
+ spec.add_development_dependency "mime-types", "2.99" # 3.0+ requires Ruby 2.0+, which we currently do not support
30
+ end
@@ -0,0 +1,7 @@
1
+ require "capybara/rc/version"
2
+ require "capybara/rc/adapter"
3
+
4
+ module Capybara
5
+ module Rc
6
+ end
7
+ end
@@ -0,0 +1,159 @@
1
+ require 'timeout'
2
+
3
+ module Capybara
4
+ module Rc
5
+ ##
6
+ # Contributes Selenium-RC reader methods to the adapter.
7
+ #
8
+ # Expects a `session` accessor to be provided where it is mixed in.
9
+ module Accessors
10
+ WAIT_FOR_CONDITION_DELAY_SECONDS = 0.2
11
+
12
+ def is_checked(locator)
13
+ element = capybara_find_by_locator(locator)
14
+ if capybara_element_is_radio_or_checkbox?(element)
15
+ element.checked?
16
+ else
17
+ fail "#{locator.inspect} is not a checkable element"
18
+ end
19
+ end
20
+
21
+ ##
22
+ # Note that Capybara does not allow access to invisible elements, so this
23
+ # is equivalent to `is_visible`.
24
+ #
25
+ # @return [Boolean]
26
+ def is_element_present(locator)
27
+ capybara_has_locator?(locator)
28
+ end
29
+
30
+ ##
31
+ # Returns true if the locator matches an element. Capybara does not make
32
+ # invisible elements available at all, so there is no distinction between
33
+ # an invisible element and one that is not present. (This method returns
34
+ # false for both.)
35
+ #
36
+ # @return [Boolean]
37
+ def is_visible(locator)
38
+ capybara_has_locator?(locator)
39
+ end
40
+
41
+ def is_text_present(pattern)
42
+ page_text = session.text
43
+ parsed = parse_selenium_rc_string_pattern(pattern)
44
+ case parsed[:type]
45
+ when :glob
46
+ page_text.include?(parsed[:string])
47
+ else
48
+ fail "Don't know how to search a string using a #{parsed[:type].inspect} Selenium pattern"
49
+ end
50
+ end
51
+
52
+ def is_editable(locator)
53
+ !capybara_find_by_locator(locator).disabled?
54
+ end
55
+
56
+ def get_title
57
+ session.title
58
+ end
59
+
60
+ def get_html_source
61
+ session.source
62
+ end
63
+
64
+ def get_location
65
+ session.current_url
66
+ end
67
+
68
+ def get_text(locator)
69
+ capybara_find_by_locator(locator).text
70
+ end
71
+
72
+ def get_value(locator)
73
+ c_element = capybara_find_by_locator(locator)
74
+ if (capybara_element_is_radio_or_checkbox?(c_element))
75
+ c_element.checked? ? 'on' : 'off'
76
+ else
77
+ c_element.value.strip
78
+ end
79
+ end
80
+
81
+ def get_attribute(attribute_locator)
82
+ match = (attribute_locator.match(/\A(.*)@(\w+)\Z/))
83
+ if match
84
+ element_locator = match[1]
85
+ attribute_name = match[2]
86
+ capybara_find_by_locator(element_locator)[attribute_name]
87
+ else
88
+ fail "Unable to parse attribute_locator #{attribute_locator.inspect}"
89
+ end
90
+ end
91
+
92
+ def get_select_options(locator)
93
+ capybara_find_select_by_locator(locator).
94
+ all('option').map { |opt_elt| opt_elt.text.strip }
95
+ end
96
+
97
+ def get_selected_label(locator)
98
+ capybara_get_selected_option_elements_by_locator(locator).first.text
99
+ end
100
+
101
+ def get_selected_value(locator)
102
+ capybara_get_selected_option_elements_by_locator(locator).first.value
103
+ end
104
+
105
+ def get_xpath_count(xpath)
106
+ session.all(:xpath, xpath).size
107
+ end
108
+
109
+ def get_table(table_cell_address)
110
+ address_parts = table_cell_address.split(".")
111
+ locator = address_parts[0..-3].join(".")
112
+ row = address_parts[-2].to_i
113
+ column = address_parts[-1].to_i
114
+
115
+ table = capybara_find_by_locator(locator)
116
+ row = table.all("tr")[row]
117
+ cell = (row.all('th').to_a + row.all('td').to_a)[column]
118
+
119
+ if cell
120
+ cell.text
121
+ else
122
+ fail Capybara::ElementNotFound
123
+ end
124
+ end
125
+
126
+ def wait_for_condition(script, timeout_milliseconds)
127
+ begin
128
+ Timeout.timeout(timeout_milliseconds / 1000.0) do
129
+ until session.evaluate_script(script)
130
+ sleep(WAIT_FOR_CONDITION_DELAY_SECONDS)
131
+ end
132
+ end
133
+ rescue Timeout::Error
134
+ fail "Condition did not become true within #{timeout_milliseconds}ms"
135
+ end
136
+ end
137
+
138
+ private
139
+
140
+ def capybara_element_is_radio_or_checkbox?(element)
141
+ element.tag_name == 'input' && %w(checkbox radio).include?(element['type'])
142
+ end
143
+
144
+ def capybara_find_select_by_locator(locator)
145
+ element = capybara_find_by_locator(locator)
146
+ if element.tag_name == 'select'
147
+ element
148
+ else
149
+ fail "#{locator.inspect} does not point to a select element"
150
+ end
151
+ end
152
+
153
+ def capybara_get_selected_option_elements_by_locator(locator)
154
+ capybara_find_select_by_locator(locator).all('option').
155
+ select { |opt_elt| opt_elt.selected? }
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,103 @@
1
+ require 'capybara/rc/selenium_rc_locators'
2
+ require 'capybara/rc/extensions'
3
+
4
+ module Capybara
5
+ module Rc
6
+ ##
7
+ # Contributes Selenium-RC action methods to the adapter.
8
+ #
9
+ # Expects a `session` accessor to be provided where it is mixed in.
10
+ module Actions
11
+ include SeleniumRcLocators
12
+ include Extensions
13
+
14
+ def open(url)
15
+ session.visit(url)
16
+ end
17
+
18
+ def get_eval(js)
19
+ session.execute_script(js)
20
+ end
21
+
22
+ def run_script(js)
23
+ session.execute_script(js); nil
24
+ end
25
+
26
+ def click(locator)
27
+ capybara_find_by_locator(locator).click
28
+ end
29
+
30
+ def check(locator)
31
+ capybara_find_by_locator(locator).set(true)
32
+ end
33
+
34
+ def uncheck(locator)
35
+ capybara_find_by_locator(locator).set(false)
36
+ end
37
+
38
+ def select(select_locator, option_locator)
39
+ select_element = capybara_find_by_locator(select_locator)
40
+ parsed = parse_selenium_rc_select_option_locator(option_locator)
41
+ if parsed[:type] == :text
42
+ select_element.select(parsed[:string])
43
+ return
44
+ end
45
+ options = select_element.all('option')
46
+ option_to_select = nil
47
+ case parsed[:type]
48
+ when :value
49
+ option_to_select = options.detect { |o| o['value'] == parsed[:string] }
50
+ else
51
+ fail "Don't know how to find a #{parsed[:type].inspect} Selenium option locator with Capybara"
52
+ end
53
+ if option_to_select
54
+ option_to_select.select_option
55
+ else
56
+ fail Capybara::ElementNotFound, "No option matching #{option_locator.inspect} for select element #{select_locator}"
57
+ end
58
+ end
59
+
60
+ def type(locator, value)
61
+ element = capybara_find_by_locator(locator)
62
+ element.set(value)
63
+ end
64
+
65
+ def type_keys(locator, value)
66
+ element = capybara_find_by_locator(locator)
67
+ element.send_keys(value)
68
+ end
69
+
70
+ def drag_and_drop_to_object(draggable_locator, target_locator)
71
+ draggable = capybara_find_by_locator(draggable_locator)
72
+ target = capybara_find_by_locator(target_locator)
73
+
74
+ draggable.drag_to(target)
75
+ end
76
+
77
+ def drag_and_drop(draggable_locator, move_by)
78
+ draggable = capybara_find_by_locator(draggable_locator).native
79
+ delta_x, delta_y = move_by.split(",").map { |int| Kernel.Integer(int) }
80
+
81
+ session.driver.browser.action.drag_and_drop_by(draggable, delta_x, delta_y).perform
82
+ end
83
+
84
+ def attach_file(locator, file_name)
85
+ capybara_element = capybara_find_by_locator(locator)
86
+ session.attach_file(capybara_element[:id] || capybara_element[:name], file_name)
87
+ end
88
+
89
+ def go_back
90
+ session.execute_script("window.history.back()")
91
+ end
92
+
93
+ def refresh
94
+ session.execute_script("window.location.reload()")
95
+ end
96
+
97
+ def mouse_over(locator)
98
+ element = capybara_find_by_locator(locator)
99
+ element.hover
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,28 @@
1
+ require 'capybara/rc/accessors'
2
+ require 'capybara/rc/actions'
3
+ require 'capybara/rc/extensions'
4
+ require 'capybara/rc/modals'
5
+ require 'capybara/rc/windows'
6
+
7
+ module Capybara
8
+ module Rc
9
+ class Adapter
10
+ include Accessors
11
+ include Actions
12
+ include Extensions
13
+ include Modals
14
+ include Windows
15
+
16
+ attr_reader :session
17
+
18
+ def initialize(session)
19
+ super
20
+ @session = session
21
+ end
22
+
23
+ def wait_for_page_to_load(timeout)
24
+ # Capybara always waits, so nothing to do here.
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,143 @@
1
+ require 'capybara/rc/selenium_rc_locators'
2
+ require 'xpath'
3
+
4
+ module Capybara
5
+ module Rc
6
+ ##
7
+ # Contributes methods to the adapter that are not part of Selenium-RC, but
8
+ # which are useful in a test suite in transition.
9
+ #
10
+ # Expects a `session` accessor to be provided where it is mixed in.
11
+ module Extensions
12
+ include SeleniumRcLocators
13
+
14
+ ##
15
+ # Parses the locator and finds the corresponding Capybara element.
16
+ # Raises Capybara::ElementNotFound when the element can't be found, or
17
+ # a StandardError if the locator is not supported.
18
+ #
19
+ # @return [Capybara::Node::Element]
20
+ def capybara_find_by_locator(locator)
21
+ parsed = parse_selenium_rc_locator(locator)
22
+
23
+ css_selector = locator_to_css_selector(parsed)
24
+ return session.find(css_selector) if css_selector
25
+
26
+ xpath_expression = locator_to_xpath_expression(parsed)
27
+ return session.find(:xpath, xpath_expression) if xpath_expression
28
+
29
+ fail "Don't know how to find a #{parsed[:type].inspect} Selenium locator with Capybara"
30
+ end
31
+
32
+ ##
33
+ # Parses the locator and queries the session to see if it is present.
34
+ #
35
+ # @return [Boolean]
36
+ def capybara_has_locator?(locator)
37
+ parsed = parse_selenium_rc_locator(locator)
38
+
39
+ css_selector = locator_to_css_selector(parsed)
40
+ return session.has_css?(css_selector) if css_selector
41
+
42
+ xpath_expression = locator_to_xpath_expression(parsed)
43
+ return session.has_xpath?(xpath_expression) if xpath_expression
44
+
45
+ fail "Don't know how to find a #{parsed[:type].inspect} Selenium locator with Capybara"
46
+ end
47
+
48
+ ##
49
+ # Parses the locator and queries the session to see if it is _not_ present.
50
+ # This saves time over `!adapter.is_visible(locator)` because Capybara
51
+ # [pauses]( https://github.com/jnicklas/capybara#asynchronous-javascript-ajax-and-friends)
52
+ # to wait for an element to show up after you find it. If you want to
53
+ # check that it isn't there, this pause is a waste of time. As a side
54
+ # effect of this Capybara behavior, this method will also wait for an
55
+ # element to disappear.
56
+ def capybara_has_no_locator?(locator)
57
+ parsed = parse_selenium_rc_locator(locator)
58
+
59
+ css_selector = locator_to_css_selector(parsed)
60
+ return session.has_no_css?(css_selector) if css_selector
61
+
62
+ xpath_expression = locator_to_xpath_expression(parsed)
63
+ return session.has_no_xpath?(xpath_expression) if xpath_expression
64
+
65
+ fail "Don't know how to find a #{parsed[:type].inspect} Selenium locator with Capybara"
66
+ end
67
+
68
+ private
69
+
70
+ ##
71
+ # @return [String, nil] A CSS selector for the given Selenium-RC locator,
72
+ # or nil if the locator is of a type which cannot be converted into a
73
+ # CSS selector.
74
+ def locator_to_css_selector(parsedLocator)
75
+ case parsedLocator[:type]
76
+ when :class
77
+ ".#{parsedLocator[:string]}"
78
+ when :css
79
+ parsedLocator[:string]
80
+ when :id
81
+ "##{parsedLocator[:string]}"
82
+ when :identifier
83
+ possibilities = [
84
+ ("##{parsedLocator[:string]}" if parsedLocator[:string] =~ /\A[a-z][a-z0-9_:\-\.]*\Z/i),
85
+ "[name=#{parsedLocator[:string].inspect}]"
86
+ ]
87
+ possibilities.compact.join(', ')
88
+ when :name
89
+ "[name=#{parsedLocator[:string].inspect}]"
90
+ else
91
+ nil
92
+ end
93
+ end
94
+
95
+ ##
96
+ # @return [String, nil] An XPath selector for the given Selenium-RC
97
+ # locator, or nil if the locator is of a type which cannot be converted
98
+ # into an XPath selector. N.b. — intended to be used after the CSS
99
+ # version, so it doesn't attempt to translate locator types handled
100
+ # there.
101
+ def locator_to_xpath_expression(parsed_locator)
102
+ case parsed_locator[:type]
103
+ when :xpath
104
+ parsed_locator[:string]
105
+ when :link
106
+ link_pattern = parse_selenium_rc_string_pattern(parsed_locator[:string])
107
+ link_pattern_to_xpath_expression(link_pattern)
108
+ else
109
+ nil
110
+ end
111
+ end
112
+
113
+ def link_pattern_to_xpath_expression(parsed_pattern)
114
+ case parsed_pattern[:type]
115
+ when :glob
116
+ link_glob_to_xpath_expression(parsed_pattern[:string])
117
+ else
118
+ fail "Don't know how to find a link using a #{parsed[:type].inspect} Selenium pattern"
119
+ end
120
+ end
121
+
122
+ def link_glob_to_xpath_expression(glob)
123
+ pos = 0
124
+ asterisk_locations = glob.split('*', glob.size).map { |part| pos += part.size }
125
+ asterisk_locations.pop # the last index is the remainder after all the asterisks have been accounted for
126
+
127
+ glob_without_asterisks = glob.gsub('*', '')
128
+ content_expression =
129
+ if asterisk_locations.empty?
130
+ XPath.string.n.equals(glob_without_asterisks)
131
+ elsif asterisk_locations == [glob_without_asterisks.size]
132
+ XPath.string.n.starts_with(glob_without_asterisks)
133
+ elsif asterisk_locations == [0, glob_without_asterisks.size]
134
+ XPath.string.n.is(glob_without_asterisks)
135
+ else
136
+ fail "Don't know how to translate the glob #{glob.inspect} into an XPath link-matching expression"
137
+ end
138
+
139
+ XPath.generate { |x| x.descendant(:a)[x.attr(:href)][content_expression] }.to_s
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,27 @@
1
+ module Capybara
2
+ module Rc
3
+ ##
4
+ # Contributes Selenium-RC methods related to modal dialogs to the adapter.
5
+ #
6
+ # Expects a `session` accessor to be provided where it is mixed in.
7
+ module Modals
8
+ def initialize(*)
9
+ choose_ok_on_next_confirmation
10
+ end
11
+
12
+ def choose_ok_on_next_confirmation
13
+ @next_confirmation = :accept_confirm
14
+ end
15
+
16
+ def choose_cancel_on_next_confirmation
17
+ @next_confirmation = :dismiss_confirm
18
+ end
19
+
20
+ def get_confirmation
21
+ result = session.send(@next_confirmation)
22
+ choose_ok_on_next_confirmation
23
+ result
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,74 @@
1
+ module Capybara
2
+ module Rc
3
+ module SeleniumRcLocators
4
+ ##
5
+ # Follows the logic & heuristics in
6
+ # https://github.com/SeleniumHQ/selenium/blob/master/javascript/selenium-atoms/se_locators.js
7
+ # function core.locators.parseLocator_
8
+ def parse_selenium_rc_locator(locator)
9
+ match = locator.match(/\A([A-Za-z]+)=.+\Z/)
10
+ if match
11
+ type = match[1]
12
+ string = locator.split('=', 2)[1]
13
+ {
14
+ type: type.to_sym,
15
+ string: string
16
+ }
17
+ elsif locator =~ %r{^//}
18
+ { type: :xpath, string: locator }
19
+ elsif locator =~ /^document\./
20
+ { type: :dom, string: locator }
21
+ else
22
+ { type: :identifier, string: locator }
23
+ end
24
+ end
25
+
26
+ ##
27
+ # Follows the logic & heuristics in
28
+ # https://github.com/SeleniumHQ/selenium/blob/master/javascript/selenium-atoms/select.js
29
+ # function core.select.option.getOptionLocator_
30
+ def parse_selenium_rc_select_option_locator(option_locator)
31
+ match = option_locator.match(/\A([A-Za-z]+)=.*\Z/)
32
+ if match
33
+ type = match[1]
34
+ type = "text" if type == "label"
35
+ string = option_locator.split('=', 2)[1]
36
+ {
37
+ type: type.to_sym,
38
+ string: string
39
+ }
40
+ else
41
+ { type: :text, string: option_locator }
42
+ end
43
+ end
44
+
45
+ def parse_selenium_rc_window_locator(window_locator)
46
+ if window_locator == nil || window_locator == "null"
47
+ return { type: :main_window, string: nil }
48
+ end
49
+ match = window_locator.match(/\A([A-Za-z]+)=.*\Z/)
50
+ if match
51
+ type = match[1]
52
+ string = window_locator.split('=', 2)[1]
53
+ { type: type.to_sym, string: string }
54
+ else
55
+ { type: :window_id, string: window_locator }
56
+ end
57
+ end
58
+
59
+ def parse_selenium_rc_string_pattern(pattern)
60
+ match = pattern.match(/\A([A-Za-z]+):.+\Z/)
61
+ if match
62
+ type = match[1]
63
+ string = pattern.split(':', 2)[1]
64
+ {
65
+ type: type.to_sym,
66
+ string: string
67
+ }
68
+ else
69
+ { type: :glob, string: pattern }
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,5 @@
1
+ module Capybara
2
+ module Rc
3
+ VERSION = "0.5.1"
4
+ end
5
+ end
@@ -0,0 +1,110 @@
1
+ module Capybara
2
+ module Rc
3
+ ##
4
+ # Contributes Selenium-RC methods related to windows to the adapter.
5
+ #
6
+ # Expects a `session` accessor to be provided where it is mixed in.
7
+ # Also requires that the adapter class's initialize method call super.
8
+ module Windows
9
+ MAIN_APP_WINDOW_NAME = "selenium_main_app_window"
10
+
11
+ def initialize(session)
12
+ super
13
+ if session.respond_to?(:current_window)
14
+ # The result from Capybara.string does not have a current window
15
+ @main_window = session.current_window
16
+ session.execute_script("window.name = #{MAIN_APP_WINDOW_NAME.inspect}")
17
+ end
18
+ end
19
+
20
+ def open_window(url, window_id)
21
+ session.execute_script("window.open(#{url.inspect}, #{window_id.inspect})")
22
+ end
23
+
24
+ def select_window(window_locator)
25
+ parsed = parse_selenium_rc_window_locator(window_locator)
26
+
27
+ window =
28
+ case parsed[:type]
29
+ when :main_window
30
+ @main_window
31
+ when :name
32
+ find_window_by_name(parsed[:string])
33
+ when :title
34
+ find_window_by_title(parsed[:string])
35
+ when :window_id
36
+ find_window_by_name_or_title(parsed[:string])
37
+ else
38
+ fail "Don't know how to find a #{parsed[:type].inspect} Selenium window locator with Capybara"
39
+ end
40
+
41
+ unless window
42
+ fail "Could not find a window matching #{window_locator.inspect}"
43
+ end
44
+ session.switch_to_window(window)
45
+ end
46
+
47
+ def close
48
+ window = session.current_window
49
+ window.close
50
+ end
51
+
52
+ def window_maximize
53
+ session.current_window.maximize
54
+ end
55
+
56
+ def get_all_window_names
57
+ all_window_names = []
58
+ in_each_window do |window, page|
59
+ all_window_names << page.execute_script("return window.name;");
60
+ end
61
+ all_window_names
62
+ end
63
+
64
+ private
65
+
66
+ def find_window_by_name(name)
67
+ in_each_window do |window, page|
68
+ this_name = page.execute_script("return window.name;");
69
+ break window if name == this_name
70
+ end
71
+ end
72
+
73
+ def find_window_by_title(title)
74
+ in_each_window do |window, page|
75
+ break window if title == page.title
76
+ end
77
+ end
78
+
79
+ def find_window_by_name_or_title(id)
80
+ # Check for name matches & return immediately if found. At the same
81
+ # time collect titles so that we can do that check if necessary without
82
+ # flipping through the windows again.
83
+ titles = {}
84
+ by_name = in_each_window do |window, page|
85
+ name = page.execute_script("return window.name;");
86
+ break window if name == id
87
+ titles[page.title] = window
88
+ end
89
+ return by_name if by_name
90
+
91
+ # If the other checks didn't find anything, return the window matching
92
+ # by title or nil if there isn't one.
93
+ titles[id]
94
+ end
95
+
96
+ def in_each_window(&block)
97
+ starting_window = session.current_window
98
+ begin
99
+ session.windows.each do |window|
100
+ session.switch_to_window(window)
101
+ yield window, session
102
+ end
103
+ nil
104
+ ensure
105
+ session.switch_to_window(starting_window)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: capybara-rc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Rhett Sutphin
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2015-12-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.10'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.10'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '10.0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '10.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '3.3'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.3'
62
+ - !ruby/object:Gem::Dependency
63
+ name: capybara
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: mime-types
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - '='
84
+ - !ruby/object:Gem::Version
85
+ version: '2.99'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - '='
92
+ - !ruby/object:Gem::Version
93
+ version: '2.99'
94
+ description: ! "\n Provides a wrapper for a Capybara session which exposes the
95
+ ancient Selenium-RC API.\n Allows gradually porting a legacy Selenium suite to
96
+ newer technologies.\n "
97
+ email:
98
+ - rhett@collaborativedrug.com
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - .gitignore
104
+ - .rspec
105
+ - .travis.yml
106
+ - CHANGELOG.md
107
+ - DIFFERENCES.md
108
+ - Gemfile
109
+ - LICENSE.txt
110
+ - README.md
111
+ - Rakefile
112
+ - bin/rspec
113
+ - capybara-rc.gemspec
114
+ - lib/capybara/rc.rb
115
+ - lib/capybara/rc/accessors.rb
116
+ - lib/capybara/rc/actions.rb
117
+ - lib/capybara/rc/adapter.rb
118
+ - lib/capybara/rc/extensions.rb
119
+ - lib/capybara/rc/modals.rb
120
+ - lib/capybara/rc/selenium_rc_locators.rb
121
+ - lib/capybara/rc/version.rb
122
+ - lib/capybara/rc/windows.rb
123
+ homepage: https://github.com/cdd/capybara-rc
124
+ licenses:
125
+ - MIT
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ! '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ segments:
137
+ - 0
138
+ hash: -3019246126874219789
139
+ required_rubygems_version: !ruby/object:Gem::Requirement
140
+ none: false
141
+ requirements:
142
+ - - ! '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ segments:
146
+ - 0
147
+ hash: -3019246126874219789
148
+ requirements: []
149
+ rubyforge_project:
150
+ rubygems_version: 1.8.25
151
+ signing_key:
152
+ specification_version: 3
153
+ summary: Provides the Selenium-RC API on top of Capybara
154
+ test_files: []