capybara-3.12.0/0000755000175000017500000000000013411315075014115 5ustar utkarsh2102utkarsh2102capybara-3.12.0/README.md0000644000175000017500000011364213411315075015403 0ustar utkarsh2102utkarsh2102# Capybara [![Build Status](https://secure.travis-ci.org/teamcapybara/capybara.svg)](https://travis-ci.org/teamcapybara/capybara) [![Build Status](https://ci.appveyor.com/api/projects/status/github/teamcapybara/capybara?svg=true)](https://ci.appveyor.com/api/projects/github/teamcapybara/capybara) [![Code Climate](https://codeclimate.com/github/teamcapybara/capybara.svg)](https://codeclimate.com/github/teamcapybara/capybara) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jnicklas/capybara?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![SemVer](https://api.dependabot.com/badges/compatibility_score?dependency-name=capybara&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=capybara&package-manager=bundler&version-scheme=semver) **Note** You are viewing the README for the 3.12.x version of Capybara. Capybara helps you test web applications by simulating how a real user would interact with your app. It is agnostic about the driver running your tests and comes with Rack::Test and Selenium support built in. WebKit is supported through an external gem. ## Support Capybara If you and/or your company find value in Capybara and would like to contribute financially to its ongoing maintenance and development, please visit Patreon **Need help?** Ask on the mailing list (please do not open an issue on GitHub): http://groups.google.com/group/ruby-capybara ## Table of contents - [Key benefits](#key-benefits) - [Setup](#setup) - [Using Capybara with Cucumber](#using-capybara-with-cucumber) - [Using Capybara with RSpec](#using-capybara-with-rspec) - [Using Capybara with Test::Unit](#using-capybara-with-testunit) - [Using Capybara with Minitest](#using-capybara-with-minitest) - [Using Capybara with Minitest::Spec](#using-capybara-with-minitestspec) - [Drivers](#drivers) - [Selecting the Driver](#selecting-the-driver) - [RackTest](#racktest) - [Selenium](#selenium) - [Capybara-webkit](#capybara-webkit) - [Poltergeist](#poltergeist) - [The DSL](#the-dsl) - [Navigating](#navigating) - [Clicking links and buttons](#clicking-links-and-buttons) - [Interacting with forms](#interacting-with-forms) - [Querying](#querying) - [Finding](#finding) - [Scoping](#scoping) - [Working with windows](#working-with-windows) - [Scripting](#scripting) - [Modals](#modals) - [Debugging](#debugging) - [Matching](#matching) - [Exactness](#exactness) - [Strategy](#strategy) - [Transactions and database setup](#transactions-and-database-setup) - [Asynchronous JavaScript (Ajax and friends)](#asynchronous-javascript-ajax-and-friends) - [Using the DSL elsewhere](#using-the-dsl-elsewhere) - [Calling remote servers](#calling-remote-servers) - [Using sessions](#using-sessions) - [XPath, CSS and selectors](#xpath-css-and-selectors) - [Beware the XPath // trap](#beware-the-xpath--trap) - [Configuring and adding drivers](#configuring-and-adding-drivers) - [Gotchas:](#gotchas) - ["Threadsafe" mode](#threadsafe) - [Development](#development) ## Key benefits - **No setup** necessary for Rails and Rack application. Works out of the box. - **Intuitive API** which mimics the language an actual user would use. - **Switch the backend** your tests run against from fast headless mode to an actual browser with no changes to your tests. - **Powerful synchronization** features mean you never have to manually wait for asynchronous processes to complete. ## Setup Capybara requires Ruby 2.3.0 or later. To install, add this line to your `Gemfile` and run `bundle install`: ```ruby gem 'capybara' ``` If the application that you are testing is a Rails app, add this line to your test helper file: ```ruby require 'capybara/rails' ``` If the application that you are testing is a Rack app, but not Rails, set Capybara.app to your Rack app: ```ruby Capybara.app = MyRackApp ``` If you need to test JavaScript, or if your app interacts with (or is located at) a remote URL, you'll need to [use a different driver](#drivers). If using Rails 5.0+, but not using the Rails system tests from 5.1, you'll probably also want to swap the "server" used to launch your app to Puma in order to match Rails defaults. ```ruby Capybara.server = :puma # Until your setup is working Capybara.server = :puma, { Silent: true } # To clean up your test output ``` ## Using Capybara with Cucumber The `cucumber-rails` gem comes with Capybara support built-in. If you are not using Rails, manually load the `capybara/cucumber` module: ```ruby require 'capybara/cucumber' Capybara.app = MyRackApp ``` You can use the Capybara DSL in your steps, like so: ```ruby When /I sign in/ do within("#session") do fill_in 'Email', with: 'user@example.com' fill_in 'Password', with: 'password' end click_button 'Sign in' end ``` You can switch to the `Capybara.javascript_driver` (`:selenium` by default) by tagging scenarios (or features) with `@javascript`: ```ruby @javascript Scenario: do something Ajaxy When I click the Ajax link ... ``` There are also explicit tags for each registered driver set up for you (`@selenium`, `@rack_test`, etc). ## Using Capybara with RSpec Load RSpec 3.5+ support by adding the following line (typically to your `spec_helper.rb` file): ```ruby require 'capybara/rspec' ``` If you are using Rails, put your Capybara specs in `spec/features` or `spec/system` (only works if [you have it configured in RSpec](https://www.relishapp.com/rspec/rspec-rails/docs/upgrade#file-type-inference-disabled)) and if you have your Capybara specs in a different directory, then tag the example groups with `type: :feature` or `type: :system` depending on which type of test you're writing. If you are not using Rails, tag all the example groups in which you want to use Capybara with `type: :feature`. You can now write your specs like so: ```ruby describe "the signin process", type: :feature do before :each do User.make(email: 'user@example.com', password: 'password') end it "signs me in" do visit '/sessions/new' within("#session") do fill_in 'Email', with: 'user@example.com' fill_in 'Password', with: 'password' end click_button 'Sign in' expect(page).to have_content 'Success' end end ``` Use `js: true` to switch to the `Capybara.javascript_driver` (`:selenium` by default), or provide a `:driver` option to switch to one specific driver. For example: ```ruby describe 'some stuff which requires js', js: true do it 'will use the default js driver' it 'will switch to one specific driver', driver: :webkit end ``` Capybara also comes with a built in DSL for creating descriptive acceptance tests: ```ruby feature "Signing in" do background do User.make(email: 'user@example.com', password: 'caplin') end scenario "Signing in with correct credentials" do visit '/sessions/new' within("#session") do fill_in 'Email', with: 'user@example.com' fill_in 'Password', with: 'caplin' end click_button 'Sign in' expect(page).to have_content 'Success' end given(:other_user) { User.make(email: 'other@example.com', password: 'rous') } scenario "Signing in as another user" do visit '/sessions/new' within("#session") do fill_in 'Email', with: other_user.email fill_in 'Password', with: other_user.password end click_button 'Sign in' expect(page).to have_content 'Invalid email or password' end end ``` `feature` is in fact just an alias for `describe ..., type:> :feature`, `background` is an alias for `before`, `scenario` for `it`, and `given`/`given!` aliases for `let`/`let!`, respectively. Finally, Capybara matchers are also supported in view specs: ```ruby RSpec.describe "todos/show.html.erb", type: :view do it "displays the todo title" do assign :todo, Todo.new(title: "Buy milk") render expect(rendered).to have_css("header h1", text: "Buy milk") end end ``` **Note: When you require 'capybara/rspec' proxy methods are installed to work around name collisions between Capybara::DSL methods `all`/`within` and the identically named built-in RSpec matchers. If you opt not to require 'capybara/rspec' you can install the proxy methods by requiring 'capybara/rspec/matcher_proxies' after requiring RSpec and 'capybara/dsl'** ## Using Capybara with Test::Unit * If you are using `Test::Unit`, define a base class for your Capybara tests like so: ```ruby require 'capybara/dsl' class CapybaraTestCase < Test::Unit::TestCase include Capybara::DSL def teardown Capybara.reset_sessions! Capybara.use_default_driver end end ``` ## Using Capybara with Minitest * If you are using Rails, add the following code in your `test_helper.rb` file to make Capybara available in all test cases deriving from `ActionDispatch::IntegrationTest`: ```ruby require 'capybara/rails' require 'capybara/minitest' class ActionDispatch::IntegrationTest # Make the Capybara DSL available in all integration tests include Capybara::DSL # Make `assert_*` methods behave like Minitest assertions include Capybara::Minitest::Assertions # Reset sessions and driver between tests # Use super wherever this method is redefined in your individual test classes def teardown Capybara.reset_sessions! Capybara.use_default_driver end end ``` * If you are not using Rails, define a base class for your Capybara tests like so: ```ruby require 'capybara/minitest' class CapybaraTestCase < Minitest::Test include Capybara::DSL include Capybara::Minitest::Assertions def teardown Capybara.reset_sessions! Capybara.use_default_driver end end ``` Remember to call `super` in any subclasses that override `teardown`. To switch the driver, set `Capybara.current_driver`. For instance, ```ruby class BlogTest < ActionDispatch::IntegrationTest setup do Capybara.current_driver = Capybara.javascript_driver # :selenium by default end test 'shows blog posts' do # ... this test is run with Selenium ... end end ``` ## Using Capybara with Minitest::Spec Follow the above instructions for Minitest and additionally require capybara/minitest/spec ```ruby page.must_have_content('Important!') ``` ## Drivers Capybara uses the same DSL to drive a variety of browser and headless drivers. ### Selecting the Driver By default, Capybara uses the `:rack_test` driver, which is fast but limited: it does not support JavaScript, nor is it able to access HTTP resources outside of your Rack application, such as remote APIs and OAuth services. To get around these limitations, you can set up a different default driver for your features. For example if you'd prefer to run everything in Selenium, you could do: ```ruby Capybara.default_driver = :selenium # :selenium_chrome and :selenium_chrome_headless are also registered ``` However, if you are using RSpec or Cucumber, you may instead want to consider leaving the faster `:rack_test` as the __default_driver__, and marking only those tests that require a JavaScript-capable driver using `js: true` or `@javascript`, respectively. By default, JavaScript tests are run using the `:selenium` driver. You can change this by setting `Capybara.javascript_driver`. You can also change the driver temporarily (typically in the Before/setup and After/teardown blocks): ```ruby Capybara.current_driver = :webkit # temporarily select different driver # tests here Capybara.use_default_driver # switch back to default driver ``` **Note**: switching the driver creates a new session, so you may not be able to switch in the middle of a test. ### RackTest RackTest is Capybara's default driver. It is written in pure Ruby and does not have any support for executing JavaScript. Since the RackTest driver interacts directly with Rack interfaces, it does not require a server to be started. However, this means that if your application is not a Rack application (Rails, Sinatra and most other Ruby frameworks are Rack applications) then you cannot use this driver. Furthermore, you cannot use the RackTest driver to test a remote application, or to access remote URLs (e.g., redirects to external sites, external APIs, or OAuth services) that your application might interact with. [capybara-mechanize](https://github.com/jeroenvandijk/capybara-mechanize) provides a similar driver that can access remote servers. RackTest can be configured with a set of headers like this: ```ruby Capybara.register_driver :rack_test do |app| Capybara::RackTest::Driver.new(app, headers: { 'HTTP_USER_AGENT' => 'Capybara' }) end ``` See the section on adding and configuring drivers. ### Selenium Capybara supports [Selenium 3.5+ (Webdriver)](https://www.seleniumhq.org/projects/webdriver/). In order to use Selenium, you'll need to install the `selenium-webdriver` gem, and add it to your Gemfile if you're using bundler. Capybara pre-registers a number of named drives that use Selenium - they are: * :selenium => Selenium driving Firefox * :selenium_headless => Selenium driving Firefox in a headless configuration * :selenium_chrome => Selenium driving Chrome * :selenium_chrome_headless => Selenium driving Chrome in a headless configuration These should work (with relevant software installation) in a local desktop configuration but you may need to customize them if using in a CI environment where additional options may need to be passed to the browsers. See the section on adding and configuring drivers. **Note**: drivers which run the server in a different thread may not share the same transaction as your tests, causing data not to be shared between your test and test server, see [Transactions and database setup](#transactions-and-database-setup) below. ### Capybara-webkit Note: `capybara-webkit` depends on QtWebkit which went EOL quite some time ago. There has been an attempt to revive the project but `capybara-webkit` is not yet (AFAIK) compatible with the revived version of QtWebKit (could be a good OSS project for someone) and as such is still limited to an old version of QtWebKit. This means its support for modern JS and CSS is severely limited. The [capybara-webkit driver](https://github.com/thoughtbot/capybara-webkit) is for true headless testing. It uses QtWebKit to start a rendering engine process. It can execute JavaScript as well. It is significantly faster than drivers like Selenium since it does not load an entire browser. You can install it with: ```bash gem install capybara-webkit ``` And you can use it by: ```ruby Capybara.javascript_driver = :webkit ``` ### Poltergeist Note: `poltergeist` depends on PhantomJS for which active development ended quite some time ago (2.1.1). As such it is roughly equivalent to a 6-7 year old version of Safari, meaning lack of support for modern JS And CSS. If any effort to update PhantomJS succeeds in the future this situation could change. [Poltergeist](https://github.com/teampoltergeist/poltergeist) is another headless driver which integrates Capybara with [PhantomJS](http://phantomjs.org/). It is truly headless, so doesn't require Xvfb to run on your CI server. It will also detect and report any Javascript errors that happen within the page. ## The DSL *A complete reference is available at [rubydoc.info](http://rubydoc.info/github/teamcapybara/capybara/master)*. **Note: By default Capybara will only locate visible elements. This is because a real user would not be able to interact with non-visible elements.** **Note**: All searches in Capybara are *case sensitive*. This is because Capybara heavily uses XPath, which doesn't support case insensitivity. ### Navigating You can use the [visit](http://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Session#visit-instance_method) method to navigate to other pages: ```ruby visit('/projects') visit(post_comments_path(post)) ``` The visit method only takes a single parameter, the request method is **always** GET. You can get the [current path](http://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Session#current_path-instance_method) of the browsing session, and test it using the [`have_current_path`](http://www.rubydoc.info/github/teamcapybara/capybara/master/Capybara/RSpecMatchers#have_current_path-instance_method) matcher: ```ruby expect(page).to have_current_path(post_comments_path(post)) ``` **Note**: You can also assert the current path by testing the value of `current_path` directly. However, using the `have_current_path` matcher is safer since it uses Capybara's [waiting behaviour](#asynchronous-javascript-ajax-and-friends) to ensure that preceding actions (such as a `click_link`) have completed. ### Clicking links and buttons *Full reference: [Capybara::Node::Actions](http://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Node/Actions)* You can interact with the webapp by following links and buttons. Capybara automatically follows any redirects, and submits forms associated with buttons. ```ruby click_link('id-of-link') click_link('Link Text') click_button('Save') click_on('Link Text') # clicks on either links or buttons click_on('Button Value') ``` ### Interacting with forms *Full reference: [Capybara::Node::Actions](http://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Node/Actions)* There are a number of tools for interacting with form elements: ```ruby fill_in('First Name', with: 'John') fill_in('Password', with: 'Seekrit') fill_in('Description', with: 'Really Long Text...') choose('A Radio Button') check('A Checkbox') uncheck('A Checkbox') attach_file('Image', '/path/to/image.jpg') select('Option', from: 'Select Box') ``` ### Querying *Full reference: [Capybara::Node::Matchers](http://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Node/Matchers)* Capybara has a rich set of options for querying the page for the existence of certain elements, and working with and manipulating those elements. ```ruby page.has_selector?('table tr') page.has_selector?(:xpath, './/table/tr') page.has_xpath?('.//table/tr') page.has_css?('table tr.foo') page.has_content?('foo') ``` **Note:** The negative forms like `has_no_selector?` are different from `not has_selector?`. Read the section on asynchronous JavaScript for an explanation. You can use these with RSpec's magic matchers: ```ruby expect(page).to have_selector('table tr') expect(page).to have_selector(:xpath, './/table/tr') expect(page).to have_xpath('.//table/tr') expect(page).to have_css('table tr.foo') expect(page).to have_content('foo') ``` ### Finding _Full reference: [Capybara::Node::Finders](http://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Node/Finders)_ You can also find specific elements, in order to manipulate them: ```ruby find_field('First Name').value find_field(id: 'my_field').value find_link('Hello', :visible => :all).visible? find_link(class: ['some_class', 'some_other_class'], :visible => :all).visible? find_button('Send').click find_button(value: '1234').click find(:xpath, ".//table/tr").click find("#overlay").find("h1").click all('a').each { |a| a[:href] } ``` If you need to find elements by additional attributes/properties you can also pass a filter block, which will be checked inside the normal waiting behavior. If you find yourself needing to use this a lot you may be better off adding a [custom selector](http://www.rubydoc.info/github/teamcapybara/capybara/Capybara#add_selector-class_method) or [adding a filter to an existing selector](http://www.rubydoc.info/github/teamcapybara/capybara/Capybara#modify_selector-class_method). ```ruby find_field('First Name'){ |el| el['data-xyz'] == '123' } find("#img_loading"){ |img| img['complete'] == true } ``` **Note**: `find` will wait for an element to appear on the page, as explained in the Ajax section. If the element does not appear it will raise an error. These elements all have all the Capybara DSL methods available, so you can restrict them to specific parts of the page: ```ruby find('#navigation').click_link('Home') expect(find('#navigation')).to have_button('Sign out') ``` ### Scoping Capybara makes it possible to restrict certain actions, such as interacting with forms or clicking links and buttons, to within a specific area of the page. For this purpose you can use the generic [within](http://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Session#within-instance_method) method. Optionally you can specify which kind of selector to use. ```ruby within("li#employee") do fill_in 'Name', with: 'Jimmy' end within(:xpath, ".//li[@id='employee']") do fill_in 'Name', with: 'Jimmy' end ``` There are special methods for restricting the scope to a specific fieldset, identified by either an id or the text of the fieldset's legend tag, and to a specific table, identified by either id or text of the table's caption tag. ```ruby within_fieldset('Employee') do fill_in 'Name', with: 'Jimmy' end within_table('Employee') do fill_in 'Name', with: 'Jimmy' end ``` ### Working with windows Capybara provides some methods to ease finding and switching windows: ```ruby facebook_window = window_opened_by do click_button 'Like' end within_window facebook_window do find('#login_email').set('a@example.com') find('#login_password').set('qwerty') click_button 'Submit' end ``` ### Scripting In drivers which support it, you can easily execute JavaScript: ```ruby page.execute_script("$('body').empty()") ``` For simple expressions, you can return the result of the script. Note that this may break with more complicated expressions: ```ruby result = page.evaluate_script('4 + 4'); ``` ### Modals In drivers which support it, you can accept, dismiss and respond to alerts, confirms and prompts. You can accept or dismiss alert messages by wrapping the code that produces an alert in a block: ```ruby accept_alert do click_link('Show Alert') end ``` You can accept or dismiss a confirmation by wrapping it in a block, as well: ```ruby dismiss_confirm do click_link('Show Confirm') end ``` You can accept or dismiss prompts as well, and also provide text to fill in for the response: ```ruby accept_prompt(with: 'Linus Torvalds') do click_link('Show Prompt About Linux') end ``` All modal methods return the message that was presented. So, you can access the prompt message by assigning the return to a variable: ```ruby message = accept_prompt(with: 'Linus Torvalds') do click_link('Show Prompt About Linux') end expect(message).to eq('Who is the chief architect of Linux?') ``` ### Debugging It can be useful to take a snapshot of the page as it currently is and take a look at it: ```ruby save_and_open_page ``` You can also retrieve the current state of the DOM as a string using [page.html](http://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Session#html-instance_method). ```ruby print page.html ``` This is mostly useful for debugging. You should avoid testing against the contents of `page.html` and use the more expressive finder methods instead. Finally, in drivers that support it, you can save a screenshot: ```ruby page.save_screenshot('screenshot.png') ``` Or have it save and automatically open: ```ruby save_and_open_screenshot ``` Screenshots are saved to `Capybara.save_path`, relative to the app directory. If you have required `capybara/rails`, `Capybara.save_path` will default to `tmp/capybara`. ## Matching It is possible to customize how Capybara finds elements. At your disposal are two options, `Capybara.exact` and `Capybara.match`. ### Exactness `Capybara.exact` and the `exact` option work together with the `is` expression inside the XPath gem. When `exact` is true, all `is` expressions match exactly, when it is false, they allow substring matches. Many of the selectors built into Capybara use the `is` expression. This way you can specify whether you want to allow substring matches or not. `Capybara.exact` is false by default. For example: ```ruby click_link("Password") # also matches "Password confirmation" Capybara.exact = true click_link("Password") # does not match "Password confirmation" click_link("Password", exact: false) # can be overridden ``` ### Strategy Using `Capybara.match` and the equivalent `match` option, you can control how Capybara behaves when multiple elements all match a query. There are currently four different strategies built into Capybara: 1. **first:** Just picks the first element that matches. 2. **one:** Raises an error if more than one element matches. 3. **smart:** If `exact` is `true`, raises an error if more than one element matches, just like `one`. If `exact` is `false`, it will first try to find an exact match. An error is raised if more than one element is found. If no element is found, a new search is performed which allows partial matches. If that search returns multiple matches, an error is raised. 4. **prefer_exact:** If multiple matches are found, some of which are exact, and some of which are not, then the first exactly matching element is returned. The default for `Capybara.match` is `:smart`. To emulate the behaviour in Capybara 2.0.x, set `Capybara.match` to `:one`. To emulate the behaviour in Capybara 1.x, set `Capybara.match` to `:prefer_exact`. ## Transactions and database setup **Note:** Rails 5.1+ "safely" shares the database connection between the app and test threads. Therefore, if using Rails 5.1+ you SHOULD be able to ignore this section. Some Capybara drivers need to run against an actual HTTP server. Capybara takes care of this and starts one for you in the same process as your test, but on another thread. Selenium is one of those drivers, whereas RackTest is not. If you are using a SQL database, it is common to run every test in a transaction, which is rolled back at the end of the test, rspec-rails does this by default out of the box for example. Since transactions are usually not shared across threads, this will cause data you have put into the database in your test code to be invisible to Capybara. Cucumber handles this by using truncation instead of transactions, i.e. they empty out the entire database after each test. You can get the same behaviour by using a gem such as [database_cleaner](https://github.com/DatabaseCleaner/database_cleaner). ## Asynchronous JavaScript (Ajax and friends) When working with asynchronous JavaScript, you might come across situations where you are attempting to interact with an element which is not yet present on the page. Capybara automatically deals with this by waiting for elements to appear on the page. When issuing instructions to the DSL such as: ```ruby click_link('foo') click_link('bar') expect(page).to have_content('baz') ``` If clicking on the *foo* link triggers an asynchronous process, such as an Ajax request, which, when complete will add the *bar* link to the page, clicking on the *bar* link would be expected to fail, since that link doesn't exist yet. However Capybara is smart enough to retry finding the link for a brief period of time before giving up and throwing an error. The same is true of the next line, which looks for the content *baz* on the page; it will retry looking for that content for a brief time. You can adjust how long this period is (the default is 2 seconds): ```ruby Capybara.default_max_wait_time = 5 ``` Be aware that because of this behaviour, the following two statements are **not** equivalent, and you should **always** use the latter! ```ruby !page.has_xpath?('a') page.has_no_xpath?('a') ``` The former would immediately fail because the content has not yet been removed. Only the latter would wait for the asynchronous process to remove the content from the page. Capybara's RSpec matchers, however, are smart enough to handle either form. The two following statements are functionally equivalent: ```ruby expect(page).not_to have_xpath('a') expect(page).to have_no_xpath('a') ``` Capybara's waiting behaviour is quite advanced, and can deal with situations such as the following line of code: ```ruby expect(find('#sidebar').find('h1')).to have_content('Something') ``` Even if JavaScript causes `#sidebar` to disappear off the page, Capybara will automatically reload it and any elements it contains. So if an AJAX request causes the contents of `#sidebar` to change, which would update the text of the `h1` to "Something", and this happened, this test would pass. If you do not want this behaviour, you can set `Capybara.automatic_reload` to `false`. ## Using the DSL elsewhere You can mix the DSL into any context by including Capybara::DSL: ```ruby require 'capybara/dsl' Capybara.default_driver = :webkit module MyModule include Capybara::DSL def login! within(:xpath, ".//form[@id='session']") do fill_in 'Email', with: 'user@example.com' fill_in 'Password', with: 'password' end click_button 'Sign in' end end ``` This enables its use in unsupported testing frameworks, and for general-purpose scripting. ## Calling remote servers Normally Capybara expects to be testing an in-process Rack application, but you can also use it to talk to a web server running anywhere on the internet, by setting app_host: ```ruby Capybara.current_driver = :selenium Capybara.app_host = 'http://www.google.com' ... visit('/') ``` **Note**: the default driver (`:rack_test`) does not support running against a remote server. With drivers that support it, you can also visit any URL directly: ```ruby visit('http://www.google.com') ``` By default Capybara will try to boot a rack application automatically. You might want to switch off Capybara's rack server if you are running against a remote application: ```ruby Capybara.run_server = false ``` ## Using sessions Capybara manages named sessions (:default if not specified) allowing multiple sessions using the same driver and test app instance to be interacted with. A new session will be created using the current driver if a session with the given name using the current driver and test app instance is not found. ### Named sessions To perform operations in a different session and then revert to the previous session ```ruby Capybara.using_session("Bob's session") do #do something in Bob's browser session end #reverts to previous session ``` To permanently switch the current session to a different session ```ruby Capybara.session_name = "some other session" ``` ### Using sessions manually For ultimate control, you can instantiate and use a [Session](http://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Session) manually. ```ruby require 'capybara' session = Capybara::Session.new(:webkit, my_rack_app) session.within("form#session") do session.fill_in 'Email', with: 'user@example.com' session.fill_in 'Password', with: 'password' end session.click_button 'Sign in' ``` ## XPath, CSS and selectors Capybara does not try to guess what kind of selector you are going to give it, and will always use CSS by default. If you want to use XPath, you'll need to do: ```ruby within(:xpath, './/ul/li') { ... } find(:xpath, './/ul/li').text find(:xpath, './/li[contains(.//a[@href = "#"]/text(), "foo")]').value ``` Alternatively you can set the default selector to XPath: ```ruby Capybara.default_selector = :xpath find('.//ul/li').text ``` Capybara allows you to add custom selectors, which can be very useful if you find yourself using the same kinds of selectors very often: ```ruby Capybara.add_selector(:id) do xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] } end Capybara.add_selector(:row) do xpath { |num| ".//tbody/tr[#{num}]" } end Capybara.add_selector(:flash_type) do css { |type| "#flash.#{type}" } end ``` The block given to xpath must always return an XPath expression as a String, or an XPath expression generated through the XPath gem. You can now use these selectors like this: ```ruby find(:id, 'post_123') find(:row, 3) find(:flash_type, :notice) ``` ## Beware the XPath // trap In XPath the expression // means something very specific, and it might not be what you think. Contrary to common belief, // means "anywhere in the document" not "anywhere in the current context". As an example: ```ruby page.find(:xpath, '//body').all(:xpath, '//script') ``` You might expect this to find all script tags in the body, but actually, it finds all script tags in the entire document, not only those in the body! What you're looking for is the .// expression which means "any descendant of the current node": ```ruby page.find(:xpath, '//body').all(:xpath, './/script') ``` The same thing goes for within: ```ruby within(:xpath, '//body') do page.find(:xpath, './/script') within(:xpath, './/table/tbody') do ... end end ``` ## Configuring and adding drivers Capybara makes it convenient to switch between different drivers. It also exposes an API to tweak those drivers with whatever settings you want, or to add your own drivers. This is how to override the selenium driver configuration to use chrome: ```ruby Capybara.register_driver :selenium do |app| Capybara::Selenium::Driver.new(app, :browser => :chrome) end ``` However, it's also possible to give this configuration a different name. ```ruby # Note: Capybara registers this by default Capybara.register_driver :selenium_chrome do |app| Capybara::Selenium::Driver.new(app, :browser => :chrome) end ``` Then tests can switch between using different browsers effortlessly: ```ruby Capybara.current_driver = :selenium_chrome ``` Whatever is returned from the block should conform to the API described by Capybara::Driver::Base, it does not however have to inherit from this class. Gems can use this API to add their own drivers to Capybara. The [Selenium wiki](https://github.com/SeleniumHQ/selenium/wiki/Ruby-Bindings) has additional info about how the underlying driver can be configured. ## Gotchas: * Access to session and request is not possible from the test, Access to response is limited. Some drivers allow access to response headers and HTTP status code, but this kind of functionality is not provided by some drivers, such as Selenium. * Access to Rails specific stuff (such as `controller`) is unavailable, since we're not using Rails' integration testing. * Freezing time: It's common practice to mock out the Time so that features that depend on the current Date work as expected. This can be problematic on ruby/platform combinations that don't support access to a monotonic process clock, since Capybara's Ajax timing uses the system time, resulting in Capybara never timing out and just hanging when a failure occurs. It's still possible to use gems which allow you to travel in time, rather than freeze time. One such gem is [Timecop](https://github.com/travisjeffery/timecop). * When using Rack::Test, beware if attempting to visit absolute URLs. For example, a session might not be shared between visits to `posts_path` and `posts_url`. If testing an absolute URL in an Action Mailer email, set `default_url_options` to match the Rails default of `www.example.com`. * Server errors will only be raised in the session that initiates the server thread. If you are testing for specific server errors and using multiple sessions make sure to test for the errors using the initial session (usually :default) ## "Threadsafe" mode In normal mode most of Capybara's configuration options are global settings which can cause issues if using multiple sessions and wanting to change a setting for only one of the sessions. To provide support for this type of usage Capybara now provides a "threadsafe" mode which can be enabled by setting ```ruby Capybara.threadsafe = true ``` This setting can only be changed before any sessions have been created. In "threadsafe" mode the following behaviors of Capybara change * Most options can now be set on a session. These can either be set at session creation time or after, and default to the global options at the time of session creation. Options which are NOT session specific are `app`, `reuse_server`, `default_driver`, `javascript_driver`, and (obviously) `threadsafe`. Any drivers and servers registered through `register_driver` and `register_server` are also global. ```ruby my_session = Capybara::Session.new(:driver, some_app) do |config| config.automatic_label_click = true # only set for my_session end my_session.config.default_max_wait_time = 10 # only set for my_session Capybara.default_max_wait_time = 2 # will not change the default_max_wait in my_session ``` * `current_driver` and `session_name` are thread specific. This means that `using_session` and `using_driver` also only affect the current thread. ## Development To set up a development environment, simply do: ```bash bundle install bundle exec rake # run the test suite with Firefox - requires `geckodriver` to be installed bundle exec rake spec_chrome # run the test suite with Chrome - require `chromedriver` to be installed ``` See [CONTRIBUTING.md](https://github.com/teamcapybara/capybara/blob/master/CONTRIBUTING.md) for how to send issues and pull requests. capybara-3.12.0/spec/0000755000175000017500000000000013411315075015047 5ustar utkarsh2102utkarsh2102capybara-3.12.0/spec/minitest_spec_spec.rb0000644000175000017500000001103313411315075021252 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'capybara/minitest' require 'capybara/minitest/spec' class MinitestSpecTest < Minitest::Spec include ::Capybara::DSL include ::Capybara::Minitest::Assertions before do visit('/form') end after do Capybara.reset_sessions! end it 'supports text expectations' do page.must_have_text('Form', minimum: 1) page.wont_have_text('Not a form') form = find(:css, 'form', text: 'Title') form.must_have_text('Customer Email') form.wont_have_text('Some other email') end it 'supports current_path expectations' do page.must_have_current_path('/form') page.wont_have_current_path('/form2') end it 'supports title expectations' do visit('/with_title') page.must_have_title('Test Title') page.wont_have_title('Not the title') end it 'supports xpath expectations' do page.must_have_xpath('.//input[@id="customer_email"]') page.wont_have_xpath('.//select[@id="not_form_title"]') page.wont_have_xpath('.//input[@id="customer_email"]') { |el| el[:id] == 'not_customer_email' } select = find(:select, 'form_title') select.must_have_xpath('.//option[@class="title"]') select.must_have_xpath('.//option', count: 1) { |option| option[:class] != 'title' && !option.disabled? } select.wont_have_xpath('.//input[@id="customer_email"]') end it 'support css expectations' do page.must_have_css('input#customer_email') page.wont_have_css('select#not_form_title') el = find(:select, 'form_title') el.must_have_css('option.title') el.wont_have_css('input#customer_email') end it 'supports link expectations' do visit('/with_html') page.must_have_link('A link') page.wont_have_link('Not on page') end it 'supports button expectations' do page.must_have_button('fresh_btn') page.wont_have_button('not_btn') end it 'supports field expectations' do page.must_have_field('customer_email') page.wont_have_field('not_on_the_form') end it 'supports select expectations' do page.must_have_select('form_title') page.wont_have_select('not_form_title') end it 'supports checked_field expectations' do page.must_have_checked_field('form_pets_dog') page.wont_have_checked_field('form_pets_cat') end it 'supports unchecked_field expectations' do page.must_have_unchecked_field('form_pets_cat') page.wont_have_unchecked_field('form_pets_dog') end it 'supports table expectations' do visit('/tables') page.must_have_table('agent_table') page.wont_have_table('not_on_form') end it 'supports all_of_selectors expectations' do page.must_have_all_of_selectors(:css, 'select#form_other_title', 'input#form_last_name') end it 'supports none_of_selectors expectations' do page.must_have_none_of_selectors(:css, 'input#not_on_page', 'input#also_not_on_page') end it 'supports any_of_selectors expectations' do page.must_have_any_of_selectors(:css, 'select#form_other_title', 'input#not_on_page') end it 'supports match_selector expectations' do find(:field, 'customer_email').must_match_selector(:field, 'customer_email') find(:select, 'form_title').wont_match_selector(:field, 'customer_email') end it 'supports match_css expectations' do find(:select, 'form_title').must_match_css('select#form_title') find(:select, 'form_title').wont_match_css('select#form_other_title') end it 'supports match_xpath expectations' do find(:select, 'form_title').must_match_xpath('.//select[@id="form_title"]') find(:select, 'form_title').wont_match_xpath('.//select[@id="not_on_page"]') end it 'handles failures' do page.must_have_select('non_existing_form_title') end it 'supports style expectations' do skip "Rack test doesn't support style" if Capybara.current_driver == :rack_test visit('/with_html') find(:css, '#second').must_have_style('display' => 'inline') end end RSpec.describe 'capybara/minitest/spec' do before do Capybara.current_driver = :rack_test Capybara.app = TestApp end after do Capybara.use_default_driver end it 'should support minitest spec' do output = StringIO.new reporter = Minitest::SummaryReporter.new(output) reporter.start MinitestSpecTest.run reporter, {} reporter.report expect(output.string).to include('20 runs, 42 assertions, 1 failures, 0 errors, 1 skips') # Make sure error messages are displayed expect(output.string).to include('expected to find select box "non_existing_form_title" but there were no matches') end end capybara-3.12.0/spec/fixtures/0000755000175000017500000000000013411315075016720 5ustar utkarsh2102utkarsh2102capybara-3.12.0/spec/fixtures/selenium_driver_rspec_failure.rb0000644000175000017500000000065713411315075025354 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'selenium-webdriver' RSpec.describe Capybara::Selenium::Driver do it 'should exit with a non-zero exit status' do options = { browser: (ENV['SELENIUM_BROWSER'] || :firefox).to_sym } browser = Capybara::Selenium::Driver.new(TestApp, options).browser expect(browser).to be_truthy expect(true).to eq(false) # rubocop:disable RSpec/ExpectActual end end capybara-3.12.0/spec/fixtures/certificate.pem0000644000175000017500000000272513411315075021713 0ustar utkarsh2102utkarsh2102-----BEGIN CERTIFICATE----- MIIEITCCAwmgAwIBAgIJALROkwd1gZHQMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNV BAYTAlVTMQswCQYDVQQIEwJDQTERMA8GA1UEChMIQ2FweWJhcmExFjAUBgNVBAMT DWNhcHliYXJhLnRlc3QxITAfBgkqhkiG9w0BCQEWEnR3YWxwb2xlQGdtYWlsLmNv bTAeFw0xODA1MDEyMDI5NDVaFw0yODA0MjgyMDI5NDVaMGgxCzAJBgNVBAYTAlVT MQswCQYDVQQIEwJDQTERMA8GA1UEChMIQ2FweWJhcmExFjAUBgNVBAMTDWNhcHli YXJhLnRlc3QxITAfBgkqhkiG9w0BCQEWEnR3YWxwb2xlQGdtYWlsLmNvbTCCASIw DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL7icqMv9uApxRXlcIQ3hSEfmULk 7CLT1aUAjEmTiqy8TkFqOeSuA3elnbVBhOW+emrb1uUyje20LvEOHbqYYw90ezlV jqNWUapawqjxb+q7KVNpA5uDCtOHIC/1Z1kxQ+yZP/8St4SvnLGUMsUhu0h+ywJa iGZ2xy8wdfjABUiUImRESdNkT7Xs0wxQGY0/FZ4K5Sec4kroOuvdxKKhyf5Js5xS NRv2tXSMWlKHbYEvYzsVtFgv/4GjqN4wVvbfJmsS8thcyrSYSMN7eE0R6dcbJaLM P/GTiw669zPJP164K84QoabLClgwyG+7mqjm7jutq9qXipwyrGsf/WR5fkUCAwEA AaOBzTCByjAdBgNVHQ4EFgQU4ut9c0oB2OGMlN/nvn0Y7twBzJowgZoGA1UdIwSB kjCBj4AU4ut9c0oB2OGMlN/nvn0Y7twBzJqhbKRqMGgxCzAJBgNVBAYTAlVTMQsw CQYDVQQIEwJDQTERMA8GA1UEChMIQ2FweWJhcmExFjAUBgNVBAMTDWNhcHliYXJh LnRlc3QxITAfBgkqhkiG9w0BCQEWEnR3YWxwb2xlQGdtYWlsLmNvbYIJALROkwd1 gZHQMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAANtqdDrdehBt20s IVOsVzOp+tJI8pn6G62WJcZWGsvLLfx6Y6eJSP24r5NW4v39cqCgE76J2znzKxeo VIShAJ1SLodR0GwzFGsl3TVKVoce6gBWZNXgkZyJTNqKGWeme8CH1EG3TqYlcybt EXWLQ6wKndD+uHJIfqUy7HfaUv+TghJGLkv8QYqcAWILQko4xqZ1NULDQ2uhjxei oaQlK9teHlVaH87dONesAg3f5rh9j6nvzGiC/T8ycdEhn/DxJMxRS2+mVZqKkk7U 60+e9Foc6qTW8UaMShIFYMspoRnr9baBxzxh9V9LZznwqVTf3vuEIck2l2j2KL/R SOBenCM= -----END CERTIFICATE----- capybara-3.12.0/spec/fixtures/key.pem0000644000175000017500000000321313411315075020212 0ustar utkarsh2102utkarsh2102-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAvuJyoy/24CnFFeVwhDeFIR+ZQuTsItPVpQCMSZOKrLxOQWo5 5K4Dd6WdtUGE5b56atvW5TKN7bQu8Q4duphjD3R7OVWOo1ZRqlrCqPFv6rspU2kD m4MK04cgL/VnWTFD7Jk//xK3hK+csZQyxSG7SH7LAlqIZnbHLzB1+MAFSJQiZERJ 02RPtezTDFAZjT8VngrlJ5ziSug6693EoqHJ/kmznFI1G/a1dIxaUodtgS9jOxW0 WC//gaOo3jBW9t8maxLy2FzKtJhIw3t4TRHp1xslosw/8ZOLDrr3M8k/XrgrzhCh pssKWDDIb7uaqObuO62r2peKnDKsax/9ZHl+RQIDAQABAoIBABhZepYmgC+IJIPu iLPVAT6AcWR/H0AyFYa+0yZvk7kFLFZb3pa1O+v/TGbavMEx0xvef0Mtd71ixrop OtGarshB65Ycu91KHZDFkx9J7STcSyFAvB0SUkc5bXmwrEZMaoW75tX65T4fyLU+ WlubOfC9e9gJBG1NqYrze5kHpaTkR8pBaSxECns5y2HLaMvs1t1kK7snqtGHFRAb deAUl0ONENaO6PF797yM3bsIEgnljdKvaVP3kYq/eAzH4jyw+qG/xq0bZ+nalJzM IL7xRSR/Yak7WZ3QUh99t3dng/z3De4yvhBLq/mc2mIVvt7aERPo/aDG4SrmQ9Jw f1GUT+ECgYEA+i96w2zPHkq0djuuhNaa/R0vybD9hVCdB7enQ8skBzQAQyhp9X9W by38VAbFRVIMrzdfbvwSFrE3Nd9BvyLWl16G+MVu+hq0jhM9p44a62vXvfgQmfFh iXyGkzCTufeLEzOFnP8S+ezacodK1hbQTjllUndn6NUhXcbfozTPfx8CgYEAw1Il xqfU7NkVX6UP/FvTx9aBKqwrD8YOPn9nj26Q45zVbmHJZZIvywlsMDCiJ6JJawEa GSkiZTDpypEvS/2UtLEY0zfbSbQGkBTnagHIFAuVh0AD1EP+kCcnrxA5Vjr8v0WE B0HBfMgoZxa8rmHMpPhrlCCo1ectRgu+dgzEKhsCgYAQ17lwBpc69tSHUSVClCAD AkABWAT5QKARsO91xOs8AOgznTjk6hmrinD+RyZosEliUlv+YMHm/S82VT1b3MCN mDOF8+SwubOGDQ2NhieRycTQaS7U7kcetl9o8VBAqMWYGVPZaeKhKKzcIPeMyiRj 38FOd/Nq3U5NveG4XwnJCQKBgGrzmnfTAsbGf+uliMFYzviIPqZNLC8w9i/Gt8BU fMYF5ODSbuNNTxpQiItCtigZtzX+nnnUil76j6o6IbnsmvbuWneeCFetWkKfD7B+ VT6UsUYkCXS73rK0nghATAUpu6hIumj22qonN+hrDNo390UGOnIcCBdIxQOr/pjJ mMitAoGAB3tFzlzZHpCE38IqdHL7CKIML9cYJSOm07nFIEUAKN2bUHGsvMH/tMS1 I3hyeHxMmYCrOLn7xLqiOVgRjbYS9JiQVdDojqeSvvnLCtY9/lHWi50pqbPfwyfK Og6QoFyxjBEsoSilc6dai58VTO6ufhoJXbXX9PcIl7le4dVPnzE= -----END RSA PRIVATE KEY----- capybara-3.12.0/spec/fixtures/selenium_driver_rspec_success.rb0000644000175000017500000000065213411315075025370 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'selenium-webdriver' RSpec.describe Capybara::Selenium::Driver do it 'should exit with a zero exit status' do options = { browser: (ENV['SELENIUM_BROWSER'] || :firefox).to_sym } browser = Capybara::Selenium::Driver.new(TestApp, options).browser expect(browser).to be_truthy expect(true).to eq(true) # rubocop:disable RSpec/ExpectActual end end capybara-3.12.0/spec/fixtures/capybara.csv0000644000175000017500000000002613411315075021215 0ustar utkarsh2102utkarsh2102test, mime-type, file capybara-3.12.0/spec/result_spec.rb0000644000175000017500000001426713411315075017736 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' RSpec.describe Capybara::Result do let :string do Capybara.string <<-STRING STRING end let :result do string.all '//li', minimum: 0 # pass minimum: 0 so lazy evaluation doesn't get triggered yet end it 'has a length' do expect(result.length).to eq(4) end it 'has a first element' do result.first.text == 'Alpha' end it 'has a last element' do result.last.text == 'Delta' end it 'can supports values_at method' do expect(result.values_at(0, 2).map(&:text)).to eq(%w[Alpha Gamma]) end it 'can return an element by its index' do expect(result.at(1).text).to eq('Beta') expect(result[2].text).to eq('Gamma') end it 'can be mapped' do expect(result.map(&:text)).to eq(%w[Alpha Beta Gamma Delta]) end it 'can be selected' do expect(result.select do |element| element.text.include? 't' end.length).to eq(2) end it 'can be reduced' do expect(result.reduce('') do |memo, element| memo + element.text[0] end).to eq('ABGD') end it 'can be sampled' do expect(result).to include(result.sample) end it 'can be indexed' do expect(result.index do |el| el.text == 'Gamma' end).to eq(2) end it 'supports all modes of []' do expect(result[1].text).to eq 'Beta' expect(result[0, 2].map(&:text)).to eq %w[Alpha Beta] expect(result[1..3].map(&:text)).to eq %w[Beta Gamma Delta] expect(result[-1].text).to eq 'Delta' expect(result[-2, 3].map(&:text)).to eq %w[Gamma Delta] expect(result[1..7].map(&:text)).to eq %w[Beta Gamma Delta] expect(result[1...3].map(&:text)).to eq %w[Beta Gamma] expect(result[2..-1].map(&:text)).to eq %w[Gamma Delta] expect(result[2...-1].map(&:text)).to eq %w[Gamma] eval <<~TEST, binding, __FILE__, __LINE__ + 1 if RUBY_VERSION.to_f > 2.5 expect(result[2..].map(&:text)).to eq %w[Gamma Delta] TEST end it 'works with filter blocks' do result = string.all('//li') { |node| node.text == 'Alpha' } expect(result.size).to eq 1 end it 'should catch invalid element errors during filtering' do allow_any_instance_of(Capybara::Node::Simple).to receive(:text).and_raise(StandardError) allow_any_instance_of(Capybara::Node::Simple).to receive(:session).and_return( instance_double('Capybara::Session', driver: instance_double('Capybara::Driver::Base', invalid_element_errors: [StandardError])) ) result = string.all('//li', text: 'Alpha') expect(result.size).to eq 0 end it 'should return non-invalid element errors during filtering' do allow_any_instance_of(Capybara::Node::Simple).to receive(:text).and_raise(StandardError) allow_any_instance_of(Capybara::Node::Simple).to receive(:session).and_return( instance_double('Capybara::Session', driver: instance_double('Capybara::Driver::Base', invalid_element_errors: [ArgumentError])) ) expect do string.all('//li', text: 'Alpha').to_a end.to raise_error(StandardError) end # Not a great test but it indirectly tests what is needed it 'should evaluate filters lazily for idx' do skip 'JRuby has an issue with lazy enumerator evaluation' if RUBY_PLATFORM == 'java' # Not processed until accessed expect(result.instance_variable_get('@result_cache').size).to be 0 # Only one retrieved when needed result.first expect(result.instance_variable_get('@result_cache').size).to be 1 # works for indexed access result[0] expect(result.instance_variable_get('@result_cache').size).to be 1 result[2] expect(result.instance_variable_get('@result_cache').size).to be 3 # All cached when converted to array result.to_a expect(result.instance_variable_get('@result_cache').size).to eq 4 end it 'should evaluate filters lazily for range' do skip 'JRuby has an issue with lazy enumerator evaluation' if RUBY_PLATFORM == 'java' result[0..1] expect(result.instance_variable_get('@result_cache').size).to be 2 expect(result[0..7].size).to eq 4 expect(result.instance_variable_get('@result_cache').size).to be 4 end it 'should evaluate filters lazily for idx and length' do skip 'JRuby has an issue with lazy enumerator evaluation' if RUBY_PLATFORM == 'java' result[1, 2] expect(result.instance_variable_get('@result_cache').size).to be 3 expect(result[2, 5].size).to eq 2 expect(result.instance_variable_get('@result_cache').size).to be 4 end it 'should only need to evaluate one result for any?' do skip 'JRuby has an issue with lazy enumerator evaluation' if RUBY_PLATFORM == 'java' result.any? expect(result.instance_variable_get('@result_cache').size).to be 1 end it 'should evaluate all elements when #to_a called' do # All cached when converted to array result.to_a expect(result.instance_variable_get('@result_cache').size).to eq 4 end context '#each' do it 'lazily evaluates' do skip 'JRuby has an issue with lazy enumerator evaluation' if RUBY_PLATFORM == 'java' results = [] result.each do |el| results << el expect(result.instance_variable_get('@result_cache').size).to eq results.size end expect(results.size).to eq 4 end context 'without a block' do it 'returns an iterator' do expect(result.each).to be_a(Enumerator) end it 'lazily evaluates' do skip 'JRuby has an issue with lazy enumerator evaluation' if RUBY_PLATFORM == 'java' result.each.with_index do |_el, idx| expect(result.instance_variable_get('@result_cache').size).to eq(idx + 1) # 0 indexing end end end end context 'lazy select' do it 'is compatible' do # This test will let us know when JRuby fixes lazy select so we can re-enable it in Result pending 'JRuby has an issue with lazy enumberator evaluation' if RUBY_PLATFORM == 'java' eval_count = 0 enum = %w[Text1 Text2 Text3].lazy.select do eval_count += 1 true end expect(eval_count).to eq 0 enum.next sleep 1 expect(eval_count).to eq 1 end end end capybara-3.12.0/spec/rspec_spec.rb0000644000175000017500000001101213411315075017515 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' RSpec.describe 'capybara/rspec' do context 'Feature', type: :feature do it 'should include Capybara in rspec' do visit('/foo') expect(page.body).to include('Another World') end it 'should include RSpec matcher proxies' do expect(self.class.ancestors).to include Capybara::RSpecMatcherProxies end context 'resetting session' do it 'sets a cookie in one example...' do visit('/set_cookie') expect(page.body).to include('Cookie set to test_cookie') end it '...then it is not available in the next' do visit('/get_cookie') expect(page.body).not_to include('test_cookie') end end context 'setting the current driver' do it 'sets the current driver in one example...' do Capybara.current_driver = :selenium end it '...then it has returned to the default in the next example' do expect(Capybara.current_driver).to eq(:rack_test) end end it 'switches to the javascript driver when giving it as metadata', js: true do expect(Capybara.current_driver).to eq(Capybara.javascript_driver) end it 'switches to the given driver when giving it as metadata', driver: :culerity do expect(Capybara.current_driver).to eq(:culerity) end context '#all' do it 'allows access to the Capybara finder' do visit('/with_html') found = all(:css, 'h2') { |element| element[:class] == 'head' } expect(found.size).to eq(5) end it 'allows access to the RSpec matcher' do visit('/with_html') strings = %w[test1 test2] expect(strings).to all(be_a(String)) end end context '#within' do it 'allows access to the Capybara scoper' do visit('/with_html') expect do within(:css, '#does_not_exist') { click_link 'Go to simple' } end.to raise_error(Capybara::ElementNotFound) end it 'allows access to the RSpec matcher' do visit('/with_html') # This reads terribly, but must call #within expect(find(:css, 'span.number').text.to_i).to within(1).of(41) end end end context 'Type: Other', type: :other do context 'when RSpec::Matchers is included after Capybara::DSL' do before do class DSLMatchersTest include Capybara::DSL include RSpec::Matchers end @test_class_instance = DSLMatchersTest.new end context '#all' do it 'allows access to the Capybara finder' do @test_class_instance.visit('/with_html') expect(@test_class_instance.all(:css, 'h2.head').size).to eq(5) end it 'allows access to the RSpec matcher' do @test_class_instance.visit('/with_html') strings = %w[test1 test2] expect(strings).to @test_class_instance.all(be_a(String)) end end context '#within' do it 'allows access to the Capybara scoper' do @test_class_instance.visit('/with_html') expect do @test_class_instance.within(:css, '#does_not_exist') { @test_class_instance.click_link 'Go to simple' } end.to raise_error(Capybara::ElementNotFound) end it 'allows access to the RSpec matcher' do @test_class_instance.visit('/with_html') # This reads terribly, but must call #within expect(@test_class_instance.find(:css, 'span.number').text.to_i).to @test_class_instance.within(1).of(41) end end context 'when `match_when_negated` is not defined in a matcher' do before do RSpec::Matchers.define :only_match_matcher do |expected| match do |actual| !(actual ^ expected) end end end it 'can be called with `not_to`' do # This test is for a bug in jruby where `super` isn't defined correctly - https://github.com/jruby/jruby/issues/4678 # Reported in https://github.com/teamcapybara/capybara/issues/2115 @test_class_instance.instance_eval do expect do expect(true).not_to only_match_matcher(false) # rubocop:disable RSpec/ExpectActual end.not_to raise_error end end end end it 'should not include Capybara' do expect { visit('/') }.to raise_error(NoMethodError) end end end feature 'Feature DSL' do scenario 'is pulled in' do visit('/foo') expect(page.body).to include('Another World') end end capybara-3.12.0/spec/selector_spec.rb0000644000175000017500000004510413411315075020232 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' RSpec.describe Capybara do describe 'Selectors' do let :string do Capybara.string <<-STRING selectors

