I'm fairly new to Cucumber/BDD and I'm kind of stumped. I'm trying to do a simple test of page contents but it seems that my test suite can only "find" the page header.
For example:
Scenario: User sees the menu page
Given a menu exists
When I visit the menu
Then I should see "menu" page title
And I should see "tuna poke" item name
Step definitions:
Given("a menu exists") do
user = User.create!(email: "test#valid.email", password: "hello")
restaurant = Restaurant.create!(name: "eleven tables", user_id: user.id)
menu = Menu.create!(restaurant_id: restaurant.id)
category = Category.create!(name: "appetizer")
menu_item = MenuItem.create!(name: "tuna poke", description: "it's light", price: 9, category: category, menu: menu)
end
When("I visit the menu") do
visit restaurant_menu_path
end
Then("I should see {string} page title") do |string|
expect(page).to have_title "#{string}"
end
And("I should see {string} item name") do |item_name|
expect(page).to have_content "#{item_name}"
end
Gives me the result:
Feature: View Menu
In order see the menu
I want to view the menu
I want to see item name
I want to see item description
I want to see item price
Scenario: User sees the menu page # features/view_menu.feature:8
Given a menu exists # features/step_definitions/view_menu_steps.rb:1
When I visit the menu # features/step_definitions/view_menu_steps.rb:9
Then I should see "menu" page title # features/step_definitions/view_menu_steps.rb:13
And I should see "tuna poke" item name # features/step_definitions/view_menu_steps.rb:17
expected to find text "tuna poke" in "menu sign in" (RSpec::Expectations::ExpectationNotMetError)
./features/step_definitions/view_menu_steps.rb:18:in `"I should see {string} item name"'
features/view_menu.feature:12:in `And I should see "tuna poke" item name'
Failing Scenarios:
cucumber features/view_menu.feature:8 # Scenario: User sees the menu page
1 scenario (1 failed)
4 steps (1 failed, 3 passed)
0m1.180s
The thing I find most peculiar is this part of the error:
expected to find text "tuna poke" in "menu sign in"
Which doesn't make a lot of sense to me. The only time those three words appear together is when you're on the /menu page of my application as a logged-out user in the nav bar. Which is precisely the case here. But I can't figure out why it can't read the contents of the entire page.
The <html> element has permitted content of one <head> element followed by one <body> element. In your case you have a <header> element outside the <body> element. This is causing whatever driver you're using (I assume rack_test since it is least lenient) to generate an implied <body> element around the <header> element so you end up with something like
<html>
<head>...</head>
<body>
<header>...</header>
<body>
<body> You're actual page content </body>
</html>
Since only one <body> element is allowed, the second is then being ignored. Fix your HTML to be valid (move the <header> inside the main <body>) and it should fix your issue
Related
on my index page I have this div:
<div class="banner">
<h1 class="glow-header">Galaxy Far, Far Away? Quick Trip to Mars?<br>
Pianeta has you covered.</h1>
<div>
In my testfile this works:
RSpec.describe 'home features' do
it 'displays the name of the app and links to the index-all planets page' do
visit root_path
expect(page).to have_content('Space is full of surprises.')
click_link('Go Beyond')
expect(current_path).to eq('/planets')
expect(page).to have_content('Galaxy Far, Far Away?')
end
end
But I would like it to be working with the h1 included.
I did this:
expect(page).to have_content('<h1 class="glow-header">Galaxy Far, Far Away? Quick Trip to Mars?<br>
Pianeta has you covered.</h1>')
end
But the test failed. What did I do wrong ?
The #has_content?/#has_text? method only checks the text content of the page. It does not look at the HTML tags.
If you want to check for content within a specific HTML element there is a #within method that takes a block and will scope the Capybara lookups within it to be within the matched element. The element referenced by #within must exist or Capybara will raise an exception.
page.within('h1.glow-header') do
expect(page).to have_content('Galaxy Far, Far Away?')
end
If you don't want to deal with scoping using within for a single expectation you could do
expect(page).to have_css('h1.glow-header', text: 'Galaxy Far, Far Away?')
If you've already got a reference to the header you could also do something like
header = find('h1.glow-header')
...
expect(header).to have_text('Galaxy Far, Far Away?')
Additionally you should not be doing expect(current_path).to eq('/planets'). Using RSpecs eq matcher with Capybara will lead to flaky tests as soon as you move to using an asynchronous (JS supporting) driver, because it prevents Capybaras auto waiting/retrying behaviors. Instead you should use the Capybara provided matcher
expect(page).to have_current_path('/planets')
This may seem unusually basic but how do I confirm the presence of a pop up confirmation?
<a data-confirm="delete this video?" rel="nofollow" data-method="delete" href="/videos/21">Delete</a>
<a is the "tag"/"element" and data-confirm is an attribute. I want to test for the existence of the "data-confirm" attribute within the <a> element/tag
I have tried
expect(page).to have_css("a.data-confirm.delete this video?")
from
capybara assert attributes of an element
but no joy.
Edit:
I've tried the expectation from Arup's comment below
expect(page).to have_content "Content"
click_link "Delete"
expect(page).to have_css('a[data-confirm="delete this video?"]')
But it raises the following (same) error
Failures:
1) Visiting the video index page should search and save movies
Failure/Error: expect(page).to have_css('a[data-confirm="delete this video?"]')
expected #has_css?("a[data-confirm=\"delete this video?\"]") to return true, got false
but the page source shows it there and it is clearly working for the user
Any assistance would be very appreciated
You can write this expectation as:
expect(page).to have_css('a[data-confirm="delete this video?"]')
The answer by Arup is correct for the title of the question (and as he stated in the comments it's just valid CSS - https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors), however it's not actually testing the more detailed part of the question "how do I confirm the presence of a pop up confirmation". All it is doing is confirming the correct data attribute is on the link element to trigger the rails provided JS that should show a confirm.
If you wanted to actually test the confirm box is shown you would need to swap to using a JS capable driver - https://github.com/teamcapybara/capybara/tree/2.17_stable#drivers - and then use something like the following in your test
expect(page).to have_content "Content"
accept_confirm "delete this video?" do
click_link "Delete" # The action that will make the system modal confirm box appear
end
See - http://www.rubydoc.info/gems/capybara/Capybara/Session#accept_confirm-instance_method
I have the following test:
it 'shows the current quantity of items inside cart' do
item = create(:item)
visit root_path
click_link("add-item-#{item.id}")
wait_for_ajax
page.find('#notice-modal-ok').click
click_link("add-item-#{item.id}")
wait_for_ajax
page.find('#notice-modal-ok').click
expect(page).to have_selector('#cart-item-count', text: '2')
end
It's basically a button with ajax thats show a success modal when return status 200. But, when the second ajax happens, for reason that I can't understand, the modal doesn't shows up. I can do this normally in dev environment.
Thanks for help!
Edit #1
I added a save_and_open_screenshot to try to debug this. The code in the end looked this way:
it 'shows the current quantity of items inside cart' do
item = create(:item)
visit root_path
click_link("add-item-#{item.id}")
page.find('#notice-modal-ok', wait: 10).click
expect(page).not_to have_selector('#notice-modal-ok')
click_link("add-item-#{item.id}")
save_and_open_screenshot # image
page.find('#notice-modal-ok', wait: 10).click # this fails
expect(page).to have_selector('#cart-item-count', text: '2', wait: 10)
end
Imagem number 1
As the images shows, the modal isn't appering on the second call. Here is the javascript thats shows it:
$(document).ready(function() {
$("a:regex(id,add-item-[0-9]+)").click(function(event) {
event.preventDefault();
var link = $(this).attr("href");
$.ajax({
url: link,
method: "GET",
dataType: "json",
success: function(data) {
$('#notice-modal').modal('show');
if(data.quantity) {
$("#cart-item-count").css('display', 'block');
$("#cart-item-count").html(data.quantity);
} else {
$("#cart-item-count").hide()
}
}
});
})
});
And in development mode, it works normally. Hope this new infos help!
Firstly, if the element with id of 'notice-modal-ok' is only appearing after the ajax request has completed then wait_for_ajax isn't required. Beyond that, assuming that clicking the modal doesn't affect the behavior of the add item link there are a few possibilities. One is that the modal hasn't actually disappeared before the link click happens and that suppresses link behavior. Another would be that `Capybara.default_max_wait_time' isn't set high enough for the hardware you're running on. To test for these you can wait for the modal to be gone before clicking the link for the second time and also temporarily increase the max wait time
Update: the root cause of the issue is that semantic-ui thinks the modal is still active (even though it has been closed) if you click the 'ok' button to close it before it has fully animated into place. The way around this in the tests is to make sure the modal has class 'active' before clicking the ok button. An alternate solution would be to disable all animations in semantic-ui when testing (if it has that option) which would also speed up tests.
it 'shows the current quantity of items inside cart' do
item = create(:item)
visit root_path
click_link("add-item-#{item.id}")
page.find('#notice-modal.active #notice-modal-ok').click
expect(page).not_to have_selector('#notice-modal') #verify/wait for the modal to disappear
click_link("add-item-#{item.id}")
page.find('#notice-modal.active #notice-modal-ok').click
expect(page).to have_selector('#cart-item-count', text: '2')
end
I get the error:
Capybara::ElementNotFound:
Unable to find field "user_email"
And this is the test code:
feature 'User' do
given!(:user) { User.new(email: 'testuserid#example.com', encrypted_password: 'test') }
scenario 'opens sign_up page' do
visit new_user_session_path
expect(page).to have_content 'unique text on the page'
end
scenario 'signs in with invalid email' do
visit new_user_session_path
fill_in('user_email',with: 'ssd')
expect(page).to have_content 'unique text on the page'
end
end
My HTML file consists of this code literally:
unique text on the page
<br>
<input type="text" id="user_email">
So this proves that the path is correct because my first scenario runs correctly. It is visiting the right page. But still I get this error for second scenario in fill_in.
I have also tried element = page.find("user_email"), it gives same error.
What am I possibly doing wrong?
I have been scratching my head like hell.
Usually the reason for this is that the input isn't actually visible on the page. You can verify this by doing
fill_in('user_email', with: 'ssd', visible: false)
If that succeeds in finding the element, then you need to change your test to first perform whatever actions make the field visible before attempting to fill it in.
Your code seems right. Maybe you are visiting wrong url or you have used user_email id once more. But you can give a try with alternative syntax like following :
find("input[id$='user_email']").set "ssd"
I have inherited an Rails app, but am unfamiliar with this particular test environment. When the cucumber tests are run we get:
Scenario: Add only an image to a profile by url # features/add_an_image_by_file_url.feature:11
Given I am logged in as a user "admin" # features/step_definitions/user_steps.rb:19
And I create a profile for "Joe Blogs" # features/step_definitions/profile_steps.rb:1
And I have a fake image url "http://fake.com/images/profile.jpg" # features/step_definitions/photo_steps.rb:1
When I follow "New photo" # features/step_definitions/web_steps.rb:32
And I fill in "Url" with "http://fake.com/images/profile.jpg" # features/step_definitions/web_steps.rb:38
And I press "Create" # features/step_definitions/web_steps.rb:26
Then I should see "Photo was successfully uploaded." # features/step_definitions/web_steps.rb:99
And I should see "profile.jpg" within first profile item # features/step_definitions/item_steps.rb:18
Unable to find css "ul#items li:first" (Capybara::ElementNotFound)
(eval):2:in `find'
./features/step_definitions/web_steps.rb:13:in `with_scope'
./features/step_definitions/web_steps.rb:100:in `/^I should see "([^\"]*)"(?: within "([^\"]*)")?$/'
features/add_an_image_by_file_url.feature:19:in `And I should see "profile.jpg" within first profile item'
This come from item_steps:
Then /^I should see "([^\"]*)" within (.*?) profile item$/ do |string, filter|
Then %Q{I should see "#{string}" within "ul#items li:#{filter}"}
end
And web steps concerned:
Then /^I should see "([^\"]*)"(?: within "([^\"]*)")?$/ do |text, selector|
with_scope(selector) do
if defined?(Spec::Rails::Matchers)
page.should have_content(text)
else
assert page.has_content?(text)
end
end
end
The HTML in question look like this:
<ul id='items'>
<li class='note clearfix'>
<div class='content'>
<img src="profile.jpg"/>
</div>
</li>
</ul>
What on Earth is going on here?
Well, "what's going on here" is pretty simple:
cucumber executes each step of the scenario using matched step definition
everything goes ok, until
it executes "I should see "profile.jpg" within first profile item"
during this step, cucumber goes to the matched step definition in item_steps
that definition in item_steps calls another step definition from web_steps
this last one checks if the page does really have the given content inside the given scope
and the check fails...
I suppose, the interesting question is "why it doesn't work". Very similar css selector works fine for me when I used it in my cucumber/capybara scenario.
Are you sure the HTML code is really displayed in the browser? There is no conditions in template or in controller which could prevent it from being displayed? What happens when you change 'ul#items li:first' to something more simple, say just 'ul#items'?
Update: the problem is really very simple: profile.jpg IS NOT a text content - it's an "invisible" html code. The only error you have is scenario itself.