How to specify paths in Cucumber - ruby-on-rails

I have set up this Cucumber feature in 'spree_fit_card/new_gift_card.feature`:
#gift-card
Feature: access gift cards
As a general user
I will be able to place an order for a gift card
Scenario: The gift card index page will redirect to /new
When I am on the gift card page
Then I will be redirected to the new gift card page
And in the support/paths.rb:
module NavigationHelpers
def path_to(page_name)
when /the gift card page/
spree.gift_cards_path
when /the new gift card page/
spree.new_gift_card_path
else
...
end
end
end
And when we checkout step_definitions/new_gift_card_steps.rb:
When(/^I am on the gift card page$/) do
pending
end
Then(/^I will be redirected to the new gift card page$/) do
pending
end
Cucumber outputs:
$ zeus cucumber --tags #gift-card
Loading fixtures
Using the default profile...
#gift-card
Feature: access gift cards
As a general user
I will be able to place an order for a gift card
Scenario: The gift card page will redirect to the new gift card page # features/spree_gift_card/new_gift_card.feature:6
When I am on the gift card page # features/spree_gift_card/new_gift_card.feature:7
Ambiguous match of "I am on the gift card page":
features/step_definitions/spree_gift_card/new_gift_cards_steps.rb:1:in `/^I am on the gift card page$/'
cucumber-websteps-0.10.0/lib/cucumber/websteps/browsing_steps.rb:1:in `/^(?:|I )am on (.+)$/'
You can run again with --guess to make Cucumber be more smart about it
(Cucumber::Ambiguous)
-e:1:in `<main>'
features/spree_gift_card/new_gift_card.feature:7:in `When I am on the gift card page'
Then I will be redirected to the new gift card page # features/step_definitions/spree_gift_card/new_gift_cards_steps.rb:5
Failing Scenarios:
cucumber features/spree_gift_card/new_gift_card.feature:6 # Scenario: The gift card page will redirect to the new gift card page
So I took Cucumber's suggestion into consideration and ran zeus cucumber --tags #gift-card --guess
Loading fixtures
Using the default profile...
#gift-card
Feature: access gift cards
As a general user
I will be able to place an order for a gift card
Scenario: The gift card page will redirect to the new gift card page # features/spree_gift_card/new_gift_card.feature:6
When I am on 'the gift card page' # features/step_definitions/spree_gift_card/new_gift_cards_steps.rb:1
TODO (Cucumber::Pending)
./features/step_definitions/spree_gift_card/new_gift_cards_steps.rb:2:in `/^I am on the gift card page$/'
features/spree_gift_card/new_gift_card.feature:7:in `When I am on the gift card page'
Then I will be redirected to the new gift card page # features/step_definitions/spree_gift_card/new_gift_cards_steps.rb:5
1 scenario (1 pending)
2 steps (1 skipped, 1 pending)
0m0.016s
Cleaning up database
I'm glad that it passes with --guess, but I don't understand why its not passing without it. I think I've set it up right, but I obviously haven't. I'm using the Spree framework if it helps with Ruby on Rails version 3.2.17.