Totally awesome

Yes it is

Some Content

Something
link
STRING end before do Capybara.add_selector :custom_selector do css { |css_class| "div.#{css_class}" } node_filter(:not_empty, boolean: true, default: true, skip_if: :all) { |node, value| value ^ (node.text == '') } end Capybara.add_selector :custom_css_selector do css(:name, :other_name) do |selector, name: nil, **| selector ||= '' selector += "[name='#{name}']" if name selector end expression_filter(:placeholder) do |expr, val| expr + "[placeholder='#{val}']" end expression_filter(:value) do |expr, val| expr + "[value='#{val}']" end expression_filter(:title) do |expr, val| expr + "[title='#{val}']" end end Capybara.add_selector :custom_xpath_selector do xpath(:valid1, :valid2) { |selector| selector } match { |value| value == 'match_me' } end end it 'supports `filter` as an alias for `node_filter`' do expect do Capybara.add_selector :filter_alias_selector do css { |_unused| 'div' } filter(:something) { |_node, _value| true } end end.not_to raise_error end describe 'adding a selector' do it 'can set default visiblity' do Capybara.add_selector :hidden_field do visible :hidden css { |_sel| 'input[type="hidden"]' } end expect(string).to have_no_css('input[type="hidden"]') expect(string).to have_selector(:hidden_field) end end describe 'modify_selector' do it 'allows modifying a selector' do el = string.find(:custom_selector, 'aa') expect(el.tag_name).to eq 'div' Capybara.modify_selector :custom_selector do css { |css_class| "h1.#{css_class}" } end el = string.find(:custom_selector, 'aa') expect(el.tag_name).to eq 'h1' end it "doesn't change existing filters" do Capybara.modify_selector :custom_selector do css { |css_class| "p.#{css_class}" } end expect(string).to have_selector(:custom_selector, 'bb', count: 1) expect(string).to have_selector(:custom_selector, 'bb', not_empty: false, count: 1) expect(string).to have_selector(:custom_selector, 'bb', not_empty: :all, count: 2) end end describe '::[]' do it 'can find a selector' do expect(Capybara::Selector[:field]).not_to be_nil end it 'raises if no selector found' do expect { Capybara::Selector[:no_exist] }.to raise_error(ArgumentError, /Unknown selector type/) end end describe '::for' do it 'finds selector that matches the locator' do expect(Capybara::Selector.for('match_me').name).to eq :custom_xpath_selector end it 'returns nil if no match' do expect(Capybara::Selector.for('nothing')).to be nil end end describe 'xpath' do it 'uses filter names passed in' do selector = Capybara::Selector.new :test do xpath(:something, :other) { |_locator| XPath.descendant } end expect(selector.expression_filters.keys).to include(:something, :other) end it 'gets filter names from block if none passed to xpath method' do selector = Capybara::Selector.new :test do xpath { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" } end expect(selector.expression_filters.keys).to include(:valid3, :valid4) end it 'ignores block parameters if names passed in' do selector = Capybara::Selector.new :test do xpath(:valid1) { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" } end expect(selector.expression_filters.keys).to include(:valid1) expect(selector.expression_filters.keys).not_to include(:valid3, :valid4) end end describe 'css' do it "supports filters specified in 'css' definition" do expect(string).to have_selector(:custom_css_selector, 'input', name: 'form[my_text_input]') expect(string).to have_no_selector(:custom_css_selector, 'input', name: 'form[not_my_text_input]') end it 'supports explicitly defined expression filters' do expect(string).to have_selector(:custom_css_selector, placeholder: 'my text') expect(string).to have_no_selector(:custom_css_selector, placeholder: 'not my text') expect(string).to have_selector(:custom_css_selector, value: 'click me', title: 'submit button') end it 'uses filter names passed in' do selector = Capybara::Selector.new :text do css(:name, :other_name) { |_locator| '' } end expect(selector.expression_filters.keys).to include(:name, :other_name) end it 'gets filter names from block if none passed to css method' do selector = Capybara::Selector.new :test do css { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" } end expect(selector.expression_filters.keys).to include(:valid3, :valid4) end it 'ignores block parameters if names passed in' do selector = Capybara::Selector.new :test do css(:valid1) { |_locator, valid3:, valid4: nil| "#{valid3} #{valid4}" } end expect(selector.expression_filters.keys).to include(:valid1) expect(selector.expression_filters.keys).not_to include(:valid3, :valid4) end end describe 'builtin selectors' do context 'when locator is nil' do it 'devolves to just finding element types' do selectors = { field: ".//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]", fieldset: './/fieldset', link: './/a[./@href]', link_or_button: ".//a[./@href] | .//input[./@type = 'submit' or ./@type = 'reset' or ./@type = 'image' or ./@type = 'button'] | .//button", fillable_field: ".//*[self::input | self::textarea][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'radio' or ./@type = 'checkbox' or ./@type = 'hidden' or ./@type = 'file')]", radio_button: ".//input[./@type = 'radio']", checkbox: ".//input[./@type = 'checkbox']", select: './/select', option: './/option', file_field: ".//input[./@type = 'file']", table: './/table' } selectors.each do |selector, xpath| results = string.all(selector, nil).to_a.map(&:native) expect(results.size).to be > 0 expect(results).to eq string.all(:xpath, xpath).to_a.map(&:native) end end end context 'with :id option' do it 'works with compound css selectors' do expect(string.all(:custom_css_selector, 'div, h1', id: 'page').size).to eq 1 expect(string.all(:custom_css_selector, 'h1, div', id: 'page').size).to eq 1 end it "works with 'special' characters" do expect(string.find(:custom_css_selector, 'div', id: '#special')[:id]).to eq '#special' expect(string.find(:custom_css_selector, 'input', id: '2checkbox')[:id]).to eq '2checkbox' end it 'accepts XPath expression for xpath based selectors' do expect(string.find(:custom_xpath_selector, './/div', id: XPath.contains('peci'))[:id]).to eq '#special' expect(string.find(:custom_xpath_selector, './/input', id: XPath.ends_with('box'))[:id]).to eq '2checkbox' end it 'errors XPath expression for CSS based selectors' do expect { string.find(:custom_css_selector, 'div', id: XPath.contains('peci')) } .to raise_error(ArgumentError, /not supported/) end it 'accepts Regexp for xpath based selectors' do expect(string.find(:custom_xpath_selector, './/div', id: /peci/)[:id]).to eq '#special' expect(string.find(:custom_xpath_selector, './/div', id: /pEcI/i)[:id]).to eq '#special' end it 'accepts Regexp for css based selectors' do expect(string.find(:custom_css_selector, 'div', id: /sp.*al/)[:id]).to eq '#special' end end context 'with :class option' do it 'works with compound css selectors' do expect(string.all(:custom_css_selector, 'div, h1', class: 'aa').size).to eq 2 expect(string.all(:custom_css_selector, 'h1, div', class: 'aa').size).to eq 2 end it 'handles negated classes' do expect(string.all(:custom_css_selector, 'div, p', class: ['bb', '!cc']).size).to eq 2 expect(string.all(:custom_css_selector, 'div, p', class: ['!cc', '!dd', 'bb']).size).to eq 1 expect(string.all(:custom_xpath_selector, XPath.descendant(:div, :p), class: ['bb', '!cc']).size).to eq 2 expect(string.all(:custom_xpath_selector, XPath.descendant(:div, :p), class: ['!cc', '!dd', 'bb']).size).to eq 1 end it 'handles classes starting with ! by requiring negated negated first' do expect(string.all(:custom_css_selector, 'div, p', class: ['!!!mine']).size).to eq 1 expect(string.all(:custom_xpath_selector, XPath.descendant(:div, :p), class: ['!!!mine']).size).to eq 1 end it "works with 'special' characters" do expect(string.find(:custom_css_selector, 'input', class: '.special')[:id]).to eq 'file' expect(string.find(:custom_css_selector, 'input', class: '2checkbox')[:id]).to eq '2checkbox' end it 'accepts XPath expression for xpath based selectors' do expect(string.find(:custom_xpath_selector, './/div', class: XPath.contains('dom wor'))[:id]).to eq 'random_words' expect(string.find(:custom_xpath_selector, './/div', class: XPath.ends_with('words'))[:id]).to eq 'random_words' end it 'errors XPath expression for CSS based selectors' do expect { string.find(:custom_css_selector, 'div', class: XPath.contains('random')) } .to raise_error(ArgumentError, /not supported/) end it 'accepts Regexp for XPath based selectors' do expect(string.find(:custom_xpath_selector, './/div', class: /dom wor/)[:id]).to eq 'random_words' expect(string.find(:custom_xpath_selector, './/div', class: /dOm WoR/i)[:id]).to eq 'random_words' end it 'accepts Regexp for CSS base selectors' do expect(string.find(:custom_css_selector, 'div', class: /random/)[:id]).to eq 'random_words' end end # :css, :xpath, :id, :field, :fieldset, :link, :button, :link_or_button, :fillable_field, :radio_button, :checkbox, :select, # :option, :file_field, :label, :table, :frame describe ':css selector' do it 'finds by CSS locator' do expect(string.find(:css, 'input#my_text_input')[:name]).to eq 'form[my_text_input]' end end describe ':xpath selector' do it 'finds by XPath locator' do expect(string.find(:xpath, './/input[@id="my_text_input"]')[:name]).to eq 'form[my_text_input]' end end describe ':id selector' do it 'finds by locator' do expect(string.find(:id, 'my_text_input')[:name]).to eq 'form[my_text_input]' expect(string.find(:id, /my_text_input/)[:name]).to eq 'form[my_text_input]' expect(string.find(:id, /_text_/)[:name]).to eq 'form[my_text_input]' expect(string.find(:id, /i[nmo]/)[:name]).to eq 'form[my_text_input]' end end describe ':field selector' do it 'finds by locator' do expect(string.find(:field, 'My Text Input')[:id]).to eq 'my_text_input' expect(string.find(:field, 'my_text_input')[:id]).to eq 'my_text_input' expect(string.find(:field, 'form[my_text_input]')[:id]).to eq 'my_text_input' end it 'finds by id string' do expect(string.find(:field, id: 'my_text_input')[:name]).to eq 'form[my_text_input]' end it 'finds by id regexp' do expect(string.find(:field, id: /my_text_inp/)[:name]).to eq 'form[my_text_input]' end it 'finds by name' do expect(string.find(:field, name: 'form[my_text_input]')[:id]).to eq 'my_text_input' end it 'finds by placeholder' do expect(string.find(:field, placeholder: 'my text')[:id]).to eq 'my_text_input' end it 'finds by type' do expect(string.find(:field, type: 'file')[:id]).to eq 'file' expect(string.find(:field, type: 'select')[:id]).to eq 'select' end end describe ':option selector' do it 'finds disabled options' do expect(string.find(:option, disabled: true).value).to eq 'b' end it 'finds selected options' do expect(string.find(:option, selected: true).value).to eq 'c' end it 'finds not selected and not disabled options' do expect(string.find(:option, disabled: false, selected: false).value).to eq 'a' end end describe ':button selector' do it 'finds by value' do expect(string.find(:button, 'click me').value).to eq 'click me' end it 'finds by title' do expect(string.find(:button, 'submit button').value).to eq 'click me' end it 'includes non-matching parameters in failure message' do expect { string.find(:button, 'click me', title: 'click me') }.to raise_error(/with title click me/) end end describe ':element selector' do it 'finds by any attributes' do expect(string.find(:element, 'input', type: 'submit').value).to eq 'click me' end it 'supports regexp matching' do expect(string.find(:element, 'input', type: /sub/).value).to eq 'click me' expect(string.find(:element, 'input', title: /sub\w.*button/).value).to eq 'click me' expect(string.find(:element, 'input', title: /sub.* b.*ton/).value).to eq 'click me' expect(string.find(:element, 'input', title: /sub.*mit.*/).value).to eq 'click me' expect(string.find(:element, 'input', title: /^submit button$/).value).to eq 'click me' expect(string.find(:element, 'input', title: /^(?:submit|other) button$/).value).to eq 'click me' expect(string.find(:element, 'input', title: /SuB.*mIt/i).value).to eq 'click me' expect(string.find(:element, 'input', title: /^Su.*Bm.*It/i).value).to eq 'click me' expect(string.find(:element, 'input', title: /^Ot.*he.*r b.*\d/i).value).to eq "don't click me" end it 'still works with system keys' do expect { string.all(:element, 'input', type: 'submit', count: 1) }.not_to raise_error end it 'works without element type' do expect(string.find(:element, type: 'submit').value).to eq 'click me' end it 'validates attribute presence when true' do expect(string.find(:element, name: true)[:id]).to eq 'my_text_input' end it 'validates attribute absence when false' do expect(string.find(:element, 'option', disabled: false, selected: false).value).to eq 'a' end it 'includes wildcarded keys in description' do expect { string.find(:element, 'input', not_there: 'bad', presence: true, absence: false, count: 1) } .to(raise_error do |e| expect(e).to be_a(Capybara::ElementNotFound) expect(e.message).to include 'not_there => bad' expect(e.message).to include 'with presence attribute' expect(e.message).to include 'without absence attribute' expect(e.message).not_to include 'count 1' end) end it 'accepts XPath::Expression' do expect(string.find(:element, 'input', type: XPath.starts_with('subm')).value).to eq 'click me' expect(string.find(:element, 'input', type: XPath.ends_with('ext'))[:type]).to eq 'text' expect(string.find(:element, 'input', type: XPath.contains('ckb'))[:type]).to eq 'checkbox' expect(string.find(:element, 'input', title: XPath.contains_word('submit'))[:type]).to eq 'submit' expect(string.find(:element, 'input', title: XPath.contains_word('button 1'))[:type]).to eq 'button' end end describe ':link_or_button selector' do around(:all) do |example| Capybara.modify_selector(:link_or_button) do expression_filter(:random) { |xpath, _| xpath } # do nothing filter end example.run Capybara::Selector[:link_or_button].expression_filters.delete(:random) end context 'when modified' do it 'should still work' do filter = Capybara::Selector[:link_or_button].expression_filters[:random] allow(filter).to receive(:apply_filter).and_call_original expect(string.find(:link_or_button, 'click me', random: 'blah').value).to eq 'click me' expect(filter).to have_received(:apply_filter).with(anything, :random, 'blah', anything) end end end end end end capybara-3.12.0/spec/selenium_spec_firefox_remote.rb0000644000175000017500000000677413411315075023342 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'selenium-webdriver' require 'shared_selenium_session' require 'rspec/shared_spec_matchers' def selenium_host ENV.fetch('SELENIUM_HOST', '0.0.0.0') end def selenium_port ENV.fetch('SELENIUM_PORT', 4445) end def ensure_selenium_running! timer = Capybara::Helpers.timer(expire_in: 20) begin TCPSocket.open(selenium_host, selenium_port) rescue StandardError if timer.expired? raise 'Selenium is not running. ' \ "You can run a selenium server easily with: \n" \ ' $ docker-compose up -d selenium_firefox' else puts 'Waiting for Selenium docker instance...' sleep 1 retry end end end Capybara.register_driver :selenium_firefox_remote do |app| ensure_selenium_running! url = "http://#{selenium_host}:#{selenium_port}/wd/hub" browser_options = ::Selenium::WebDriver::Firefox::Options.new Capybara::Selenium::Driver.new app, browser: :remote, desired_capabilities: :firefox, options: browser_options, url: url end FIREFOX_REMOTE_DRIVER = :selenium_firefox_remote module TestSessions RemoteFirefox = Capybara::Session.new(FIREFOX_REMOTE_DRIVER, TestApp) end TestSessions::RemoteFirefox.driver.browser.file_detector = lambda do |args| # args => ["/path/to/file"] str = args.first.to_s str if File.exist?(str) end skipped_tests = %i[response_headers status_code trigger download] Capybara::SpecHelper.run_specs TestSessions::RemoteFirefox, FIREFOX_REMOTE_DRIVER.to_s, capybara_skip: skipped_tests do |example| case example.metadata[:full_description] when 'Capybara::Session selenium_firefox_remote node #click should allow multiple modifiers' skip "Firefox doesn't generate an event for shift+control+click" if firefox_gte?(62, @session) when 'Capybara::Session selenium_firefox_remote #accept_prompt should accept the prompt with a blank response when there is a default' pending "Geckodriver doesn't set a blank response in FF < 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1486485" if firefox_lt?(63, @session) when 'Capybara::Session selenium_firefox_remote #attach_file with multipart form should fire change once for each set of files uploaded' pending 'Gekcodriver appends files so we have to first call clear for multiple files which creates an extra change ' \ 'if files are already set' when 'Capybara::Session selenium_firefox_remote #attach_file with multipart form should fire change once when uploading multiple files from empty' pending "FF < 62 doesn't support setting all files at once" if firefox_lt?(62, @session) when 'Capybara::Session selenium_firefox_remote #reset_session! removes ALL cookies' pending "Geckodriver doesn't provide a way to remove cookies outside the current domain" when /#accept_confirm should work with nested modals$/ # skip because this is timing based and hence flaky when set to pending skip 'Broken in FF 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1487358' if firefox_gte?(63, @session) end end RSpec.describe 'Capybara::Session with remote firefox' do include Capybara::SpecHelper include_examples 'Capybara::Session', TestSessions::RemoteFirefox, FIREFOX_REMOTE_DRIVER include_examples Capybara::RSpecMatchers, TestSessions::RemoteFirefox, FIREFOX_REMOTE_DRIVER it 'is considered to be firefox' do expect(session.driver.browser.browser).to eq :firefox end end capybara-3.12.0/spec/server_spec.rb0000644000175000017500000001711513411315075017721 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' RSpec.describe Capybara::Server do it 'should spool up a rack server' do @app = proc { |_env| [200, {}, ['Hello Server!']] } @server = Capybara::Server.new(@app).boot @res = Net::HTTP.start(@server.host, @server.port) { |http| http.get('/') } expect(@res.body).to include('Hello Server') end it 'should do nothing when no server given' do expect do @server = Capybara::Server.new(nil).boot end.not_to raise_error end it 'should bind to the specified host' do # TODO: travis with jruby in container mode has an issue with this test skip 'This platform has an issue with this test' if (ENV['TRAVIS'] && (RUBY_ENGINE == 'jruby')) || Gem.win_platform? begin app = proc { |_env| [200, {}, ['Hello Server!']] } Capybara.server_host = '127.0.0.1' server = Capybara::Server.new(app).boot res = Net::HTTP.get(URI("http://127.0.0.1:#{server.port}")) expect(res).to eq('Hello Server!') Capybara.server_host = '0.0.0.0' server = Capybara::Server.new(app).boot res = Net::HTTP.get(URI("http://127.0.0.1:#{server.port}")) expect(res).to eq('Hello Server!') ensure Capybara.server_host = nil end end it 'should use specified port' do Capybara.server_port = 22789 @app = proc { |_env| [200, {}, ['Hello Server!']] } @server = Capybara::Server.new(@app).boot @res = Net::HTTP.start(@server.host, 22789) { |http| http.get('/') } expect(@res.body).to include('Hello Server') Capybara.server_port = nil end it 'should use given port' do @app = proc { |_env| [200, {}, ['Hello Server!']] } @server = Capybara::Server.new(@app, port: 22790).boot @res = Net::HTTP.start(@server.host, 22790) { |http| http.get('/') } expect(@res.body).to include('Hello Server') Capybara.server_port = nil end it 'should find an available port' do @app1 = proc { |_env| [200, {}, ['Hello Server!']] } @app2 = proc { |_env| [200, {}, ['Hello Second Server!']] } @server1 = Capybara::Server.new(@app1).boot @server2 = Capybara::Server.new(@app2).boot @res1 = Net::HTTP.start(@server1.host, @server1.port) { |http| http.get('/') } expect(@res1.body).to include('Hello Server') @res2 = Net::HTTP.start(@server2.host, @server2.port) { |http| http.get('/') } expect(@res2.body).to include('Hello Second Server') end it 'should support SSL' do begin key = File.join(Dir.pwd, 'spec', 'fixtures', 'key.pem') cert = File.join(Dir.pwd, 'spec', 'fixtures', 'certificate.pem') Capybara.server = :puma, { Host: "ssl://#{Capybara.server_host}?key=#{key}&cert=#{cert}" } app = proc { |_env| [200, {}, ['Hello SSL Server!']] } server = Capybara::Server.new(app).boot expect do Net::HTTP.start(server.host, server.port) { |http| http.get('/__idntify__') } end.to raise_error(EOFError) res = Net::HTTP.start(server.host, server.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE) do |https| https.get('/') end expect(res.body).to include('Hello SSL Server!') ensure Capybara.server = :default end end context 'When Capybara.reuse_server is true' do before do @old_reuse_server = Capybara.reuse_server Capybara.reuse_server = true end after do Capybara.reuse_server = @old_reuse_server end it 'should use the existing server if it already running' do @app = proc { |_env| [200, {}, ['Hello Server!']] } @server1 = Capybara::Server.new(@app).boot @server2 = Capybara::Server.new(@app).boot res = Net::HTTP.start(@server1.host, @server1.port) { |http| http.get('/') } expect(res.body).to include('Hello Server') res = Net::HTTP.start(@server2.host, @server2.port) { |http| http.get('/') } expect(res.body).to include('Hello Server') expect(@server1.port).to eq(@server2.port) end it 'detects and waits for all reused server sessions pending requests' do done = 0 app = proc do |env| request = Rack::Request.new(env) sleep request.params['wait_time'].to_f done += 1 [200, {}, ['Hello Server!']] end server1 = Capybara::Server.new(app).boot server2 = Capybara::Server.new(app).boot expect do start_request(server1, 1.0) start_request(server2, 3.0) server1.wait_for_pending_requests end.to change { done }.from(0).to(2) expect(server2.send(:pending_requests?)).to eq(false) end end context 'When Capybara.reuse_server is false' do before do @old_reuse_server = Capybara.reuse_server Capybara.reuse_server = false end after do Capybara.reuse_server = @old_reuse_server end it 'should not reuse an already running server' do @app = proc { |_env| [200, {}, ['Hello Server!']] } @server1 = Capybara::Server.new(@app).boot @server2 = Capybara::Server.new(@app).boot res = Net::HTTP.start(@server1.host, @server1.port) { |http| http.get('/') } expect(res.body).to include('Hello Server') res = Net::HTTP.start(@server2.host, @server2.port) { |http| http.get('/') } expect(res.body).to include('Hello Server') expect(@server1.port).not_to eq(@server2.port) end it 'detects and waits for only one sessions pending requests' do done = 0 app = proc do |env| request = Rack::Request.new(env) sleep request.params['wait_time'].to_f done += 1 [200, {}, ['Hello Server!']] end server1 = Capybara::Server.new(app).boot server2 = Capybara::Server.new(app).boot expect do start_request(server1, 1.0) start_request(server2, 3.0) server1.wait_for_pending_requests end.to change { done }.from(0).to(1) expect(server2.send(:pending_requests?)).to eq(true) expect do server2.wait_for_pending_requests end.to change { done }.from(1).to(2) end end it 'should raise server errors when the server errors before the timeout' do begin Capybara.register_server :kaboom do sleep 0.1 raise 'kaboom' end Capybara.server = :kaboom expect do Capybara::Server.new(proc { |e| }).boot end.to raise_error(RuntimeError, 'kaboom') ensure Capybara.server = :default end end it 'is not #responsive? when Net::HTTP raises a SystemCallError' do app = -> { [200, {}, ['Hello, world']] } server = Capybara::Server.new(app) allow(Net::HTTP).to receive(:start).and_raise(SystemCallError.allocate) expect(server.responsive?).to eq false end [EOFError, Net::ReadTimeout].each do |err| it "should attempt an HTTPS connection if HTTP connection returns #{err}" do app = -> { [200, {}, ['Hello, world']] } ordered_errors = [Errno::ECONNREFUSED, err] allow(Net::HTTP).to receive(:start).with(anything, anything, hash_excluding(:use_ssl)) do raise ordered_errors.shift end response = Net::HTTPSuccess.allocate allow(response).to receive(:body).and_return app.object_id.to_s allow(Net::HTTP).to receive(:start).with(anything, anything, hash_including(use_ssl: true)).and_return(response).once Capybara::Server.new(app).boot expect(Net::HTTP).to have_received(:start).exactly(3).times end end def start_request(server, wait_time) # Start request, but don't wait for it to finish socket = TCPSocket.new(server.host, server.port) socket.write "GET /?wait_time=#{wait_time} HTTP/1.0\r\n\r\n" sleep 0.1 socket.close sleep 0.1 end end capybara-3.12.0/spec/basic_node_spec.rb0000644000175000017500000001104013411315075020470 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' RSpec.describe Capybara do describe '.string' do let :string do Capybara.string <<-STRING simple_node not document title

Totally awesome

Yes it is

STRING end it 'allows using matchers' do expect(string).to have_css('#page') expect(string).not_to have_css('#does-not-exist') end it 'allows using custom matchers' do Capybara.add_selector :lifeform do xpath { |name| ".//option[contains(.,'#{name}')]" } end expect(string).to have_selector(:id, 'page') expect(string).not_to have_selector(:id, 'does-not-exist') expect(string).to have_selector(:lifeform, 'Monkey') expect(string).not_to have_selector(:lifeform, 'Gorilla') end it 'allows custom matcher using css' do Capybara.add_selector :section do css { |css_class| "section .#{css_class}" } end expect(string).to have_selector(:section, 'subsection') expect(string).not_to have_selector(:section, 'section_8') end it 'allows using matchers with text option' do expect(string).to have_css('h1', text: 'Totally awesome') expect(string).not_to have_css('h1', text: 'Not so awesome') end it 'allows finding only visible nodes' do expect(string.all(:css, '#secret', visible: true)).to be_empty expect(string.all(:css, '#secret', visible: false).size).to eq(1) end it 'allows finding elements and extracting text from them' do expect(string.find('//h1').text).to eq('Totally awesome') end it 'allows finding elements and extracting attributes from them' do expect(string.find('//h1')[:data]).to eq('fantastic') end it 'allows finding elements and extracting the tag name from them' do expect(string.find('//h1').tag_name).to eq('h1') end it 'allows finding elements and extracting the path' do expect(string.find('//h1').path).to eq('/html/body/div/div[1]/h1') end it 'allows finding elements and extracting the value' do expect(string.find('//div/input').value).to eq('bar') expect(string.find('//select').value).to eq('Capybara') end it 'allows finding elements and checking if they are visible' do expect(string.find('//h1')).to be_visible expect(string.find(:css, '#secret', visible: false)).not_to be_visible end it 'allows finding elements and checking if they are disabled' do expect(string.find('//form/input[@name="bleh"]')).to be_disabled expect(string.find('//form/input[@name="meh"]')).not_to be_disabled end describe '#title' do it 'returns the page title' do expect(string.title).to eq('simple_node') end end describe '#has_title?' do it 'returns whether the page has the given title' do expect(string.has_title?('simple_node')).to be_truthy expect(string.has_title?('monkey')).to be_falsey end it 'allows regexp matches' do expect(string.has_title?(/s[a-z]+_node/)).to be_truthy expect(string.has_title?(/monkey/)).to be_falsey end end describe '#has_no_title?' do it 'returns whether the page does not have the given title' do expect(string.has_no_title?('simple_node')).to be_falsey expect(string.has_no_title?('monkey')).to be_truthy end it 'allows regexp matches' do expect(string.has_no_title?(/s[a-z]+_node/)).to be_falsey expect(string.has_no_title?(/monkey/)).to be_truthy end end end end capybara-3.12.0/spec/rspec/0000755000175000017500000000000013411315075016163 5ustar utkarsh2102utkarsh2102capybara-3.12.0/spec/rspec/shared_spec_matchers.rb0000644000175000017500000007711713411315075022673 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'capybara/dsl' require 'capybara/rspec/matchers' require 'benchmark' # rubocop:disable RSpec/ExpectActual RSpec.shared_examples Capybara::RSpecMatchers do |session, _mode| include Capybara::DSL include Capybara::RSpecMatchers describe 'have_css matcher' do it 'gives proper description' do expect(have_css('h1').description).to eq('have visible css "h1"') end context 'on a string' do context 'with should' do it 'passes if has_css? returns true' do expect('

Text

').to have_css('h1') end it 'fails if has_css? returns false' do expect do expect('

Text

').to have_css('h2') end.to raise_error(/expected to find css "h2" but there were no matches/) end it 'passes if matched node count equals expected count' do expect('

Text

').to have_css('h1', count: 1) end it 'fails if matched node count does not equal expected count' do expect do expect('

Text

').to have_css('h1', count: 2) end.to raise_error('expected to find visible css "h1" 2 times, found 1 match: "Text"') end it 'fails if matched node count is less than expected minimum count' do expect do expect('

Text

').to have_css('p', minimum: 1) end.to raise_error('expected to find css "p" at least 1 time but there were no matches') end it 'fails if matched node count is more than expected maximum count' do expect do expect('

Text

Text

Text

').to have_css('h1', maximum: 2) end.to raise_error('expected to find visible css "h1" at most 2 times, found 3 matches: "Text", "Text", "Text"') end it 'fails if matched node count does not belong to expected range' do expect do expect('

Text

').to have_css('h1', between: 2..3) end.to raise_error('expected to find visible css "h1" between 2 and 3 times, found 1 match: "Text"') end end context 'with should_not' do it 'passes if has_no_css? returns true' do expect('

Text

').not_to have_css('h2') end it 'fails if has_no_css? returns false' do expect do expect('

Text

').not_to have_css('h1') end.to raise_error(/expected not to find visible css "h1"/) end it 'passes if matched node count does not equal expected count' do expect('

Text

').not_to have_css('h1', count: 2) end it 'fails if matched node count equals expected count' do expect do expect('

Text

').not_to have_css('h1', count: 1) end.to raise_error(/expected not to find visible css "h1"/) end end it 'supports compounding' do expect('

Text

Text

').to have_css('h1').and have_css('h2') expect('

Text

Text

').to have_css('h3').or have_css('h1') expect('

Text

Text

').to have_no_css('h4').and have_css('h2') expect('

Text

Text

').to have_no_css('h2').or have_css('h1') end end context 'on a page or node' do before do visit('/with_html') end context 'with should' do it 'passes if has_css? returns true' do expect(page).to have_css('h1') end it 'fails if has_css? returns false' do expect do expect(page).to have_css('h1#doesnotexist') end.to raise_error(/expected to find css "h1#doesnotexist" but there were no matches/) end end context 'with should_not' do it 'passes if has_no_css? returns true' do expect(page).not_to have_css('h1#doesnotexist') end it 'fails if has_no_css? returns false' do expect do expect(page).not_to have_css('h1') end.to raise_error(/expected not to find visible css "h1"/) end end end end describe 'have_xpath matcher' do it 'gives proper description' do expect(have_xpath('//h1').description).to eq("have visible xpath \"\/\/h1\"") end context 'on a string' do context 'with should' do it 'passes if has_xpath? returns true' do expect('

Text

').to have_xpath('//h1') end it 'fails if has_xpath? returns false' do expect do expect('

Text

').to have_xpath('//h2') end.to raise_error(%r{expected to find xpath "//h2" but there were no matches}) end end context 'with should_not' do it 'passes if has_no_xpath? returns true' do expect('

Text

').not_to have_xpath('//h2') end it 'fails if has_no_xpath? returns false' do expect do expect('

Text

').not_to have_xpath('//h1') end.to raise_error(%r{expected not to find visible xpath "//h1"}) end end it 'supports compounding' do expect('

Text

Text

').to have_xpath('//h1').and have_xpath('//h2') expect('

Text

Text

').to have_xpath('//h3').or have_xpath('//h1') expect('

Text

Text

').to have_no_xpath('//h4').and have_xpath('//h1') expect('

Text

Text

').to have_no_xpath('//h4').or have_xpath('//h4') end end context 'on a page or node' do before do visit('/with_html') end context 'with should' do it 'passes if has_xpath? returns true' do expect(page).to have_xpath('//h1') end it 'fails if has_xpath? returns false' do expect do expect(page).to have_xpath("//h1[@id='doesnotexist']") end.to raise_error(%r{expected to find xpath "//h1\[@id='doesnotexist'\]" but there were no matches}) end end context 'with should_not' do it 'passes if has_no_xpath? returns true' do expect(page).not_to have_xpath('//h1[@id="doesnotexist"]') end it 'fails if has_no_xpath? returns false' do expect do expect(page).not_to have_xpath('//h1') end.to raise_error(%r{expected not to find visible xpath "//h1"}) end end end end describe 'have_selector matcher' do it 'gives proper description' do matcher = have_selector('//h1') expect('

Text

').to matcher expect(matcher.description).to eq('have visible xpath "//h1"') end context 'on a string' do context 'with should' do it 'passes if has_selector? returns true' do expect('

Text

').to have_selector('//h1') end it 'fails if has_selector? returns false' do expect do expect('

Text

').to have_selector('//h2') end.to raise_error(%r{expected to find xpath "//h2" but there were no matches}) end end context 'with should_not' do it 'passes if has_no_selector? returns true' do expect('

Text

').not_to have_selector(:css, 'h2') end it 'fails if has_no_selector? returns false' do expect do expect('

Text

').not_to have_selector(:css, 'h1') end.to raise_error(/expected not to find visible css "h1"/) end end end context 'on a page or node' do before do visit('/with_html') end context 'with should' do it 'passes if has_selector? returns true' do expect(page).to have_selector('//h1', text: 'test') end it 'fails if has_selector? returns false' do expect do expect(page).to have_selector("//h1[@id='doesnotexist']") end.to raise_error(%r{expected to find xpath "//h1\[@id='doesnotexist'\]" but there were no matches}) end it 'includes text in error message' do expect do expect(page).to have_selector('//h1', text: 'wrong text') end.to raise_error(%r{expected to find visible xpath "//h1" with text "wrong text" but there were no matches}) end end context 'with should_not' do it 'passes if has_no_css? returns true' do expect(page).not_to have_selector(:css, 'h1#doesnotexist') end it 'fails if has_no_selector? returns false' do expect do expect(page).not_to have_selector(:css, 'h1', text: 'test') end.to raise_error(/expected not to find visible css "h1" with text "test"/) end end end it 'supports compounding' do expect('

Text

Text

').to have_selector('//h1').and have_selector('//h2') expect('

Text

Text

').to have_selector('//h3').or have_selector('//h1') expect('

Text

Text

').to have_no_selector('//h3').and have_selector('//h1') end end describe 'have_content matcher' do it 'gives proper description' do expect(have_content('Text').description).to eq('text "Text"') end context 'on a string' do context 'with should' do it 'passes if has_content? returns true' do expect('

Text

').to have_content('Text') end it 'passes if has_content? returns true using regexp' do expect('

Text

').to have_content(/ext/) end it 'fails if has_content? returns false' do expect do expect('

Text

').to have_content('No such Text') end.to raise_error(/expected to find text "No such Text" in "Text"/) end end context 'with should_not' do it 'passes if has_no_content? returns true' do expect('

Text

').not_to have_content('No such Text') end it 'passes because escapes any characters that would have special meaning in a regexp' do expect('

Text

').not_to have_content('.') end it 'fails if has_no_content? returns false' do expect do expect('

Text

').not_to have_content('Text') end.to raise_error(/expected not to find text "Text" in "Text"/) end end end context 'on a page or node' do before do visit('/with_html') end context 'with should' do it 'passes if has_content? returns true' do expect(page).to have_content('This is a test') end it 'passes if has_content? returns true using regexp' do expect(page).to have_content(/test/) end it 'fails if has_content? returns false' do expect do expect(page).to have_content('No such Text') end.to raise_error(/expected to find text "No such Text" in "(.*)This is a test(.*)"/) end context 'with default selector CSS' do before { Capybara.default_selector = :css } after { Capybara.default_selector = :xpath } it 'fails if has_content? returns false' do expect do expect(page).to have_content('No such Text') end.to raise_error(/expected to find text "No such Text" in "(.*)This is a test(.*)"/) end end end context 'with should_not' do it 'passes if has_no_content? returns true' do expect(page).not_to have_content('No such Text') end it 'fails if has_no_content? returns false' do expect do expect(page).not_to have_content('This is a test') end.to raise_error(/expected not to find text "This is a test"/) end end end it 'supports compounding' do expect('

Text

And

').to have_content('Text').and have_content('And') expect('

Text

Or

').to have_content('XYZ').or have_content('Or') expect('

Text

Or

').to have_no_content('XYZ').and have_content('Or') end end describe 'have_text matcher' do it 'gives proper description' do expect(have_text('Text').description).to eq('text "Text"') end context 'on a string' do context 'with should' do it 'passes if text contains given string' do expect('

Text

').to have_text('Text') end it 'passes if text matches given regexp' do expect('

Text

').to have_text(/ext/) end it "fails if text doesn't contain given string" do expect do expect('

Text

').to have_text('No such Text') end.to raise_error(/expected to find text "No such Text" in "Text"/) end it "fails if text doesn't match given regexp" do expect do expect('

Text

').to have_text(/No such Text/) end.to raise_error('expected to find text matching /No such Text/ in "Text"') end it 'casts Integer to string' do expect do expect('

Text

').to have_text(3) end.to raise_error(/expected to find text "3" in "Text"/) end it 'fails if matched text count does not equal to expected count' do expect do expect('

Text

').to have_text('Text', count: 2) end.to raise_error('expected to find text "Text" 2 times but found 1 time in "Text"') end it 'fails if matched text count is less than expected minimum count' do expect do expect('

Text

').to have_text('Lorem', minimum: 1) end.to raise_error('expected to find text "Lorem" at least 1 time but found 0 times in "Text"') end it 'fails if matched text count is more than expected maximum count' do expect do expect('

Text TextText

').to have_text('Text', maximum: 2) end.to raise_error('expected to find text "Text" at most 2 times but found 3 times in "Text TextText"') end it 'fails if matched text count does not belong to expected range' do expect do expect('

Text

').to have_text('Text', between: 2..3) end.to raise_error('expected to find text "Text" between 2 and 3 times but found 1 time in "Text"') end end context 'with should_not' do it "passes if text doesn't contain a string" do expect('

Text

').not_to have_text('No such Text') end it 'passes because escapes any characters that would have special meaning in a regexp' do expect('

Text

').not_to have_text('.') end it 'fails if text contains a string' do expect do expect('

Text

').not_to have_text('Text') end.to raise_error(/expected not to find text "Text" in "Text"/) end end end context 'on a page or node' do before do visit('/with_html') end context 'with should' do it 'passes if has_text? returns true' do expect(page).to have_text('This is a test') end it 'passes if has_text? returns true using regexp' do expect(page).to have_text(/test/) end it 'can check for all text' do expect(page).to have_text(:all, 'Some of this text is hidden!') end it 'can check for visible text' do expect(page).to have_text(:visible, 'Some of this text is') expect(page).not_to have_text(:visible, 'Some of this text is hidden!') end it 'fails if has_text? returns false' do expect do expect(page).to have_text('No such Text') end.to raise_error(/expected to find text "No such Text" in "(.*)This is a test(.*)"/) end context 'with default selector CSS' do before { Capybara.default_selector = :css } after { Capybara.default_selector = :xpath } it 'fails if has_text? returns false' do expect do expect(page).to have_text('No such Text') end.to raise_error(/expected to find text "No such Text" in "(.*)This is a test(.*)"/) end end end context 'with should_not' do it 'passes if has_no_text? returns true' do expect(page).not_to have_text('No such Text') end it 'fails if has_no_text? returns false' do expect do expect(page).not_to have_text('This is a test') end.to raise_error(/expected not to find text "This is a test"/) end end end it 'supports compounding' do expect('

Text

And

').to have_text('Text').and have_text('And') expect('

Text

Or

').to have_text('Not here').or have_text('Or') end end describe 'have_link matcher' do let(:html) { 'Just a linkAnother link' } it 'gives proper description' do expect(have_link('Just a link').description).to eq('have visible link "Just a link"') end it 'passes if there is such a link' do expect(html).to have_link('Just a link') end it 'fails if there is no such link' do expect do expect(html).to have_link('No such Link') end.to raise_error(/expected to find link "No such Link"/) end it 'supports compounding' do expect(html).to have_link('Just a link').and have_link('Another link') expect(html).to have_link('Not a link').or have_link('Another link') expect(html).to have_no_link('Not a link').and have_link('Another link') end end describe 'have_title matcher' do it 'gives proper description' do expect(have_title('Just a title').description).to eq('have title "Just a title"') end context 'on a string' do let(:html) { 'Just a title' } it 'passes if there is such a title' do expect(html).to have_title('Just a title') end it 'fails if there is no such title' do expect do expect(html).to have_title('No such title') end.to raise_error('expected "Just a title" to include "No such title"') end it "fails if title doesn't match regexp" do expect do expect(html).to have_title(/[[:upper:]]+[[:lower:]]+l{2}o/) end.to raise_error('expected "Just a title" to match /[[:upper:]]+[[:lower:]]+l{2}o/') end end context 'on a page or node' do it 'passes if there is such a title' do visit('/with_js') expect(page).to have_title('with_js') end it 'fails if there is no such title' do visit('/with_js') expect do expect(page).to have_title('No such title') end.to raise_error('expected "with_js" to include "No such title"') end context 'with wait' do before do @session = session @session.visit('/with_js') end it 'waits if wait time is more than timeout' do @session.click_link('Change title') using_wait_time 0 do expect(@session).to have_title('changed title', wait: 2) end end it "doesn't wait if wait time is less than timeout" do @session.click_link('Change title') using_wait_time 3 do expect(@session).not_to have_title('changed title', wait: 0) end end end end it 'supports compounding' do expect('I compound').to have_title('I dont compound').or have_title('I compound') end end describe 'have_current_path matcher' do it 'gives proper description' do expect(have_current_path('http://www.example.com').description).to eq('have current path "http://www.example.com"') end context 'on a page or node' do it 'passes if there is such a current path' do visit('/with_js') expect(page).to have_current_path('/with_js') end it 'fails if there is no such current_path' do visit('/with_js') expect do expect(page).to have_current_path('/not_with_js') end.to raise_error('expected "/with_js" to equal "/not_with_js"') end context 'with wait' do before do @session = session @session.visit('/with_js') end it 'waits if wait time is more than timeout' do @session.click_link('Change page') using_wait_time 0 do expect(@session).to have_current_path('/with_html', wait: 2) end end it "doesn't wait if wait time is less than timeout" do @session.click_link('Change page') using_wait_time 0 do expect(@session).not_to have_current_path('/with_html') end end end end it 'supports compounding' do visit('/with_html') expect(page).to have_current_path('/not_with_html').or have_current_path('/with_html') end end describe 'have_button matcher' do let(:html) { '' } it 'gives proper description' do expect(have_button('A button').description).to eq('have visible button "A button"') end it 'passes if there is such a button' do expect(html).to have_button('A button') end it 'fails if there is no such button' do expect do expect(html).to have_button('No such Button') end.to raise_error(/expected to find button "No such Button"/) end it 'supports compounding' do expect(html).to have_button('Not this button').or have_button('A button') end end describe 'have_field matcher' do let(:html) { '

' } it 'gives proper description' do expect(have_field('Text field').description).to eq('have visible field "Text field" that is not disabled') end it 'gives proper description for a given value' do expect(have_field('Text field', with: 'some value').description).to eq('have visible field "Text field" that is not disabled with value "some value"') end it 'passes if there is such a field' do expect(html).to have_field('Text field') end it 'passes if there is such a field with value' do expect(html).to have_field('Text field', with: 'some value') end it 'fails if there is no such field' do expect do expect(html).to have_field('No such Field') end.to raise_error(/expected to find field "No such Field"/) end it 'fails if there is such field but with false value' do expect do expect(html).to have_field('Text field', with: 'false value') end.to raise_error(/expected to find visible field "Text field"/) end it 'treats a given value as a string' do class Foo def to_s 'some value' end end expect(html).to have_field('Text field', with: Foo.new) end it 'supports compounding' do expect(html).to have_field('Not this one').or have_field('Text field') end end describe 'have_checked_field matcher' do let(:html) do ' ' end it 'gives proper description' do expect(have_checked_field('it is checked').description).to eq('have visible field "it is checked" that is checked and not disabled') end context 'with should' do it 'passes if there is such a field and it is checked' do expect(html).to have_checked_field('it is checked') end it 'fails if there is such a field but it is not checked' do expect do expect(html).to have_checked_field('unchecked field') end.to raise_error(/expected to find visible field "unchecked field"/) end it 'fails if there is no such field' do expect do expect(html).to have_checked_field('no such field') end.to raise_error(/expected to find field "no such field"/) end end context 'with should not' do it 'fails if there is such a field and it is checked' do expect do expect(html).not_to have_checked_field('it is checked') end.to raise_error(/expected not to find visible field "it is checked"/) end it 'passes if there is such a field but it is not checked' do expect(html).not_to have_checked_field('unchecked field') end it 'passes if there is no such field' do expect(html).not_to have_checked_field('no such field') end end it 'supports compounding' do expect(html).to have_checked_field('not this one').or have_checked_field('it is checked') end end describe 'have_unchecked_field matcher' do let(:html) do ' ' end it 'gives proper description' do expect(have_unchecked_field('unchecked field').description).to eq('have visible field "unchecked field" that is not checked and not disabled') end context 'with should' do it 'passes if there is such a field and it is not checked' do expect(html).to have_unchecked_field('unchecked field') end it 'fails if there is such a field but it is checked' do expect do expect(html).to have_unchecked_field('it is checked') end.to raise_error(/expected to find visible field "it is checked"/) end it 'fails if there is no such field' do expect do expect(html).to have_unchecked_field('no such field') end.to raise_error(/expected to find field "no such field"/) end end context 'with should not' do it 'fails if there is such a field and it is not checked' do expect do expect(html).not_to have_unchecked_field('unchecked field') end.to raise_error(/expected not to find visible field "unchecked field"/) end it 'passes if there is such a field but it is checked' do expect(html).not_to have_unchecked_field('it is checked') end it 'passes if there is no such field' do expect(html).not_to have_unchecked_field('no such field') end end it 'supports compounding' do expect(html).to have_unchecked_field('it is checked').or have_unchecked_field('unchecked field') end end describe 'have_select matcher' do let(:html) { '' } it 'gives proper description' do expect(have_select('Select Box').description).to eq('have visible select box "Select Box" that is not disabled') end it 'gives proper description for a given selected value' do expect(have_select('Select Box', selected: 'some value').description).to eq('have visible select box "Select Box" that is not disabled with "some value" selected') end it 'passes if there is such a select' do expect(html).to have_select('Select Box') end it 'fails if there is no such select' do expect do expect(html).to have_select('No such Select box') end.to raise_error(/expected to find select box "No such Select box"/) end it 'supports compounding' do expect(html).to have_select('Not this one').or have_select('Select Box') end end describe 'have_table matcher' do let(:html) { '
Lovely table
' } it 'gives proper description' do expect(have_table('Lovely table').description).to eq('have visible table "Lovely table"') expect(have_table('Lovely table', caption: 'my caption').description).to eq('have visible table "Lovely table" with caption "my caption"') end it 'gives proper description when :visible option passed' do expect(have_table('Lovely table', visible: true).description).to eq('have visible table "Lovely table"') expect(have_table('Lovely table', visible: :hidden).description).to eq('have non-visible table "Lovely table"') expect(have_table('Lovely table', visible: :all).description).to eq('have table "Lovely table"') expect(have_table('Lovely table', visible: false).description).to eq('have table "Lovely table"') end it 'passes if there is such a table' do expect(html).to have_table('Lovely table') end it 'fails if there is no such table' do expect do expect(html).to have_table('No such Table') end.to raise_error(/expected to find table "No such Table"/) end it 'supports compounding' do expect(html).to have_table('nope').or have_table('Lovely table') end end context 'compounding timing' do before do @session = session @session.visit('/with_js') @el = @session.find(:css, '#reload-me') end context '#and' do it "should run 'concurrently'" do Capybara.using_wait_time(2) do matcher = have_text('this is not there').and have_text('neither is this') expect(Benchmark.realtime do expect do expect(@el).to matcher end.to raise_error RSpec::Expectations::ExpectationNotMetError end).to be_between(2, 3) end end it "should run 'concurrently' and retry" do @session.click_link('reload-link') @session.using_wait_time(2) do expect(Benchmark.realtime do expect do expect(@el).to have_text('waiting to be reloaded').and(have_text('has been reloaded')) end.to raise_error RSpec::Expectations::ExpectationNotMetError, /expected to find text "waiting to be reloaded" in "has been reloaded"/ end).to be_between(2, 3) end end it 'should ignore :wait options' do @session.using_wait_time(2) do matcher = have_text('this is not there', wait: 5).and have_text('neither is this', wait: 6) expect(Benchmark.realtime do expect do expect(@el).to matcher end.to raise_error RSpec::Expectations::ExpectationNotMetError end).to be_between(2, 3) end end it 'should work on the session' do @session.using_wait_time(2) do @session.click_link('reload-link') expect(@session).to have_selector(:css, 'h1', text: 'FooBar').and have_text('has been reloaded') end end end context '#and_then' do it 'should run sequentially' do @session.click_link('reload-link') expect(@el).to have_text('waiting to be reloaded').and_then have_text('has been reloaded') end end context '#or' do it "should run 'concurrently'" do @session.using_wait_time(3) do expect(Benchmark.realtime do expect(@el).to have_text('has been reloaded').or have_text('waiting to be reloaded') end).to be < 1 end end it 'should retry' do @session.using_wait_time(3) do expect(Benchmark.realtime do expect do expect(@el).to have_text('has been reloaded').or have_text('random stuff') end.to raise_error RSpec::Expectations::ExpectationNotMetError end).to be > 3 end end it 'should ignore :wait options' do @session.using_wait_time(2) do expect(Benchmark.realtime do expect do expect(@el).to have_text('this is not there', wait: 10).or have_text('neither is this', wait: 15) end.to raise_error RSpec::Expectations::ExpectationNotMetError end).to be_between(2, 3) end end it 'should work on the session' do @session.using_wait_time(2) do @session.click_link('reload-link') expect(@session).to have_selector(:css, 'h1', text: 'Not on the page').or have_text('has been reloaded') end end end end end # rubocop:enable RSpec/ExpectActual capybara-3.12.0/spec/rspec/scenarios_spec.rb0000644000175000017500000000102513411315075021506 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'capybara/rspec' RSpec.configuration.before(:each, file_path: './spec/rspec/scenarios_spec.rb') do @in_filtered_hook = true end feature 'if fscenario aliases focused tag then' do fscenario 'scenario should have focused meta tag' do |example| # rubocop:disable RSpec/Focus expect(example.metadata[:focus]).to eq true end end feature 'if xscenario aliases to pending then' do xscenario "this test should be 'temporarily disabled with xscenario'" do end end capybara-3.12.0/spec/rspec/views_spec.rb0000644000175000017500000000057713411315075020670 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' RSpec.describe 'capybara/rspec', type: :view do it 'allows matchers to be used on strings' do html = %(

Test header

) expect(html).to have_css('h1', text: 'Test header') end it "doesn't include RSpecMatcherProxies" do expect(self.class.ancestors).not_to include(Capybara::RSpecMatcherProxies) end end capybara-3.12.0/spec/rspec/features_spec.rb0000644000175000017500000000470713411315075021350 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'capybara/rspec' RSpec.configuration.before(:each, file_path: './spec/rspec/features_spec.rb') do @in_filtered_hook = true end feature "Capybara's feature DSL" do background do @in_background = true end scenario 'includes Capybara' do visit('/') expect(page).to have_content('Hello world!') end scenario 'preserves description' do |ex| expect(ex.metadata[:full_description]) .to eq("Capybara's feature DSL preserves description") end scenario 'allows driver switching', driver: :selenium do expect(Capybara.current_driver).to eq(:selenium) end scenario 'runs background' do expect(@in_background).to be_truthy end scenario 'runs hooks filtered by file path' do expect(@in_filtered_hook).to be_truthy end scenario "doesn't pollute the Object namespace" do expect(Object.new).not_to respond_to(:feature) end feature 'nested features' do scenario 'work as expected' do visit '/' expect(page).to have_content 'Hello world!' end scenario 'are marked in the metadata as capybara_feature' do |ex| expect(ex.metadata[:capybara_feature]).to be_truthy end scenario 'have a type of :feature' do |ex| expect(ex.metadata[:type]).to eq :feature end end end feature 'given and given! aliases to let and let!' do given(:value) { :available } given!(:value_in_background) { :available } background do expect(value_in_background).to be(:available) end scenario 'given and given! work as intended' do expect(value).to be(:available) expect(value_in_background).to be(:available) end end feature "Capybara's feature DSL with driver", driver: :culerity do scenario 'switches driver' do expect(Capybara.current_driver).to eq(:culerity) end end # rubocop:disable RSpec/RepeatedExample xfeature 'if xfeature aliases to pending then' do scenario "this should be 'temporarily disabled with xfeature'" do # dummy end scenario "this also should be 'temporarily disabled with xfeature'" do # dummy end end ffeature 'if ffeature aliases focused tag then' do # rubocop:disable RSpec/Focus scenario 'scenario inside this feature has metatag focus tag' do |example| expect(example.metadata[:focus]).to eq true end scenario 'other scenarios also has metatag focus tag ' do |example| expect(example.metadata[:focus]).to eq true end end # rubocop:enable RSpec/RepeatedExample capybara-3.12.0/spec/regexp_dissassembler_spec.rb0000644000175000017500000001441313411315075022623 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' RSpec.describe Capybara::Selector::RegexpDisassembler do it 'handles strings' do verify_strings( /abcdef/ => %w[abcdef], /abc def/ => ['abc def'] ) end it 'handles escaped characters' do verify_strings( /abc\\def/ => %w[abc\def], /abc\.def/ => %w[abc.def], /\nabc/ => ["\nabc"], %r{abc/} => %w[abc/], /ab\++cd/ => %w[ab+ cd] ) end it 'handles wildcards' do verify_strings( /abc.*def/ => %w[abc def], /.*def/ => %w[def], /abc./ => %w[abc], /abc.*/ => %w[abc], /abc.def/ => %w[abc def], /abc.def.ghi/ => %w[abc def ghi], /abc.abcd.abcde/ => %w[abcde], /.*/ => [] ) end it 'ignores optional characters for substrings' do { /abc*def/ => %w[ab def], /abc*/ => %w[ab], /c*/ => [], /abc?def/ => %w[ab def], /abc?/ => %w[ab], /abc?def?/ => %w[ab de], /abc?def?g/ => %w[ab de g], /d?/ => [] }.each do |regexp, expected| expect(Capybara::Selector::RegexpDisassembler.new(regexp).substrings).to eq expected end end it 'handles optional characters for #alternated_substrings' do verify_alternated_strings( /abc*def/ => [%w[ab def]], /abc*/ => [%w[ab]], /c*/ => [], /abc?def/ => [%w[abdef], %w[abcdef]], /abc?/ => [%w[ab]], /abc?def?/ => [%w[abde], %w[abcde]], /abc?def?g/ => [%w[abdeg], %w[abdefg], %w[abcdeg], %w[abcdefg]], /d?/ => [] ) end it 'handles character classes' do verify_strings( /abc[a-z]/ => %w[abc], /abc[a-z]def[0-9]g/ => %w[abc def g], /[0-9]abc/ => %w[abc], /[0-9]+/ => [], /abc[0-9&&[^7]]/ => %w[abc] ) end it 'handles posix bracket expressions' do verify_strings( /abc[[:alpha:]]/ => %w[abc], /[[:digit:]]abc/ => %w[abc], /abc[[:print:]]def/ => %w[abc def] ) end it 'handles repitition' do verify_strings( /abc{3}/ => %w[abccc], /abc{3}d/ => %w[abcccd], /abc{0}/ => %w[ab], /abc{,2}/ => %w[ab], /abc{2,}/ => %w[abcc], /def{1,5}/ => %w[def], /abc+def/ => %w[abc def], /ab(cde){,4}/ => %w[ab], /(ab){,2}cd/ => %w[cd], /(abc){2,3}/ => %w[abcabc], /(abc){3}/ => %w[abcabcabc], /ab{2,3}cd/ => %w[abb cd], /(ab){2,3}cd/ => %w[abab cd] ) end it 'handles non-greedy repetition' do verify_strings( /abc.*?/ => %w[abc], /abc+?/ => %w[abc], /abc*?cde/ => %w[ab cde], /(abc)+?def/ => %w[abc def], /ab(cde)*?fg/ => %w[ab fg] ) end it 'ignores alternation for #substrings' do { /abc|def/ => [], /ab(?:c|d)/ => %w[ab], /ab(c|d|e)fg/ => %w[ab fg], /ab?(c|d)fg/ => %w[a fg], /ab(c|d)ef/ => %w[ab ef], /ab(cd?|ef)g/ => %w[ab g], /ab(cd|ef*)g/ => %w[ab g], /ab|cd*/ => [], /cd(?:ef|gh)|xyz/ => [], /(cd(?:ef|gh)|xyz)/ => [], /cd(ef|gh)+/ => %w[cd], /cd(ef|gh)?/ => %w[cd], /cd(ef|gh)?ij/ => %w[cd ij], /cd(ef|gh)+ij/ => %w[cd ij], /cd(ef|gh){2}ij/ => %w[cd ij], /(cd(ef|g*))/ => %w[cd], /ab(cd){0,2}ef/ => %w[ab ef], /ab(cd){0,1}ef/ => %w[ab ef], /ab(cd|cd)ef/ => %w[ab ef], /ab(cd|cd)?ef/ => %w[ab ef], /ab\\?cd/ => %w[ab cd] }.each do |regexp, expected| expect(Capybara::Selector::RegexpDisassembler.new(regexp).substrings).to eq expected end end it 'handles alternation for #alternated_substrings' do verify_alternated_strings( /abc|def/ => [%w[abc], %w[def]], /ab(?:c|d)/ => [%w[abc], %w[abd]], /ab(c|d|e)fg/ => [%w[abcfg], %w[abdfg], %w[abefg]], /ab?(c|d)fg/ => [%w[acfg], %w[adfg], %w[abcfg], %w[abdfg]], /ab(c|d)ef/ => [%w[abcef], %w[abdef]], /ab(cd?|ef)g/ => [%w[abcg], %w[abcdg], %w[abefg]], /ab(cd|ef*)g/ => [%w[abcdg], %w[abe g]], /ab|cd*/ => [%w[ab], %w[c]], /cd(?:ef|gh)|xyz/ => [%w[cdef], %w[cdgh], %w[xyz]], /(cd(?:ef|gh)|xyz)/ => [%w[cdef], %w[cdgh], %w[xyz]], /cd(ef|gh)+/ => [%w[cdef], %w[cdgh]], /cd(ef|gh)?/ => [%w[cd]], /cd(ef|gh)?ij/ => [%w[cdij], %w[cdefij], %w[cdghij]], /cd(ef|gh)+ij/ => [%w[cdef ij], %w[cdgh ij]], /cd(ef|gh){2}ij/ => [%w[cdefefij], %w[cdefghij], %w[cdghefij], %w[cdghghij]], /(cd(ef|g*))/ => [%w[cd]], /a|b*/ => [], /ab(?:c|d?)/ => [%w[ab]], /ab(c|d)|a*/ => [], /(abc)?(d|e)/ => [%w[d], %w[e]], /(abc*de)?(d|e)/ => [%w[d], %w[e]], /(abc*de)?(d|e?)/ => [], /(abc)?(d|e?)/ => [], /ab(cd){0,2}ef/ => [%w[ab ef]], /ab(cd){0,1}ef/ => [%w[abef], %w[abcdef]], /ab(cd|cd)ef/ => [%w[abcdef]], /ab(cd|cd)?ef/ => [%w[abef], %w[abcdef]], /ab\\?cd/ => [%w[abcd], %w[ab\cd]] ) end it 'handles grouping' do verify_strings( /(abc)/ => %w[abc], /(abc)?/ => [], /ab(cde)/ => %w[abcde], /(abc)de/ => %w[abcde], /ab(cde)fg/ => %w[abcdefg], /ab(?cd)ef/ => %w[abcdef], /gh(?>ij)kl/ => %w[ghijkl], /m(n.*p)q/ => %w[mn pq], /(?:ab(cd)*){2,3}/ => %w[ab], /(ab(cd){3})?/ => [], /(ab(cd)+){2}/ => %w[abcd] ) end it 'handles meta characters' do verify_strings( /abc\d/ => %w[abc], /abc\wdef/ => %w[abc def], /\habc/ => %w[abc] ) end it 'handles character properties' do verify_strings( /ab\p{Alpha}cd/ => %w[ab cd], /ab\p{Blank}/ => %w[ab], /\p{Digit}cd/ => %w[cd] ) end it 'handles backreferences' do verify_strings( /a(?abc).\k.+/ => %w[aabc] ) end it 'handles subexpressions' do verify_strings( /\A(?a\g*b)+\z/ => %w[a b] ) end it 'handles anchors' do verify_strings( /^abc/ => %w[abc], /def$/ => %w[def], /^abc$/ => %w[abc] ) end def verify_strings(hsh) hsh.each do |regexp, expected| expect(Capybara::Selector::RegexpDisassembler.new(regexp).substrings).to eq expected end verify_alternated_strings(hsh, wrap: true) end def verify_alternated_strings(hsh, wrap: false) hsh.each do |regexp, expected| expected = [expected] if wrap && (expected != []) expect(Capybara::Selector::RegexpDisassembler.new(regexp).alternated_substrings).to eq expected end end end capybara-3.12.0/spec/rspec_matchers_spec.rb0000644000175000017500000000404113411315075021407 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' RSpec.describe 'Capybara RSpec Matchers', type: :feature do context 'after called on session' do it 'HaveSelector should allow getting a description of the matcher' do visit('/with_html') matcher = have_selector(:css, 'h2.head', minimum: 3) expect(page).to matcher expect { matcher.description }.not_to raise_error end it 'HaveText should allow getting a description' do visit('/with_html') matcher = have_text('Lorem') expect(page).to matcher expect { matcher.description }.not_to raise_error end it 'should produce the same error for .to have_no_xxx and .not_to have_xxx' do visit('/with_html') not_to_msg = error_msg_for { expect(page).not_to have_selector(:css, '#referrer') } have_no_msg = error_msg_for { expect(page).to have_no_selector(:css, '#referrer') } expect(not_to_msg).to eq have_no_msg not_to_msg = error_msg_for { expect(page).not_to have_text('This is a test') } have_no_msg = error_msg_for { expect(page).to have_no_text('This is a test') } expect(not_to_msg).to eq have_no_msg end end context 'after called on element' do it 'HaveSelector should allow getting a description' do visit('/with_html') el = find(:css, '#first') matcher = have_selector(:css, 'a#foo') expect(el).to matcher expect { matcher.description }.not_to raise_error end it 'MatchSelector should allow getting a description' do visit('/with_html') el = find(:css, '#first') matcher = match_selector(:css, '#first') expect(el).to matcher expect { matcher.description }.not_to raise_error end it 'HaveText should allow getting a description' do visit('/with_html') el = find(:css, '#first') matcher = have_text('Lorem') expect(el).to matcher expect { matcher.description }.not_to raise_error end end def error_msg_for(&block) expect(&block).to(raise_error { |e| return e.message }) end end capybara-3.12.0/spec/selenium_spec_edge.rb0000644000175000017500000000210713411315075021213 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'selenium-webdriver' require 'shared_selenium_session' require 'rspec/shared_spec_matchers' Capybara.register_driver :selenium_edge do |app| # ::Selenium::WebDriver.logger.level = "debug" Capybara::Selenium::Driver.new(app, browser: :edge) end module TestSessions SeleniumEdge = Capybara::Session.new(:selenium_edge, TestApp) end skipped_tests = %i[response_headers status_code trigger modals] $stdout.puts `#{Selenium::WebDriver::Edge.driver_path} --version` if ENV['CI'] Capybara::SpecHelper.run_specs TestSessions::SeleniumEdge, 'selenium', capybara_skip: skipped_tests do |example| case example.metadata[:description] when /#refresh it reposts$/ skip 'Edge insists on prompting without providing a way to suppress' end end RSpec.describe 'Capybara::Session with Edge', capybara_skip: skipped_tests do include Capybara::SpecHelper include_examples 'Capybara::Session', TestSessions::SeleniumEdge, :selenium_edge include_examples Capybara::RSpecMatchers, TestSessions::SeleniumEdge, :selenium_edge end capybara-3.12.0/spec/per_session_config_spec.rb0000644000175000017500000000524713411315075022274 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'capybara/dsl' RSpec.describe Capybara::SessionConfig do describe 'threadsafe' do it 'defaults to global session options' do Capybara.threadsafe = true session = Capybara::Session.new(:rack_test, TestApp) %i[default_host app_host always_include_port run_server default_selector default_max_wait_time ignore_hidden_elements automatic_reload match exact raise_server_errors visible_text_only automatic_label_click enable_aria_label save_path asset_host].each do |m| expect(session.config.public_send(m)).to eq Capybara.public_send(m) end end it "doesn't change global session when changed" do Capybara.threadsafe = true host = 'http://my.example.com' session = Capybara::Session.new(:rack_test, TestApp) do |config| config.default_host = host config.automatic_label_click = !config.automatic_label_click config.server_errors << ArgumentError end expect(Capybara.default_host).not_to eq host expect(session.config.default_host).to eq host expect(Capybara.automatic_label_click).not_to eq session.config.automatic_label_click expect(Capybara.server_errors).not_to eq session.config.server_errors end it "doesn't allow session configuration block when false" do Capybara.threadsafe = false expect do Capybara::Session.new(:rack_test, TestApp) { |config| } end.to raise_error 'A configuration block is only accepted when Capybara.threadsafe == true' end it "doesn't allow session config when false" do Capybara.threadsafe = false session = Capybara::Session.new(:rack_test, TestApp) expect { session.config.default_selector = :title }.to raise_error(/Per session settings are only supported when Capybara.threadsafe == true/) expect do session.configure do |config| config.exact = true end end.to raise_error(/Session configuration is only supported when Capybara.threadsafe == true/) end it 'uses the config from the session' do Capybara.threadsafe = true session = Capybara::Session.new(:rack_test, TestApp) do |config| config.default_selector = :link end session.visit('/with_html') expect(session.find('foo').tag_name).to eq 'a' end it "won't change threadsafe once a session is created" do Capybara.threadsafe = true Capybara.threadsafe = false Capybara::Session.new(:rack_test, TestApp) expect { Capybara.threadsafe = true }.to raise_error(/Threadsafe setting cannot be changed once a session is created/) end end end capybara-3.12.0/spec/css_splitter_spec.rb0000644000175000017500000000211713411315075021125 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' RSpec.describe Capybara::Selector::CSS::Splitter do let :splitter do ::Capybara::Selector::CSS::Splitter.new end context 'split not needed' do it 'normal CSS selector' do css = 'div[id="abc"]' expect(splitter.split(css)).to eq [css] end it 'comma in strings' do css = 'div[id="a,bc"]' expect(splitter.split(css)).to eq [css] end it 'comma in pseudo-selector' do css = 'div.class1:not(.class1, .class2)' expect(splitter.split(css)).to eq [css] end end context 'split needed' do it 'root level comma' do css = 'div.class1, span, p.class2' expect(splitter.split(css)).to eq ['div.class1', 'span', 'p.class2'] end it 'root level comma when quotes and pseudo selectors' do css = 'div.class1[id="abc\\"def,ghi"]:not(.class3, .class4), span[id=\'a"c\\\'de\'], section, #abc\\,def' expect(splitter.split(css)).to eq ['div.class1[id="abc\\"def,ghi"]:not(.class3, .class4)', 'span[id=\'a"c\\\'de\']', 'section', '#abc\\,def'] end end end capybara-3.12.0/spec/selenium_spec_chrome_remote.rb0000644000175000017500000000531213411315075023140 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'selenium-webdriver' require 'shared_selenium_session' require 'rspec/shared_spec_matchers' def selenium_host ENV.fetch('SELENIUM_HOST', '0.0.0.0') end def selenium_port ENV.fetch('SELENIUM_PORT', 4444) end def ensure_selenium_running! timer = Capybara::Helpers.timer(expire_in: 20) begin TCPSocket.open(selenium_host, selenium_port) rescue StandardError if timer.expired? raise 'Selenium is not running. ' \ "You can run a selenium server easily with: \n" \ ' $ docker-compose up -d selenium_chrome' else puts 'Waiting for Selenium docker instance...' sleep 1 retry end end end def selenium_gte?(version) defined?(Selenium::WebDriver::VERSION) && (Selenium::WebDriver::VERSION.to_f >= version) end Capybara.register_driver :selenium_chrome_remote do |app| ensure_selenium_running! url = "http://#{selenium_host}:#{selenium_port}/wd/hub" browser_options = ::Selenium::WebDriver::Chrome::Options.new Capybara::Selenium::Driver.new app, browser: :remote, desired_capabilities: :chrome, options: browser_options, url: url end CHROME_REMOTE_DRIVER = :selenium_chrome_remote module TestSessions Chrome = Capybara::Session.new(CHROME_REMOTE_DRIVER, TestApp) end TestSessions::Chrome.driver.browser.file_detector = lambda do |args| # args => ["/path/to/file"] str = args.first.to_s str if File.exist?(str) end skipped_tests = %i[response_headers status_code trigger download] Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_REMOTE_DRIVER.to_s, capybara_skip: skipped_tests do |example| case example.metadata[:full_description] when 'Capybara::Session selenium_chrome_remote #attach_file with multipart form should not break when using HTML5 multiple file input uploading multiple files', 'Capybara::Session selenium_chrome_remote #attach_file with multipart form should fire change once for each set of files uploaded', 'Capybara::Session selenium_chrome_remote #attach_file with multipart form should fire change once when uploading multiple files from empty' pending "Selenium with Remote Chrome doesn't support multiple file upload" unless selenium_gte?(3.14) end end RSpec.describe 'Capybara::Session with remote Chrome' do include Capybara::SpecHelper include_examples 'Capybara::Session', TestSessions::Chrome, CHROME_REMOTE_DRIVER include_examples Capybara::RSpecMatchers, TestSessions::Chrome, CHROME_REMOTE_DRIVER it 'is considered to be chrome' do expect(session.driver.browser.browser).to eq :chrome end end capybara-3.12.0/spec/css_builder_spec.rb0000644000175000017500000000706513411315075020714 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' RSpec.describe Capybara::Selector::CSSBuilder do let :builder do ::Capybara::Selector::CSSBuilder.new(@css) end context 'add_attribute_conditions' do it 'adds a single string condition to a single selector' do @css = 'div' selector = builder.add_attribute_conditions(random: 'abc') expect(selector).to eq %(div[random='abc']) end it 'adds multiple string conditions to a single selector' do @css = 'div' selector = builder.add_attribute_conditions(random: 'abc', other: 'def') expect(selector).to eq %(div[random='abc'][other='def']) end it 'adds a single string condition to a multiple selector' do @css = 'div, ul' selector = builder.add_attribute_conditions(random: 'abc') expect(selector).to eq %(div[random='abc'], ul[random='abc']) end it 'adds multiple string conditions to a multiple selector' do @css = 'div, ul' selector = builder.add_attribute_conditions(random: 'abc', other: 'def') expect(selector).to eq %(div[random='abc'][other='def'], ul[random='abc'][other='def']) end it 'adds simple regexp conditions to a single selector' do @css = 'div' selector = builder.add_attribute_conditions(random: /abc/, other: /def/) expect(selector).to eq %(div[random*='abc'][other*='def']) end it 'adds wildcard regexp conditions to a single selector' do @css = 'div' selector = builder.add_attribute_conditions(random: /abc.*def/, other: /def.*ghi/) expect(selector).to eq %(div[random*='abc'][random*='def'][other*='def'][other*='ghi']) end it 'adds alternated regexp conditions to a single selector' do @css = 'div' selector = builder.add_attribute_conditions(random: /abc|def/, other: /def|ghi/) expect(selector).to eq %(div[random*='abc'][other*='def'], div[random*='abc'][other*='ghi'], div[random*='def'][other*='def'], div[random*='def'][other*='ghi']) end it 'adds alternated regexp conditions to a multiple selector' do @css = 'div,ul' selector = builder.add_attribute_conditions(other: /def.*ghi|jkl/) expect(selector).to eq %(div[other*='def'][other*='ghi'], div[other*='jkl'], ul[other*='def'][other*='ghi'], ul[other*='jkl']) end it "returns original selector when regexp can't be substringed" do @css = 'div' selector = builder.add_attribute_conditions(other: /.+/) expect(selector).to eq 'div' end context ':class' do it 'handles string with CSS .' do @css = 'a' selector = builder.add_attribute_conditions(class: 'my_class') expect(selector).to eq 'a.my_class' end it 'handles negated string with CSS .' do @css = 'a' selector = builder.add_attribute_conditions(class: '!my_class') expect(selector).to eq 'a:not(.my_class)' end it 'handles array of string with CSS .' do @css = 'a' selector = builder.add_attribute_conditions(class: %w[my_class my_other_class]) expect(selector).to eq 'a.my_class.my_other_class' end it 'handles array of string with CSS . when negated included' do @css = 'a' selector = builder.add_attribute_conditions(class: %w[my_class !my_other_class]) expect(selector).to eq 'a.my_class:not(.my_other_class)' end end context ':id' do it 'handles string with CSS #' do @css = 'ul' selector = builder.add_attribute_conditions(id: 'my_id') expect(selector).to eq 'ul#my_id' end end end end capybara-3.12.0/spec/shared_selenium_session.rb0000644000175000017500000004620613411315075022316 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'selenium-webdriver' RSpec.shared_examples 'Capybara::Session' do |session, mode| let(:session) { session } context 'with selenium driver' do describe '#driver' do it 'should be a selenium driver' do expect(session.driver).to be_an_instance_of(Capybara::Selenium::Driver) end end describe '#mode' do it 'should remember the mode' do expect(session.mode).to eq(mode) end end describe '#reset!' do it 'freshly reset session should not be touched' do session.instance_variable_set(:@touched, true) session.reset! expect(session.instance_variable_get(:@touched)).to eq false end end describe 'exit codes' do before do @current_dir = Dir.getwd Dir.chdir(File.join(File.dirname(__FILE__), '..')) @env = { 'SELENIUM_BROWSER' => session.driver.options[:browser].to_s } end after do Dir.chdir(@current_dir) end it 'should have return code 1 when running selenium_driver_rspec_failure.rb' do skip 'only setup for local non-headless' if headless_or_remote? system(@env, 'rspec spec/fixtures/selenium_driver_rspec_failure.rb', out: File::NULL, err: File::NULL) expect($CHILD_STATUS.exitstatus).to eq(1) end it 'should have return code 0 when running selenium_driver_rspec_success.rb' do skip 'only setup for local non-headless' if headless_or_remote? system(@env, 'rspec spec/fixtures/selenium_driver_rspec_success.rb', out: File::NULL, err: File::NULL) expect($CHILD_STATUS.exitstatus).to eq(0) end end describe '#accept_alert', requires: [:modals] do it 'supports a blockless mode' do session.visit('/with_js') session.click_link('Open alert') session.accept_alert expect { session.driver.browser.switch_to.alert }.to raise_error(session.driver.send(:modal_error)) end it 'can be called before visiting' do session.accept_alert 'Initial alert' do session.visit('/initial_alert') end expect(session).to have_text('Initial alert page') end end context '#fill_in_with empty string and no options' do it 'should trigger change when clearing a field' do session.visit('/with_js') session.fill_in('with_change_event', with: '') # click outside the field to trigger the change event session.find(:css, 'body').click expect(session).to have_selector(:css, '.change_event_triggered', match: :one) end end context '#fill_in with { :clear => :backspace } fill_option', requires: [:js] do before do # Firefox has an issue with change events if the main window doesn't think it's focused session.execute_script('window.focus()') end it 'should fill in a field, replacing an existing value' do session.visit('/form') session.fill_in('form_first_name', with: 'Harry', fill_options: { clear: :backspace }) expect(session.find(:fillable_field, 'form_first_name').value).to eq('Harry') end it 'should fill in a field, replacing an existing value, even with caret position' do session.visit('/form') session.find(:css, '#form_first_name').execute_script <<-JS this.focus(); this.setSelectionRange(0, 0); JS session.fill_in('form_first_name', with: 'Harry', fill_options: { clear: :backspace }) expect(session.find(:fillable_field, 'form_first_name').value).to eq('Harry') end it 'should fill in if the option is set via global option' do Capybara.default_set_options = { clear: :backspace } session.visit('/form') session.fill_in('form_first_name', with: 'Thomas') expect(session.find(:fillable_field, 'form_first_name').value).to eq('Thomas') end it 'should only trigger onchange once' do session.visit('/with_js') session.fill_in('with_change_event', with: 'some value', fill_options: { clear: :backspace }) # click outside the field to trigger the change event session.find(:css, '#with_focus_event').click expect(session.find(:css, '.change_event_triggered', match: :one, wait: 5)).to have_text 'some value' end it 'should trigger change when clearing field' do session.visit('/with_js') session.fill_in('with_change_event', with: '', fill_options: { clear: :backspace }) # click outside the field to trigger the change event session.find(:css, '#with_focus_event').click expect(session).to have_selector(:css, '.change_event_triggered', match: :one, wait: 5) end it 'should trigger input event field_value.length times' do session.visit('/with_js') session.fill_in('with_change_event', with: '', fill_options: { clear: :backspace }) # click outside the field to trigger the change event session.find(:css, 'body').click expect(session).to have_xpath('//p[@class="input_event_triggered"]', count: 13) end end context '#fill_in with { clear: :none } fill_options' do it 'should append to content in a field' do session.visit('/form') session.fill_in('form_first_name', with: 'Harry', fill_options: { clear: :none }) expect(session.find(:fillable_field, 'form_first_name').value).to eq('JohnHarry') end end context '#fill_in with Date' do before do session.visit('/form') session.find(:css, '#form_date').execute_script <<-JS window.capybara_formDateFiredEvents = []; var fd = this; ['focus', 'input', 'change'].forEach(function(eventType) { fd.addEventListener(eventType, function() { window.capybara_formDateFiredEvents.push(eventType); }); }); JS # work around weird FF issue where it would create an extra focus issue in some cases session.find(:css, 'body').click end it 'should generate standard events on changing value' do pending "IE 11 doesn't support date input type" if ie?(session) session.fill_in('form_date', with: Date.today) expect(session.evaluate_script('window.capybara_formDateFiredEvents')).to eq %w[focus input change] end it 'should not generate input and change events if the value is not changed' do pending "IE 11 doesn't support date input type" if ie?(session) session.fill_in('form_date', with: Date.today) session.fill_in('form_date', with: Date.today) # Chrome adds an extra focus for some reason - ok for now expect(session.evaluate_script('window.capybara_formDateFiredEvents')).to eq(%w[focus input change]) end end context '#fill_in with { clear: Array } fill_options' do it 'should pass the array through to the element' do # this is mainly for use with [[:control, 'a'], :backspace] - however since that is platform dependant I'm testing with something less useful session.visit('/form') session.fill_in('form_first_name', with: 'Harry', fill_options: { clear: [[:shift, 'abc'], :backspace] }) expect(session.find(:fillable_field, 'form_first_name').value).to eq('JohnABHarry') end end describe '#path' do it 'returns xpath' do # this is here because it is testing for an XPath that is specific to the algorithm used in the selenium driver session.visit('/path') element = session.find(:link, 'Second Link') expect(element.path).to eq('/HTML/BODY/DIV[2]/A[1]') end it 'handles namespaces in xhtml' do pending "IE 11 doesn't handle all XPath querys (namespace-uri, etc)" if ie?(session) session.visit '/with_namespace' rect = session.find(:css, 'div svg rect:first-of-type') expect(rect.path).to eq("/HTML/BODY/DIV/./*[((local-name(.) = 'svg') and (namespace-uri(.) = 'http://www.w3.org/2000/svg'))]/./*[((local-name(.) = 'rect') and (namespace-uri(.) = 'http://www.w3.org/2000/svg'))][1]") expect(session.find(:xpath, rect.path)).to eq rect end it 'handles default namespaces in html5' do pending "IE 11 doesn't handle all XPath querys (namespace-uri, etc)" if ie?(session) session.visit '/with_html5_svg' rect = session.find(:css, 'div svg rect:first-of-type') expect(rect.path).to eq("/HTML/BODY/DIV/./*[((local-name(.) = 'svg') and (namespace-uri(.) = 'http://www.w3.org/2000/svg'))]/./*[((local-name(.) = 'rect') and (namespace-uri(.) = 'http://www.w3.org/2000/svg'))][1]") expect(session.find(:xpath, rect.path)).to eq rect end it 'handles case sensitive element names' do pending "IE 11 doesn't handle all XPath querys (namespace-uri, etc)" if ie?(session) session.visit '/with_namespace' els = session.all(:css, 'div *', visible: :all) expect { els.map(&:path) }.not_to raise_error lg = session.find(:css, 'div linearGradient', visible: :all) expect(session.find(:xpath, lg.path, visible: :all)).to eq lg end end describe 'all with disappearing elements' do it 'ignores stale elements in results' do session.visit('/path') elements = session.all(:link) { |_node| raise Selenium::WebDriver::Error::StaleElementReferenceError } expect(elements.size).to eq 0 end end describe '#evaluate_script' do it 'can return an element' do session.visit('/form') element = session.evaluate_script("document.getElementById('form_title')") expect(element).to eq session.find(:id, 'form_title') end it 'can return arrays of nested elements' do session.visit('/form') elements = session.evaluate_script('document.querySelectorAll("#form_city option")') expect(elements).to all(be_instance_of Capybara::Node::Element) expect(elements).to eq session.find(:css, '#form_city').all(:css, 'option').to_a end it 'can return hashes with elements' do session.visit('/form') result = session.evaluate_script("{ a: document.getElementById('form_title'), b: {c: document.querySelectorAll('#form_city option')}}") expect(result).to eq( 'a' => session.find(:id, 'form_title'), 'b' => { 'c' => session.find(:css, '#form_city').all(:css, 'option').to_a } ) end describe '#evaluate_async_script' do it 'will timeout if the script takes too long' do session.visit('/with_js') expect do session.using_wait_time(1) do session.evaluate_async_script('var cb = arguments[0]; setTimeout(function(){ cb(null) }, 3000)') end end.to raise_error Selenium::WebDriver::Error::ScriptTimeoutError end end end describe 'Element#inspect' do it 'outputs obsolete elements' do session.visit('/form') el = session.find(:button, 'Click me!').click expect(session).to have_no_button('Click me!') allow(el).to receive(:synchronize) expect(el.inspect).to eq 'Obsolete #' expect(el).not_to have_received(:synchronize) end end describe 'Element#click' do it 'should handle fixed headers/footers' do session.visit('/with_fixed_header_footer') # session.click_link('Go to root') session.find(:link, 'Go to root').click expect(session).to have_current_path('/') end end describe 'Element#drag_to' do before do skip "Firefox < 62 doesn't support a DataTransfer constuctor" if firefox_lt?(62.0, session) skip "IE doesn't support a DataTransfer constuctor" if ie?(session) end it 'should HTML5 drag and drop an object' do session.visit('/with_js') element = session.find('//div[@id="drag_html5"]') target = session.find('//div[@id="drop_html5"]') element.drag_to(target) expect(session).to have_xpath('//div[contains(., "HTML5 Dropped drag_html5")]') end it 'should not HTML5 drag and drop on a non HTML5 drop element' do session.visit('/with_js') element = session.find('//div[@id="drag_html5"]') target = session.find('//div[@id="drop_html5"]') target.execute_script("$(this).removeClass('drop');") element.drag_to(target) sleep 1 expect(session).not_to have_xpath('//div[contains(., "HTML5 Dropped drag_html5")]') end it 'should HTML5 drag and drop when scrolling needed' do session.visit('/with_js') element = session.find('//div[@id="drag_html5_scroll"]') target = session.find('//div[@id="drop_html5_scroll"]') element.drag_to(target) expect(session).to have_xpath('//div[contains(., "HTML5 Dropped drag_html5_scroll")]') end it 'should drag HTML5 default draggable elements' do session.visit('/with_js') link = session.find_link('drag_link_html5') target = session.find(:id, 'drop_html5') link.drag_to target expect(session).to have_xpath('//div[contains(., "HTML5 Dropped")]') end end describe 'Capybara#Node#attach_file' do it 'can attach a directory' do pending "Geckodriver doesn't support uploading a directory" if firefox?(session) pending "Selenium remote doesn't support transferring a directory" if remote?(session) pending "Headless Chrome doesn't support directory upload - https://bugs.chromium.org/p/chromedriver/issues/detail?id=2521&q=directory%20upload&colspec=ID%20Status%20Pri%20Owner%20Summary" if chrome?(session) && ENV['HEADLESS'] session.visit('/form') @test_file_dir = File.expand_path('./fixtures', File.dirname(__FILE__)) session.attach_file('Directory Upload', @test_file_dir) session.click_button('Upload Multiple') expect(session.body).to include('5 | ') # number of files end end context 'Windows' do it "can't close the primary window" do expect do session.current_window.close end.to raise_error(ArgumentError, 'Not allowed to close the primary window') end end describe 'Capybara#disable_animation' do context 'when set to `true`' do before(:context) do # rubocop:disable RSpec/BeforeAfterAll # NOTE: Although Capybara.SpecHelper.reset! sets Capybara.disable_animation to false, # it doesn't affect any of these tests because the settings are applied per-session Capybara.disable_animation = true @animation_session = Capybara::Session.new(session.mode, TestApp.new) end after(:context) do # rubocop:disable RSpec/BeforeAfterAll @animation_session = nil end it 'should disable CSS transitions' do @animation_session.visit('with_animation') @animation_session.click_link('transition me away') expect(@animation_session).to have_no_link('transition me away', wait: 0.5) end it 'should disable CSS animations' do @animation_session.visit('with_animation') @animation_session.click_link('animate me away') expect(@animation_session).to have_no_link('animate me away', wait: 0.5) end end context 'if we pass in css that matches elements' do before(:context) do # rubocop:disable RSpec/BeforeAfterAll # NOTE: Although Capybara.SpecHelper.reset! sets Capybara.disable_animation to false, # it doesn't affect any of these tests because the settings are applied per-session Capybara.disable_animation = '#with_animation a' @animation_session_with_matching_css = Capybara::Session.new(session.mode, TestApp.new) end after(:context) do # rubocop:disable RSpec/BeforeAfterAll @animation_session_with_matching_css = nil end it 'should disable CSS transitions' do @animation_session_with_matching_css.visit('with_animation') @animation_session_with_matching_css.click_link('transition me away') expect(@animation_session_with_matching_css).to have_no_link('transition me away', wait: 0.5) end it 'should disable CSS animations' do @animation_session_with_matching_css.visit('with_animation') @animation_session_with_matching_css.click_link('animate me away') expect(@animation_session_with_matching_css).to have_no_link('animate me away', wait: 0.5) end end context 'if we pass in css that does not match elements' do before(:context) do # rubocop:disable RSpec/BeforeAfterAll # NOTE: Although Capybara.SpecHelper.reset! sets Capybara.disable_animation to false, # it doesn't affect any of these tests because the settings are applied per-session Capybara.disable_animation = '.this-class-matches-nothing' @animation_session_without_matching_css = Capybara::Session.new(session.mode, TestApp.new) end after(:context) do # rubocop:disable RSpec/BeforeAfterAll @animation_session_without_matching_css = nil end it 'should not disable CSS transitions' do @animation_session_without_matching_css.visit('with_animation') @animation_session_without_matching_css.click_link('transition me away') sleep 0.5 # Wait long enough for click to have been processed expect(@animation_session_without_matching_css).to have_link('transition me away', wait: false) expect(@animation_session_without_matching_css).to have_no_link('transition me away', wait: 5) end it 'should not disable CSS animations' do @animation_session_without_matching_css.visit('with_animation') @animation_session_without_matching_css.click_link('animate me away') sleep 0.5 # Wait long enough for click to have been processed expect(@animation_session_without_matching_css).to have_link('animate me away', wait: false) expect(@animation_session_without_matching_css).to have_no_link('animate me away', wait: 5) end end end describe ':element selector' do it 'can find html5 svg elements' do session.visit('with_html5_svg') expect(session).to have_selector(:element, :svg) expect(session).to have_selector(:element, :rect, visible: true) expect(session).to have_selector(:element, :circle) expect(session).to have_selector(:element, :linearGradient, visible: :all) end it 'can query attributes with strange characters' do session.visit('/form') expect(session).to have_selector(:element, "{custom}": true) expect(session).to have_selector(:element, "{custom}": 'abcdef') end end end def headless_or_remote? !ENV['HEADLESS'].nil? || session.driver.options[:browser] == :remote end end capybara-3.12.0/spec/session_spec.rb0000644000175000017500000000373013411315075020074 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' RSpec.describe Capybara::Session do context '#new' do it 'should raise an error if passed non-existent driver' do expect do Capybara::Session.new(:quox, TestApp).driver end.to raise_error(Capybara::DriverNotFoundError) end it 'verifies a passed app is a rack app' do expect do Capybara::Session.new(:unknown, random: 'hash') end.to raise_error TypeError, 'The second parameter to Session::new should be a rack app if passed.' end end context 'current_driver' do around do |example| orig_driver = Capybara.current_driver example.run Capybara.current_driver = orig_driver end it 'is global when threadsafe false' do Capybara.threadsafe = false Capybara.current_driver = :selenium thread = Thread.new do Capybara.current_driver = :random end thread.join expect(Capybara.current_driver).to eq :random end it 'is thread specific threadsafe true' do Capybara.threadsafe = true Capybara.current_driver = :selenium thread = Thread.new do Capybara.current_driver = :random end thread.join expect(Capybara.current_driver).to eq :selenium end end context 'session_name' do around do |example| orig_name = Capybara.session_name example.run Capybara.session_name = orig_name end it 'is global when threadsafe false' do Capybara.threadsafe = false Capybara.session_name = 'sess1' thread = Thread.new do Capybara.session_name = 'sess2' end thread.join expect(Capybara.session_name).to eq 'sess2' end it 'is thread specific when threadsafe true' do Capybara.threadsafe = true Capybara.session_name = 'sess1' thread = Thread.new do Capybara.session_name = 'sess2' end thread.join expect(Capybara.session_name).to eq 'sess1' end end end capybara-3.12.0/spec/rack_test_spec.rb0000644000175000017500000002064113411315075020370 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' nokogumbo_required = begin require 'nokogumbo' true rescue LoadError false end module TestSessions RackTest = Capybara::Session.new(:rack_test, TestApp) end skipped_tests = %i[ js modals screenshot frames windows send_keys server hover about_scheme download css ] Capybara::SpecHelper.run_specs TestSessions::RackTest, 'RackTest', capybara_skip: skipped_tests do |example| case example.metadata[:full_description] when /has_css\? should support case insensitive :class and :id options/ pending "Nokogiri doesn't support case insensitive CSS attribute matchers" end end RSpec.describe Capybara::Session do # rubocop:disable RSpec/MultipleDescribes context 'with rack test driver' do before do @session = TestSessions::RackTest end describe '#driver' do it 'should be a rack test driver' do expect(@session.driver).to be_an_instance_of(Capybara::RackTest::Driver) end end describe '#mode' do it 'should remember the mode' do expect(@session.mode).to eq(:rack_test) end end describe '#click_link' do after do @session.driver.options[:respect_data_method] = false end it 'should use data-method if option is true' do @session.driver.options[:respect_data_method] = true @session.visit '/with_html' @session.click_link 'A link with data-method' expect(@session.html).to include('The requested object was deleted') end it 'should not use data-method if option is false' do @session.driver.options[:respect_data_method] = false @session.visit '/with_html' @session.click_link 'A link with data-method' expect(@session.html).to include('Not deleted') end it "should use data-method if available even if it's capitalized" do @session.driver.options[:respect_data_method] = true @session.visit '/with_html' @session.click_link 'A link with capitalized data-method' expect(@session.html).to include('The requested object was deleted') end end describe '#fill_in' do it 'should warn that :fill_options are not supported' do allow_any_instance_of(Capybara::RackTest::Node).to receive(:warn) @session.visit '/with_html' field = @session.fill_in 'test_field', with: 'not_monkey', fill_options: { random: true } expect(@session).to have_field('test_field', with: 'not_monkey') expect(field.base).to have_received(:warn).with("Options passed to Node#set but the RackTest driver doesn't support any - ignoring") end end describe '#attach_file' do context 'with multipart form' do it 'should submit an empty form-data section if no file is submitted' do @session.visit('/form') @session.click_button('Upload Empty') expect(@session.html).to include('Successfully ignored empty file field.') end end it 'should not submit an obsolete mime type' do @test_jpg_file_path = File.expand_path('fixtures/capybara.csv', File.dirname(__FILE__)) @session.visit('/form') @session.attach_file 'form_document', @test_jpg_file_path @session.click_button('Upload Single') expect(@session).to have_content('Content-type: text/csv') end end describe '#click' do context 'on a label' do it 'should toggle the associated checkbox' do @session.visit('/form') expect(@session).to have_unchecked_field('form_pets_cat') @session.find(:label, 'Cat').click expect(@session).to have_checked_field('form_pets_cat') @session.find(:label, 'Cat').click expect(@session).to have_unchecked_field('form_pets_cat') @session.find(:label, 'McLaren').click expect(@session).to have_checked_field('form_cars_mclaren', visible: :hidden) end it 'should toggle the associated radio' do @session.visit('/form') expect(@session).to have_unchecked_field('gender_male') @session.find(:label, 'Male').click expect(@session).to have_checked_field('gender_male') @session.find(:label, 'Female').click expect(@session).to have_unchecked_field('gender_male') end end end describe '#text' do it 'should return original text content for textareas' do @session.visit('/with_html') @session.find_field('normal', type: 'textarea', with: 'banana').set('hello') normal = @session.find(:css, '#normal') expect(normal.value).to eq 'hello' expect(normal.text).to eq 'banana' end end describe '#style' do it 'should raise an error' do @session.visit('/with_html') el = @session.find(:css, '#first') expect { el.style('display') }.to raise_error NotImplementedError, /not process CSS/ end end end end RSpec.describe Capybara::RackTest::Driver do before do @driver = TestSessions::RackTest.driver end describe ':headers option' do it 'should always set headers' do @driver = Capybara::RackTest::Driver.new(TestApp, headers: { 'HTTP_FOO' => 'foobar' }) @driver.visit('/get_header') expect(@driver.html).to include('foobar') end it 'should keep headers on link clicks' do @driver = Capybara::RackTest::Driver.new(TestApp, headers: { 'HTTP_FOO' => 'foobar' }) @driver.visit('/header_links') @driver.find_xpath('.//a').first.click expect(@driver.html).to include('foobar') end it 'should keep headers on form submit' do @driver = Capybara::RackTest::Driver.new(TestApp, headers: { 'HTTP_FOO' => 'foobar' }) @driver.visit('/header_links') @driver.find_xpath('.//input').first.click expect(@driver.html).to include('foobar') end it 'should keep headers on redirects' do @driver = Capybara::RackTest::Driver.new(TestApp, headers: { 'HTTP_FOO' => 'foobar' }) @driver.visit('/get_header_via_redirect') expect(@driver.html).to include('foobar') end end describe ':follow_redirects option' do it 'defaults to following redirects' do @driver = Capybara::RackTest::Driver.new(TestApp) @driver.visit('/redirect') expect(@driver.response.header['Location']).to be_nil expect(@driver.current_url).to match %r{/landed$} end it 'is possible to not follow redirects' do @driver = Capybara::RackTest::Driver.new(TestApp, follow_redirects: false) @driver.visit('/redirect') expect(@driver.response.header['Location']).to match %r{/redirect_again$} expect(@driver.current_url).to match %r{/redirect$} end end describe ':redirect_limit option' do context 'with default redirect limit' do before do @driver = Capybara::RackTest::Driver.new(TestApp) end it 'should follow 5 redirects' do @driver.visit('/redirect/5/times') expect(@driver.html).to include('redirection complete') end it 'should not follow more than 6 redirects' do expect do @driver.visit('/redirect/6/times') end.to raise_error(Capybara::InfiniteRedirectError) end end context 'with 21 redirect limit' do before do @driver = Capybara::RackTest::Driver.new(TestApp, redirect_limit: 21) end it 'should follow 21 redirects' do @driver.visit('/redirect/21/times') expect(@driver.html).to include('redirection complete') end it 'should not follow more than 21 redirects' do expect do @driver.visit('/redirect/22/times') end.to raise_error(Capybara::InfiniteRedirectError) end end end end RSpec.describe 'Capybara::String' do it 'should use gumbo' do skip 'Only valid if gumbo is included' unless nokogumbo_required allow(Nokogiri).to receive(:HTML5).and_call_original Capybara.string('
') expect(Nokogiri).to have_received(:HTML5) end end module CSSHandlerIncludeTester def dont_extend_css_handler raise 'should never be called' end end RSpec.describe Capybara::RackTest::CSSHandlers do include CSSHandlerIncludeTester it 'should not be extended by global includes' do expect(Capybara::RackTest::CSSHandlers.new).not_to respond_to(:dont_extend_css_handler) end end capybara-3.12.0/spec/dsl_spec.rb0000644000175000017500000002113213411315075017167 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'capybara/dsl' class TestClass include Capybara::DSL end Capybara::SpecHelper.run_specs TestClass.new, 'DSL', capybara_skip: %i[ js modals screenshot frames windows send_keys server hover about_scheme psc download css driver ] do |example| case example.metadata[:full_description] when /has_css\? should support case insensitive :class and :id options/ pending "Nokogiri doesn't support case insensitive CSS attribute matchers" end end RSpec.describe Capybara::DSL do after do Capybara.session_name = nil Capybara.default_driver = nil Capybara.javascript_driver = nil Capybara.use_default_driver Capybara.app = TestApp end describe '#default_driver' do it 'should default to rack_test' do expect(Capybara.default_driver).to eq(:rack_test) end it 'should be changeable' do Capybara.default_driver = :culerity expect(Capybara.default_driver).to eq(:culerity) end end describe '#current_driver' do it 'should default to the default driver' do expect(Capybara.current_driver).to eq(:rack_test) Capybara.default_driver = :culerity expect(Capybara.current_driver).to eq(:culerity) end it 'should be changeable' do Capybara.current_driver = :culerity expect(Capybara.current_driver).to eq(:culerity) end end describe '#javascript_driver' do it 'should default to selenium' do expect(Capybara.javascript_driver).to eq(:selenium) end it 'should be changeable' do Capybara.javascript_driver = :culerity expect(Capybara.javascript_driver).to eq(:culerity) end end describe '#use_default_driver' do it 'should restore the default driver' do Capybara.current_driver = :culerity Capybara.use_default_driver expect(Capybara.current_driver).to eq(:rack_test) end end describe '#using_driver' do before do expect(Capybara.current_driver).not_to eq(:selenium) # rubocop:disable RSpec/ExpectInHook end it 'should set the driver using Capybara.current_driver=' do driver = nil Capybara.using_driver(:selenium) { driver = Capybara.current_driver } expect(driver).to eq(:selenium) end it 'should return the driver to default if it has not been changed' do Capybara.using_driver(:selenium) do expect(Capybara.current_driver).to eq(:selenium) end expect(Capybara.current_driver).to eq(Capybara.default_driver) end it 'should reset the driver even if an exception occurs' do driver_before_block = Capybara.current_driver begin Capybara.using_driver(:selenium) { raise 'ohnoes!' } rescue Exception # rubocop:disable Lint/RescueException,Lint/HandleExceptions end expect(Capybara.current_driver).to eq(driver_before_block) end it 'should return the driver to what it was previously' do Capybara.current_driver = :selenium Capybara.using_driver(:culerity) do Capybara.using_driver(:rack_test) do expect(Capybara.current_driver).to eq(:rack_test) end expect(Capybara.current_driver).to eq(:culerity) end expect(Capybara.current_driver).to eq(:selenium) end it 'should yield the passed block' do called = false Capybara.using_driver(:selenium) { called = true } expect(called).to eq(true) end end describe '#using_wait_time' do before do @previous_wait_time = Capybara.default_max_wait_time end after do Capybara.default_max_wait_time = @previous_wait_time end it 'should switch the wait time and switch it back' do in_block = nil Capybara.using_wait_time 6 do in_block = Capybara.default_max_wait_time end expect(in_block).to eq(6) expect(Capybara.default_max_wait_time).to eq(@previous_wait_time) end it 'should ensure wait time is reset' do expect do Capybara.using_wait_time 6 do raise 'hell' end end.to raise_error(RuntimeError, 'hell') expect(Capybara.default_max_wait_time).to eq(@previous_wait_time) end end describe '#app' do it 'should be changeable' do Capybara.app = 'foobar' expect(Capybara.app).to eq('foobar') end end describe '#current_session' do it 'should choose a session object of the current driver type' do expect(Capybara.current_session).to be_a(Capybara::Session) end it 'should use #app as the application' do Capybara.app = proc {} expect(Capybara.current_session.app).to eq(Capybara.app) end it 'should change with the current driver' do expect(Capybara.current_session.mode).to eq(:rack_test) Capybara.current_driver = :selenium expect(Capybara.current_session.mode).to eq(:selenium) end it 'should be persistent even across driver changes' do object_id = Capybara.current_session.object_id expect(Capybara.current_session.object_id).to eq(object_id) Capybara.current_driver = :selenium expect(Capybara.current_session.mode).to eq(:selenium) expect(Capybara.current_session.object_id).not_to eq(object_id) Capybara.current_driver = :rack_test expect(Capybara.current_session.object_id).to eq(object_id) end it 'should change when changing application' do object_id = Capybara.current_session.object_id expect(Capybara.current_session.object_id).to eq(object_id) Capybara.app = proc {} expect(Capybara.current_session.object_id).not_to eq(object_id) expect(Capybara.current_session.app).to eq(Capybara.app) end it 'should change when the session name changes' do object_id = Capybara.current_session.object_id Capybara.session_name = :administrator expect(Capybara.session_name).to eq(:administrator) expect(Capybara.current_session.object_id).not_to eq(object_id) Capybara.session_name = :default expect(Capybara.session_name).to eq(:default) expect(Capybara.current_session.object_id).to eq(object_id) end end describe '#using_session' do it 'should change the session name for the duration of the block' do expect(Capybara.session_name).to eq(:default) Capybara.using_session(:administrator) do expect(Capybara.session_name).to eq(:administrator) end expect(Capybara.session_name).to eq(:default) end it 'should reset the session to the default, even if an exception occurs' do begin Capybara.using_session(:raise) do raise end rescue Exception # rubocop:disable Lint/RescueException,Lint/HandleExceptions end expect(Capybara.session_name).to eq(:default) end it 'should yield the passed block' do called = false Capybara.using_session(:administrator) { called = true } expect(called).to eq(true) end it 'should be nestable' do Capybara.using_session(:outer) do expect(Capybara.session_name).to eq(:outer) Capybara.using_session(:inner) do expect(Capybara.session_name).to eq(:inner) end expect(Capybara.session_name).to eq(:outer) end expect(Capybara.session_name).to eq(:default) end it 'should allow a session object' do original_session = Capybara.current_session new_session = Capybara::Session.new(:rack_test, proc {}) Capybara.using_session(new_session) do expect(Capybara.current_session).to eq(new_session) end expect(Capybara.current_session).to eq(original_session) end end describe '#session_name' do it 'should default to :default' do expect(Capybara.session_name).to eq(:default) end end describe 'the DSL' do before do @session = Class.new { include Capybara::DSL }.new end it 'should be possible to include it in another class' do @session.visit('/with_html') @session.click_link('ullamco') expect(@session.body).to include('Another World') end it "should provide a 'page' shortcut for more expressive tests" do @session.page.visit('/with_html') @session.page.click_link('ullamco') expect(@session.page.body).to include('Another World') end it "should provide an 'using_session' shortcut" do allow(Capybara).to receive(:using_session) @session.using_session(:name) expect(Capybara).to have_received(:using_session).with(:name) end it "should provide a 'using_wait_time' shortcut" do allow(Capybara).to receive(:using_wait_time) @session.using_wait_time(6) expect(Capybara).to have_received(:using_wait_time).with(6) end end end capybara-3.12.0/spec/xpath_builder_spec.rb0000644000175000017500000000772113411315075021247 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' RSpec.describe Capybara::Selector::XPathBuilder do let :builder do ::Capybara::Selector::XPathBuilder.new(@xpath) end context 'add_attribute_conditions' do it 'adds a single string condition to a single selector' do @xpath = './/div' selector = builder.add_attribute_conditions(random: 'abc') expect(selector).to eq %((.//div)[(./@random = 'abc')]) end it 'adds multiple string conditions to a single selector' do @xpath = './/div' selector = builder.add_attribute_conditions(random: 'abc', other: 'def') expect(selector).to eq %(((.//div)[(./@random = 'abc')])[(./@other = 'def')]) end it 'adds a single string condition to a multiple selector' do @xpath = XPath.descendant(:div, :ul) selector = builder.add_attribute_conditions(random: 'abc') expect(selector.to_s).to eq @xpath[XPath.attr(:random) == 'abc'].to_s end it 'adds multiple string conditions to a multiple selector' do @xpath = XPath.descendant(:div, :ul) selector = builder.add_attribute_conditions(random: 'abc', other: 'def') expect(selector.to_s).to eq %(.//*[self::div | self::ul][(./@random = 'abc')][(./@other = 'def')]) end it 'adds simple regexp conditions to a single selector' do @xpath = XPath.descendant(:div) selector = builder.add_attribute_conditions(random: /abc/, other: /def/) expect(selector.to_s).to eq %(.//div[./@random[contains(., 'abc')]][./@other[contains(., 'def')]]) end it 'adds wildcard regexp conditions to a single selector' do @xpath = './/div' selector = builder.add_attribute_conditions(random: /abc.*def/, other: /def.*ghi/) expect(selector).to eq %(((.//div)[./@random[(contains(., 'abc') and contains(., 'def'))]])[./@other[(contains(., 'def') and contains(., 'ghi'))]]) end it 'adds alternated regexp conditions to a single selector' do @xpath = XPath.descendant(:div) selector = builder.add_attribute_conditions(random: /abc|def/, other: /def|ghi/) expect(selector.to_s).to eq %(.//div[./@random[(contains(., 'abc') or contains(., 'def'))]][./@other[(contains(., 'def') or contains(., 'ghi'))]]) end it 'adds alternated regexp conditions to a multiple selector' do @xpath = XPath.descendant(:div, :ul) selector = builder.add_attribute_conditions(other: /def.*ghi|jkl/) expect(selector.to_s).to eq %(.//*[self::div | self::ul][./@other[((contains(., 'def') and contains(., 'ghi')) or contains(., 'jkl'))]]) end it "returns original selector when regexp can't be substringed" do @xpath = './/div' selector = builder.add_attribute_conditions(other: /.+/) expect(selector).to eq '(.//div)[./@other]' end context ':class' do it 'handles string' do @xpath = './/a' selector = builder.add_attribute_conditions(class: 'my_class') expect(selector).to eq %((.//a)[contains(concat(' ', normalize-space(./@class), ' '), ' my_class ')]) end it 'handles negated strings' do @xpath = XPath.descendant(:a) selector = builder.add_attribute_conditions(class: '!my_class') expect(selector.to_s).to eq @xpath[!XPath.attr(:class).contains_word('my_class')].to_s end it 'handles array of strings' do @xpath = './/a' selector = builder.add_attribute_conditions(class: %w[my_class my_other_class]) expect(selector).to eq %((.//a)[(contains(concat(' ', normalize-space(./@class), ' '), ' my_class ') and contains(concat(' ', normalize-space(./@class), ' '), ' my_other_class '))]) end it 'handles array of string when negated included' do @xpath = XPath.descendant(:a) selector = builder.add_attribute_conditions(class: %w[my_class !my_other_class]) expect(selector.to_s).to eq @xpath[XPath.attr(:class).contains_word('my_class') & !XPath.attr(:class).contains_word('my_other_class')].to_s end end end end capybara-3.12.0/spec/capybara_spec.rb0000644000175000017500000000565713411315075020205 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' RSpec.describe Capybara do describe 'default_max_wait_time' do after do Capybara.default_max_wait_time = @previous_default_time end it 'should be changeable' do @previous_default_time = Capybara.default_max_wait_time Capybara.default_max_wait_time = 5 expect(Capybara.default_max_wait_time).to eq(5) end end describe '.register_driver' do it 'should add a new driver' do Capybara.register_driver :schmoo do |app| Capybara::RackTest::Driver.new(app) end session = Capybara::Session.new(:schmoo, TestApp) session.visit('/') expect(session.body).to include('Hello world!') end end describe '.register_server' do it 'should add a new server' do Capybara.register_server :blob do |_app, _port, _host| # do nothing end expect(Capybara.servers).to have_key(:blob) end end describe '.server' do after do Capybara.server = :default end it 'should default to a proc that calls run_default_server' do mock_app = Object.new allow(Capybara).to receive(:run_default_server).and_return(true) Capybara.server.call(mock_app, 8000) expect(Capybara).to have_received(:run_default_server).with(mock_app, 8000) end it 'should return a custom server proc' do server = ->(_app, _port) {} Capybara.register_server :custom, &server Capybara.server = :custom expect(Capybara.server).to eq(server) end it 'should have :webrick registered' do expect(Capybara.servers[:webrick]).not_to be_nil end it 'should have :puma registered' do expect(Capybara.servers[:puma]).not_to be_nil end end describe 'server=' do after do Capybara.server = :default end it 'accepts a proc' do server = ->(_app, _port) {} Capybara.server = server expect(Capybara.server).to eq server end end describe 'app_host' do after do Capybara.app_host = nil end it 'should warn if not a valid URL' do expect { Capybara.app_host = 'www.example.com' }.to raise_error(ArgumentError, /Capybara\.app_host should be set to a url/) end it 'should not warn if a valid URL' do expect { Capybara.app_host = 'http://www.example.com' }.not_to raise_error end it 'should not warn if nil' do expect { Capybara.app_host = nil }.not_to raise_error end end describe 'default_host' do around do |test| old_default = Capybara.default_host test.run Capybara.default_host = old_default end it 'should raise if not a valid URL' do expect { Capybara.default_host = 'www.example.com' }.to raise_error(ArgumentError, /Capybara\.default_host should be set to a url/) end it 'should not warn if a valid URL' do expect { Capybara.default_host = 'http://www.example.com' }.not_to raise_error end end end capybara-3.12.0/spec/minitest_spec.rb0000644000175000017500000001021313411315075020237 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'capybara/minitest' class MinitestTest < Minitest::Test include Capybara::DSL include Capybara::Minitest::Assertions def setup visit('/form') end def teardown Capybara.reset_sessions! end def test_assert_text assert_text('Form') assert_no_text('Not on the page') refute_text('Also Not on the page') end def test_assert_title visit('/with_title') assert_title('Test Title') assert_no_title('Not the title') refute_title('Not the title') end def test_assert_current_path assert_current_path('/form') assert_no_current_path('/not_form') refute_current_path('/not_form') end def test_assert_xpath assert_xpath('.//select[@id="form_title"]') assert_xpath('.//select', count: 1) { |el| el[:id] == 'form_title' } assert_no_xpath('.//select[@id="not_form_title"]') assert_no_xpath('.//select') { |el| el[:id] == 'not_form_title' } refute_xpath('.//select[@id="not_form_title"]') end def test_assert_css assert_css('select#form_title') assert_no_css('select#not_form_title') end def test_assert_selector assert_selector(:css, 'select#form_title') assert_selector(:xpath, './/select[@id="form_title"]') assert_no_selector(:css, 'select#not_form_title') assert_no_selector(:xpath, './/select[@id="not_form_title"]') refute_selector(:css, 'select#not_form_title') end def test_assert_link visit('/with_html') assert_link('A link') assert_link(count: 1) { |el| el.text == 'A link' } assert_no_link('Not on page') end def test_assert_button assert_button('fresh_btn') assert_button(count: 1) { |el| el[:id] == 'fresh_btn' } assert_no_button('not_btn') end def test_assert_field assert_field('customer_email') assert_no_field('not_on_the_form') end def test_assert_select assert_select('form_title') assert_no_select('not_form_title') end def test_assert_checked_field assert_checked_field('form_pets_dog') assert_no_checked_field('form_pets_cat') refute_checked_field('form_pets_snake') end def test_assert_unchecked_field assert_unchecked_field('form_pets_cat') assert_no_unchecked_field('form_pets_dog') refute_unchecked_field('form_pets_snake') end def test_assert_table visit('/tables') assert_table('agent_table') assert_no_table('not_on_form') refute_table('not_on_form') end def test_assert_all_of_selectors assert_all_of_selectors(:css, 'select#form_other_title', 'input#form_last_name') end def test_assert_none_of_selectors assert_none_of_selectors(:css, 'input#not_on_page', 'input#also_not_on_page') end def test_assert_any_of_selectors assert_any_of_selectors(:css, 'input#not_on_page', 'select#form_other_title') end def test_assert_matches_selector assert_matches_selector(find(:field, 'customer_email'), :field, 'customer_email') assert_not_matches_selector(find(:select, 'form_title'), :field, 'customer_email') refute_matches_selector(find(:select, 'form_title'), :field, 'customer_email') end def test_assert_matches_css assert_matches_css(find(:select, 'form_title'), 'select#form_title') refute_matches_css(find(:select, 'form_title'), 'select#form_other_title') end def test_assert_matches_xpath assert_matches_xpath(find(:select, 'form_title'), './/select[@id="form_title"]') refute_matches_xpath(find(:select, 'form_title'), './/select[@id="form_other_title"]') end def test_assert_style skip "Rack test doesn't support style" if Capybara.current_driver == :rack_test visit('/with_html') assert_style(find(:css, '#second'), display: 'inline') end end RSpec.describe 'capybara/minitest' do before do Capybara.current_driver = :rack_test Capybara.app = TestApp end after do Capybara.use_default_driver end it 'should support minitest' do output = StringIO.new reporter = Minitest::SummaryReporter.new(output) reporter.start MinitestTest.run reporter, {} reporter.report expect(output.string).to include('20 runs, 50 assertions, 0 failures, 0 errors, 1 skips') end end capybara-3.12.0/spec/selenium_spec_ie.rb0000644000175000017500000000636213411315075020713 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'selenium-webdriver' require 'shared_selenium_session' require 'rspec/shared_spec_matchers' Capybara.register_driver :selenium_ie do |app| # ::Selenium::WebDriver.logger.level = "debug" options = ::Selenium::WebDriver::IE::Options.new options.require_window_focus = true Capybara::Selenium::Driver.new( app, browser: :ie, desired_capabilities: ::Selenium::WebDriver::Remote::Capabilities.ie, options: options ) end module TestSessions SeleniumIE = Capybara::Session.new(:selenium_ie, TestApp) end TestSessions::SeleniumIE.current_window.resize_to(800, 500) skipped_tests = %i[response_headers status_code trigger modals hover form_attribute windows] $stdout.puts `#{Selenium::WebDriver::IE.driver_path} --version` if ENV['CI'] TestSessions::SeleniumIE.current_window.resize_to(1600, 1200) Capybara::SpecHelper.run_specs TestSessions::SeleniumIE, 'selenium', capybara_skip: skipped_tests do |example| case example.metadata[:full_description] when /#refresh it reposts$/ skip 'IE insists on prompting without providing a way to suppress' when /#click_link can download a file$/ skip 'Not sure how to configure IE for automatic downloading' when /#fill_in with Date / pending "IE 11 doesn't support date input types" when /#click_link_or_button with :disabled option happily clicks on links which incorrectly have the disabled attribute$/ skip 'IE 11 obeys non-standard disabled attribute on anchor tag' when /#right_click should allow modifiers$/ skip "Windows can't :meta click because :meta triggers start menu" when /#click should allow multiple modifiers$/ skip "Windows can't :meta click because :meta triggers start menu" when /#double_click should allow multiple modifiers$/ skip "Windows can't :alt double click due to being properties shortcut" when /via clicking the wrapping label if possible$/ pending 'IEDriver has an issue with the click location of elements with multiple children if the first child is a text node and the page is scrolled' end end RSpec.describe 'Capybara::Session with Internet Explorer', capybara_skip: skipped_tests do # rubocop:disable RSpec/MultipleDescribes include Capybara::SpecHelper include_examples 'Capybara::Session', TestSessions::SeleniumIE, :selenium_ie include_examples Capybara::RSpecMatchers, TestSessions::SeleniumIE, :selenium_ie end RSpec.describe Capybara::Selenium::Node do it '#right_click should allow modifiers' do session = TestSessions::SeleniumIE session.visit('/with_js') session.find(:css, '#click-test').right_click(:control) expect(session).to have_link('Has been control right clicked') end it '#click should allow multiple modifiers' do session = TestSessions::SeleniumIE session.visit('with_js') # IE triggers system behavior with :meta so can't use those here session.find(:css, '#click-test').click(:ctrl, :shift, :alt) expect(session).to have_link('Has been alt control shift clicked') end it '#double_click should allow modifiers' do session = TestSessions::SeleniumIE session.visit('/with_js') session.find(:css, '#click-test').double_click(:shift) expect(session).to have_link('Has been shift double clicked') end end capybara-3.12.0/spec/selenium_spec_firefox.rb0000644000175000017500000001657513411315075021767 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'selenium-webdriver' require 'shared_selenium_session' require 'rspec/shared_spec_matchers' browser_options = ::Selenium::WebDriver::Firefox::Options.new browser_options.headless! if ENV['HEADLESS'] # browser_options.add_option("log", {"level": "trace"}) browser_options.profile = Selenium::WebDriver::Firefox::Profile.new.tap do |profile| profile['browser.download.dir'] = Capybara.save_path profile['browser.download.folderList'] = 2 profile['browser.helperApps.neverAsk.saveToDisk'] = 'text/csv' end Capybara.register_driver :selenium_firefox do |app| # ::Selenium::WebDriver.logger.level = "debug" Capybara::Selenium::Driver.new( app, browser: :firefox, options: browser_options, timeout: 31, # Get a trace level log from geckodriver # :driver_opts => { args: ['-vv'] } ) end Capybara.register_driver :selenium_firefox_not_clear_storage do |app| Capybara::Selenium::Driver.new( app, browser: :firefox, clear_local_storage: false, clear_session_storage: false, options: browser_options ) end module TestSessions SeleniumFirefox = Capybara::Session.new(:selenium_firefox, TestApp) end skipped_tests = %i[response_headers status_code trigger] $stdout.puts `#{Selenium::WebDriver::Firefox.driver_path} --version` if ENV['CI'] Capybara::SpecHelper.run_specs TestSessions::SeleniumFirefox, 'selenium', capybara_skip: skipped_tests do |example| case example.metadata[:full_description] when 'Capybara::Session selenium node #click should allow multiple modifiers' pending "Firefox doesn't generate an event for shift+control+click" if firefox_gte?(62, @session) && !Gem.win_platform? when /^Capybara::Session selenium node #double_click/ pending "selenium-webdriver/geckodriver doesn't generate double click event" if firefox_lt?(59, @session) when 'Capybara::Session selenium #accept_prompt should accept the prompt with a blank response when there is a default' pending "Geckodriver doesn't set a blank response in FF < 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1486485" if firefox_lt?(63, @session) when 'Capybara::Session selenium #attach_file with multipart form should fire change once for each set of files uploaded' pending 'Gekcodriver appends files so we have to first call clear for multiple files which creates an extra change ' \ 'if files are already set' when 'Capybara::Session selenium #attach_file with multipart form should fire change once when uploading multiple files from empty' pending "FF < 62 doesn't support setting all files at once" if firefox_lt?(62, @session) when 'Capybara::Session selenium #accept_confirm should work with nested modals' skip 'Broken in FF 63 - https://bugzilla.mozilla.org/show_bug.cgi?id=1487358' if firefox_gte?(63, @session) when 'Capybara::Session selenium #click_link can download a file' skip 'Need to figure out testing of file downloading on windows platform' if Gem.win_platform? when 'Capybara::Session selenium #reset_session! removes ALL cookies' pending "Geckodriver doesn't provide a way to remove cookies outside the current domain" end end RSpec.describe 'Capybara::Session with firefox' do # rubocop:disable RSpec/MultipleDescribes include Capybara::SpecHelper include_examples 'Capybara::Session', TestSessions::SeleniumFirefox, :selenium_firefox include_examples Capybara::RSpecMatchers, TestSessions::SeleniumFirefox, :selenium_firefox end RSpec.describe Capybara::Selenium::Driver do before do @driver = Capybara::Selenium::Driver.new(TestApp, browser: :firefox, options: browser_options) end describe '#quit' do it 'should reset browser when quit' do expect(@driver.browser).to be_truthy @driver.quit # access instance variable directly so we don't create a new browser instance expect(@driver.instance_variable_get(:@browser)).to be_nil end context 'with errors' do before do @original_browser = @driver.browser end after do # Ensure browser is actually quit so we don't leave hanging processe RSpec::Mocks.space.proxy_for(@original_browser).reset @original_browser.quit end it 'warns UnknownError returned during quit because the browser is probably already gone' do allow(@driver).to receive(:warn) allow(@driver.browser).to( receive(:quit) .and_raise(Selenium::WebDriver::Error::UnknownError, 'random message') ) expect { @driver.quit }.not_to raise_error expect(@driver.instance_variable_get(:@browser)).to be_nil expect(@driver).to have_received(:warn).with(/random message/) end it 'ignores silenced UnknownError returned during quit because the browser is almost definitely already gone' do allow(@driver).to receive(:warn) allow(@driver.browser).to( receive(:quit) .and_raise(Selenium::WebDriver::Error::UnknownError, 'Error communicating with the remote browser') ) expect { @driver.quit }.not_to raise_error expect(@driver.instance_variable_get(:@browser)).to be_nil expect(@driver).not_to have_received(:warn) end end end context 'storage' do describe '#reset!' do it 'clears storage by default' do @session = TestSessions::SeleniumFirefox @session.visit('/with_js') @session.find(:css, '#set-storage').click @session.reset! @session.visit('/with_js') expect(@session.driver.browser.local_storage.keys).to be_empty expect(@session.driver.browser.session_storage.keys).to be_empty end it 'does not clear storage when false' do @session = Capybara::Session.new(:selenium_firefox_not_clear_storage, TestApp) @session.visit('/with_js') @session.find(:css, '#set-storage').click @session.reset! @session.visit('/with_js') expect(@session.driver.browser.local_storage.keys).not_to be_empty expect(@session.driver.browser.session_storage.keys).not_to be_empty end end end context 'timeout' do it 'sets the http client read timeout' do expect(TestSessions::SeleniumFirefox.driver.browser.send(:bridge).http.read_timeout).to eq 31 end end end RSpec.describe Capybara::Selenium::Node do context '#click' do it 'warns when attempting on a table row' do session = TestSessions::SeleniumFirefox session.visit('/tables') tr = session.find(:css, '#agent_table tr:first-child') allow(tr.base).to receive(:warn) tr.click expect(tr.base).to have_received(:warn).with(/Clicking the first cell in the row instead/) end it 'should allow multiple modifiers', requires: [:js] do session = TestSessions::SeleniumFirefox session.visit('with_js') # Firefox v62+ doesn't generate an event for control+shift+click session.find(:css, '#click-test').click(:alt, :ctrl, :meta) # it also triggers a contextmenu event when control is held so don't check click type expect(session).to have_link('Has been alt control meta') end end context '#send_keys' do it 'should process space' do session = TestSessions::SeleniumFirefox session.visit('/form') session.find(:css, '#address1_city').send_keys('ocean', [:shift, :space, 'side']) expect(session.find(:css, '#address1_city').value).to eq 'ocean SIDE' end end end capybara-3.12.0/spec/selenium_spec_chrome.rb0000644000175000017500000000743013411315075021570 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' require 'selenium-webdriver' require 'shared_selenium_session' require 'rspec/shared_spec_matchers' CHROME_DRIVER = :selenium_chrome browser_options = ::Selenium::WebDriver::Chrome::Options.new browser_options.headless! if ENV['HEADLESS'] browser_options.add_option(:w3c, !!ENV['W3C']) Capybara.register_driver :selenium_chrome do |app| Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options, timeout: 30).tap do |driver| driver.browser.download_path = Capybara.save_path end end Capybara.register_driver :selenium_chrome_not_clear_storage do |app| chrome_options = { browser: :chrome, options: browser_options } Capybara::Selenium::Driver.new(app, chrome_options.merge(clear_local_storage: false, clear_session_storage: false)) end module TestSessions Chrome = Capybara::Session.new(CHROME_DRIVER, TestApp) end skipped_tests = %i[response_headers status_code trigger] $stdout.puts `#{Selenium::WebDriver::Chrome.driver_path} --version` if ENV['CI'] Capybara::SpecHelper.run_specs TestSessions::Chrome, CHROME_DRIVER.to_s, capybara_skip: skipped_tests do |example| case example.metadata[:full_description] when /#click_link can download a file$/ skip 'Need to figure out testing of file downloading on windows platform' if Gem.win_platform? when /Capybara::Session selenium_chrome Capybara::Window#maximize/ pending "Chrome headless doesn't support maximize" if ENV['HEADLESS'] end end RSpec.describe 'Capybara::Session with chrome' do include Capybara::SpecHelper include_examples 'Capybara::Session', TestSessions::Chrome, CHROME_DRIVER include_examples Capybara::RSpecMatchers, TestSessions::Chrome, CHROME_DRIVER context 'storage' do describe '#reset!' do it 'clears storage by default' do @session = TestSessions::Chrome @session.visit('/with_js') @session.find(:css, '#set-storage').click @session.reset! @session.visit('/with_js') expect(@session.evaluate_script('Object.keys(localStorage)')).to be_empty expect(@session.evaluate_script('Object.keys(sessionStorage)')).to be_empty end it 'does not clear storage when false' do @session = Capybara::Session.new(:selenium_chrome_not_clear_storage, TestApp) @session.visit('/with_js') @session.find(:css, '#set-storage').click @session.reset! @session.visit('/with_js') expect(@session.evaluate_script('Object.keys(localStorage)')).not_to be_empty expect(@session.evaluate_script('Object.keys(sessionStorage)')).not_to be_empty end end end context 'timeout' do it 'sets the http client read timeout' do expect(TestSessions::Chrome.driver.browser.send(:bridge).http.read_timeout).to eq 30 end end describe 'filling in Chrome-specific date and time fields with keystrokes' do let(:datetime) { Time.new(1983, 6, 19, 6, 30) } before do @session = TestSessions::Chrome @session.visit('/form') end it 'should fill in a date input with a String' do @session.fill_in('form_date', with: '06/19/1983') @session.click_button('awesome') expect(Date.parse(extract_results(@session)['date'])).to eq datetime.to_date end it 'should fill in a time input with a String' do @session.fill_in('form_time', with: '06:30A') @session.click_button('awesome') results = extract_results(@session)['time'] expect(Time.parse(results).strftime('%r')).to eq datetime.strftime('%r') end it 'should fill in a datetime input with a String' do @session.fill_in('form_datetime', with: "06/19/1983\t06:30A") @session.click_button('awesome') expect(Time.parse(extract_results(@session)['datetime'])).to eq datetime end end end capybara-3.12.0/spec/filter_set_spec.rb0000644000175000017500000000272613411315075020555 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'spec_helper' RSpec.describe Capybara::Selector::FilterSet do after do Capybara::Selector::FilterSet.remove(:test) end it 'allows node filters' do fs = Capybara::Selector::FilterSet.add(:test) do node_filter(:node_test, :boolean) { |_node, _value| true } expression_filter(:expression_test, :boolean) { |_expr, _value| true } end expect(fs.node_filters.keys).to include(:node_test) expect(fs.node_filters.keys).not_to include(:expression_test) end it 'allows expression filters' do fs = Capybara::Selector::FilterSet.add(:test) do node_filter(:node_test, :boolean) { |_node, _value| true } expression_filter(:expression_test, :boolean) { |_expr, _value| true } end expect(fs.expression_filters.keys).to include(:expression_test) expect(fs.expression_filters.keys).not_to include(:node_test) end it 'allows node filter and expression filter with the same name' do fs = Capybara::Selector::FilterSet.add(:test) do node_filter(:test, :boolean) { |_node, _value| true } expression_filter(:test, :boolean) { |_expr, _value| true } end expect(fs.expression_filters[:test]).not_to eq fs.node_filters[:test] end it 'allows `filter` as an alias of `node_filter`' do fs = Capybara::Selector::FilterSet.add(:test) do filter(:node_test, :boolean) { |_node, _value| true } end expect(fs.node_filters.keys).to include(:node_test) end end capybara-3.12.0/spec/spec_helper.rb0000644000175000017500000000301713411315075017666 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'rspec/expectations' require 'capybara/spec/spec_helper' require 'webdrivers' if ENV['CI'] module Capybara module SpecHelper def firefox?(session) browser_name(session) == :firefox && session.driver.browser.capabilities.is_a?(::Selenium::WebDriver::Remote::W3C::Capabilities) end def firefox_lt?(version, session) firefox?(session) && (session.driver.browser.capabilities[:browser_version].to_f < version) end def firefox_gte?(version, session) firefox?(session) && (session.driver.browser.capabilities[:browser_version].to_f >= version) end def chrome?(session) browser_name(session) == :chrome end def chrome_lt?(version, session) chrome?(session) && (session.driver.browser.capabilities[:version].to_f < version) end def chrome_gte?(version, session) chrome?(session) && (session.driver.browser.capabilities[:version].to_f >= version) end def edge?(session) browser_name(session) == :edge end def ie?(session) %i[internet_explorer ie].include?(browser_name(session)) end def browser_name(session) session.driver.browser.browser if session.respond_to?(:driver) end def remote?(session) session.driver.browser.is_a? ::Selenium::WebDriver::Remote::Driver end end end RSpec.configure do |config| Capybara::SpecHelper.configure(config) config.filter_run_including focus_: true unless ENV['CI'] config.run_all_when_everything_filtered = true end capybara-3.12.0/License.txt0000644000175000017500000000211113411315075016233 0ustar utkarsh2102utkarsh2102(The MIT License) Copyright (c) 2009-2018 Thomas Walpole, Jonas Nicklas Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. capybara-3.12.0/capybara.gemspec0000644000175000017500000004127113411315075017251 0ustar utkarsh2102utkarsh2102######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: capybara 3.12.0 ruby lib Gem::Specification.new do |s| s.name = "capybara".freeze s.version = "3.12.0" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "changelog_uri" => "https://github.com/teamcapybara/capybara/blob/master/History.md", "source_code_uri" => "https://github.com/teamcapybara/capybara" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Thomas Walpole".freeze, "Jonas Nicklas".freeze] s.cert_chain = ["gem-public_cert.pem".freeze] s.date = "2018-11-28" s.description = "Capybara is an integration testing tool for rack based web applications. It simulates how a user would interact with a website".freeze s.email = ["twalpole@gmail.com".freeze, "jonas.nicklas@gmail.com".freeze] s.files = ["History.md".freeze, "License.txt".freeze, "README.md".freeze, "lib/capybara.rb".freeze, "lib/capybara/config.rb".freeze, "lib/capybara/cucumber.rb".freeze, "lib/capybara/driver/base.rb".freeze, "lib/capybara/driver/node.rb".freeze, "lib/capybara/dsl.rb".freeze, "lib/capybara/helpers.rb".freeze, "lib/capybara/minitest.rb".freeze, "lib/capybara/minitest/spec.rb".freeze, "lib/capybara/node/actions.rb".freeze, "lib/capybara/node/base.rb".freeze, "lib/capybara/node/document.rb".freeze, "lib/capybara/node/document_matchers.rb".freeze, "lib/capybara/node/element.rb".freeze, "lib/capybara/node/finders.rb".freeze, "lib/capybara/node/matchers.rb".freeze, "lib/capybara/node/simple.rb".freeze, "lib/capybara/queries/ancestor_query.rb".freeze, "lib/capybara/queries/base_query.rb".freeze, "lib/capybara/queries/current_path_query.rb".freeze, "lib/capybara/queries/match_query.rb".freeze, "lib/capybara/queries/selector_query.rb".freeze, "lib/capybara/queries/sibling_query.rb".freeze, "lib/capybara/queries/style_query.rb".freeze, "lib/capybara/queries/text_query.rb".freeze, "lib/capybara/queries/title_query.rb".freeze, "lib/capybara/rack_test/browser.rb".freeze, "lib/capybara/rack_test/css_handlers.rb".freeze, "lib/capybara/rack_test/driver.rb".freeze, "lib/capybara/rack_test/form.rb".freeze, "lib/capybara/rack_test/node.rb".freeze, "lib/capybara/rails.rb".freeze, "lib/capybara/result.rb".freeze, "lib/capybara/rspec.rb".freeze, "lib/capybara/rspec/features.rb".freeze, "lib/capybara/rspec/matcher_proxies.rb".freeze, "lib/capybara/rspec/matchers.rb".freeze, "lib/capybara/rspec/matchers/base.rb".freeze, "lib/capybara/rspec/matchers/become_closed.rb".freeze, "lib/capybara/rspec/matchers/compound.rb".freeze, "lib/capybara/rspec/matchers/have_current_path.rb".freeze, "lib/capybara/rspec/matchers/have_selector.rb".freeze, "lib/capybara/rspec/matchers/have_style.rb".freeze, "lib/capybara/rspec/matchers/have_text.rb".freeze, "lib/capybara/rspec/matchers/have_title.rb".freeze, "lib/capybara/rspec/matchers/match_selector.rb".freeze, "lib/capybara/selector.rb".freeze, "lib/capybara/selector/builders/css_builder.rb".freeze, "lib/capybara/selector/builders/xpath_builder.rb".freeze, "lib/capybara/selector/css.rb".freeze, "lib/capybara/selector/filter.rb".freeze, "lib/capybara/selector/filter_set.rb".freeze, "lib/capybara/selector/filters/base.rb".freeze, "lib/capybara/selector/filters/expression_filter.rb".freeze, "lib/capybara/selector/filters/locator_filter.rb".freeze, "lib/capybara/selector/filters/node_filter.rb".freeze, "lib/capybara/selector/regexp_disassembler.rb".freeze, "lib/capybara/selector/selector.rb".freeze, "lib/capybara/selenium/driver.rb".freeze, "lib/capybara/selenium/driver_specializations/chrome_driver.rb".freeze, "lib/capybara/selenium/driver_specializations/firefox_driver.rb".freeze, "lib/capybara/selenium/extensions/html5_drag.rb".freeze, "lib/capybara/selenium/node.rb".freeze, "lib/capybara/selenium/nodes/chrome_node.rb".freeze, "lib/capybara/selenium/nodes/firefox_node.rb".freeze, "lib/capybara/selenium/patches/pause_duration_fix.rb".freeze, "lib/capybara/server.rb".freeze, "lib/capybara/server/animation_disabler.rb".freeze, "lib/capybara/server/checker.rb".freeze, "lib/capybara/server/middleware.rb".freeze, "lib/capybara/session.rb".freeze, "lib/capybara/session/config.rb".freeze, "lib/capybara/session/matchers.rb".freeze, "lib/capybara/spec/fixtures/another_test_file.txt".freeze, "lib/capybara/spec/fixtures/capybara.jpg".freeze, "lib/capybara/spec/fixtures/no_extension".freeze, "lib/capybara/spec/fixtures/test_file.txt".freeze, "lib/capybara/spec/public/jquery-ui.js".freeze, "lib/capybara/spec/public/jquery.js".freeze, "lib/capybara/spec/public/test.js".freeze, "lib/capybara/spec/session/accept_alert_spec.rb".freeze, "lib/capybara/spec/session/accept_confirm_spec.rb".freeze, "lib/capybara/spec/session/accept_prompt_spec.rb".freeze, "lib/capybara/spec/session/all_spec.rb".freeze, "lib/capybara/spec/session/ancestor_spec.rb".freeze, "lib/capybara/spec/session/assert_all_of_selectors_spec.rb".freeze, "lib/capybara/spec/session/assert_current_path_spec.rb".freeze, "lib/capybara/spec/session/assert_selector_spec.rb".freeze, "lib/capybara/spec/session/assert_style_spec.rb".freeze, "lib/capybara/spec/session/assert_text_spec.rb".freeze, "lib/capybara/spec/session/assert_title_spec.rb".freeze, "lib/capybara/spec/session/attach_file_spec.rb".freeze, "lib/capybara/spec/session/body_spec.rb".freeze, "lib/capybara/spec/session/check_spec.rb".freeze, "lib/capybara/spec/session/choose_spec.rb".freeze, "lib/capybara/spec/session/click_button_spec.rb".freeze, "lib/capybara/spec/session/click_link_or_button_spec.rb".freeze, "lib/capybara/spec/session/click_link_spec.rb".freeze, "lib/capybara/spec/session/current_scope_spec.rb".freeze, "lib/capybara/spec/session/current_url_spec.rb".freeze, "lib/capybara/spec/session/dismiss_confirm_spec.rb".freeze, "lib/capybara/spec/session/dismiss_prompt_spec.rb".freeze, "lib/capybara/spec/session/element/assert_match_selector_spec.rb".freeze, "lib/capybara/spec/session/element/match_css_spec.rb".freeze, "lib/capybara/spec/session/element/match_xpath_spec.rb".freeze, "lib/capybara/spec/session/element/matches_selector_spec.rb".freeze, "lib/capybara/spec/session/evaluate_async_script_spec.rb".freeze, "lib/capybara/spec/session/evaluate_script_spec.rb".freeze, "lib/capybara/spec/session/execute_script_spec.rb".freeze, "lib/capybara/spec/session/fill_in_spec.rb".freeze, "lib/capybara/spec/session/find_button_spec.rb".freeze, "lib/capybara/spec/session/find_by_id_spec.rb".freeze, "lib/capybara/spec/session/find_field_spec.rb".freeze, "lib/capybara/spec/session/find_link_spec.rb".freeze, "lib/capybara/spec/session/find_spec.rb".freeze, "lib/capybara/spec/session/first_spec.rb".freeze, "lib/capybara/spec/session/frame/frame_title_spec.rb".freeze, "lib/capybara/spec/session/frame/frame_url_spec.rb".freeze, "lib/capybara/spec/session/frame/switch_to_frame_spec.rb".freeze, "lib/capybara/spec/session/frame/within_frame_spec.rb".freeze, "lib/capybara/spec/session/go_back_spec.rb".freeze, "lib/capybara/spec/session/go_forward_spec.rb".freeze, "lib/capybara/spec/session/has_all_selectors_spec.rb".freeze, "lib/capybara/spec/session/has_any_selectors_spec.rb".freeze, "lib/capybara/spec/session/has_button_spec.rb".freeze, "lib/capybara/spec/session/has_css_spec.rb".freeze, "lib/capybara/spec/session/has_current_path_spec.rb".freeze, "lib/capybara/spec/session/has_field_spec.rb".freeze, "lib/capybara/spec/session/has_link_spec.rb".freeze, "lib/capybara/spec/session/has_none_selectors_spec.rb".freeze, "lib/capybara/spec/session/has_select_spec.rb".freeze, "lib/capybara/spec/session/has_selector_spec.rb".freeze, "lib/capybara/spec/session/has_style_spec.rb".freeze, "lib/capybara/spec/session/has_table_spec.rb".freeze, "lib/capybara/spec/session/has_text_spec.rb".freeze, "lib/capybara/spec/session/has_title_spec.rb".freeze, "lib/capybara/spec/session/has_xpath_spec.rb".freeze, "lib/capybara/spec/session/headers_spec.rb".freeze, "lib/capybara/spec/session/html_spec.rb".freeze, "lib/capybara/spec/session/node_spec.rb".freeze, "lib/capybara/spec/session/node_wrapper_spec.rb".freeze, "lib/capybara/spec/session/refresh_spec.rb".freeze, "lib/capybara/spec/session/reset_session_spec.rb".freeze, "lib/capybara/spec/session/response_code_spec.rb".freeze, "lib/capybara/spec/session/save_and_open_page_spec.rb".freeze, "lib/capybara/spec/session/save_and_open_screenshot_spec.rb".freeze, "lib/capybara/spec/session/save_page_spec.rb".freeze, "lib/capybara/spec/session/save_screenshot_spec.rb".freeze, "lib/capybara/spec/session/screenshot_spec.rb".freeze, "lib/capybara/spec/session/select_spec.rb".freeze, "lib/capybara/spec/session/selectors_spec.rb".freeze, "lib/capybara/spec/session/sibling_spec.rb".freeze, "lib/capybara/spec/session/source_spec.rb".freeze, "lib/capybara/spec/session/text_spec.rb".freeze, "lib/capybara/spec/session/title_spec.rb".freeze, "lib/capybara/spec/session/uncheck_spec.rb".freeze, "lib/capybara/spec/session/unselect_spec.rb".freeze, "lib/capybara/spec/session/visit_spec.rb".freeze, "lib/capybara/spec/session/window/become_closed_spec.rb".freeze, "lib/capybara/spec/session/window/current_window_spec.rb".freeze, "lib/capybara/spec/session/window/open_new_window_spec.rb".freeze, "lib/capybara/spec/session/window/switch_to_window_spec.rb".freeze, "lib/capybara/spec/session/window/window_opened_by_spec.rb".freeze, "lib/capybara/spec/session/window/window_spec.rb".freeze, "lib/capybara/spec/session/window/windows_spec.rb".freeze, "lib/capybara/spec/session/window/within_window_spec.rb".freeze, "lib/capybara/spec/session/within_spec.rb".freeze, "lib/capybara/spec/spec_helper.rb".freeze, "lib/capybara/spec/test_app.rb".freeze, "lib/capybara/spec/views/buttons.erb".freeze, "lib/capybara/spec/views/fieldsets.erb".freeze, "lib/capybara/spec/views/form.erb".freeze, "lib/capybara/spec/views/frame_child.erb".freeze, "lib/capybara/spec/views/frame_one.erb".freeze, "lib/capybara/spec/views/frame_parent.erb".freeze, "lib/capybara/spec/views/frame_two.erb".freeze, "lib/capybara/spec/views/header_links.erb".freeze, "lib/capybara/spec/views/host_links.erb".freeze, "lib/capybara/spec/views/initial_alert.erb".freeze, "lib/capybara/spec/views/obscured.erb".freeze, "lib/capybara/spec/views/path.erb".freeze, "lib/capybara/spec/views/popup_one.erb".freeze, "lib/capybara/spec/views/popup_two.erb".freeze, "lib/capybara/spec/views/postback.erb".freeze, "lib/capybara/spec/views/tables.erb".freeze, "lib/capybara/spec/views/with_animation.erb".freeze, "lib/capybara/spec/views/with_base_tag.erb".freeze, "lib/capybara/spec/views/with_count.erb".freeze, "lib/capybara/spec/views/with_fixed_header_footer.erb".freeze, "lib/capybara/spec/views/with_hover.erb".freeze, "lib/capybara/spec/views/with_html.erb".freeze, "lib/capybara/spec/views/with_html5_svg.erb".freeze, "lib/capybara/spec/views/with_html_entities.erb".freeze, "lib/capybara/spec/views/with_js.erb".freeze, "lib/capybara/spec/views/with_namespace.erb".freeze, "lib/capybara/spec/views/with_scope.erb".freeze, "lib/capybara/spec/views/with_simple_html.erb".freeze, "lib/capybara/spec/views/with_slow_unload.erb".freeze, "lib/capybara/spec/views/with_title.erb".freeze, "lib/capybara/spec/views/with_unload_alert.erb".freeze, "lib/capybara/spec/views/with_windows.erb".freeze, "lib/capybara/spec/views/within_frames.erb".freeze, "lib/capybara/version.rb".freeze, "lib/capybara/window.rb".freeze, "spec/basic_node_spec.rb".freeze, "spec/capybara_spec.rb".freeze, "spec/css_builder_spec.rb".freeze, "spec/css_splitter_spec.rb".freeze, "spec/dsl_spec.rb".freeze, "spec/filter_set_spec.rb".freeze, "spec/fixtures/capybara.csv".freeze, "spec/fixtures/certificate.pem".freeze, "spec/fixtures/key.pem".freeze, "spec/fixtures/selenium_driver_rspec_failure.rb".freeze, "spec/fixtures/selenium_driver_rspec_success.rb".freeze, "spec/minitest_spec.rb".freeze, "spec/minitest_spec_spec.rb".freeze, "spec/per_session_config_spec.rb".freeze, "spec/rack_test_spec.rb".freeze, "spec/regexp_dissassembler_spec.rb".freeze, "spec/result_spec.rb".freeze, "spec/rspec/features_spec.rb".freeze, "spec/rspec/scenarios_spec.rb".freeze, "spec/rspec/shared_spec_matchers.rb".freeze, "spec/rspec/views_spec.rb".freeze, "spec/rspec_matchers_spec.rb".freeze, "spec/rspec_spec.rb".freeze, "spec/selector_spec.rb".freeze, "spec/selenium_spec_chrome.rb".freeze, "spec/selenium_spec_chrome_remote.rb".freeze, "spec/selenium_spec_edge.rb".freeze, "spec/selenium_spec_firefox.rb".freeze, "spec/selenium_spec_firefox_remote.rb".freeze, "spec/selenium_spec_ie.rb".freeze, "spec/server_spec.rb".freeze, "spec/session_spec.rb".freeze, "spec/shared_selenium_session.rb".freeze, "spec/spec_helper.rb".freeze, "spec/xpath_builder_spec.rb".freeze] s.homepage = "https://github.com/teamcapybara/capybara".freeze s.licenses = ["MIT".freeze] s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze) s.rubygems_version = "2.5.2.1".freeze s.summary = "Capybara aims to simplify the process of integration testing Rack applications, such as Rails, Sinatra or Merb".freeze if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_runtime_dependency(%q.freeze, [">= 0"]) s.add_development_dependency(%q.freeze, [">= 0"]) s.add_development_dependency(%q.freeze, [">= 2.3.0"]) s.add_development_dependency(%q.freeze, [">= 0"]) s.add_development_dependency(%q.freeze, [">= 1.0.0"]) s.add_development_dependency(%q.freeze, [">= 2.0.4"]) s.add_runtime_dependency(%q.freeze, [">= 0.1.3"]) s.add_development_dependency(%q.freeze, [">= 0"]) s.add_runtime_dependency(%q.freeze, ["~> 1.8"]) s.add_development_dependency(%q.freeze, [">= 0"]) s.add_runtime_dependency(%q.freeze, [">= 1.6.0"]) s.add_runtime_dependency(%q.freeze, [">= 0.6.3"]) s.add_development_dependency(%q.freeze, [">= 0"]) s.add_runtime_dependency(%q.freeze, ["~> 1.2"]) s.add_development_dependency(%q.freeze, [">= 3.5.0"]) s.add_development_dependency(%q.freeze, [">= 0"]) s.add_development_dependency(%q.freeze, [">= 0"]) s.add_development_dependency(%q.freeze, ["~> 3.5"]) s.add_development_dependency(%q.freeze, [">= 1.4.0"]) s.add_runtime_dependency(%q.freeze, ["~> 3.2"]) s.add_development_dependency(%q.freeze, [">= 0.9.0"]) else s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 2.3.0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 1.0.0"]) s.add_dependency(%q.freeze, [">= 2.0.4"]) s.add_dependency(%q.freeze, [">= 0.1.3"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["~> 1.8"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 1.6.0"]) s.add_dependency(%q.freeze, [">= 0.6.3"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["~> 1.2"]) s.add_dependency(%q.freeze, [">= 3.5.0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["~> 3.5"]) s.add_dependency(%q.freeze, [">= 1.4.0"]) s.add_dependency(%q.freeze, ["~> 3.2"]) s.add_dependency(%q.freeze, [">= 0.9.0"]) end else s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 2.3.0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 1.0.0"]) s.add_dependency(%q.freeze, [">= 2.0.4"]) s.add_dependency(%q.freeze, [">= 0.1.3"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["~> 1.8"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 1.6.0"]) s.add_dependency(%q.freeze, [">= 0.6.3"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["~> 1.2"]) s.add_dependency(%q.freeze, [">= 3.5.0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["~> 3.5"]) s.add_dependency(%q.freeze, [">= 1.4.0"]) s.add_dependency(%q.freeze, ["~> 3.2"]) s.add_dependency(%q.freeze, [">= 0.9.0"]) end end capybara-3.12.0/History.md0000644000175000017500000016345213411315075016113 0ustar utkarsh2102utkarsh2102# Version 3.12.0 Release date: 2018-11-28 ### Added * Support Ruby 2.6 endless range in Result#[] and query `:between` option * Pre-registered headless firefox driver :selenium_headless [Andrew Havens] * Selenium driver now defaults to clearing `sessionStorage` and `localStorage`. To disable pass `clear_local_storage: false` and/or `clear_session_storage: false` when creating Capybara::Selenium::Driver instance in your driver registration ### Fixed * Raise error if only :x or :y are passed as an offset to click methods ### Removed * Support for RSpec < 3.5 # Version 3.11.1 Release date: 2018-11-16 ### Fixed * Fixed :link_or_button XPath generation when it has had an expression filter added # Version 3.11.0 Release date: 2018-11-14 ### Added * Ability for node filters to set detailed error messages * `Capybara::HTML` Will use `nokogumbo` for HTML parsing if installed * `Selector#locator_filter` added to allow for dynamic locator in selectors ### Fixed * Node filters are evaluated in the context of the Selector they are used in to ensure the correct options are used # Version 3.10.1 Release date: 2018-11-03 ### Fixed * Fix `aria-label` and `test_id` matching for `link_or_button` selector type - Issue #2125 * Fixed crash in element path creation for matcher failure messages - Issue #2120 # Version 3.10.0 Release date: 2018-10-23 ### Added * :class filter can now check for class names starting with ! * Selector `xpath`/`css` expression definitions will get filter names from block parameters if not explicitly provided * `any_of_selectors` assertions and matchers to complement `all_of_selectors` and `none_of_selectors` ### Fixed * Selector `css` expression definiton declared filters now work again * Cleaned up warnings [Yuji Yaginuma] * Workaround installation of rspec matcher proxies under jruby by reverting to the old solution not using prepend, so jruby bugs are not hit - Issue #2115 # Version 3.9.0 Release date: 2018-10-03 ### Added * Selenium with Chrome removes all cookies at session reset instead of just cookies from current domain if possible * Support for Regexp for system :id and :class filters where possible * `using_session` now accepts a session object as well as the name of the session for users who manually manage sessions * The `:field` selector will now find `type = "hidden"` fields if the `type: "hidden"` filter option is provided # Version 3.8.2 Release date: 2018-09-26 ### Fixed * Fixed negated class selector option - Issue #2103 # Version 3.8.1 Release date: 2018-09-22 ### Fixed * Filling in of date fields with a string when using selenium chrome regression [Micah Geisel] # Version 3.8.0 Release date: 2018-09-20 ### Added * Workaround geckodriver 0.22 issue with undefined pause durations * :element selector ignores XML namespaces ### Fixed * Added Errno::ECONNRESET to the errors which will allows https server detection # Version 3.7.2 Release date: 2018-09-12 ### Fixed * Fix MatchQuery based matchers when used on a root element found using any type of parent/ancestor query - Issue #2097 * Fix Chrome/FF HTML5 drag simulation for elements (a, img) which default to draggable - Issue #2098 # Version 3.7.1 Release date: 2018-09-05 ### Fixed * Restored ability to pass symbol as the CSS selector when calling `has_css?`/`have_css`/etc - Issue #2093 # Version 3.7.0 Release date: 2018-09-02 ### Added * `Capybara.disable_animation` can be set to a CSS selector to identify which elements will have animation disabled [Michael Glass] * `Capybara.default_normalize_ws` option which sets whether or not text predicates and matchers (`has_text?`, `has_content?`, `assert_text`, etc) use `normalize_ws` option by default. Defaults to false. [Stegalin Ivan] * Selector based predicates, matchers, and finders now support the `:normalize_ws` option for the `:text`/`:exact_text` filters. Defaults to the `Capybara.default_normalize_ws`setting above. * Element `choose`/`check`/`uncheck`/`attach_file`/`fill_in` can now operate on the element they're called on or a descendant if no locator is passed. ### Fixed * All CSS styles applied by the `Element#attach_file` `:make_visible` option will now have `!important` priority set to ensure they override any other specified style. * Firefox file inputs are only manually cleared when necessary. # Version 3.6.0 Release date: 2018-08-14 ### Added * Workaround geckodriver/firefox send_keys issues as much as possible using the Selenium actions API * Workaround lack of HTML5 native drag and drop events when using Selenium driver with Chrome and FF >= 62 * `Capybara.predicates_wait` option which sets whether or not Capybaras matcher predicate methods (`has_css?`, `has_selector?`, `has_text?`, etc.) default to using waiting/retrying behavior (defaults to true) # Version 3.5.1 Release date: 2018-08-03 ### Fixed * Fixed misspelled method name `refute_matches_elector` => `refute_matches_selector` # Version 3.5.0 Release date: 2018-08-01 ### Added * text predicates and matchers (`has_text?`, `has_content?`, `assert_text`, etc) now support a `normalize_ws` option ### Fixed * `attach_file` with Selenium and local Firefox 62+ now correctly generates only one change event when attaching multiple files # Version 3.4.2 Release date: 2018-07-24 ### Fixed * `match_xxx` selectors and `matches_xxx?` predicates work correctly with elements found using a sibling selector - Issue #2073 # Version 3.4.1 Release date: 2018-07-20 ### Fixed * `Session#evaluate_script` now strips the script in `Session` rather than only in the Selenium driver # Version 3.4.0 Release date: 2018-07-19 ### Fixed * Make selenium driver :backspace clear stategy work even if caret location is in middle of field content [Champier Cyril] * Selenium issue with fieldset nested in disabled fieldset not being considered disabled * `Session#evaluate_script` and `Element#evaluate_script` now strip leading/trailing whitespace from scripts [Ian Lesperance] ### Added * Work around Selenium lack of support for `file_detector` with remote geckodriver * `#within_frame` locator is optional when only one frame exists * `Capybara.test_id` option that allows for matching the Capybara provided selector types on an arbitrary attribute (defaults to nil), set to your test id attribute ('data-test-id, etc) if using test id attributes in your project # Version 3.3.1 Release date: 2018-06-27 ### Fixed * `selenium-webdriver` version check [ahorek] * Selenium driver correctly responds to `disabled?` for fieldset elements - Issue #2059 [Thomas Walpole] # Version 3.3.0 Release date: 2018-06-25 ### Added * RackTest driver now handles 307/308 redirects * `execute_async_script` can now be called on elements to run the JS in the context of the element * `:download` filter option on `:link' selector * `Window#fullscreen` * `Element#style` and associated matchers ### Changed * Minimum "supported" `selenium-webdriver` is raised to 3.5.0 (but you really should be using newer than that) ### Fixes * Selenium driver with Firefox workaround for clicking on table row - https://github.com/mozilla/geckodriver/issues/1228 * :class and :id filters applied to CSS based selectors now correctly handle the CSS comma * Selenium driver handles namespaces when generating an elements `#path` - Issue #2048 # Version 3.2.1 Release date: 2018-06-04 ### Fixes * Only split CSS selectors when :class or :id options are given. Restores 3.1.1 functionality for now but the underlying issue will require a larger fix, hopefully coming soon. - Issue #2044 [Thomas Walpole] # Version 3.2.0 Release date: 2018-06-01 ### Changed * Ruby 2.3.0+ is now required * `ElementNotFound` errors raised in selector filters are interpreted as non-matches ### Added * New global configuration `default_set_options` used in `Capybara::Node::Element#set` as default `options` hash [Champier Cyril] * `execute_script` and `evaluate_script` can now be called on elements to run the JS in the context of the element [Thomas Walpole] * Filters in custom selectors now support a `matcher` Regexp to handle multiple filter options [Thomas Walpole] * `:element` selector type which will match on any attribute (other than the reserved names) passed as a filter option [Thomas Walpole] * `:class` filter option now supports preceding class names with `!` to indicate not having that class [Thomas Walpole] * `:class` and `:id` filter options now accept `XPath::Expression` objects to allow for more flexibility in matching [Thomas Walpole] * `Capybara.disable_animation` setting which triggers loading of a middleware that attempts to disable animations in pages. This is very much a beta feature and may change/disappear in the future. [Thomas Walpole] # Version 3.1.1 Release date: 2018-05-25 ### Fixes * Ensure keystrokes are sent when setting time/date fields to a string with the Selenium driver [Thomas Walpole] # Version 3.1.0 Release date: 2018-05-10 ### Added * Support for using `select` with text inputs associated with a datalist element * `type` filter on `:button` selector * Support for server operating in https mode * Selenium driver now uses JS to fill_in/set date and time fields when passed date or time objects [Aleksei Gusev, Thomas Walpole] # Version 3.0.3 Release date: 2018-04-30 ### Fixes * Issue in `check` where the locator string could not be omitted * Selenium browser type detection when using remote [Ian Ker-Seymer] * Potential hang when waiting for requests to complete [Chris Zetter] # Version 3.0.2 Release date: 2018-04-13 ### Fixes * Fix expression filter descriptions in some selector failure messages * Fix compounding of negated matechers - Issue #2010 # Version 3.0.1 Release date: 2018-04-06 ### Changed * Restored ability for `Capybara.server=` to accept a proc which was accidentally removed in 3.0.0 # Version 3.0.0 Release date: 2018-04-05 ### Changed * Selenium driver only closes extra windows for browsers where that is known to work (Firefox, Chrome) * "threadsafe" mode is no longer considered beta ### Fixes * Multiple file attach_file with Firefox * Use Puma::Server directly rather than Rack::Handler::Puma so signal handlers don't prevent test quitting # Version 3.0.0.rc2 Release date: 2018-03-23 ### Changed * Visibile text whitespace is no longer fully normalized in favor of being more in line with the WebDriver spec for visible text * Drivers are expected to close extra windows when resetting the session * Selenium driver supports Date/Time when filling in date/time/datetime-local inputs * `current_url` returns the url for the top level browsing context * `title` returns the title for the top level browsing context ### Added * `Driver#frame_url` returns the url for the current frame * `Driver#frame_title` returns the title for the current frame # Version 3.0.0.rc1 Release date: 2018-03-02 ### Added * Support for libraries wrapping Capybara elements and providing a `#to_capybara_node` method ### Changed * `first` now raises ElementNotFound, by default, instead of returning nil when no matches are found - Issue #1507 * 'all' now waits for at least one matching element by default. Pass `wait: false` if you want the previous behavior where an empty result would be returned immediately if no matching elements exist yet. * ArgumentError raised if extra parameters passed to selector queries ### Removed * Ruby < 2.2.2 support * `Capybara.exact_options` no longer exists. Just use `exact: true` on relevant actions/finders if necessary. * All previously deprecated methods removed * RSpec 2.x support * selenium-webdriver 2.x support * Nokogiri < 1.8 support * `field_labeled` alias for `find_field` # Version 2.18.0 Release date: 2018-02-12 ### Fixed * Firefox/geckodriver setting of contenteditable childs contents * Ignore Selenium::WebDriver::Error::SessionNotCreatedError when quitting driver [Tim Connor] ### Removed * Headless chrome modal JS injection that is no longer needed for Chrome 64+/chromedriver 2.35+ # Version 2.17.0 Release date: 2018-01-02 ### Added * `have_all_of_selectors`, `have_none_of_selectors` RSpec matchers for parity with minitest assertions [Thomas Walpole] ### Fixed * Allow xpath 3.x gem [Thomas Walpole] * Issue when drivers returned nil for `current_path` and a matcher was used with a Regexp [Thomas Walpole] * Error message when visible element not found, but non-visible was [Andy Klimczak] # Version 2.16.1 Release date: 2017-11-20 ### Fixed * Fix rack_test driver for rack_test 0.7.1/0.8.0 [Thomas Walpole] * `accept_prompt` response text can contain quotes when using selenium with headless chrome [Thomas Walpole] # Version 2.16.0 Release date: 2017-11-13 ### Added * Attempt to move element into view when selenium doesn't correctly do it - See PR #1917 [Thomas Walpole] * `current_path` matchers will now autodetect path vs url based on string to be matched. Deprecates `:only_path` in favor of `:ignore_query` option [Thomas Walpole] * Session#evaluate_async_script [Thomas Walpole] ### Fixed * Default prompt value when using headless Chrome works correctly [Thomas Walpole] * Support new modal error returned by selenium-webdriver 3.7 for W3C drivers [Thomas Walpole] * Calling `respond_to?` on the object passed to `Capybara.configure` block - Issue #1935 # Version 2.15.4 Release date: 2017-10-07 ### Fixed * Visiting an absolute URL shouldn't overwrite the port when no server or always_include_port=false - Issue #1921 # Version 2.15.3 Release date: 2017-10-03 ### Fixed * Visiting '/' when Capybara.app_host has a trailing '/' - Issue #1918 [Thomas Walpole] # Version 2.15.2 Release date: 2017-10-02 ### Fixed * Include within scope description in element not found/ambiguous errors [Thomas Walpole] * Raise error when no activation block is passed to modal methods if using headless chrome [Thomas Walpole] * Don't retry element access when inspecting [Ivan Neverov] * Don't override a specified port (even if it is default port) in visited url [Thomas Walpole] # Version 2.15.1 Release date: 2017-08-04 ### Fixed * `attach_file` with no extension/MIME type when using the `:rack_test` driver [Thomas Walpole] # Version 2.15.0 Release date: 2017-08-04 ### Added * `sibling` and `ancestor` finders added [Thomas Walpole] * Added ability to pass options to registered servers when setting * Added basic built-in driver registrations `:selenium_chrome` and `:selenium_chrome_headless` [Thomas Walpole] * Add `and_then` to Capybara RSpec matchers which behaves like the previous `and` compounder. [Thomas Walpole] * Compound RSpec expectations with Capybara matchers now run both matchers inside a retry loop rather than waiting for one to pass/fail before checking the second. Will make `#or` more performant and confirm both conditions are true "simultaneously" for `and`. [Thomas Walpole] If you still want the * Default filter values are now included in error descriptions [Thomas Walpole] * Add `Session#refresh` [Thomas Walpole] * Loosened restrictions on where `Session#within_window` can be called from [Thomas Walpole] * Switched from `mime-types` dependency to `mini_mime` [Jason Frey] # Version 2.14.4 Release date: 2017-06-27 ### Fixed * Fix retrieval of session_options for HaveSelector matcher descriptions - Issue #1883 # Version 2.14.3 Release date: 2017-06-15 ### Fixed * Minitest assertions now raise the correct error type - Issue #1879 [Thomas Walpole] * Improve flexibility of detecting Chrome headless mode [Thomas Walpole] # Version 2.14.2 Release date: 2017-06-09 ### Fixed * Workaround for system modals when using headless Chrome now works if the page changes # Version 2.14.1 Release date: 2017-06-07 ### Fixed * Catch correct error when unexpected system modals are discovered in latest selenium [Thomas Walpole] * Update default `puma` server registration to encourage it to run in single mode [Thomas Walpole] * Suppress invalid element errors raised while lazily evaluating the results of `all` [Thomas Walpole] * Added missing `with_selected` option to the :select selector to match `options`/`with_options` options - Issue #1865 [Bartosz Nowak] * Workaround broken system modals when using selenium with headless Chrome # Version 2.14.0 Release date: 2017-05-01 ### Added * "threadsafe" mode that allows per-session configuration * `:type` filter added to the `:fillable_field` selector * Proxy methods when using RSpec for `all`/`within` that call either the Capybara::DSL or RSpec matchers depending on arguments passed * Support for the new errors in selenium-webdriver 3.4 ### Fixed * Element#inspect doesn't raise an error on obsolete elements * Setting a contenteditable element with Selenium and Chrome 59 * Workaround a hang while setting the window size when using geckodriver 0.16 and Firefox 53 * Clicking on url with a blank href goes to the current url when using the RackTest driver # Version 2.13.0 Release date: 2017-03-16 ### Added * Selenium driver supports returning element(s) from evaluate_script [Thomas Walpole] * rack_test driver supports click on checkboxes and radio buttons to change their states [Thomas Walpole] * Support RSpec equivalent assertions and expectations for MiniTest [Thomas Walpole] ### Fixed * Editing of content editable children with selenium # Version 2.12.1 Release date: 2017-02-16 ### Fixed * Disable lazy Capybara::Results evaluation for JRuby due to ongoing issues # Version 2.12.0 Release date: 2017-01-22 ### Added * Session#switch_to_frame for manually handling frame switching - Issue #1365 [Thomas Walpole] * Session#within_frame now accepts a selector type (defaults to :frame) and locator [Thomas Walpole] * Session#execute_script and Session#evaluate_script now accept optional arguments that will be passed to the JS function. This may not be supported by all drivers, and the types of arguments that may be passed is limited. If drivers opt to support this feature they should support passing page elements. [Thomas Walpole] * :exact option for text and title matchers - Issue #1256 [Thomas Walpole] * :exact_text option for selector finders/minders - Issue #1256 [Thomas Walpole] * Capybara.exact_text setting that affects the text matchers and :text options passed to selector finders/matchers. Issue #1256 [Thomas Walpole] * :make_visible option for #attach_file that allows for convenient changing of the CSS style of a file input element before attaching the file to it. Requires driver support for passing page elements to Session#execute_script [Thomas Walpole] * assert_all_selectors/assert_none_of_selectors assertions added * :link selector (used by find_link/click_link) now supports finding hyperlink placeholders (no href attribute) when href: nil option is specified [Thomas Walpole] * `within_element` as an alias of `within` due to RSpec collision ### Fixed * Fields inside a disabled fieldset are now correctly considered disabled - Issue #1816 [Thomas Walpole] * Lazy Capybara::Results evaluation enabled for JRuby 9.1.6.0+ * A driver returning nil for #current_url won't raise an exception when calling #current_path [Dylan Reichstadt] * Support Ruby 2.4.0 unified Integer [Koichi ITO] * RackTest driver no longer modifies the text content of textarea elements in order to behave more like a real browser [Thomas Walpole] * TextQuery (assert_text/have_text/etc) now ignores errors when trying to generate more helpful errors messages so the original error isn't hidden [Thomas Walpole] # Version 2.11.0 Release date: 2016-12-05 ### Added * Options for clearing session/local storage on reset added to the Selenium driver * Window size changes wait for the size to stabilize * Defined return value for most actions * Ignore specific error when qutting selenium driver instance - Issue #1773 [Dylan Reichstadt, Thomas Walpole] * Warn on selenium unknown errors rather than raising when quitting driver [Adam Pohorecki, Thomas Walpole] * Capybara::Result#each now returns an `Enumerator` when called without a block - Issue #1777 [Thomas Walpole] ### Fixed * Selenium driver with Chrome should support multiple file upload [Thomas Walpole] * Fix visible: :hidden with :text option behavior [Thomas Walpole] # Version 2.10.2 Release date: 2016-11-30 ### Fixed * App exceptions with multiple parameter initializers now re-raised correctly - Issue #1785 [Michael Lutsiuk] * Use Addressable::URI when parsing current_path since it's more lenient of technically invalid URLs - Issue #1801 [Marcos Duque, Thomas Walpole] # Version 2.10.1 Release date: 2016-10-08 ### Fixed * App errors are now correctly raised with the explanatory cause in JRuby [Thomas Walpole] * Capybara::Result optimization disabled in JRuby due to issue with lazy enumerator evaluation [Thomas Walpole] See: https://github.com/jruby/jruby/issues/4212 # Version 2.10.0 Release date: 2016-10-05 ### Added * Select ` capybara-3.12.0/lib/capybara/spec/views/form.erb0000644000175000017500000005373513411315075022206 0ustar utkarsh2102utkarsh2102

