I have a bunch of anchor tags with text Delete Image. How do I loop through all of them and click them one by one? They all use AJAX. This doesn't work:
When(/^I delete all the section images$/) do
page.all(:link, "Delete Image").each do |link|
click_link(link)
page.driver.browser.switch_to.alert.accept
end
sleep 1
end
When(/^I delete all the section images$/) do
all(:link, "Delete Image").each do |link|
link.click
page.driver.browser.switch_to.alert.accept
end
sleep 1
end
the link parameter passed into the loop is an instance of a capybara class that can respond to #click. click_link("text") only works on matching the text.
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')
I have this link in records_path page:
link_to "CLICK HERE", edit_record_path(record), remote: true
The controller:
def edit
puts request.format # Just to show you part of the issue
end
And when clicked, it executes the edit.js.coffee and renders a modal for editing the record.
It works great. The problem I have is with Capybara.
context 'when on records list page' do
before { visit records_path }
context 'when clicking on "CLICK HERE"', js: true do
before { click_link('CLICK HERE') }
it 'shows the record name' do
expect(page).to have_content record.name
end
end
end
When I run the test, it raise an error:
Capybara::ElementNotFound: Unable to find link "CLICK HERE"
But if I remove the js: true from the context, it works (It executes the edit method on the RecordsController) but the printed format is html instead of js. I don't even have an edit.html, I just have the edit.js.coffee and that is the one that should be rendered in the test.
What am I doing wrong? If the link has the remote: true prop, and the test has the js: true shouldn't be enough for make it work?
In your setup you don't actually create a record. I am assuming this is the cause of your problem. The edit_record link will not show up if you do not have records to edit. I could be wrong but from what you pasted I think this could be the cause.
I use capybara to test this code which located in my Comment model (5 minutes are conventional):
def editable?
self.created_at < (Time.now - 5.minute)
end
The view of link:
- unless comment.editable?
= link_to 'Edit', edit_category_theme_comment_path(#category, #theme, comment)
So after 5 minutes link to edit must hide from the page (but we need to refresh the page). Here is my code in RSpec for creating comments and testing the link-hide functionality:
def create_comment(options={})
options[:content] ||= 'I am a comment'
visit category_theme_path(category, theme)
within '.comment-form' do
fill_in 'Content', with: options[:content]
click_button 'Submit'
end
end
context 'Comment has content' do
before(:each) { create_comment }
it 'hides the edit symbol due 5 minutes after comment was created' do
using_wait_time 400 do
visit category_theme_path(category, theme)
save_and_open_page
expect(page).to have_no_css('.comment-edit')
end
end
end
But I got: Failure/Error: expect(page).to have_no_css('.comment-edit')expected #has_no_css?(".comment-edit") to return true, got false
I also try to use page.reload!, expect(page).to have_no_css('.comment-edit', wait: 400) and other related staff, but capybara don't want to wait. Maybe I use using_wait_time for a wrong place, if that - how I can test this?
There are a number of things wrong with your approach to testing this. The reason your attempt is failing is because using_wait_time just sets the amount of time Capybaras matchers will wait for their expectations to become true, it doesn't actually make the program wait. Therefore expect(page).to have_no_css('.comment-edit') will wait up to the time your using_wait_time specifies, rechecking the page every 50 milliseconds or so for the content, but it never reloads the page and doesn't wait before the loading the page. For your approach to work you would need to sleep before visiting the page
it 'hides the edit symbol due 5 minutes after comment was created' do
sleep 5.minutes
visit category_theme_path(category, theme)
save_and_open_page
expect(page).to have_no_css('.comment-edit')
end
however this is a terrible idea since your tests will become ridiculously slow.
Rather than that approach you could be generating your test comment already "old" (as pascal betz suggests), using something like FactoryGirl and specifying a created_at that is more than 5 minutes ago.
FactoryGirl.create(:comment, created_at: 5.minutes.ago)
or if you want to continue creating your comment through the interface then include something like the Timecop gem and you can do
Timecop.travel(5.minutes.from_now) do
visit category_theme_path(category, theme)
save_and_open_page
expect(page).to have_no_css('.comment-edit')
end
which will move the clock 5 minutes forward before visiting the page, and then reset it back to normal once the block if finished.
Additionally, your editable? method is comparing in the wrong direction and should be
def editable?
self.created_at > (Time.now - 5.minute)
end
Then the view should be
- if comment.editable?
= link_to 'Edit', edit_category_theme_comment_path(#category, #theme, comment)
Work with seed data that is 5 minutes old and visit that page. Like this you do not need to wait five minutes.
Also your code
- unless comment.editable?
= link_to 'Edit', edit_category_theme_comment_path(#category, #theme, comment)
should probably read
- if comment.editable?
= link_to 'Edit', edit_category_theme_comment_path(#category, #theme, comment)
I have issue with testing uploading multiple files with capybara using cocoon. How can I find all fields with id: "image" and attach to each of them file?
Here is my test code:
click_link "Add photo"
wait_for_ajax
click_link "Add photo"
wait_for_ajax
page.all(:class, '.attach_fileds').each do |el|
attach_file( el, "#{Rails.root}/spec/assets/example.jpg")
end
click_button "Add"
expect(Post.count).to eq(1)
expect(Post.last.images.size).to eq(2)
Error message:
Unable to find file field #<Capybara::Element tag="input" path="/html/body/div/div/div/form[#id='new_post']/div[#id='educations']/div[1]/input[#id='image']">
You shouldn't have multiple elements with the same id, since thats illegal html. Based on your sample code (not sure what the :class selector is so guessing) you really want every element with a class of 'attach_field'. #attach_file doesn't take an element as its first parameter, instead it takes the name, id, or label text of the element. Since you already have the elements you can just call #set on each of them
page.all(:css, '.attach_field').each do |el
el.set "path of file to upload"
end
If instead you just want every file input you could do
page.all(:css, 'input[type="file"]').each do |el|
el.set "path of file to upload")
end
Also note that you need something between click_button "Add" and expect(Post.count).to eq(1) to make the test wait for the submission to finish - something like
expect(page).to have_text('Image Uploaded') # if the page adds text when image is uploaded
or
expect(page).to have_current_path('/some_new_path') # if the page url changes after the upload is complete
How can I write an integration test for editing an item? My "create" test looks like this:
it "lets a user create a product" do
login_user
click_link("Products")
click_link("New")
fill_in "Identifier", :with => "MyString"
click_button "Create"
assert page.has_content?("Product was successfully created")
end
And that works great. What I am confused about is how to do the Edit and Destroy tests. My index page provides a list of all products. So first I use a factory to create a couple of products. Now I am in the situation where there are multiple "Edit" and "Destroy" buttons. I can't just say:
click_button "Destroy"
because there are two of them. How do I tell it which one to click?
And if I do get the correct "Destroy" button clicked, how do I hit the "OK" button in the Javascript window that pops up?
Assuming that you're using Webrat, you can use the "within" selector.
The Webrat "within" method takes a CSS selector as an argument. Supposing your "Destroy" button is in a div with an id like "#product-2", you can isolate that button with:
within "#product-2" do |scope|
scope.click_button "Destroy"
end
If you need to / would rather use XPath, you can do something like:
response.should have_xpath(xpath) do |button|
click_button(button)
end
Alternatively, if you are using Capybara, then you can use the "find" method:
find("#product-2").find("button").click
find(:xpath, "//div/div/button").click