I'm trying to implement a feature in my Rails 3 site using BDD:
Feature: Patents Administration
Scenario: Patents index
Given I am on the admin patents page
Then I should see "Patents"
And the title should be "Wavetronix - Patents"
And here are the corresponding steps:
Given /^I am on the (.*?) page$/ do |text|
visit eval("#{text.downcase.gsub(/\s/, '_')}_path(locale: 'en')")
end
Then /^I should see "(.*?)"$/ do |text|
page.must_have_selector('h1', text: text)
end
Then /^the title should be "(.*?)"$/ do |text|
page.must_have_selector('title', text: text)
end
The first step fails as expected: I need to implement Admin::PatentsController:
module Admin
class PatentsController < BaseController
before_filter :find_patent
def index
end
private
def find_patent
#patent = Patent.find(params[:id]) if params[:id]
end
end
end
Because it inherits from Admin::BaseController—which has its own index action and view:
module Admin
class BaseController < ApplicationController
filter_access_to :index
def index
end
end
end
Admin::PatentsController inherits that action and view as well. When I override the BaseController implementation by explicitly defining an index action and view for the PatentsController, I can see the change in the browser—that it picks up the new index action and view—but the Cucumber step fails because it appears to still be looking at the BaseController index action and view.
I created a gist with more code for reference.
Is this a bug? Is there a better way to test this?
Bonehead: I am not accounting for existing authentication mechanism, which is redirecting because the test request isn't authenticated.
Related
I have an object Foo which has fields id and token (both are unique, both have db indexes). I want to be able to get to Foo 1 by going to url "/admin/foos/token-of-foo-1" in addition to being able to use the url "/admin/foos/1". I know that I will need to do something like the below in my routes.rb, but I'm having trouble. Help?
ActiveAdmin.routes(self) # keep this
get 'admin/???', to: 'admin/???' # add ...something
Hmm, some thoughts:
#admin/foo.rb
controller do
def find_resource
if params[:id].length == 16 # it's a token
end_of_association_chain.find_by_token(params[:id])
else
end_of_association_chain.find(params[:id])
end
end
end
For this kind of purposes I use this gem called FriendlyID. You can select which fields should form the URL slug, in your case the field token:
class Foo < ApplicationRecord
extend FriendlyId
friendly_id :token, use: :slugged
end
Let me know if you have more doubts on how to configure. It has a great integration with Active Admin.
I am using ActiveAdmin on my Rails project. I use Draper as decorator but I don't understand why the show view is not decorated. According to the documentation it should be working just adding the decorate_with MyDecorator in my ActiveAdmin resource.
Here is my code:
ActiveAdmin.register Home do
...
decorate_with HomeDecorator
show do
attributes_table do # not being decorated
row :content
row :status
row :image
row :author_with_avatar
end
end
end
Does someone know what is wrong with my code ?
When I've run into this problem, it's because I've customized the controller's find_resource method. In that case be sure to return a manually decorated object:
def find_resource
MyModel.find_by_some_unique_finder(params[:id]).decorate
end
If all else fails you can force it using this approach:
show
attributes_table_for model.decorate do
row :decorator_method
row(:custom_label) { |m| m.decorator_method }
See http://activeadmin.info/docs/6-show-pages.html for more details
I have a form for doing a search on a search page:
<form action="{{ url_for('searchresults') }}" method="get" name="noname" id="theform">
{{ form2.page(id="hiddenpage") }}
... some form inputs
<button id = "mybutton" type = "submit" >Apply</button>
</form>
The form is a SearchForm, where
class SearchForm(Form):
page = HiddenField()
categories = SelectMultipleField(u'Text', validators=[Optional()])
# some other stuff...
The view for searchresults handles the form:
#app.route('/searchresults', methods=['GET'])
def searchresults():
form = SearchForm()
# handle the form and get the search results using pagination
page = int(request.args.getlist('page')[0])
results = models.Product.query....paginate(page, 10, False)
return render_template('searchresults.html', form=form, results=results, curpage=page)
The results in render_template will be the first 10 results of my query. In searchresults.html I display the results, along with a next and previous link for the other results. This page also contains the same search form which I re-instate as per the initial submission. Currently I'm handling the next and previous links using
Next
So the next link re-submits the same initial form, but with the page value increased. I'm not really happy with this approach because when I hover over the next link I don't see the actual page I will be directed to. It also is starting to feel like a bit of a hack. Is there a more natural way to do this? When the form is initially submitted I could use it to create a long string of the desired parameters and use that string in the rendered page as href=" {{ url_for('searchresults') }}?mystring", but this also seems unnatural. How should I handle this?
You have your form configured to submit as a GET request, so you don't really need to force a re-submission through Javascript, you can achieve the same result by setting the next and prev links to URLs that include all the parameters in your form with the page modified to the correct next and previous page numbers.
This is really easy to do with url_for(). Any argument you add that do not match route components will be added to the query string, so you can do something like this:
Next
One thing to keep in mind is CSRF. If you have it enabled in your form, then your next/prev URLs will also need to have a valid token. Or you can disable CSRF, which for a search form might be okay.
Take advantage of the fact that your form arguments are already present in the URL and use request.args to pass the URL parameters into your form:
form = SearchForm(request.args)
Then, if you make your page field an IntegerField with a HiddenInput widget instead of a string field:
from wtforms.widgets import HiddenInput
class SearchForm(Form):
page = HiddenField(widget=HiddenInput, default=1)
you can increment page before you pass the form off to your search results page:
form.page.data += 1
And then, in your page, you simply create the link to the next page:
Next
I am having problem to use page object method call to return an object on the page.
Here is my example codes in test_log_in.rb
...
class TestLogIn < Test::Unit::TestCase
.....
def test_failed_log_in
#log_in_page= LogIn.new(#browser)
#log_in_page.go_to_log_in
#log_in_page.log_in("174773476","test","aaa111as")
puts #log_in_page.error_message
puts #log_in_page.get_error_message
end
end
My log in class is defined below:
class LogIn
include PageObject
...
h3(:error_message, :class => 'no-margin white-text')
...
def log_in (access_number, user_id, password)
self.access_number = access_number
self.user_id = user_id
self.password = password
log_me_in
AccountSummary.new(#browser)
end
....
def get_error_message
self.error_message
end
....
end
Why will the following lines returns no output?
puts #log_in_page.error_message
puts #log_in_page.get_error_message
Can you please help me?
Thanks.
My guess is that there is a timing issue due to the error message being displayed through javascript. It is likely that the error message element is always in the html of the page, but when login fails, the error message element's style is changed to be something visible.
When a page object (or watir) element returns its text, it only includes the visible text. So if the error message is not yet visible, you get no text.
Try waiting to ensure the element is visible to the user:
def get_error_message
error_message_element.when_present.text
end
when_present will wait up to 5 seconds for the element to become visible to the user and then return the text.
Update - Multiple h3s:
It looks like the actual problem is that there are multiple h3 elements that have that class. Watir always returns the first match, which in this case is the cookie error heading. Given that it is not being displayed, you get the blank text.
The solution would be to reduce the search scope so that you the first h3 is the one you want. This can be done by only looking in the error message list. I would define the page object as follows. Note that you do not need to specify the class of the h3 element since it is the only h3 element in the list.
class LogIn
include PageObject
div(:error_message_list, :id => 'errorMessageList')
h3(:error_message){ error_message_list_element.h3_element }
end
This will then find the right h3 element:
browser = Watir::Browser.new
browser.goto 'https://www.bnz.co.nz/ib4b/app/login'
browser.button(:text => 'Login').click
page = LogIn.new(browser)
page.error_message
#=> "There were 3 errors on the page."
I have the answer for the Rails 2.X but not for Rails 3. How can I read the name of a current layout rendered inside a view.
My Rails2 question: Rails Layout name inside view
Thx.
Getting this to work in Rails 3.2 is a little more complicated than previously outlined. If your controller explicitly declares a layout, then the result of controller.send(:_layout) is a String, but otherwise it's an ActionView::Template. Try this:
module ApplicationHelper
def current_layout
layout = controller.send(:_layout)
if layout.instance_of? String
layout
else
File.basename(layout.identifier).split('.').first
end
end
end
in rails 5
This works for me:
def current_layout
layout = controller.class.send(:_layout)
if layout.nil?
default_layout
elsif layout.instance_of? String or layout.instance_of? Symbol
layout
else
File.basename(layout.identifier).split('.').first
end
end
For Rails 4:
controller.send(:_layout)
=> 'application'
For Rails 3.2:
controller.send(:_layout)
=> #<ActionView::Template:0x000000082bb788>
But controller.send(:_layout).identifier returns the fullpath:
/home/davidm/Documentos/Devel/myapp/app/views/layouts/application.haml
I think it should be in core, but for now you can make a helper method:
def current_layout
controller.send :_layout
end
it will return currently used layout name
I have used in Rails4 at view pages and got reuslt.
controller.send(:_layout)
I hope this help.
For rails 5:
controller.class.send(:_layout)
This does NOT work:
controller.send(:_layout)
You can do what I've done in my Ajax gem for Rails which is to wrap the _render_layout method:
ActionView::Base.class_eval do
def _render_layout_with_tracking(layout, locals, &block)
controller.instance_variable_set(:#_rendered_layout, layout)
_render_layout_without_tracking(layout, locals, &block)
end
alias_method_chain :_render_layout, :tracking
end
Then you can access the value that was set from your view (I'm pretty sure you have access to the controller there...) or in your controller in an after_filter, which is what I do.
I've written a custom RSpec 2 matcher which can be used to test layout rendering in Rails 3.
All the approaches in the previous answers try to guess the name via private methods, but there's no need to guess and can be easily accomplished with the public API:
class ApplicationController
layout :set_layout
attr_reader :layout_name
helper_method :layout_name
private
def set_layout
#layout_name = "application"
end
end
Override in any controller that won't use the standard layout:
class MyController < ApplicationController
private
def set_layout
#layout_name = "my_layout"
end
end
And now in your views:
<%= layout_name %>