Form



First address

Second address

Disabled Child
Nested Disabled Another WLegend
Disabled?

Emergency Number

capybara-3.12.0/lib/capybara/spec/views/obscured.erb0000644000175000017500000000202613411315075023034 0ustar utkarsh2102utkarsh2102 with_animation
capybara-3.12.0/lib/capybara/spec/views/header_links.erb0000644000175000017500000000021213411315075023651 0ustar utkarsh2102utkarsh2102

Link

capybara-3.12.0/lib/capybara/spec/views/with_title.erb0000644000175000017500000000011613411315075023400 0ustar utkarsh2102utkarsh2102 Test Title abcdefg capybara-3.12.0/lib/capybara/spec/views/with_windows.erb0000644000175000017500000000270113411315075023753 0ustar utkarsh2102utkarsh2102 With Windows
My scoped content
capybara-3.12.0/lib/capybara/spec/views/popup_two.erb0000644000175000017500000000026013411315075023260 0ustar utkarsh2102utkarsh2102 Title of popup two
This is the text of divInPopupTwo
capybara-3.12.0/lib/capybara/spec/views/frame_two.erb0000644000175000017500000000027413411315075023214 0ustar utkarsh2102utkarsh2102 This is the title of frame two
This is the text of divInFrameTwo
capybara-3.12.0/lib/capybara/spec/views/initial_alert.erb0000644000175000017500000000027713411315075024054 0ustar utkarsh2102utkarsh2102
Initial alert page
capybara-3.12.0/lib/capybara/spec/views/with_slow_unload.erb0000644000175000017500000000053513411315075024612 0ustar utkarsh2102utkarsh2102
This delays unload by 2 seconds
capybara-3.12.0/lib/capybara/spec/views/with_hover.erb0000644000175000017500000000132013411315075023400 0ustar utkarsh2102utkarsh2102 with_hover
Some text here so the wrapper has size
Here I am
Some text here so the wrapper has size
Here I am
capybara-3.12.0/lib/capybara/spec/views/with_html5_svg.erb0000644000175000017500000000126313411315075024173 0ustar utkarsh2102utkarsh2102 Namespace
capybara-3.12.0/lib/capybara/spec/views/fieldsets.erb0000644000175000017500000000125413411315075023212 0ustar utkarsh2102utkarsh2102
Agent

