Cucumber step ambiguity when using built in 'within' step scoper - ruby-on-rails

I've created a custom cucumber step for checking the destination for a link, and I'm using Cucumber's new built in support in web_steps for scoping these lookups. So I have two cucumber steps involved:
# My step to verify the link
Then /^"([^\"]*)" should link to (.*)$/ do |link_text,page_name|
page.should have_link(link_text, :href => path_to(page_name))
end
# Cucumber's built in step to scope things
# Single-line step scoper
When /^(.*) within ([^:]+)$/ do |step, parent|
with_scope(parent) { When step }
end
I use this by having cucumber scripts that do things like
And "home" should link to the home page within the "Email Signature" section
My problem is that I'm getting ambiguous matches on the above between these two steps, because the 'within' clause can't be told apart from the "the home page", because the latter doesn't have any bounding quotes.
I've tried changing the link step to read like this, thinking it might resolve the ambiguity by not matching the 'within', but I think the 'within' gets swallowed by the preceeding group instead:
Then /^"([^\"]*)" should link to (.*)(?!within)$/ do |link_text,page_name|
page.should have_link(link_text, :href => path_to(page_name))
end
Any thoughts on how to resolve this?

Not directly an answer to the question I posed (for that see Qtax's answer), but here's what I've wound up doing. I think it's a nicer solution anyway, for what it's worth...
I've created a custom version of the scoping helper that looks like this:
Then /^within ([^,]*), (.+)$/ do |parent, step|
with_scope(parent) { When step }
end
This allows me to write steps like this:
And within the "Email Signature" section, "home" should link to the home page
Which I think (a) reads more naturally (it's clearer that we're talking about the link being in the e-mail signature section, not the home page), and (b) works around the problem I was having, because the unquoted 'within' selector is well out of the way of the unquoted page name.

Try something like:
/^"([^"]*)" should link to ((?:(?!within).)+)$/
Don't know anything about Cucumber, I'm just going by what you tried to do.

Related

Cucumber, Rails: has_content? test passes even though the string isn't there

Scenario is:
Scenario: View welcome page
Given I am on the home page
Then I should see 'Welcome'
And definition of the step is
Then("I should see {string}") do |string|
page.has_content?(string)
end
The test passes whether the word "welcome" is in the home page or not. What am I doing wrong?
A step will only fail if it throws an exception. By its naming convention, the has_content? method returns false if the content is not in the page, and thus does not throw an exception. This will cause your step to "pass" when you intend it to fail.
You need to make an assertion with some sort of unit testing library (my Ruby is a little rusty)
Then("I should see {string}") do |string|
page.has_content?(string).should_be true
end
You'll need something like RSpec to gain access to a library allowing you to make assertions.
Doing this the way shown in the other answer will work but will not give useful error messages. Instead you want
For RSpec
expect(page).to have_content(string)
For minitest
assert_content(string)
For others
page.assert_content(string)
Note that assert_content/assert_text and have_content/have_text are aliases of each other so use whichever reads better.

How to test that a form successfully submitted using Rspec?

I'm writing feature specs for my Article form.
In one test, I have RSpec look for certain form inputs using the labels:
expect(find_field("Title")).to_not be_nil
Now I'd like to know if the form was submitted properly. How can I do this using RSpec, if you aren't supposed to check the database from a feature spec?
For example, what if I mistyped the name attribute on the Title input? The label would still be found by my find_field() call, and controller specs would have me specify the title in a hash.
In this case one would assume a Title would be required for an Article so if the name attribute was misspelled the creation of the Article would fail and you'd end up on an error page (Also an unpermitted parameter would raise in the creation by default in test mode with strong parameters). If its a field not required for creation of the Article then the assumption is that the info is displayed in the app somewhere and can be checked for on that page.
As an aside your sample expect would read much clearer if rewritten as
expect(page).to have_field('Title')
There are loads of way to approach this:
expect(page).to have_field('Title')
expect(page).to have_css('#someSelector')
expect(current_path).to eq article_path (for example)
expect(page).to have_content "ABC" (i.e. some content which will be rendered)
The Capybara readme on Github is a really good one full of easy to follow examples - https://github.com/jnicklas/capybara.