Reason can be found back in the errors you're giving:
cucumber-websteps-0.10.0/lib/cucumber/websteps/browsing_steps.rb:1:in `/^(?:|I )am on (.+)$/'
shows that there is a "default" step, in browsing_steps.rb, which also matches with your test. That's the reason why you get this error. As you see, the matching is "less" than with your step, and that's the reason that "guess" works: it will chose automatically for the "best fitting" step definition.

Related

Is is it a good idea to test if records were displayed on a page with feature specs

I have a page where user should see items he created. So I decided I want a feature test for that(with rspec and capybara).
What I wanted to do something like this:
require 'spec_helper'
feature "Accounts pages" do
scenario "user views his accounts" do
user = create(:user)
sign_in user
expect(current_path).to eq accounts_path
expect(page).to have_content "Your accounts"
#here I want to check if records were loaded and/or displayed on page
end
end
So my questions:
Is it a good idea to test if records were loaded on a specific page in a feature spec, or should I write controller spec for that?
If it's ok to go with feature spec here, what would be idiomatically correct way to do that?
You can't use controller spec to test if records are shown by any page as controller doesn't care what's displayed and what's not. In that case using feature spec sounds just fine.

click_link rspec test not finding tag by id

I've already added the reply-to-microposts functionality to Michael Hartl's Rails 3 Tutorial book, but I'm now trying to write the tests for them (I know I did it backwards). The replies addition works, but I'm trying to write the following test
create user and two posts from user
create other_user and two posts from other_user
have user follow other_user
check that user's home page feed includes other_user's post
...
My tests are currently failing at step four. Below is the snippet from the spec file, the failure error, and the link to my repo. (user is defined before this describe, but within the scope of this block)
describe "replying to a micropost" do
let (:other_user) { FactoryGirl.create(:user) }
let (:first_post) { FactoryGirl.create(:micropost, user: other_user, content: "Whatever.") }
let (:second_post) { FactoryGirl.create(:micropost, user: other_user, content: "Nevermind.") }
before do
user.follow!(other_user)
visit root_path
end
it "should render the posts from other user" do
page.should have_selector("li##{first_post.id}", text: first_post.content)
page.should have_selector("li##{second_post.id}", text: second_post.content)
end
Failures:
1) Static pages Home page for signed-in users replying to a micropost should render the posts from other user
Failure/Error: page.should have_selector("li##{first_post.id}", text: first_post.content)
expected css "li#203" with text "Whatever." to return something
# ./spec/requests/static_pages_spec.rb:85:in `block (5 levels) in <top (required)>'
Finished in 3.43 seconds
17 examples, 1 failure
Failed examples:
rspec ./spec/requests/static_pages_spec.rb:84 # Static pages Home page for signed-in users replying to a micropost should render the posts from other user
Done.
https://github.com/johnklawlor/sample_app
In short, my tests failed because I need to user let! (let bang) in order to create the micropost at the time of the let! call. Using a simple let call (i.e. without the !) doesn't create the micropost until the page.should have_selector line, which is after the visit root_path call and therefore does not find the micropost on the page.
Here's how I figured it out... It's related to a statement iterated in this question regarding let vs before initializations, where s/he states, "For the method defined by let, the initialization code only runs if the example calls it." (source: stackoverflow.com/questions/5359558/when-to-use-rspec-let) In my example, first_post and second_post weren't being created until I called on their id's in the have_selector('li'... call, which means they aren't created until after the visit root_path call and therefore after the user's feed is built. The only thing I have to back up this assumption is that if you add some line like first_post.content=first_post.content (and the same for second_post) before the visit root_path call, the tags are generated for the posts as attested by the page opened from save_and_open_page and by the passing tests. Inserting a line like first_post.content=first_post.content after visit root_path produces no li tags and failing tests.

In RSpec/Rails acceptance tests,is there any way to push messages to the RSpec output and maintain the nice indentation?

My acceptance tests (RSpec/Capybara) have some long sequences of steps all under a single it...do block, and I'd like to manually add some additional documentation of each step into the RSpec documentation output.
So where the RSpec output (in documentation format) currently looks like:
FooController
New user creates account and creates profile
it passes
Within the long sequence of steps I'd like to push some additional info to the output:
FooController
New user creates account and creates profile
... new user saw signup page!
... new user got email confirmation!
... confirmation link worked!
... new user saw empty profile!
... new user filled in profile
it passes
In terms of documenting the app, those extra statements would be better than a large black box with a single 'it passed' result message.
Since there is apparently no way to use multiple it...do blocks to build the long sequence of steps for a acceptance test, I'm hoping there is a simple way to push additional messages to the RSpec output stream, ideally with them being indented and displayed (red/green) as if they were pat or separate it...do examples.
Eventually, I opted for customization of the DocumentationFormatter, by including the following code somewhere in the spec/support folder (to ensure automatic loading):
require "rspec/core/formatters/documentation_formatter"
class RSpec::Core::ExampleGroup
def step(msg)
example.metadata[:step_messages] << msg if example.metadata[:step_messages]
yield
end
end
class RSpec::Core::Formatters::DocumentationFormatter
def example_started(example)
example.metadata[:step_messages] = []
end
def example_passed(example)
output.puts passed_output(example)
print_steps(example)
end
private
def print_steps(example)
example.metadata[:step_messages].each do |msg|
output.puts detail_color("#{' ' * (#group_level + 1)}#{msg}")
end
end
end
With this trick, you obtain a step method that you can use within your it blocks.
When running rspec with --format documentation, relevant messages from step blocks will be printed out, appropriately indented.
For instance, the following code
it "should show all and only appoved posts" do
step "show all approved posts" do
Post.all.approved.each do |post|
should have_content(post.title)
end
end
step "show only approved posts" do
should have_selector(".post", count: Post.all.approved.count)
end
end
will produce the following output (with step strings colorized in light blue):
should show all and only appoved posts
show all approved posts
show only approved posts
This is admittedly a very rough solution, but it can probably be made nicer with a little bit more work.
Here's what I did... added a nextstep("message") method to my specs that outputs "message" to the console using awesome_print gem so I can color it, and also to the logger.
def nextstep(txt)
$step += 1
#speclog.debug ''
#speclog.debug ''
#speclog.debug "#{$scene}, step: #{$step}: " + txt
ap (' ' * ($indent * 2 - 1)) + "step: #{$step}: " + txt, {:color => {:string => :blueish}}
end
A little hackish, but it does give us very nice descriptive output when running rspec, for example if we have
it "New user creates account and creates profile" do
# some assertions
nextstep "... new user saw signup page!"
# some assertions
nextstep " ... new user got email confirmation!"
# some assertions
nextstep " ... confirmation link worked!"
# some assertions
nextstep "... new user saw empty profile!"
# some assertions
nextstep "... new user filled in profile"
end
we get the more descriptive spec output as shown in the question (and if there's a failure we see the step we were on):
step 1: ... new user saw signup page!
step 2: ... new user got email confirmation!
step 3: ... confirmation link worked!
step 4: ... new user saw empty profile!
step 5: ... new user filled in profile"

Cucumber Seems to Skip Given Step

As best I can tell cucumber is only hitting the database once between these two scenarios, but it's clearing out the database between scenarios.
The Features:
Feature: a new user vists the site and signs up
in order to get new users
when an unlogged in users comes to the website
then they should see the sign-up dialog
and be able to signup for the website
Background:
Given I have at least one deal
Scenario: a new user is asked to signup
Given I am on show deal
Then I should see "New Here?"
#javascript
Scenario: new user signup failure
Given I am on show deal
When I fill in "consumer[user_attributes][email]" with "test#test.com"
And I press "consumer_submit"
Then I should see "1 error prohibited"
The Step Definition:
Given /^I have at least one deal$/ do
Deal.create copy:'Example Deal Copy', copy_header:'Example Deal Header', copy_subheader:'Example Deal Subheader' if Deal.all.size == 0
end
The Result:
Background: # features/new_user_signup.feature:7
Given I have at least one deal # features/step_definitions/new_user_signup_steps.rb:1
Scenario: a new user is asked to signup # features/new_user_signup.feature:10
Given I am on show deal # features/step_definitions/web_steps.rb:44
Then I should see "New Here?" # features/step_definitions/web_steps.rb:105
#javascript
Scenario: new user signup failure # features/new_user_signup.feature:15
Given I am on show deal # features/step_definitions/web_steps.rb:44
Couldn't find Deal with ID=1 (ActiveRecord::RecordNotFound)
./app/controllers/deals_controller.rb:17:in `show'
<internal:prelude>:10:in `synchronize'
./features/step_definitions/web_steps.rb:45:in `/^(?:|I )am on (.+)$/'
features/new_user_signup.feature:16:in `Given I am on show deal'
When I fill in "consumer[user_attributes][email]" with "test#test.com" # features/step_definitions/web_steps.rb:60
And I press "consumer_submit" # features/step_definitions/web_steps.rb:52
Then I should see "1 error prohibited" # features/step_definitions/web_steps.rb:105
Failing Scenarios:
cucumber features/new_user_signup.feature:15 # Scenario: new user signup failure
Whichever scenario I put second will give the ActiveRecord error. Why are there no records in the database for my second scenario?
Now I know how you've mapped "show deal" I am tempted to say that the problem is that the Deal instance possibly exists but it's id is not equal 1. Can you check please?
And here is a tip: while you're defining paths in your path.rb, you may do something like this:
when /the edit deal page/
edit_deal_path(Deal.first)
or even this:
when /the deal page for deal named ".*"/
deal_name = page_name.scan(/".*"/).first.gsub("\"", '')
deal = Deal.find_by_name(deal_name)
deal_path(deal)
As long as you've defined your "I am on" webstep like this:
Given /^(?:|I )am on (.+)$/ do |page_name|
visit path_to(page_name)
end
It's far better than "deals/1" :)

How to use Cucumber/Factory Girl with Persistent Roles

I use db/seeds.rb to populate my database with 2 user roles ("Admin", "User") that will never change. When i run tests though, the seed data does not get carried over and the results are error tests.
When i try to run cucumber i get the following error:
Using the default profile... Feature: Sign in In order to get access
to protected sections of the site A valid user Should be able to
sign in
Scenario: User is not signed up #
features/users/sign_in.feature:6 Not registered: role
(ArgumentError)
/Users/x/.rvm/gems/ruby-1.9.2-p180/gems/factory_girl-2.0.0.rc4/lib/factory_girl/registry.rb:15:in
find'
/Users/x/.rvm/gems/ruby-1.9.2-p180/gems/factory_girl-2.0.0.rc4/lib/factory_girl.rb:39:in
factory_by_name'
/Users/x/.rvm/gems/ruby-1.9.2-p180/gems/factory_girl-2.0.0.rc4/lib/factory_girl/syntax/vintage.rb:53:in
default_strategy'
/Users/x/.rvm/gems/ruby-1.9.2-p180/gems/factory_girl-2.0.0.rc4/lib/factory_girl/syntax/vintage.rb:146:in
Factory'
/Users/x/rails/ply_rails/features/support/db_setup.rb:6:in
`Before'
Given I am not logged in #
features/step_definitions/user_steps.rb:36
Here is what my setup looks like:
# features/support/db_setup.rb
Before do
# Truncates the DB before each Scenario,
# make sure you've added database_cleaner to your Gemfile.
DatabaseCleaner.clean
Factory(:role, :name => 'Admin')
Factory(:role, :name => 'User')
end
# features/users/sign_in.feature
Feature: Sign in
In order to get access to protected sections of the site
A valid user
Should be able to sign in
Scenario: User is not signed up # THIS IS LINE 6
Given I am not logged in
And no user exists with an email of "user#user.com"
When I go to the sign in page
And I sign in as "user#user.com/password"
Then I should see "Invalid email or password."
And I go to the home page
And I should be signed out
# features/step_definitions/user_steps.rb
Given /^I am a "([^"]*)" named "([^"]*)" with an email "([^"]*)" and password "([^"]*)"$/ do |role, name, email, password|
User.new(:name => name,
:email => email,
:role => Role.find_by_name(role.to_s.camelize),
:password => password,
:password_confirmation => password).save!
end
Not sure where to start on getting this working, thank you for your help/time!
Well the point of tests is to start with a clean database, i.e a consistent state, so it's kind of good that everything gets wiped.
Secondly, in terms of cucumber, you should be defining a Background block to do the set up. This will run for each scenario, and has the benefit of every action being explicitly known. This is especially useful if you use the cucumber scenario plain text to show to clients. So you should do something like:
Background:
Given that the role "Admin" exists
And that the role "User" exists
Scenario:
etc
And make custom steps for the that the role [blank] exists that will create the role for you.

Resources