Villain

capybara-3.12.0/lib/capybara/spec/views/with_simple_html.erb0000644000175000017500000000000513411315075024571 0ustar utkarsh2102utkarsh2102 Bar capybara-3.12.0/lib/capybara/spec/views/tables.erb0000644000175000017500000000245013411315075022501 0ustar utkarsh2102utkarsh2102
Agent
Girl
Villain
capybara-3.12.0/lib/capybara/spec/views/with_unload_alert.erb0000644000175000017500000000033413411315075024732 0ustar utkarsh2102utkarsh2102
This triggers an alert on unload
Go away capybara-3.12.0/lib/capybara/spec/views/with_fixed_header_footer.erb0000644000175000017500000000073113411315075026247 0ustar utkarsh2102utkarsh2102
My headers
A tall block
Go to root
My footer
capybara-3.12.0/lib/capybara/spec/views/with_namespace.erb0000644000175000017500000000137613411315075024224 0ustar utkarsh2102utkarsh2102 Namespace
capybara-3.12.0/lib/capybara/spec/views/with_js.erb0000644000175000017500000001015513411315075022677 0ustar utkarsh2102utkarsh2102 with_js

FooBar

This is text

This link is non-HTML5 draggable

This is a draggable element.

It should be dropped here.

It should be dropped here.