Rails Rspec and capybara - using to perform a, um, sort of test

Check edit at bottom of page
My boss has a sitemap up- it's basically just every route as a link, with a button to click that says "valid?" or "ignore" which will mark it valid or ignore it on the page.
He asked me to manually go through and click each link, test that page isn't a 500 or 404, and then mark it valid if it isn't.
This seems silly to me, as it is basically just a user facing test for working routes.
I could, in the same time, write out routing specs in Rspec for all those, but I guess he wants some sort of documentation that this is happening on the front end for himself and users.
I was thinking a fun way to work around this boring clicking would be to do it with some programming WHILE writing the specs. Makes him happy, and also adds actual value and test to the app that can be reused.
Is there a way to, in a spec, write something like:
links = page.all('a.routing-links)
link.each do |link|
link.click
if page status != 404 || 500
Link.find(id).update_attribute("verified", true)
end
end
I tried putting that in my spec, but when link.click hits an incorrect route, it stops the test (which makes sense, as that route is broken and this is a test.
What I'd like is to be able to take that error and use it to update the attribute of my model.
Am I going about this completely wrong? Any better ideas or inspiration?
Thanks
Edit
I agree with the poster who said this is better left to a script or rake task.
I'm a bit lost on how to write a script that will go to a page, find every link, record its status_code, and then find and update a model. Any suggestions or tips? Ideally it would be run within in the application, so that I could have access to my models and controllers.
Thanks
Personally I wouldn't actually put this in a spec since you're not actually expecting anything to fail.
Instead I'd create a quick script, or even rake task to run through the links as you described.
That being said, this article: http://agileleague.com/2012/12/rails-3-2-custom-error-pages-the-exceptions_app-and-testing-with-capybara/ details how to bypass the normal fail in these circumstances, namely:
In your config/environments/test.rb
config.consider_all_requests_local = false
config.action_dispatch.show_exceptions = true
Though this would affect all tests, which is quite possibly not what you want.
Also, a minor thing that you'd probably figure out in no time when testing this - you'll either need to revisit the list page after clicking the link, or rather relying on link clicks, you could visit the href instead which would be a bit quicker.
links = page.all('a.routing-links')
link.each do |link|
visit link[:href]
if page.status != 404 || 500
Link.find(id).update_attribute("verified", true)
end
end
I haven't tested that, so not sure if it would work like that, but you should be able to get the idea.

Creating a path with an object id to map with cucumber scenario

I'm trying to create a cucumber scenario that checks to see if elements are loaded for an 'edit posting' page. My trouble, however, is that I don't know how to create a path that will direct it to the page.
The general path is something like: /posting/id/edit
i.e. /posting/11/edit
Here is my posting.feature scenario
# Editing existing post
Scenario: Saving the edits to an existing post
Given I am logged in
Given there is a posting
Given I am on the edit posting page
When I fill in "posting_title" with "blah"
And I fill in "posting_location" with "blegh"
When I press "Update posting"
Then I should see "Posting was successfully updated."
I dabbled around with some Factory Girl stuff, but I don't have the knowledge to use it appropriately (if it offers a solution), and wasn't able to find a relevant example.
I've also seen a lot of suggestions with regards to 'Pickle', but if possible I'd like to avoid that route to keep things simple seeing as I have very limited experience. Thanks!
Is there a link on your website that would take someone to the edit page? Then you could do something like:
Given I am on the homepage
And I follow "Posts"
And I follow "Edit"
This assumes that there is a link on your homepage whose text is Posts, and then another one in the resulting page called Edit. This is the best way to accomplish this, because there should be a direct route to whatever page you are testing. Those steps are also provided in web_steps.rb
You could also make a custom step like you did there with Given I am on the edit posting page and the code would be something like:
Given /^I am on the edit posting page$/ do
visit("/posting/11/edit")
end
Which you of course could also generalize like I am on the edit posting page for posting 11. But in general, cucumber tests are acceptance tests, which means not bypassing things like this. You should have a link to the edit page that can be clicked.
I came up with a solution, but I am not sure of its validity in terms of how clean it is. I ended up using Factory Girl (installed the gem).
I kept my scenario the same.
Under features/step_definitions I created posting_steps.rb
Given /^there is a posting$/ do
Factory(:posting)
end
Under features/support I created a file factories.rb with the following inside:
Factory.define :posting do |f|
f.association :user
f.title 'blah'
f.location 'Some place'
end
In my paths.rb I used
when /the edit posting page/
edit_posting_path(Posting.first)
How it works (or at least how I think it works) is that as
Given there is a posting
is executed, the posting_step.rb is invoked (Factory(:posting) is basically Factory.create(:posting)), which in turn uses the factory definition I created in factories.rb. This leads to an instance of a posting being created.
Then in my paths.rb
when /the edit posting page/
edit_posting_path(Posting.first)
gets passed the id from the instance, to ultimately get a path that could resemble /posting/1/edit , and the test continues on its way!
If there are any corrections to be made, please let me know as I am just learning the ropes.
Hopefully this will help other newbies out there!

How to avoid hard coding values in cucumber features?

When writing my scenerios, is it possible to not have to hard code text in the steps?
Like say I am insert a username in a textbox field, and a password in the password field.
If I need to do this in many places, it would be a pain to fix.
Example:
Given I am the registered member "myusername"
And I am on the login page
When I fill in "email" with "email#example.com"
And I fill in "password" with "123"
And I press "Login"
Then I should see "Account Activity"
I don't want my username, email, and password hard-coded.
Okay, you're still using the older version of the cucumber-rails gem which comes with the training wheels installed by default. Read this post by Aslak Hellesøy "The training wheels came off".
The gist of the post is that using web_steps.rb, although it having been the "standard" for years is now terribly wrong and that we should feel bad for doing that.
The purpose of Cucumber is to use it to make readable / understandable features for all people.
Writing a scenario like this is long and boring:
And I am on the login page
When I fill in "email" with "email#example.com"
And I fill in "password" with "123"
And I press "Login"
Then I should see "Account Activity"
What you want to actually be testing is that you should be able to login and after that see something to do with being logged in. Whatever that something is shouldn't be written in the scenario.
So ideally, your Scenario (in a more exciting fashion) would look like this:
When I login successfully
Then I should see that I am logged in
Then the task of doing the legwork goes to some new step definitions. Those two steps aren't defined automatically for you, like web_steps.rb does, but rather need to have them written in a file within feature/step_definitions. What you call the file is up to you, but it'll contain content similar to this:
When /I login successfully/ do
visit root_path
click_link "Login"
fill_in "Email", :with => "you#example.com"
fill_in "Password", :with => "password"
end
Then /^I should see I am logged in$/ do
page.should have_content("Account Activity")
end
No more excessive web_steps.rb file and cleaner step definitions. Exactly what Cucumber should be.
Create a step to encapsulate the logging in behavior as described here
If this works for you, I would then suggest tweaking the Given /I am logged in/ step to capybara calls to get a slight boost to performance. Also, in the future it is recommended that you avoid using web_steps for reasons described here.
Ryan's example is a change from imperative steps to declarative. This is generally a better idea. It means that the implementation has been moved into step definitions which makes the feature more readable and focuses on the behavior instead of the details.
But if you need to be specific with your steps (imperative), you could try something like
Given I am a registered member
And I am on the login page
When I fill in the login form
And I submit the login form
Then I should see I am logged in
You could also combine those steps to make it even simpler. But still, Ryan's idea is probably better in most cases.
Edit: Ryan has written a book. It's quite good. Hi Ryan!
You can create csv file in framework under data folder,add your private values to csv file, then call it in your feature file

Resources