Wednesday, April 22, 2009

Pagination, Page 1

‹prev | My Chain | next›

I still have several stray scenarios in need of some Given/When/Thens. First to receive the full Cucumber treatment is "Paginating results", which I give form as:
    Scenario: Paginating results

Given 50 yummy recipes
And a 0.5 second wait to allow the search index to be updated
When I search for "yummy"
Then I should see 20 results
And 3 pages of results
And I should not be able to go to a previous page
When I visit page 3
Then I should see 10 results
And I should not be able to go to a next page
When I visit the previous page
Then I should see 20 results
And I should be able to go to a previous page
When I visit the next page
Then I should see 10 results
When I visit page -1
Then I should see page 1
When I visit page "foo"
Then I should see page 1
When I visit page 4
Then I should see page 1
(commit)

That is a pretty complete description of pagination, including navigating via the next / previous link, the page number and even some exploration of boundary conditions.

Implementing the Given 50 yummy recipes step is a breeze with using ruby's range operator:
Given /^(\d+) (.+) recipes$/ do |count, keyword|
date = Date.new(2009, 4, 22)

(0..count.to_i).each do |i|
permalink = "id-#{i}-#{keyword.gsub(/\W/, '-')}"

@pancake_recipe = {
:title => "#{keyword} recipe #{i}",
:date => date
}

RestClient.put "#{@@db}/#{permalink}",
@pancake_recipe.to_json,
:content_type => 'application/json'
end
end
That step will create zero through count (non-inclusive) recipes with titles containing the keyword specified in the Cucumber text. Since each document contains that text, searching for that keyword will return all of them—perfect for pagination testing.

Running the scenario should now get through the first 3 steps, but...
cstrom@jaynestown:~/repos/eee-code$ cucumber features/recipe_search.feature -n \
-s "Paginating results"
Feature: Search for recipes

So that I can find one recipe among many
As a web user
I want to be able search recipes

Scenario: Paginating results
Given 50 yummy recipes
And a 0.5 second wait to allow the search index to be updated
When I search for "yummy"
No such file or directory - /home/cstrom/.gem/ruby/1.8/gems/polyglot-0.2.5/lib/views/search.haml (Errno::ENOENT)
./features/support/../../eee.rb:23:in `GET /recipes/search'
(eval):7:in `get'
features/recipe_search.feature:56:in `When I search for "yummy"'
...
This is what I get for upgrading gems. Fortunately I remember which gems I upgraded today: webrat. That makes is relatively easy to track down this thread and the fix, adding this to features/support/env.rb:
# Force the application name because polyglot breaks the auto-detection logic.
Sinatra::Application.app_file = File.join(File.dirname(__FILE__), *%w[.. .. eee.rb])
With the first three steps passing, next up is Then I should see 20 results step, which can be defined as:
Then /^I should see (\d+) results$/ do |count|
response.should have_selector("table a", :count => count.to_i)
end
When run, this step fails with the less than helpful error message:
cstrom@jaynestown:~/repos/eee-code$ cucumber features/recipe_search.feature -n \
-s "Paginating results"
Feature: Search for recipes

So that I can find one recipe among many
As a web user
I want to be able search recipes

Scenario: Paginating results
Given 50 yummy recipes
And a 0.5 second wait to allow the search index to be updated
When I search for "yummy"
Then I should see 25 results
expected following output to contain a <table a/> tag:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><table>
<tr>
<th>Name</th>
<th>Date</th>
</tr>
<tr class="row0">
<td>
<a href="/recipes/id-0-yummy">yummy recipe 0</a>
</td>
<td>2009-04-22</td>
</tr>
...
Clearly, the HTML does contain "a" tags inside a "table". The expectation string representation is the first argument of have_selector ("table a"), but the actual expectation in this case is more specific—a specific number of those selectors are expected. It is that specific case that is not being met here.

To rectify, I need to make a code change. This means that it is time to work my way inside. The couchdb-lucene add-on specifies the page size with the limit query option. The change that I want is to ensure that the couchdb-lucene query string matches limit=20:
    it "should have pages sizes of 20 records" do
RestClient.should_receive(:get).
with(/limit=20/).
and_return('{"total_rows":1,"rows":[]}')

get "/recipes/search?q=title:eggs"
end
Writing the code that implements this example is simple enough—add limit=20 to the RestClient query of couchdb-lucene:
get '/recipes/search' do
data = RestClient.get "#{@@db}/_fti?limit=20&q=#{params[:q]}"
@results = JSON.parse(data)

haml :search
end
That breaks some older examples that were a bit too specific. Loosening their expectations (e.g. matching "q=title:eggs" instead of the exact string "#{@@db}/_fti?q=eggs") resolves those failures.

This also fixes the cucumber failure:
cstrom@jaynestown:~/repos/eee-code$ cucumber features/recipe_search.feature -n \
-s "Paginating results"
Feature: Search for recipes

So that I can find one recipe among many
As a web user
I want to be able search recipes

Scenario: Paginating results
Given 50 yummy recipes
And a 0.5 second wait to allow the search index to be updated
When I search for "yummy"
Then I should see 20 results
And 3 pages of results
And I should not be able to go to a previous page
When I click page 3
Then I should see 10 results
And I should not be able to go to a next page
When I click the previous page
Then I should see 20 results
And I should be able to go to a previous page
When I click the next page
Then I should see 10 results
When I visit page -1
Then I should see page 1
When I visit page "foo"
Then I should see page 1
When I visit page 4
Then I should see page 1

1 scenario
3 skipped steps
13 undefined steps
4 passed steps
(commit)

Four steps down, 13 to go. That is as good a stopping point as any for today. I will pick things up with the next pending spec tomorrow.

No comments:

Post a Comment