It should be dropped here.

This is an HTML5 draggable element.

This is an HTML5 draggable link

It should be dropped here.

Click me

Slowly

Editable content
Some content
Content

Reload! this won't change

waiting to be reloaded

Fetch new list!

  • Item 1
  • Item 2

Change title

Change size

Click me

Open alert Alert page change

Open delayed alert Open slow alert

Open confirm

Open check twice

Open prompt

Open defaulted prompt

Change page Non-escaped query options Escaped query options

This is a draggable element.

This is an HTML5 draggable element.

capybara-3.12.0/lib/capybara/spec/views/with_animation.erb0000644000175000017500000000230213411315075024235 0ustar utkarsh2102utkarsh2102 with_animation transition me away animate me away capybara-3.12.0/lib/capybara/spec/views/path.erb0000644000175000017500000000035613411315075022166 0ustar utkarsh2102utkarsh2102 Text node capybara-3.12.0/lib/capybara/spec/views/host_links.erb0000644000175000017500000000046613411315075023411 0ustar utkarsh2102utkarsh2102

Relative Host Absolute Host

capybara-3.12.0/lib/capybara/spec/views/with_scope.erb0000644000175000017500000000250413411315075023373 0ustar utkarsh2102utkarsh2102

This page is used for testing various scopes

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim venia. Go

  • With Simple HTML: Go

  • Bar: Go

  • With Simple HTML: Go
