I'm in the process of setting up Selinium to work with Cucumber and Capybara in my Rails app.Currently, my env.rb configuration file contains only:
Capybara.default_driver = :selenium
Cucumber::Rails::World.use_transactional_fixtures = false
I'm not even sure if the second line is necessary ?
The point is, I have Given step definition that creates Model data
Given /^a question named Question1$/ do
#question = Question.create!(name: 'question1')
end
And, in view checkbox and label are created for that 'question1' entry.Now, When step definition is checking that checkbox:
check "question1"
and that scenario passes.
The problem is that when driver is switched to Selenium, the label and checkbox are not rendered on the page, as if #question = Question.create!(name: 'question1') is not executed, and that scenario fails:
Unable to find checkbox "question1" (Capybara::ElementNotFound)
Found the solution. env.rb should contain
DatabaseCleaner.strategy = :truncation
Related
I have set up a very simple rails 5 project to narrow down my problem:
https://github.com/benedikt-voigt/capybara_js_demo
In this project the data mutation done by the Capybara JS is not deleted, neither by Rails nor by the Database cleaner I added.
The following great blog argues, that no DatabaseCleaner is needed:
http://brandonhilkert.com/blog/7-reasons-why-im-sticking-with-minitest-and-fixtures-in-rails
but this works only for fixtures, not for the mutation done by an out-of-thread Capybara test.
I added the Database cleaner, but this also needed work.
Does anybody has a sample setup?
From a quick look at your test I would say it's leaving data because the data is actually being added after DatabaseCleaner cleans. The click_on call occurs asynchronously, so when your assert_no_content call happens there's no guarantee the app has handled the request yet or the page has changed yet and since the current page doesn't have the text 'Name has already been taken' on it the assertion passes and the database gets cleaned. While that is happening the click gets processed by the app and the new data is created after the cleaning has occurred. You need to check/wait for content that will appear on the page after the click - something like
page.assert_text('New Foo Created')
You should only be asserting there is no content once you already know the page has changed, or you're expecting something to disappear from the current page.
I solved now the problem by setting the DB connection to one
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || ConnectionPool::Wrapper.new(:size => 1) { retrieve_connection }
end
end
ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
as describe here:
https://mattbrictson.com/minitest-and-rails
I uploaded the working repo here:
https://github.com/benedikt-voigt/capybara_js_demo_working
I am new to using PageObject::PageFactory.
I can’t get this simple scenario to work. Can someone Help me with this?
My Feature file
Feature: Find Pens
Scenario:
Given a user goes to Amazon website
When they search for “pens”
Then they are able to find “pens”
My Step_definition
Given(/^a user goes to Amazon website$/) do
visit HomePage
end
When(/^they search for “(.*?)”$/) do |page|
on HomePage do
page.look_pens
end
end
Then(/^they are able to find “(.*?)”$/) do |arg1|
pending # express the regexp above with the code you wish you had
end
Page Object file
QUESTION: Is it right to put this file in Support folder or should this be living elsewhere?
class HomePage
include PageObject
page_url(‘http://www.amazon.co.uk’)
text_field(:name, :id=> ‘twotabsearchtextbox’) #:id is the web-element from the actual webpage. Should this be something else?
button(:search, :value=>’Go’)
def look_pens(name,search)
self.name = pens
self.search
end
end
The Given part when run with Cucumber-SeleniumWebdriver does open the Amazon browser, but after that gives the following error for the When part
The Error
p.rb:1
When they search for “pens” # features/step_definitions/buy_pens_ste
p.rb:5
undefined method `look_pens’ for “pens”:String (NoMethodError)
./features/step_definitions/buy_pens_step.rb:7:in `block (2 levels) in ‘
./features/step_definitions/buy_pens_step.rb:6:in `/^they search for “(.*?
)”$/’
features\buy_pens.feature:5:in `When they search for “pens”‘
My env.rb in the support folder contains:
require ‘page-object’
require ‘selenium-webdriver’
require ‘page-object/page_factory’
Before do
#browser = Selenium::WebDriver.for(:firefox)
end
After do
#browser.close
end
World(PageObject::PageFactory)
Typically page object files are maintained in a separate folder that is at the same level as the features directory. You will need to use additional require statements so that Ruby knows that they are located in that folder.
Re the error: Looks like the compiler is having difficulty recognizing that look_pens is a method of the HomePage class. Try moving the |page| from the end of the When step def to the second line, so that it looks like code below.
When(/^they search for “(.*?)”$/) do
on HomePage do |page|
page.look_pens
end
end
I have this problem with my code.This page inside my application is using http basic authentication. I'm testing my application with Rspec and Capybara, but I have problem with data confirmation when deleting comment, it just can't seem to pass.
Here is my test code:
before(:each) do
page.driver.browser.authorize ENV['ADMIN_LOGIN'], ENV['ADMIN_PASSWORD']
...
end
...
it "should redirect to create new inspiring post and create new post on click" do
visit admin_path
click_link("create_inspi")
fill_in("Title",:with => "Test title")
fill_in("Short",:with => "Test short desc")
fill_in("Body",:with => "Test body desc")
fill_in("Date",:with => "12.12.2012")
click_button("Create new post")
test(
have_content("Test title"),
have_content("12.12.2012")
)
end
example "when inside Link to all... you can delete particular comment",:js => true do
visit admin_path
click_link("Link to all comments in Web development and Motivation category")
click_link(#webdev_comment.id)
page.driver.browser.accept_js_confirms
end
Now for my test by default I use Capybara::RackTest::Driver, but now since I can't find a way to confirm data confirmation with RackTest I have to use Selenium driver to make this data confirmed. But now when using this Selenium driver for my last example test I found next problem:
1) testing admin webpage when inside Link to all... you can delete particular comment
Failure/Error: page.driver.browser.authorize ENV['ADMIN_LOGIN'], ENV['ADMIN_PASSWORD']
NoMethodError:
undefined method `authorize' for #<Selenium::WebDriver::Driver:0x00000003e6ad68>
# ./spec/features/admin.rb:13:in `block (2 levels) in <top (required)>'
MY QUESTION IS:
How to authenticate basic http with selenium?
-------------- EDITED ------------------
I found a way to log in into admin with selenium driver by using this code
example "when inside Link to all... you can delete particular comment",:js => true do
visit root_path
#url = current_url
#tablica = #url.split("http://")
#correct_url = #tablica[1]
admin_selenium_path = "http://#{ENV["ADMIN_LOGIN"]}:#{ENV["ADMIN_PASSWORD"]}##{#correct_url}admin"
page.visit admin_selenium_path
click_link("Link to all comments in Web development and Motivation category")
click_link(#webdev_comment.id)
page.driver.browser.accept_js_confirms
end
But now , even though it's logging in , there is literally no data inside test database, and it's throwing errors around saying that particular object couldn't be found.
before(:each) do
# page.driver.browser.authorize ENV['ADMIN_LOGIN'], ENV['ADMIN_PASSWORD']
#mystory = Mystory.create(name: "My story:",body: "My name is Matthew...")
end
This code above inside before block is not creating a required #mystory record inside database for capybara with selenium driver.
Any ideas how to populate database for capybara with selenium driver?
I'm not sure if it's exactly the same issue but I add this somewhere above/before to skip over confirm dialogs with Capybara
page.evaluate_script('window.confirm = function() { return true; }')
Can I execute a javascript in a link with capybara click_link('next_page') ?
The link looks like this:
<a onclick="$('#submit_direction').attr('value', '1');$('#quizForm').submit()" id="next_page" href="#">Next Question</a>
I read at capybara at github that I can submit a form by click at its submit button like this:
click_on('Submit Answer')
But, in my case, I need to submit the form using javascript in a link, so, how to test the link that has javascript inside ? isn't click_link('next_page') sufficient ?
EDIT
after setting :js=> true my test looks like this:
it "should pass when answering all correct", :js=>true do
login_as(student, :scope => :student)
visit ("/student_courses")
#page.execute_script("$('#submit_direction').attr('value', '1');$('#quizForm').submit()")
trace "HTML:------------", page.html
end
Before :js=> true, I could visit the page normally, But, I've noticed that the page cannot be visited after :js=> true, here is the error I got after visiting the page:
Started GET "/student_courses" for 127.0.0.1 at 2012-01-23 06:29:26
+0200 (5010.7ms) UPDATE "students" SET "last_sign_in_at" = '2012-01-23 04:29:26.274285', "current_sign_in_at" = '2012-01-23
04:29:26.274285', "last_sign_in_ip" = '127.0.0.1',
"current_sign_in_ip" = '127.0.0.1', "sign_in_count" = 1, "updated_at"
= '2012-01-23 04:29:26.276279' WHERE "students"."id" = 1 SQLite3::BusyException: database is locked: UPDATE "students" SET
"last_sign_in_at" = '2012-01-23 04:29:26.274285', "current_sign_in_at"
= '2012-01-23 04:29:26.274285', "last_sign_in_ip" = '127.0.0.1', "current_sign_in_ip" = '127.0.0.1', "sign_in_count" = 1, "updated_at"
= '2012-01-23 04:29:26.276279' WHERE "students"."id" = 1 HTML:------------__
Internal
Server Error
Internal Server Error
cannot rollback transaction - SQL statements in progress
WEBrick/1.3.1 (Ruby/1.9.3/2011-10-30) at
127.0.0.1:34718
so, why SQLite3::BusyException: database is locked now ?!
I just spent 8 hours resolving a similar issue, and I found the solution. The fix is so simple, I could cry.
First, diagnosis
The reason you're getting "SQLite3::BusyException: database is locked" is that you are launching an asynchronous thread, namely a form submission, that ends up losing the "database write" race to your test's main thread. In effect, as your test has already completed and is running your "after each" database cleanup routine (defined in your spec_helper), the form action has only just begun trying to run the business logic (which relies on the data that your test after:each hook is busy destroying).
This problem is a lot more likely to occur with tests that click on an AJAX POST button and then terminate without asserting something about the view change.
Second, the fix
As it turns out, Capybara is designed to "synchronize" all your requests. But only if you implicitly let it. Notice that after your form submission, you're not having Capybara look at your page. Therefore it thinks you're done and takes your data out of scope (while your form submission thread is hanging in the background.)
Simply add the following line of code to the end of your test, and it should suddenly work:
page.should_not have_content "dude, you forgot to assert anything about the view"
Third, make it pretty
Don't use execute_script. That's what Capybara is for. But also don't rely on "click_on" because it's not a great abstraction. You need to know too much about its internals. Instead, use CSS selectors like so:
page.find("#submit_button").click
And one more thing - your test shouldn't try to manipulate the DOM. A good Selenium test assumes to have to follow the same steps a normal user follows.
So in conclusion
it "should pass when answering all correct", :js => true do
login_as(student, :scope => :student)
visit ("/student_courses")
page.find(".my-js-enabled-button").click
page.find("#submit_button").click
# Synchronizes your view to your database state before exiting test,
# Therefore makes sure no threads remain unfinished before your teardown.
page.should_not have_content "dude, you forgot to expect / assert anything."
end
To expand on Alex's comment Capybara won't execute JS unless it's explicitly turned on for the given tests.
To do this, use :js => true on either your describe block or individual test eg.
describe "in such and such a context", :js => true do
# some stuff
end
It should work. with capybara, you are essentially test your application through the browser, and it will behave the same.
I have the following lines in my feature file:
Given I have website "www.google.co.uk"
When I click the website "www.google.co.uk"
Then "www.google.co.uk" page is opened in a new window
I am struggling to find a way to test that the webpage is definately opened in a new window.
Currently I have been using this in my step def:
Then /url "([^"]*)" is opened in new window/ do |url|
browser = page.driver.browser
current_id = browser.window_handle
tab_id = page.driver.find_window(url)
browser.switch_to.window tab_id
page.driver.browser.close
browser.switch_to.window current_id
end
but this still passes the test if the webpage is loaded on the same page, I want it to fail if the webpage is loaded on the same window/tab.
Any suggestions?
Many thanks
I see no assertions in your test.
My approach would be to test size of window_handles array after performing clicking action on the link, since before clicking the size should equal 1 and after the clicking window_handles should equal 2.
assert page.driver.browser.window_handles.size == 2
Imho, good enough, since if the webpage is loaded in the same tab, the size will be 1 and the test will fail.