I am writing request specs and I am using poltergeist-1.0.2 and capybara-1.1.2. I have the following code:
login_as #user, 'Test1234!'
click_on 'Virtual Terminal'
the login has flash message that shows to the user that he is successfully logged in. When I am using click_link, the spec fails because Capybara can't find element 'Virtual Terminal' but when I am using click_on everything passes. 'Virtual Terminal' is not a button, it is a link.
What is the difference between click_on and click_link.
Click link uses a finder which specifies that you are looking for a link with the locator you provided and then does click on it as such:
def click_link(locator, options={})
find(:link, locator, options).click
end
Click on instead uses a finder that specifies that it should be a link or a button as:
def click_link_or_button(locator, options={})
find(:link_or_button, locator, options).click
end
alias_method :click_on, :click_link_or_button
Source: Capybara actions
This leads us in turn to the selectors :link and :link_or_button and that is defined as follows:
Capybara.add_selector(:link_or_button) do
label "link or button"
xpath { |locator| XPath::HTML.link_or_button(locator) }
filter(:disabled, :default => false) { |node, value| node.tag_name == "a" or not(value ^ node.disabled?) }
end
Capybara.add_selector(:link) do
xpath { |locator| XPath::HTML.link(locator) }
filter(:href) do |node, href|
node.first(:xpath, XPath.axis(:self)[XPath.attr(:href).equals(href.to_s)])
end
end
Source: Capubara selectors
The Xpath locators only differ in searching for link or link and button as shown in this sourcecode:
def link_or_button(locator)
link(locator) + button(locator)
end
def link(locator)
link = descendant(:a)[attr(:href)]
link[attr(:id).equals(locator) | string.n.contains(locator) | attr(:title).contains(locator) | descendant(:img)[attr(:alt).contains(locator)]]
end
def button(locator)
button = descendant(:input)[attr(:type).one_of('submit', 'reset', 'image', 'button')][attr(:id).equals(locator) | attr(:value).contains(locator) | attr(:title).contains(locator)]
button += descendant(:button)[attr(:id).equals(locator) | attr(:value).contains(locator) | string.n.contains(locator) | attr(:title).contains(locator)]
button += descendant(:input)[attr(:type).equals('image')][attr(:alt).contains(locator)]
end
Source: Xpath html
As you can see the button locator actually finds a lot of different types which your link might fall under, if I had the html source code I could tell if it does or not.
Related
I have a form that needs to submit a .csv file to the server and then append the words in it to a textarea in my page. I am using Remotipart to upload the .csv using AJAX but I cannot get the javascript in the server response to execute. Here are the relevant parts of my code:
The Form:
=form_tag(upload_canvas_words_admin_page_widget_widget_instance_path(widget.page, widget),:method=>'post',:remote=>true,:multipart=>true,:class=>"upload_words_csv") do
= label_tag "Upload File"
= file_field_tag "file"
= submit_tag "Upload"
The Controller:
def upload_canvas_words
#csv_text = params[:file].read
end
The .js.haml file:
= remotipart_response do
- if remotipart_submitted?
alert('#{#csv_text}');
alert('!');
- else
alert('WHYYYYY?');
When I look at the response I see the javascript being wrapped in a bunch of html, which I assume has something to do with the iFrame transport. But the javascript never actually executes.
Refer this issue. And try to follow the solution given here.
https://github.com/JangoSteve/remotipart/issues/89
So what happens is that reponse arrives to the browser with html entity (like ") inside the textarea. When the js code for evaluation is extracted the html entities are replaced by theirs respective characters (like " to ').
That's a characteristic of a textarea. So it doesn't get executed
Adding data: {type: :script} to the form should be the fix
I don't quite get why this is happening, maybe someone of you does... here it goes.
I've created a nested resource:
resources :order do
resources :ordered_vehicles
end
I've added a link_to the new action and passed the order.id like so new_order_ordered_vehicle_path(order.id) the page is loaded nicely. The problem is after I press the button to submit the choice. He switches the path from http://localhost:3000/order/3/ordered_vehicles/new to http://localhost:3000/order/R076027535/ordered_vehicles and displays error Couldn't find Order with id=R076027535... go figure.
The error is being raised in the controller in this method
private
def find_order
#order = Order.find(params[:order_id])
end
Which is a before_filter.
the new.html.haml file looks like this
= form_for [#order, #ordered_vehicle], html: { multipart: true } do |f|
= #order.number
%br= #order.id
= f.fields_for :vehicles do |car|
.... #some fields here
= car.submit "Save your choice"
That id he can't find is the #order.number but I don't get why the switch.
EDIT:
Just to be thorough, I'll add the controller methods:
def new
#ordered_vehicle = #order.ordered_vehicles.build(params.slice(:order_id, :vehicle_id))
end
def create
binding.pry
#ordered_vehicle = #order.ordered_vehicles.build(params.slice(:order_id, :vehicle_id))
if #ordered_vehicle.save
flash[:notice] = "Save successful."
redirect_to account_path
end
end
POST request (I hope that's the one, still new to all this stuff):
"action_dispatch.request.formats"=>[text/html]},
#request_method="POST", #filtered_parameters={"utf8"=>"✓", "authenticity_token"=>
"Ar4vy8pqCSpA2ch0qG0qiJXAJUbNALYxm/FbuKbdzCc=", "ordered_vehicle"=>
{"vehicles"=> {"maker_id"=>"", "model_id"=>"", "year"=>"", "body"=>"", "capacity"=>"", "id"=>"1"}},
"commit"=>"Save your choice", "action"=>"create",
"controller"=>"spree/ordered_vehicles", "order_id"=>"R076027535"}, #method="POST",
#fullpath="/order/R076027535/ordered_vehicles">
As per request ;)
Well, it turned out to be a problem with Spree which I'm currently tweaking (I know I didn't mention it explicitly, but didn't want to just post too much information).
Bottom line:
In the Order model the method to_param was overwritten to pass the number column in to the params. Didn't overwrote it again, just left it there and adapted. In my find_order method I wrote:
def find_order
#order = Order.find_by_number(params[:order_id])
end
Also I've stored the order number in the table, there was a problem I believe with out that, but can't remember explicitly. Anyways, thanks for the help.
P.S. Sorry for the mess
say I have an action template like this
# home/index.html.erb
<%= img_tag "logo.gif" %>
if I want to add alt/title attribute to it, I can just do
# home/index.html.erb
<%= img_tag "logo.gif", alt: "alt!!", title: "title!!" %>
but I have 1000 image tags and I don't want to change every each one of them.
I then thought of using rack middleware and modify image tags before outputting from server.
http://railscasts.com/episodes/151-rack-middleware?view=asciicast
doc = Nokogiri.HTML(#response.body)
doc.search("img").each do |tag|
[:alt, :title].each{|attribute| tag[attribute] = "changed!!" }
end
but when I follow the railscast episode, it appends entire body on the top of the original rather than replacing it.
Am I doing it wrong in the rack, or is there a smarter way to do this?
Updated answer:
# /config/initializers/image_tag_helper.rb
module ActionView
module Helpers
module AssetTagHelper
def image_tag(source, options={})
options[:src] = path_to_image(source)
options[:alt] = "Default Alt" unless options.has_key?(:alt)
options[:title] = "Default Title" unless options.has_key?(:title)
tag(:img, options)
end
end
end
end
This overrides the image_tag helper method to set default alt and title attributes.
I have a failing rspec view test but the code works - I probably have a variable incorrectly setup but can't figure out what it is.
When I display the contents of #incident_report (pp #incident_report) in my spec, it properly displays the record created by FactoryGirl.
When I display the actual rendered content (puts rendered), it shows the values from the the record I created with FactoryGirl...
But the "rendered.should contain(work_order)" spec fails with:
1) incident_reports/show.html displays the work order number on the incident
Failure/Error: rendered.should contain(work_order)
expected the following element's content to include "54785":
and none of the data is displayed, only the HTML template
spec/views/incident_report/show.html.haml_spec.rb code
require 'spec_helper'
describe "incident_reports/show.html" do
before(:each) do
#incident_report = Factory(:incident_report)
end
it "displays the work order number on the incident" do
work_order = #incident_report.work_order
pp #incident_report #displays an incident_report, id => 1
assign(:incident_report, #incident_report)
render
puts rendered #this DOES have the content from #incident_report
rendered.should contain("Work Order:")
rendered.should contain(work_order)
end
end
show.html.haml code
%h1 Display Incident Report
.navigation
= link_to 'Edit', edit_incident_report_path(#incident_report)
|
\#{link_to 'Back', incident_reports_path}
= render 'form'
.navigation
= link_to 'Edit', edit_incident_report_path(#incident_report)
|
\#{link_to 'Back', incident_reports_path}
Gotta be something really simple I'm overlooking.
Turns out it's because I was using simple_form and when simple_form displays for a "show" action, it puts the field values into the html as a 'value="54785"' attribute. If you display it in a browser, the labels and values all show up correctly, but rspec can't see them.
I had to add
rendered.should have_tag 'input', :with => { :value => "54765", :name => 'incident_report[work_order]' }
to my example to get it to work.
Seems like there should be a better solution but at least now I can continue testing.
If i have the following javascript code
var myVar = 0;
function setNewValforVar(newValue){
myVar = newValue;
}
and i'm calling setNewValforVar function n times
so when I click on a link it'd send myVar value to the controller action in a remote link like
<%=link_to "My Link",{:action=>"myAction"},:data=>''sval='+myVar',:remote=>true%>
I Rails 2.3.x I'd do this with
<%=link_to_remote "My Link",:url=>{:action=>"myAction"},:with=>"'sval='+myVar"%>
and i was getting this on the controller's action with
params["myVar"]
how do I do this on Rails 3 with the link_to helper?
Rails 3 no longer supports this behavior. You will have to either create a form to submit the value, or modify the href of the link to include the value in the query string.
I found some solution that use callbacks. Link must be marked in some way, for example by adding the id:
<%=link_to "My Link", {:action=>"myAction"}, :remote=>true, :id => "mylink" %>
There is an example for prototype_ujs: the parameter is simply appended to the request's URL (the code is a bit simplified I assume that some parameters already exist).
<%= javascript_tag("document.on('ajax:create', 'a#mylink',
function(event) { event.memo.request.url += '&sval=' + myVar; })") %>
Some advantage of this (ugly a bit) solution is the possibility of using one function for a particular class of links instead of the indicated id.