capybara-3.12.0/lib/capybara/spec/views/within_frames.erb0000644000175000017500000000070113411315075024063 0ustar utkarsh2102utkarsh2102 With Frames
This is the text for divInMainWindow
capybara-3.12.0/lib/capybara/spec/views/popup_one.erb0000644000175000017500000000026613411315075023236 0ustar utkarsh2102utkarsh2102 Title of the first popup
This is the text of divInPopupOne
capybara-3.12.0/lib/capybara/spec/views/frame_parent.erb0000644000175000017500000000027713411315075023677 0ustar utkarsh2102utkarsh2102 This is the parent frame title capybara-3.12.0/lib/capybara/spec/views/with_html.erb0000644000175000017500000001347113411315075023233 0ustar utkarsh2102utkarsh2102
<%= referrer %>

This is a test

Header Class Test One

Header Class Test Two

Header Class Test Three

Header Class Test Four

Header Class Test Five

42 Other span

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. awesome image

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat Redirect pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia text with   whitespace id est laborum.

BackToMyself A link came first A link A link with data-method A link with capitalized data-method No Href Blank Href Blank Anchor Blank JS Anchor Normal Anchor Anchor on different page Anchor on same page Relative Protocol very fine image fine image Disabled link Naked Query String <% if params[:query_string] %> Query String sent <% end %>

Some of this text is hidden!
Some of this text is not hidden and some is hidden
Number 42
  • John
  • Paul
  • George
  • Ringo
singular
multiple one
multiple two
almost singular but not quite
almost singular
text here
Ancestor
Ancestor
Ancestor
Child
Pre Sibling
Mid Sibling
Post Sibling
Pre Sibling
Post Sibling
needs escaping
Some text
More text
And more text
Even more    text on multiple lines
                  
Download Me Download Other capybara-3.12.0/lib/capybara/spec/views/with_base_tag.erb0000644000175000017500000000043613411315075024031 0ustar utkarsh2102utkarsh2102 with_external_source

FooBar

This is the title of frame one
This is the text of divInFrameOne
capybara-3.12.0/lib/capybara/spec/views/postback.erb0000644000175000017500000000032613411315075023035 0ustar utkarsh2102utkarsh2102

Postback

capybara-3.12.0/lib/capybara/spec/public/0000755000175000017500000000000013411315075020655 5ustar utkarsh2102utkarsh2102capybara-3.12.0/lib/capybara/spec/public/test.js0000644000175000017500000001411413411315075022173 0ustar utkarsh2102utkarsh2102var activeRequests = 0; $(function() { $('#change').text('I changed it'); $('#drag, #drag_scroll, #drag_link').draggable(); $('#drop, #drop_scroll').droppable({ drop: function(event, ui) { ui.draggable.remove(); $(this).html('Dropped!'); } }); $('#drag_html5, #drag_html5_scroll').on('dragstart', function(ev){ ev.originalEvent.dataTransfer.setData("text", ev.target.id); }); $('#drop_html5, #drop_html5_scroll').on('dragover', function(ev){ if ($(this).hasClass('drop')) { ev.preventDefault(); } }); $('#drop_html5, #drop_html5_scroll').on('drop', function(ev){ ev.preventDefault(); $(this).html('HTML5 Dropped ' + ev.originalEvent.dataTransfer.getData("text")); }); $('#clickable').click(function(e) { var link = $(this); setTimeout(function() { $(link).after('Has been clicked'); $(link).after(''); $(link).after(''); $('#change').remove(); }, 1000); return false; }); $('#slow-click').click(function() { var link = $(this); setTimeout(function() { $(link).after('Slow link clicked'); }, 4000); return false; }); $('#waiter').change(function() { activeRequests = 1; setTimeout(function() { activeRequests = 0; }, 500); }); $('#with_focus_event').focus(function() { $('body').append('

Focus Event triggered

'); }); $('#with_change_event').change(function() { $('body').append($('

').text(this.value)); }); $('#with_change_event').on('input', function() { $('body').append($('

').text(this.value)); }); $('#checkbox_with_event').click(function() { $('body').append('

Checkbox event triggered

'); }); $('#fire_ajax_request').click(function() { $.ajax({url: "/slow_response", context: document.body, success: function() { $('body').append('

Ajax request done

'); }}); }); $('#reload-link').click(function() { setTimeout(function() { $('#reload-me').replaceWith(''); }, 250) }); $('#reload-list').click(function() { setTimeout(function() { $('#the-list').html('
  • Foo
  • Bar
  • '); }, 550) }); $('#change-title').click(function() { setTimeout(function() { $('title').text('changed title') }, 400) }); $('#change-size').click(function() { setTimeout(function() { document.getElementById('change').style.fontSize = '50px'; }, 500) }); $('#click-test').on({ click: function(e) { var desc = ""; if (e.altKey) desc += 'alt '; if (e.ctrlKey) desc += 'control '; if (e.metaKey) desc += 'meta '; if (e.shiftKey) desc += 'shift '; var pos = this.getBoundingClientRect(); $(this).after('Has been ' + desc + 'clicked at ' + (e.clientX - pos.left) + ',' + (e.clientY - pos.top) + ''); }, dblclick: function(e) { var desc = ""; if (e.altKey) desc += 'alt '; if (e.ctrlKey) desc += 'control '; if (e.metaKey) desc += 'meta '; if (e.shiftKey) desc += 'shift '; var pos = this.getBoundingClientRect(); $(this).after('Has been ' + desc + 'double clicked at ' + (e.clientX - pos.left) + ',' + (e.clientY - pos.top) + ''); }, contextmenu: function(e) { e.preventDefault(); var desc = ""; if (e.altKey) desc += 'alt '; if (e.ctrlKey) desc += 'control '; if (e.metaKey) desc += 'meta '; if (e.shiftKey) desc += 'shift '; var pos = this.getBoundingClientRect(); $(this).after('Has been ' + desc + 'right clicked at ' + (e.clientX - pos.left) + ',' + (e.clientY - pos.top) + ''); } }); $('#open-alert').click(function() { alert('Alert opened [*Yay?*]'); $(this).attr('opened', 'true'); }); $('#open-delayed-alert').click(function() { var link = this; setTimeout(function() { alert('Delayed alert opened'); $(link).attr('opened', 'true'); }, 250); }); $('#open-slow-alert').click(function() { var link = this; setTimeout(function() { alert('Delayed alert opened'); $(link).attr('opened', 'true'); }, 3000); }); $('#alert-page-change').click(function() { alert('Page is changing'); return true; }); $('#open-confirm').click(function() { if(confirm('Confirm opened')) { $(this).attr('confirmed', 'true'); } else { $(this).attr('confirmed', 'false'); } }); $('#open-prompt').click(function() { var response = prompt('Prompt opened'); if(response === null) { $(this).attr('response', 'dismissed'); } else { $(this).attr('response', response); } }); $('#open-prompt-with-default').click(function() { var response = prompt('Prompt opened', 'Default value!'); if(response === null) { $(this).attr('response', 'dismissed'); } else { $(this).attr('response', response); } }); $('#open-twice').click(function() { if (confirm('Are you sure?')) { if (!confirm('Are you really sure?')) { $(this).attr('confirmed', 'false'); } } }); $('#delayed-page-change').click(function() { setTimeout(function() { window.location.pathname = '/with_html' }, 500) }); $('#with-key-events').keydown(function(e){ $('#key-events-output').append('keydown:'+e.which+' ') }); $('#disable-on-click').click(function(e){ var input = this; setTimeout(function() { input.disabled = true; }, 500) }); $('#set-storage').click(function(e){ sessionStorage.setItem('session', 'session_value'); localStorage.setItem('local', 'local value'); }); $('#multiple-file').change(function(e){ $('body').append($('

    File input changed

    ')); }) }); capybara-3.12.0/lib/capybara/spec/spec_helper.rb0000644000175000017500000000771113411315075022223 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'rspec' require 'rspec/expectations' require 'capybara' require 'capybara/rspec' # Required here instead of in rspec_spec to avoid RSpec deprecation warning require 'capybara/spec/test_app' require 'nokogiri' Capybara.save_path = File.join(Dir.pwd, 'save_path_tmp') module Capybara module SpecHelper class << self def configure(config) config.filter_run_excluding requires: method(:filter).to_proc config.before { Capybara::SpecHelper.reset! } config.after { Capybara::SpecHelper.reset! } config.shared_context_metadata_behavior = :apply_to_host_groups end def reset! Capybara.app = TestApp Capybara.app_host = nil Capybara.default_selector = :xpath Capybara.default_max_wait_time = 1 Capybara.ignore_hidden_elements = true Capybara.exact = false Capybara.raise_server_errors = true Capybara.visible_text_only = false Capybara.match = :smart Capybara.enable_aria_label = false Capybara.default_set_options = {} Capybara.disable_animation = false Capybara.test_id = nil Capybara.predicates_wait = true Capybara.default_normalize_ws = false reset_threadsafe end def filter(requires, metadata) if requires && metadata[:capybara_skip] requires.any? do |require| metadata[:capybara_skip].include?(require) end else false end end def spec(name, *options, &block) @specs ||= [] @specs << [name, options, block] end def run_specs(session, name, **options, &filter_block) specs = @specs RSpec.describe Capybara::Session, name, options do # rubocop:disable RSpec/EmptyExampleGroup include Capybara::SpecHelper include Capybara::RSpecMatchers # rubocop:disable RSpec/ScatteredSetup before do |example| @session = session instance_exec(example, &filter_block) if filter_block end after do session.reset_session! end before :each, psc: true do SpecHelper.reset_threadsafe(true, session) end after psc: true do SpecHelper.reset_threadsafe(false, session) end before :each, :exact_false do Capybara.exact = false end # rubocop:enable RSpec/ScatteredSetup specs.each do |spec_name, spec_options, block| describe spec_name, *spec_options do # rubocop:disable RSpec/EmptyExampleGroup class_eval(&block) end end end end def reset_threadsafe(bool = false, session = nil) Capybara::Session.class_variable_set(:@@instance_created, false) # Work around limit on when threadsafe can be changed Capybara.threadsafe = bool session = session.current_session if session.respond_to?(:current_session) session&.instance_variable_set(:@config, nil) end end def silence_stream(stream) old_stream = stream.dup stream.reopen(RbConfig::CONFIG['host_os'] =~ /rmswin|mingw/ ? 'NUL:' : '/dev/null') stream.sync = true yield ensure stream.reopen(old_stream) end def quietly silence_stream(STDOUT) do silence_stream(STDERR) do yield end end end def extract_results(session) expect(session).to have_xpath("//pre[@id='results']") # YAML.load Nokogiri::HTML(session.body).xpath("//pre[@id='results']").first.inner_html.lstrip YAML.load Capybara::HTML(session.body).xpath("//pre[@id='results']").first.inner_html.lstrip end def be_an_invalid_element_error(session) satisfy { |error| session.driver.invalid_element_errors.any? { |e| error.is_a? e } } end end end Dir[File.dirname(__FILE__) + '/session/**/*.rb'].each { |file| require_relative file } capybara-3.12.0/lib/capybara/server/0000755000175000017500000000000013411315075017753 5ustar utkarsh2102utkarsh2102capybara-3.12.0/lib/capybara/server/checker.rb0000644000175000017500000000147713411315075021715 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true module Capybara class Server class Checker TRY_HTTPS_ERRORS = [EOFError, Net::ReadTimeout, Errno::ECONNRESET].freeze def initialize(host, port) @host, @port = host, port @ssl = false end def request(&block) ssl? ? https_request(&block) : http_request(&block) rescue *TRY_HTTPS_ERRORS res = https_request(&block) @ssl = true res end def ssl? @ssl end private def http_request(&block) Net::HTTP.start(@host, @port, read_timeout: 2, &block) end def https_request(&block) Net::HTTP.start(@host, @port, ssl_options, &block) end def ssl_options { use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_NONE } end end end end capybara-3.12.0/lib/capybara/server/middleware.rb0000644000175000017500000000227313411315075022421 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true module Capybara class Server class Middleware class Counter attr_reader :value def initialize @value = 0 @mutex = Mutex.new end def increment @mutex.synchronize { @value += 1 } end def decrement @mutex.synchronize { @value -= 1 } end end attr_reader :error def initialize(app, server_errors, extra_middleware = []) @app = app @extended_app = extra_middleware.inject(@app) do |ex_app, klass| klass.new(ex_app) end @counter = Counter.new @server_errors = server_errors end def pending_requests? @counter.value.positive? end def clear_error @error = nil end def call(env) if env['PATH_INFO'] == '/__identify__' [200, {}, [@app.object_id.to_s]] else @counter.increment begin @extended_app.call(env) rescue *@server_errors => err @error ||= err raise err ensure @counter.decrement end end end end end end capybara-3.12.0/lib/capybara/server/animation_disabler.rb0000644000175000017500000000274713411315075024136 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true module Capybara class Server class AnimationDisabler def self.selector_for(css_or_bool) case css_or_bool when String css_or_bool when true '*' else raise CapybaraError, 'Capybara.disable_animation supports either a String (the css selector to disable) or a boolean' end end def initialize(app) @app = app @disable_markup = format(DISABLE_MARKUP_TEMPLATE, selector: self.class.selector_for(Capybara.disable_animation)) end def call(env) @status, @headers, @body = @app.call(env) return [@status, @headers, @body] unless html_content? response = Rack::Response.new([], @status, @headers) @body.each { |html| response.write insert_disable(html) } @body.close if @body.respond_to?(:close) response.finish end private attr_reader :disable_markup def html_content? !!(@headers['Content-Type'] =~ /html/) end def insert_disable(html) html.sub(%r{()}, disable_markup + '\\1') end DISABLE_MARKUP_TEMPLATE = <<~HTML HTML end end end capybara-3.12.0/lib/capybara/selenium/0000755000175000017500000000000013411315075020266 5ustar utkarsh2102utkarsh2102capybara-3.12.0/lib/capybara/selenium/nodes/0000755000175000017500000000000013411315075021376 5ustar utkarsh2102utkarsh2102capybara-3.12.0/lib/capybara/selenium/nodes/chrome_node.rb0000644000175000017500000000121213411315075024201 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'capybara/selenium/extensions/html5_drag' class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node include Html5Drag def set_file(value) # rubocop:disable Naming/AccessorMethodName super(value) rescue ::Selenium::WebDriver::Error::ExpectedError => err if err.message =~ /File not found : .+\n.+/m raise ArgumentError, "Selenium < 3.14 with remote Chrome doesn't support multiple file upload" end raise end def drag_to(element) return super unless html5_draggable? html5_drag_to(element) end private def bridge driver.browser.send(:bridge) end end capybara-3.12.0/lib/capybara/selenium/nodes/firefox_node.rb0000644000175000017500000001037213411315075024375 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'capybara/selenium/extensions/html5_drag' class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node include Html5Drag def click(keys = [], **options) super rescue ::Selenium::WebDriver::Error::ElementNotInteractableError if tag_name == 'tr' warn 'You are attempting to click a table row which has issues in geckodriver/marionette - '\ 'see https://github.com/mozilla/geckodriver/issues/1228. Your test should probably be '\ 'clicking on a table cell like a user would. Clicking the first cell in the row instead.' return find_css('th:first-child,td:first-child')[0].click(keys, options) end raise end def disabled? # Not sure exactly what version of FF fixed the below issue, but it is definitely fixed in 61+ return super unless browser_version < 61.0 return true if super # workaround for selenium-webdriver/geckodriver reporting elements as enabled when they are nested in disabling elements if %w[option optgroup].include? tag_name find_xpath('parent::*[self::optgroup or self::select]')[0].disabled? else !find_xpath(DISABLED_BY_FIELDSET_XPATH).empty? end end def set_file(value) # rubocop:disable Naming/AccessorMethodName # By default files are appended so we have to clear here if its multiple and already set native.clear if multiple? && driver.evaluate_script('arguments[0].files', self).any? return super if browser_version >= 62.0 # Workaround lack of support for multiple upload by uploading one at a time path_names = value.to_s.empty? ? [] : Array(value) if (fd = bridge.file_detector) && !driver.browser.respond_to?(:upload) path_names.map! { |path| upload(fd.call([path])) || path } end path_names.each { |path| native.send_keys(path) } end def send_keys(*args) # https://github.com/mozilla/geckodriver/issues/846 return super(*args.map { |arg| arg == :space ? ' ' : arg }) if args.none? { |arg| arg.is_a? Array } native.click _send_keys(args).perform end def drag_to(element) return super unless (browser_version >= 62.0) && html5_draggable? html5_drag_to(element) end private def click_with_options(click_options) # Firefox/marionette has an issue clicking with offset near viewport edge # scroll element to middle just in case scroll_to_center if click_options.coords? super end def _send_keys(keys, actions = browser_action, down_keys = ModifierKeysStack.new) case keys when :control, :left_control, :right_control, :alt, :left_alt, :right_alt, :shift, :left_shift, :right_shift, :meta, :left_meta, :right_meta, :command down_keys.press(keys) actions.key_down(keys) when String # https://bugzilla.mozilla.org/show_bug.cgi?id=1405370 keys = keys.upcase if (browser_version < 64.0) && down_keys&.include?(:shift) actions.send_keys(keys) when Symbol actions.send_keys(keys) when Array down_keys.push keys.each { |sub_keys| _send_keys(sub_keys, actions, down_keys) } down_keys.pop.reverse_each { |key| actions.key_up(key) } else raise ArgumentError, 'Unknown keys type' end actions end def bridge driver.browser.send(:bridge) end def upload(local_file) return nil unless local_file raise ArgumentError, "You may only upload files: #{local_file.inspect}" unless File.file?(local_file) file = ::Selenium::WebDriver::Zipper.zip_file(local_file) bridge.http.call(:post, "session/#{bridge.session_id}/file", file: file)['value'] end def browser_version driver.browser.capabilities[:browser_version].to_f end DISABLED_BY_FIELDSET_XPATH = XPath.generate do |x| x.parent(:fieldset)[ x.attr(:disabled) ] + x.ancestor[ ~x.self(:legned) | x.preceding_sibling(:legend) ][ x.parent(:fieldset)[ x.attr(:disabled) ] ] end.to_s.freeze class ModifierKeysStack def initialize @stack = [] end def include?(key) @stack.flatten.include?(key) end def press(key) @stack.last.push(key) end def push @stack.push [] end def pop @stack.pop end end private_constant :ModifierKeysStack end capybara-3.12.0/lib/capybara/selenium/node.rb0000644000175000017500000002616613411315075021553 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true # Selenium specific implementation of the Capybara::Driver::Node API class Capybara::Selenium::Node < Capybara::Driver::Node def visible_text native.text end def all_text text = driver.execute_script('return arguments[0].textContent', self) text.gsub(/[\u200b\u200e\u200f]/, '') .gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ') .gsub(/\A[[:space:]&&[^\u00a0]]+/, '') .gsub(/[[:space:]&&[^\u00a0]]+\z/, '') .tr("\u00a0", ' ') end def [](name) native.attribute(name.to_s) rescue Selenium::WebDriver::Error::WebDriverError nil end def value if tag_name == 'select' && multiple? native.find_elements(:css, 'option:checked').map { |el| el[:value] || el.text } else native[:value] end end def style(styles) styles.each_with_object({}) do |style, result| result[style] = native.css_value(style) end end ## # # Set the value of the form element to the given value. # # @param [String] value The new value # @param [Hash{}] options Driver specific options for how to set the value # @option options [Symbol,Array] :clear (nil) The method used to clear the previous value
    # nil => clear via javascript
    # :none => append the new value to the existing value
    # :backspace => send backspace keystrokes to clear the field
    # Array => an array of keys to send before the value being set, e.g. [[:command, 'a'], :backspace] def set(value, **options) raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}" if value.is_a?(Array) && !multiple? case tag_name when 'input' case self[:type] when 'radio' click when 'checkbox' click if value ^ checked? when 'file' set_file(value) when 'date' set_date(value) when 'time' set_time(value) when 'datetime-local' set_datetime_local(value) else set_text(value, options) end when 'textarea' set_text(value, options) else set_content_editable(value) if content_editable? end end def select_option click unless selected? || disabled? end def unselect_option raise Capybara::UnselectNotAllowed, 'Cannot unselect option from single select box.' unless select_node.multiple? click if selected? end def click(keys = [], **options) click_options = ClickOptions.new(keys, options) return native.click if click_options.empty? click_with_options(click_options) rescue StandardError => err if err.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) || err.message =~ /Other element would receive the click/ scroll_to_center end raise err end def right_click(keys = [], **options) click_options = ClickOptions.new(keys, options) click_with_options(click_options) do |action| click_options.coords? ? action.context_click : action.context_click(native) end end def double_click(keys = [], **options) click_options = ClickOptions.new(keys, options) click_with_options(click_options) do |action| click_options.coords? ? action.double_click : action.double_click(native) end end def send_keys(*args) native.send_keys(*args) end def hover scroll_if_needed { browser_action.move_to(native).perform } end def drag_to(element) # Due to W3C spec compliance - The Actions API no longer scrolls to elements when necessary # which means Seleniums `drag_and_drop` is now broken - do it manually scroll_if_needed { browser_action.click_and_hold(native).perform } element.scroll_if_needed { browser_action.move_to(element.native).release.perform } end def tag_name native.tag_name.downcase end def visible?; boolean_attr(native.displayed?); end def readonly?; boolean_attr(self[:readonly]); end def multiple?; boolean_attr(self[:multiple]); end def selected?; boolean_attr(native.selected?); end alias :checked? :selected? def disabled? return true unless native.enabled? # WebDriver only defines `disabled?` for form controls but fieldset makes sense too tag_name == 'fieldset' && find_xpath('ancestor-or-self::fieldset[@disabled]').any? end def content_editable? native.attribute('isContentEditable') end def find_xpath(locator) native.find_elements(:xpath, locator).map { |el| self.class.new(driver, el) } end def find_css(locator) native.find_elements(:css, locator).map { |el| self.class.new(driver, el) } end def ==(other) native == other.native end def path path = find_xpath(XPath.ancestor_or_self).reverse result = [] default_ns = path.last[:namespaceURI] while (node = path.shift) parent = path.first selector = node[:tagName] if node[:namespaceURI] != default_ns selector = XPath.child.where((XPath.local_name == selector) & (XPath.namespace_uri == node[:namespaceURI])).to_s end selector += sibling_index(parent, node, selector) if parent result.push selector end '/' + result.reverse.join('/') end protected def scroll_if_needed yield rescue ::Selenium::WebDriver::Error::MoveTargetOutOfBoundsError scroll_to_center yield end private def sibling_index(parent, node, selector) siblings = parent.find_xpath(selector) case siblings.size when 0 '[ERROR]' # IE doesn't support full XPath (namespace-uri, etc) when 1 '' # index not necessary when only one matching element else idx = siblings.index(node) # Element may not be found in the siblings if it has gone away idx.nil? ? '[ERROR]' : "[#{idx + 1}]" end end def boolean_attr(val) val && (val != 'false') end # a reference to the select node if this is an option node def select_node find_xpath(XPath.ancestor(:select)[1]).first end def set_text(value, clear: nil, **_unused) value = value.to_s if value.empty? && clear.nil? native.clear elsif clear == :backspace # Clear field by sending the correct number of backspace keys. backspaces = [:backspace] * self.value.to_s.length send_keys(*([:end] + backspaces + [value])) elsif clear.is_a? Array send_keys(*clear, value) else # Clear field by JavaScript assignment of the value property. # Script can change a readonly element which user input cannot, so # don't execute if readonly. driver.execute_script "arguments[0].value = ''", self unless clear == :none send_keys(value) end end def click_with_options(click_options) scroll_if_needed do action_with_modifiers(click_options) do |action| if block_given? yield action else click_options.coords? ? action.click : action.click(native) end end end end def scroll_to_center script = <<-'JS' try { arguments[0].scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'}); } catch(e) { arguments[0].scrollIntoView(true); } JS begin driver.execute_script(script, self) rescue StandardError # rubocop:disable Lint/HandleExceptions # Swallow error if scrollIntoView with options isn't supported end end def set_date(value) # rubocop:disable Naming/AccessorMethodName value = SettableValue.new(value) return set_text(value) unless value.dateable? # TODO: this would be better if locale can be detected and correct keystrokes sent update_value_js(value.to_date_str) end def set_time(value) # rubocop:disable Naming/AccessorMethodName value = SettableValue.new(value) return set_text(value) unless value.timeable? # TODO: this would be better if locale can be detected and correct keystrokes sent update_value_js(value.to_time_str) end def set_datetime_local(value) # rubocop:disable Naming/AccessorMethodName value = SettableValue.new(value) return set_text(value) unless value.timeable? # TODO: this would be better if locale can be detected and correct keystrokes sent update_value_js(value.to_datetime_str) end def update_value_js(value) driver.execute_script(<<-JS, self, value) if (document.activeElement !== arguments[0]){ arguments[0].focus(); } if (arguments[0].value != arguments[1]) { arguments[0].value = arguments[1] arguments[0].dispatchEvent(new InputEvent('input')); arguments[0].dispatchEvent(new Event('change', { bubbles: true })); } JS end def set_file(value) # rubocop:disable Naming/AccessorMethodName path_names = value.to_s.empty? ? [] : value native.send_keys(Array(path_names).join("\n")) end def set_content_editable(value) # rubocop:disable Naming/AccessorMethodName # Ensure we are focused on the element click script = <<-JS var range = document.createRange(); var sel = window.getSelection(); arguments[0].focus(); range.selectNodeContents(arguments[0]); sel.removeAllRanges(); sel.addRange(range); JS driver.execute_script script, self # The action api has a speed problem but both chrome and firefox 58 raise errors # if we use the faster direct send_keys. For now just send_keys to the element # we've already focused. # native.send_keys(value.to_s) browser_action.send_keys(value.to_s).perform end def action_with_modifiers(click_options) actions = browser_action.move_to(native, *click_options.coords) modifiers_down(actions, click_options.keys) yield actions modifiers_up(actions, click_options.keys) actions.perform ensure act = browser_action act.release_actions if act.respond_to?(:release_actions) end def modifiers_down(actions, keys) each_key(keys) { |key| actions.key_down(key) } end def modifiers_up(actions, keys) each_key(keys) { |key| actions.key_up(key) } end def browser_action driver.browser.action end def each_key(keys) keys.each do |key| key = case key when :ctrl then :control when :command, :cmd then :meta else key end yield key end end # SettableValue encapsulates time/date field formatting class SettableValue attr_reader :value def initialize(value) @value = value end def to_s value.to_s end def dateable? !value.is_a?(String) && value.respond_to?(:to_date) end def to_date_str value.to_date.strftime('%Y-%m-%d') end def timeable? !value.is_a?(String) && value.respond_to?(:to_time) end def to_time_str value.to_time.strftime('%H:%M') end def to_datetime_str value.to_time.strftime('%Y-%m-%dT%H:%M') end end private_constant :SettableValue # ClickOptions encapsulates click option logic class ClickOptions attr_reader :keys, :options def initialize(keys, options) @keys = keys @options = options end def coords? options[:x] && options[:y] end def coords [options[:x], options[:y]] end def empty? keys.empty? && !coords? end end private_constant :ClickOptions end capybara-3.12.0/lib/capybara/selenium/driver.rb0000644000175000017500000002674113411315075022120 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'uri' require 'English' class Capybara::Selenium::Driver < Capybara::Driver::Base DEFAULT_OPTIONS = { browser: :firefox, clear_local_storage: nil, clear_session_storage: nil }.freeze SPECIAL_OPTIONS = %i[browser clear_local_storage clear_session_storage timeout].freeze attr_reader :app, :options def self.load_selenium require 'selenium-webdriver' warn "Warning: You're using an unsupported version of selenium-webdriver, please upgrade." if Gem.loaded_specs['selenium-webdriver'].version < Gem::Version.new('3.5.0') rescue LoadError => err raise err if err.message !~ /selenium-webdriver/ raise LoadError, "Capybara's selenium driver is unable to load `selenium-webdriver`, please install the gem and add `gem 'selenium-webdriver'` to your Gemfile if you are using bundler." end def browser @browser ||= begin if options[:timeout] options[:http_client] ||= Selenium::WebDriver::Remote::Http::Default.new(read_timeout: options[:timeout]) end processed_options = options.reject { |key, _val| SPECIAL_OPTIONS.include?(key) } Selenium::WebDriver.for(options[:browser], processed_options).tap do |driver| specialize_driver(driver) setup_exit_handler end end @browser end def initialize(app, **options) self.class.load_selenium @app = app @browser = nil @exit_status = nil @frame_handles = Hash.new { |hash, handle| hash[handle] = [] } @options = DEFAULT_OPTIONS.merge(options) @node_class = ::Capybara::Selenium::Node end def visit(path) browser.navigate.to(path) end def refresh browser.navigate.refresh end def go_back browser.navigate.back end def go_forward browser.navigate.forward end def html browser.page_source end def title browser.title end def current_url browser.current_url end def find_xpath(selector) browser.find_elements(:xpath, selector).map(&method(:build_node)) end def find_css(selector) browser.find_elements(:css, selector).map(&method(:build_node)) end def wait?; true; end def needs_server?; true; end def execute_script(script, *args) browser.execute_script(script, *native_args(args)) end def evaluate_script(script, *args) result = execute_script("return #{script}", *args) unwrap_script_result(result) end def evaluate_async_script(script, *args) browser.manage.timeouts.script_timeout = Capybara.default_max_wait_time result = browser.execute_async_script(script, *native_args(args)) unwrap_script_result(result) end def save_screenshot(path, **_options) browser.save_screenshot(path) end def reset! # Use instance variable directly so we avoid starting the browser just to reset the session return unless @browser navigated = false timer = Capybara::Helpers.timer(expire_in: 10) begin # Only trigger a navigation if we haven't done it already, otherwise it # can trigger an endless series of unload modals reset_browser_state unless navigated navigated = true # Ensure the page is empty and trigger an UnhandledAlertError for any modals that appear during unload wait_for_empty_page(timer) rescue Selenium::WebDriver::Error::UnhandledAlertError, Selenium::WebDriver::Error::UnexpectedAlertOpenError # This error is thrown if an unhandled alert is on the page # Firefox appears to automatically dismiss this alert, chrome does not # We'll try to accept it accept_unhandled_reset_alert # try cleaning up the browser again retry end end def switch_to_frame(frame) handles = @frame_handles[current_window_handle] case frame when :top handles.clear browser.switch_to.default_content when :parent handles.pop browser.switch_to.parent_frame else handles << frame.native browser.switch_to.frame(frame.native) end end def current_window_handle browser.window_handle end def window_size(handle) within_given_window(handle) do size = browser.manage.window.size [size.width, size.height] end end def resize_window_to(handle, width, height) within_given_window(handle) do browser.manage.window.resize_to(width, height) end end def maximize_window(handle) within_given_window(handle) do browser.manage.window.maximize end sleep 0.1 # work around for https://code.google.com/p/selenium/issues/detail?id=7405 end def fullscreen_window(handle) within_given_window(handle) do browser.manage.window.full_screen end end def close_window(handle) raise ArgumentError, 'Not allowed to close the primary window' if handle == window_handles.first within_given_window(handle) do browser.close end end def window_handles browser.window_handles end def open_new_window browser.execute_script('window.open();') end def switch_to_window(handle) browser.switch_to.window handle end def accept_modal(_type, **options) yield if block_given? modal = find_modal(options) modal.send_keys options[:with] if options[:with] message = modal.text modal.accept message end def dismiss_modal(_type, **options) yield if block_given? modal = find_modal(options) message = modal.text modal.dismiss message end def quit @browser&.quit rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED # rubocop:disable Lint/HandleExceptions # Browser must have already gone rescue Selenium::WebDriver::Error::UnknownError => err unless silenced_unknown_error_message?(err.message) # Most likely already gone # probably already gone but not sure - so warn warn "Ignoring Selenium UnknownError during driver quit: #{err.message}" end ensure @browser = nil end def invalid_element_errors [ ::Selenium::WebDriver::Error::StaleElementReferenceError, ::Selenium::WebDriver::Error::UnhandledError, ::Selenium::WebDriver::Error::ElementNotVisibleError, ::Selenium::WebDriver::Error::InvalidSelectorError, # Work around a chromedriver go_back/go_forward race condition ::Selenium::WebDriver::Error::ElementNotInteractableError, ::Selenium::WebDriver::Error::ElementClickInterceptedError, ::Selenium::WebDriver::Error::InvalidElementStateError, ::Selenium::WebDriver::Error::ElementNotSelectableError, ::Selenium::WebDriver::Error::ElementNotSelectableError, ::Selenium::WebDriver::Error::NoSuchElementError, # IE ::Selenium::WebDriver::Error::InvalidArgumentError # IE ] end def no_such_window_error Selenium::WebDriver::Error::NoSuchWindowError end private def native_args(args) args.map { |arg| arg.is_a?(Capybara::Selenium::Node) ? arg.native : arg } end def clear_browser_state delete_all_cookies clear_storage rescue Selenium::WebDriver::Error::UnhandledError # rubocop:disable Lint/HandleExceptions # delete_all_cookies fails when we've previously gone # to about:blank, so we rescue this error and do nothing # instead. end def delete_all_cookies @browser.manage.delete_all_cookies end def clear_storage clear_session_storage unless options[:clear_session_storage] == false clear_local_storage unless options[:clear_local_storage] == false rescue Selenium::WebDriver::Error::JavascriptError # rubocop:disable Lint/HandleExceptions # session/local storage may not be available if on non-http pages (e.g. about:blank) end def clear_session_storage if @browser.respond_to? :session_storage @browser.session_storage.clear else warn 'sessionStorage clear requested but is not supported by this driver' unless options[:clear_session_storage].nil? end end def clear_local_storage if @browser.respond_to? :local_storage @browser.local_storage.clear else warn 'localStorage clear requested but is not supported by this driver' unless options[:clear_local_storage].nil? end end def navigate_with_accept(url) @browser.navigate.to(url) sleep 0.1 # slight wait for alert @browser.switch_to.alert.accept rescue modal_error # rubocop:disable Lint/HandleExceptions # alert now gone, should mean navigation happened end def modal_error Selenium::WebDriver::Error::NoSuchAlertError end def within_given_window(handle) original_handle = current_window_handle if handle == original_handle yield else switch_to_window(handle) result = yield switch_to_window(original_handle) result end end def find_modal(text: nil, **options) # Selenium has its own built in wait (2 seconds)for a modal to show up, so this wait is really the minimum time # Actual wait time may be longer than specified wait = Selenium::WebDriver::Wait.new( timeout: options.fetch(:wait, session_options.default_max_wait_time) || 0, ignore: modal_error ) begin wait.until do alert = @browser.switch_to.alert regexp = text.is_a?(Regexp) ? text : Regexp.escape(text.to_s) alert.text.match(regexp) ? alert : nil end rescue Selenium::WebDriver::Error::TimeOutError raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{text}" if text}" end end def silenced_unknown_error_message?(msg) silenced_unknown_error_messages.any? { |regex| msg =~ regex } end def silenced_unknown_error_messages [/Error communicating with the remote browser/] end def unwrap_script_result(arg) case arg when Array arg.map { |arr| unwrap_script_result(arr) } when Hash arg.each { |key, value| arg[key] = unwrap_script_result(value) } when Selenium::WebDriver::Element build_node(arg) else arg end end def build_node(native_node) ::Capybara::Selenium::Node.new(self, native_node) end def specialize_driver(sel_driver) case sel_driver.browser when :chrome extend ChromeDriver when :firefox require 'capybara/selenium/patches/pause_duration_fix' if pause_broken?(sel_driver) extend FirefoxDriver if sel_driver.capabilities.is_a?(::Selenium::WebDriver::Remote::W3C::Capabilities) end end def pause_broken?(driver) driver.capabilities['moz:geckodriverVersion']&.start_with?('0.22.') end def setup_exit_handler main = Process.pid at_exit do # Store the exit status of the test run since it goes away after calling the at_exit proc... @exit_status = $ERROR_INFO.status if $ERROR_INFO.is_a?(SystemExit) quit if Process.pid == main exit @exit_status if @exit_status # Force exit with stored status end end def reset_browser_state clear_browser_state @browser.navigate.to('about:blank') end def wait_for_empty_page(timer) until find_xpath('/html/body/*').empty? raise Capybara::ExpectationNotMet, 'Timed out waiting for Selenium session reset' if timer.expired? sleep 0.05 end end def accept_unhandled_reset_alert @browser.switch_to.alert.accept sleep 0.25 # allow time for the modal to be handled rescue modal_error # The alert is now gone. # If navigation has not occurred attempt again and accept alert # since FF may have dismissed the alert at first attempt. navigate_with_accept('about:blank') if current_url != 'about:blank' end end require 'capybara/selenium/driver_specializations/chrome_driver' require 'capybara/selenium/driver_specializations/firefox_driver' capybara-3.12.0/lib/capybara/selenium/patches/0000755000175000017500000000000013411315075021715 5ustar utkarsh2102utkarsh2102capybara-3.12.0/lib/capybara/selenium/patches/pause_duration_fix.rb0000644000175000017500000000030613411315075026131 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true module PauseDurationFix def encode super.tap { |output| output[:duration] ||= 0 } end end ::Selenium::WebDriver::Interactions::Pause.prepend PauseDurationFix capybara-3.12.0/lib/capybara/selenium/driver_specializations/0000755000175000017500000000000013411315075025042 5ustar utkarsh2102utkarsh2102capybara-3.12.0/lib/capybara/selenium/driver_specializations/chrome_driver.rb0000644000175000017500000000322213411315075030216 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'capybara/selenium/nodes/chrome_node' module Capybara::Selenium::Driver::ChromeDriver def fullscreen_window(handle) within_given_window(handle) do begin super rescue NoMethodError => err raise unless err.message =~ /full_screen_window/ result = bridge.http.call(:post, "session/#{bridge.session_id}/window/fullscreen", {}) result['value'] end end end def resize_window_to(handle, width, height) super rescue Selenium::WebDriver::Error::UnknownError => err raise unless err.message =~ /failed to change window state/ # Chromedriver doesn't wait long enough for state to change when coming out of fullscreen # and raises unnecessary error. Wait a bit and try again. sleep 0.5 super end def reset! # Use instance variable directly so we avoid starting the browser just to reset the session return unless @browser switch_to_window(window_handles.first) window_handles.slice(1..-1).each { |win| close_window(win) } super end private def delete_all_cookies execute_cdp('Network.clearBrowserCookies') rescue Selenium::WebDriver::Error::UnhandledError, Selenium::WebDriver::Error::WebDriverError # If the CDP clear isn't supported do original limited clear super end def execute_cdp(cmd, params = {}) args = { cmd: cmd, params: params } result = bridge.http.call(:post, "session/#{bridge.session_id}/goog/cdp/execute", args) result['value'] end def build_node(native_node) ::Capybara::Selenium::ChromeNode.new(self, native_node) end def bridge browser.send(:bridge) end end capybara-3.12.0/lib/capybara/selenium/driver_specializations/firefox_driver.rb0000644000175000017500000000267113411315075030412 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'capybara/selenium/nodes/firefox_node' module Capybara::Selenium::Driver::FirefoxDriver def resize_window_to(handle, width, height) within_given_window(handle) do # Don't set the size if already set - See https://github.com/mozilla/geckodriver/issues/643 if window_size(handle) == [width, height] {} else super end end end def reset! # Use instance variable directly so we avoid starting the browser just to reset the session return unless @browser switch_to_window(window_handles.first) window_handles.slice(1..-1).each { |win| close_window(win) } super end def refresh # Accept any "will repost content" confirmation that occurs accept_modal :confirm, wait: 0.1 do super end rescue Capybara::ModalNotFound # rubocop:disable Lint/HandleExceptions # No modal was opened - page has refreshed - ignore end def switch_to_frame(frame) return super unless frame == :parent # geckodriver/firefox has an issue if the current frame is removed from within it # so we have to move to the default_content and iterate back through the frames handles = @frame_handles[current_window_handle] browser.switch_to.default_content handles.tap(&:pop).each { |fh| browser.switch_to.frame(fh) } end private def build_node(native_node) ::Capybara::Selenium::FirefoxNode.new(self, native_node) end end capybara-3.12.0/lib/capybara/selenium/extensions/0000755000175000017500000000000013411315075022465 5ustar utkarsh2102utkarsh2102capybara-3.12.0/lib/capybara/selenium/extensions/html5_drag.rb0000644000175000017500000000372613411315075025050 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true class Capybara::Selenium::Node module Html5Drag private def html5_drag_to(element) driver.execute_script MOUSEDOWN_TRACKER scroll_if_needed { browser_action.click_and_hold(native).perform } if driver.evaluate_script('window.capybara_mousedown_prevented') element.scroll_if_needed { browser_action.move_to(element.native).release.perform } else driver.execute_script HTML5_DRAG_DROP_SCRIPT, self, element browser_action.release.perform end end def html5_draggable? # Workaround https://github.com/SeleniumHQ/selenium/issues/6396 native.property('draggable') end MOUSEDOWN_TRACKER = <<~JS document.addEventListener('mousedown', ev => { window.capybara_mousedown_prevented = ev.defaultPrevented; }, { once: true, passive: true }) JS HTML5_DRAG_DROP_SCRIPT = <<~JS var source = arguments[0]; var target = arguments[1]; var dt = new DataTransfer(); var opts = { cancelable: true, bubbles: true, dataTransfer: dt }; if (source.tagName == 'A'){ dt.setData('text/uri-list', source.href); dt.setData('text', source.href); } if (source.tagName == 'IMG'){ dt.setData('text/uri-list', source.src); dt.setData('text', source.src); } var dragEvent = new DragEvent('dragstart', opts); source.dispatchEvent(dragEvent); target.scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'}); var dragOverEvent = new DragEvent('dragover', opts); target.dispatchEvent(dragOverEvent); var dragLeaveEvent = new DragEvent('dragleave', opts); target.dispatchEvent(dragLeaveEvent); if (dragOverEvent.defaultPrevented) { var dropEvent = new DragEvent('drop', opts); target.dispatchEvent(dropEvent); } var dragEndEvent = new DragEvent('dragend', opts); source.dispatchEvent(dragEndEvent); JS end end capybara-3.12.0/lib/capybara.rb0000644000175000017500000004614613411315075017005 0ustar utkarsh2102utkarsh2102# frozen_string_literal: true require 'timeout' require 'nokogiri' require 'xpath' require 'forwardable' require 'capybara/config' module Capybara class CapybaraError < StandardError; end class DriverNotFoundError < CapybaraError; end class FrozenInTime < CapybaraError; end class ElementNotFound < CapybaraError; end class ModalNotFound < CapybaraError; end class Ambiguous < ElementNotFound; end class ExpectationNotMet < ElementNotFound; end class FileNotFound < CapybaraError; end class UnselectNotAllowed < CapybaraError; end class NotSupportedByDriverError < CapybaraError; end class InfiniteRedirectError < CapybaraError; end class ScopeError < CapybaraError; end class WindowError < CapybaraError; end class ReadOnlyElementError < CapybaraError; end class << self extend Forwardable # DelegateCapybara global configurations # @!method app # See {Capybara.configure} # @!method reuse_server # See {Capybara.configure} # @!method threadsafe # See {Capybara.configure} # @!method server # See {Capybara.configure} # @!method default_driver # See {Capybara.configure} # @!method javascript_driver # See {Capybara.configure} Config::OPTIONS.each do |method| def_delegators :config, method, "#{method}=" end # Delegate Capybara global configurations # @!method default_selector # See {Capybara.configure} # @!method default_max_wait_time # See {Capybara.configure} # @!method app_host # See {Capybara.configure} # @!method always_include_port # See {Capybara.configure} SessionConfig::OPTIONS.each do |method| def_delegators :config, method, "#{method}=" end ## # # Configure Capybara to suit your needs. # # Capybara.configure do |config| # config.run_server = false # config.app_host = 'http://www.google.com' # end # # === Configurable options # # [app_host = String/nil] The default host to use when giving a relative URL to visit, must be a valid URL e.g. http://www.example.com # [always_include_port = Boolean] Whether the Rack server's port should automatically be inserted into every visited URL unless another port is explicitly specified (Default: false) # [asset_host = String] Where dynamic assets are hosted - will be prepended to relative asset locations if present (Default: nil) # [run_server = Boolean] Whether to start a Rack server for the given Rack app (Default: true) # [raise_server_errors = Boolean] Should errors raised in the server be raised in the tests? (Default: true) # [server_errors = Array\] Error classes that should be raised in the tests if they are raised in the server and Capybara.raise_server_errors is true (Default: [StandardError]) # [default_selector = :css/:xpath] Methods which take a selector use the given type by default (Default: :css) # [default_max_wait_time = Numeric] The maximum number of seconds to wait for asynchronous processes to finish (Default: 2) # [ignore_hidden_elements = Boolean] Whether to ignore hidden elements on the page (Default: true) # [automatic_reload = Boolean] Whether to automatically reload elements as Capybara is waiting (Default: true) # [save_path = String] Where to put pages saved through save_(page|screenshot), save_and_open_(page|screenshot) (Default: Dir.pwd) # [automatic_label_click = Boolean] Whether Node#choose, Node#check, Node#uncheck will attempt to click the associated label element if the checkbox/radio button are non-visible (Default: false) # [enable_aria_label = Boolean] Whether fields, links, and buttons will match against aria-label attribute (Default: false) # [reuse_server = Boolean] Reuse the server thread between multiple sessions using the same app object (Default: true) # [threadsafe = Boolean] Whether sessions can be configured individually (Default: false) # [server = Symbol] The name of the registered server to use when running the app under test (Default: :webrick) # [default_set_options = Hash] The default options passed to Node::set (Default: {}) # [test_id = Symbol/String/nil] Optional attribute to match locator aginst with builtin selectors along with id (Default: nil) # [predicates_wait = Boolean] Whether Capybaras predicate matchers use waiting behavior by default (Default: true) # [default_normalize_ws = Boolean] Whether text predicates and matchers use normalize whitespace behaviour (Default: false) # # === DSL Options # # when using capybara/dsl, the following options are also available: # # [default_driver = Symbol] The name of the driver to use by default. (Default: :rack_test) # [javascript_driver = Symbol] The name of a driver to use for JavaScript enabled tests. (Default: :selenium) # def configure yield config end ## # # Register a new driver for Capybara. # # Capybara.register_driver :rack_test do |app| # Capybara::RackTest::Driver.new(app) # end # # @param [Symbol] name The name of the new driver # @yield [app] This block takes a rack app and returns a Capybara driver # @yieldparam [] app The rack application that this driver runs against. May be nil. # @yieldreturn [Capybara::Driver::Base] A Capybara driver instance # def register_driver(name, &block) drivers[name] = block end ## # # Register a new server for Capybara. # # Capybara.register_server :webrick do |app, port, host| # require 'rack/handler/webrick' # Rack::Handler::WEBrick.run(app, ...) # end # # @param [Symbol] name The name of the new driver # @yield [app, port, host] This block takes a rack app and a port and returns a rack server listening on that port # @yieldparam [] app The rack application that this server will contain. # @yieldparam port The port number the server should listen on # @yieldparam host The host/ip to bind to # def register_server(name, &block) servers[name.to_sym] = block end ## # # Add a new selector to Capybara. Selectors can be used by various methods in Capybara # to find certain elements on the page in a more convenient way. For example adding a # selector to find certain table rows might look like this: # # Capybara.add_selector(:row) do # xpath { |num| ".//tbody/tr[#{num}]" } # end # # This makes it possible to use this selector in a variety of ways: # # find(:row, 3) # page.find('table#myTable').find(:row, 3).text # page.find('table#myTable').has_selector?(:row, 3) # within(:row, 3) { expect(page).to have_content('$100.000') } # # Here is another example: # # Capybara.add_selector(:id) do # xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] } # end # # Note that this particular selector already ships with Capybara. # # @param [Symbol] name The name of the selector to add # @yield A block executed in the context of the new {Capybara::Selector} # def add_selector(name, &block) Capybara::Selector.add(name, &block) end ## # # Modify a selector previously created by {Capybara.add_selector}. # For example, adding a new filter to the :button selector to filter based on # button style (a class) might look like this # # Capybara.modify_selector(:button) do # filter (:style, valid_values: [:primary, :secondary]) { |node, style| node[:class].split.include? "btn-#{style}" } # end # # # @param [Symbol] name The name of the selector to modify # @yield A block executed in the context of the existing {Capybara::Selector} # def modify_selector(name, &block) Capybara::Selector.update(name, &block) end def drivers @drivers ||= {} end def servers @servers ||= {} end # Wraps the given string, which should contain an HTML document or fragment # in a {Capybara::Node::Simple} which exposes all {Capybara::Node::Matchers}, # {Capybara::Node::Finders} and {Capybara::Node::DocumentMatchers}. This allows you to query # any string containing HTML in the exact same way you would query the current document in a Capybara # session. # # Example: A single element # # node = Capybara.string('bar') # anchor = node.first('a') # anchor[:href] #=> 'foo' # anchor.text #=> 'bar' # # Example: Multiple elements # # node = Capybara.string <<-HTML #
      #
    • Home
    • #
    • Projects
    • #
    # HTML # # node.find('#projects').text # => 'Projects' # node.has_selector?('li#home', text: 'Home') # node.has_selector?('#projects') # node.find('ul').find('li:first-child').text # => 'Home' # # @param [String] html An html fragment or document # @return [Capybara::Node::Simple] A node which has Capybara's finders and matchers # def string(html) Capybara::Node::Simple.new(html) end ## # # Runs Capybara's default server for the given application and port # under most circumstances you should not have to call this method # manually. # # @param [Rack Application] app The rack application to run # @param [Integer] port The port to run the application on # def run_default_server(app, port) servers[:puma].call(app, port, server_host) end ## # # @return [Symbol] The name of the driver currently in use # def current_driver if threadsafe Thread.current['capybara_current_driver'] else @current_driver end || default_driver end alias_method :mode, :current_driver def current_driver=(name) if threadsafe Thread.current['capybara_current_driver'] = name else @current_driver = name end end ## # # Use the default driver as the current driver # def use_default_driver self.current_driver = nil end ## # # Yield a block using a specific driver # def using_driver(driver) previous_driver = Capybara.current_driver Capybara.current_driver = driver yield ensure self.current_driver = previous_driver end ## # # Yield a block using a specific wait time # def using_wait_time(seconds) previous_wait_time = Capybara.default_max_wait_time Capybara.default_max_wait_time = seconds yield ensure Capybara.default_max_wait_time = previous_wait_time end ## # # The current Capybara::Session based on what is set as Capybara.app and Capybara.current_driver # # @return [Capybara::Session] The currently used session # def current_session specified_session || session_pool["#{current_driver}:#{session_name}:#{app.object_id}"] end ## # # Reset sessions, cleaning out the pool of sessions. This will remove any session information such # as cookies. # def reset_sessions! # reset in reverse so sessions that started servers are reset last session_pool.reverse_each { |_mode, session| session.reset! } end alias_method :reset!, :reset_sessions! ## # # The current session name. # # @return [Symbol] The name of the currently used session. # def session_name if threadsafe Thread.current['capybara_session_name'] ||= :default else @session_name ||= :default end end def session_name=(name) if threadsafe Thread.current['capybara_session_name'] = name else @session_name = name end end ## # # Yield a block using a specific session name or Capybara::Session instance. # def using_session(name_or_session) previous_session_info = { specified_session: specified_session, session_name: session_name, current_driver: current_driver, app: app } self.specified_session = self.session_name = nil if name_or_session.is_a? Capybara::Session self.specified_session = name_or_session else self.session_name = name_or_session end yield ensure self.session_name, self.specified_session = previous_session_info.values_at(:session_name, :specified_session) self.current_driver, self.app = previous_session_info.values_at(:current_driver, :app) if threadsafe end ## # # Parse raw html into a document using Nokogiri, and adjust textarea contents as defined by the spec. # # @param [String] html The raw html # @return [Nokogiri::HTML::Document] HTML document # def HTML(html) # rubocop:disable Naming/MethodName if Nokogiri.respond_to?(:HTML5) # Nokogumbo installed Nokogiri::HTML5(html).tap do |document| document.xpath('//textarea').each do |textarea| # The Nokogumbo HTML5 parser already returns spec compliant contents textarea['_capybara_raw_value'] = textarea.content end end else Nokogiri::HTML(html).tap do |document| document.xpath('//textarea').each do |textarea| textarea['_capybara_raw_value'] = textarea.content.sub(/\A\n/, '') end end end end def session_options config.session_options end private def config @config ||= Capybara::Config.new end def session_pool @session_pool ||= Hash.new do |hash, name| hash[name] = Capybara::Session.new(current_driver, app) end end def specified_session if threadsafe Thread.current['capybara_specified_session'] else @specified_session ||= nil end end def specified_session=(session) if threadsafe Thread.current['capybara_specified_session'] = session else @specified_session = session end end end self.default_driver = nil self.current_driver = nil self.server_host = nil module Driver; end module RackTest; end module Selenium; end require 'capybara/helpers' require 'capybara/session' require 'capybara/window' require 'capybara/server' require 'capybara/selector' require 'capybara/result' require 'capybara/version' require 'capybara/queries/base_query' require 'capybara/queries/selector_query' require 'capybara/queries/text_query' require 'capybara/queries/title_query' require 'capybara/queries/current_path_query' require 'capybara/queries/match_query' require 'capybara/queries/ancestor_query' require 'capybara/queries/sibling_query' require 'capybara/queries/style_query' require 'capybara/node/finders' require 'capybara/node/matchers' require 'capybara/node/actions' require 'capybara/node/document_matchers' require 'capybara/node/simple' require 'capybara/node/base' require 'capybara/node/element' require 'capybara/node/document' require 'capybara/driver/base' require 'capybara/driver/node' require 'capybara/rack_test/driver' require 'capybara/rack_test/node' require 'capybara/rack_test/form' require 'capybara/rack_test/browser' require 'capybara/rack_test/css_handlers.rb' require 'capybara/selenium/node' require 'capybara/selenium/driver' end Capybara.register_server :default do |app, port, _host| Capybara.run_default_server(app, port) end Capybara.register_server :webrick do |app, port, host, **options| require 'rack/handler/webrick' options = { Host: host, Port: port, AccessLog: [], Logger: WEBrick::Log.new(nil, 0) }.merge(options) Rack::Handler::WEBrick.run(app, options) end Capybara.register_server :puma do |app, port, host, **options| begin require 'rack/handler/puma' rescue LoadError raise LoadError, 'Capybara is unable to load `puma` for its server, please add `puma` to your project or specify a different server via something like `Capybara.server = :webrick`.' else unless Rack::Handler::Puma.respond_to?(:config) raise LoadError, 'Capybara requires `puma` version 3.8.0 or higher, please upgrade `puma` or register and specify your own server block' end end # If we just run the Puma Rack handler it installs signal handlers which prevent us from being able to interrupt tests. # Therefore construct and run the Server instance ourselves. # Rack::Handler::Puma.run(app, { Host: host, Port: port, Threads: "0:4", workers: 0, daemon: false }.merge(options)) options = { Host: host, Port: port, Threads: '0:4', workers: 0, daemon: false }.merge(options) conf = Rack::Handler::Puma.config(app, options) events = conf.options[:Silent] ? ::Puma::Events.strings : ::Puma::Events.stdio events.log 'Capybara starting Puma...' events.log "* Version #{Puma::Const::PUMA_VERSION} , codename: #{Puma::Const::CODE_NAME}" events.log "* Min threads: #{conf.options[:min_threads]}, max threads: #{conf.options[:max_threads]}" Puma::Server.new(conf.app, events, conf.options).tap do |s| s.binder.parse conf.options[:binds], s.events s.min_threads, s.max_threads = conf.options[:min_threads], conf.options[:max_threads] end.run.join end Capybara.configure do |config| config.always_include_port = false config.run_server = true config.server = :default config.default_selector = :css config.default_max_wait_time = 2 config.ignore_hidden_elements = true config.default_host = 'http://www.example.com' config.automatic_reload = true config.match = :smart config.exact = false config.exact_text = false config.raise_server_errors = true config.server_errors = [StandardError] config.visible_text_only = false config.automatic_label_click = false config.enable_aria_label = false config.reuse_server = true config.default_set_options = {} config.test_id = nil config.predicates_wait = true config.default_normalize_ws = false end Capybara.register_driver :rack_test do |app| Capybara::RackTest::Driver.new(app) end Capybara.register_driver :selenium do |app| Capybara::Selenium::Driver.new(app) end Capybara.register_driver :selenium_headless do |app| Capybara::Selenium::Driver.load_selenium browser_options = ::Selenium::WebDriver::Firefox::Options.new browser_options.args << '-headless' Capybara::Selenium::Driver.new(app, browser: :firefox, options: browser_options) end Capybara.register_driver :selenium_chrome do |app| Capybara::Selenium::Driver.new(app, browser: :chrome) end Capybara.register_driver :selenium_chrome_headless do |app| Capybara::Selenium::Driver.load_selenium browser_options = ::Selenium::WebDriver::Chrome::Options.new browser_options.args << '--headless' browser_options.args << '--disable-gpu' if Gem.win_platform? Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options) end