Successfully reported this slideshow.

Cultivating Cucumber

5

Share

Loading in …3
×
1 of 186
1 of 186

More Related Content

Related Books

Free with a 14 day trial from Scribd

See all

Cultivating Cucumber

  1. 1. Friday, February 4, 2011
  2. 2. @leshill Friday, February 4, 2011
  3. 3. Friday, February 4, 2011
  4. 4. Rocketeer #7 Friday, February 4, 2011
  5. 5. Friday, February 4, 2011
  6. 6. We are hiring. http://pictureofhealth.com Friday, February 4, 2011
  7. 7. Cultivating Cucumber It is great food for thought! Friday, February 4, 2011
  8. 8. Happy Friday, February 4, 2011
  9. 9. Not so Happy Friday, February 4, 2011
  10. 10. Confident! And Happy! Friday, February 4, 2011
  11. 11. Background Friday, February 4, 2011
  12. 12. Feature 1 Feature: Design and Build an app using BDD 2 In order to produce an app at low cost and high speed 3 As a developer 4 I should employ Rails with Cucumber Friday, February 4, 2011
  13. 13. Scenario and Steps Scenario: Description of what to test Friday, February 4, 2011
  14. 14. Scenario and Steps Scenario: Description of what to test Given puts the system into a known state Friday, February 4, 2011
  15. 15. Scenario and Steps Scenario: Description of what to test Given puts the system into a known state When specifies the user’s actions Friday, February 4, 2011
  16. 16. Scenario and Steps Scenario: Description of what to test Given puts the system into a known state When specifies the user’s actions Then we can observe the outcome Friday, February 4, 2011
  17. 17. Scenario and Steps Scenario: Description of what to test Given puts the system into a known state Given specifies the user’s actions Given we can observe the outcome Friday, February 4, 2011
  18. 18. Step Definitions def log(*args) puts args.join(',') end When /^three (.*) mice$/ do |disability| log disability end When /^three blind (.*)$/ do |animal| log animal end Friday, February 4, 2011
  19. 19. Step Definitions def log(*args) puts args.join(',') end When /^three (.*) mice$/ do |disability| log disability end When /^three blind (.*)$/ do |animal| log animal end Friday, February 4, 2011
  20. 20. Step Definitions def log(*args) puts args.join(',') end When /^three (.*) mice$/ do |disability| log disability end When /^three blind (.*)$/ do |animal| log animal end Friday, February 4, 2011
  21. 21. Step Definitions def log(*args) puts args.join(',') end When /^three (.*) mice$/ do |disability| log disability end When /^three blind (.*)$/ do |animal| log animal end Friday, February 4, 2011
  22. 22. Step Definitions def log(*args) puts args.join(',') end When /^three (.*) mice$/ do |disability| log disability end When /^three blind (.*)$/ do |animal| log animal end Friday, February 4, 2011
  23. 23. Step Definitions def log(*args) puts args.join(',') end When /^three (.*) mice$/ do |disability| log disability end When /^three blind (.*)$/ do |animal| log animal end Friday, February 4, 2011
  24. 24. Step Definitions def log(*args) puts args.join(',') end When /^three (.*) mice$/ do |disability| log disability end When /^three blind (.*)$/ do |animal| log animal end Friday, February 4, 2011
  25. 25. Hooks Friday, February 4, 2011
  26. 26. Around Hook Around do |scenario, block| 2 puts "1st" 3 block.call 4 puts "1st" 5 end 6 7 Around do |scenario, block| 8 puts "2nd" 9 block.call 10 puts "2nd" 11 end Friday, February 4, 2011
  27. 27. Around Hook Scenario: How are the around hooks sequenced When I run Friday, February 4, 2011
  28. 28. Around Hook Scenario: How are the around hooks sequenced When I run %wip Using the wip profile... Feature: Around hook sequencing @wip Scenario: How are the around hooks sequenced # features/around_hook.feature:7 1st 2nd When I run # features/step_definitions/run_steps.rb:5 2nd 1st 1 scenario (1 passed) 1 step (1 passed) 0m1.569s The --wip switch was used, so I didn't expect anything to pass. These scenarios passed: Friday, February 4, 2011
  29. 29. Around Hook Scenario: How are the around hooks sequenced When I run %wip Using the wip profile... Feature: Around hook sequencing @wip Scenario: How are the around hooks sequenced # features/around_hook.feature:7 1st 2nd When I run # features/step_definitions/run_steps.rb:5 2nd 1st 1 scenario (1 passed) 1 step (1 passed) 0m1.569s The --wip switch was used, so I didn't expect anything to pass. These scenarios passed: Friday, February 4, 2011
  30. 30. Around Hook Scenario: What happens when there is a failure When I fail Friday, February 4, 2011
  31. 31. Around Hook Scenario: What happens when there is a failure When I fail % wip Feature: Around hook sequencing @wip Scenario: What happens when there is a failure # features/around_hook.feature:10 1st 2nd When I fail # features/step_definitions/run_steps.rb:9 (RuntimeError) ./features/step_definitions/run_steps.rb:10:in `/^I fail$/' features/around_hook.feature:11:in `When I fail' 2nd 1st Failing Scenarios: cucumber -p wip features/around_hook.feature:10 # Scenario: What happens when there is a failure 1 scenario (1 failed) 1 step (1 failed) 0m1.522s The --wip switch was used, so the failures were expected. All is good. Friday, February 4, 2011
  32. 32. Around Hook Scenario: What happens when there is a failure When I fail % wip Feature: Around hook sequencing @wip Scenario: What happens when there is a failure # features/around_hook.feature:10 1st 2nd When I fail # features/step_definitions/run_steps.rb:9 (RuntimeError) ./features/step_definitions/run_steps.rb:10:in `/^I fail$/' features/around_hook.feature:11:in `When I fail' 2nd 1st Failing Scenarios: cucumber -p wip features/around_hook.feature:10 # Scenario: What happens when there is a failure 1 scenario (1 failed) 1 step (1 failed) 0m1.522s The --wip switch was used, so the failures were expected. All is good. Friday, February 4, 2011
  33. 33. Happy Friday, February 4, 2011
  34. 34. AfterStep Hook 13 AfterStep do |scenario| 14 puts "after_step" 15 end Friday, February 4, 2011
  35. 35. AfterStep Hook Scenario: What happens when there is a failure When I run And I fail Scenario: What happens when there is a failure # features/after_step_hook.feature:13 after_step When I run # features/step_definitions/run_steps.rb:5 And I fail # features/step_definitions/run_steps.rb:9 (RuntimeError) ./features/step_definitions/run_steps.rb:10:in `/^I fail$/' features/after_step_hook.feature:15:in `And I fail' Friday, February 4, 2011
  36. 36. Not so Happy Friday, February 4, 2011
  37. 37. Global Hooks # features/support/global.rb my_heavy_object = HeavyObject.new 18 my_heavy_object.do_it 19 20 at_exit do 21 my_heavy_object.undo_it 22 end Friday, February 4, 2011
  38. 38. AfterConfiguration Hook AfterConfiguration do |config| 25 puts "Features in #{config.feature_dirs}" 26 end Friday, February 4, 2011
  39. 39. Tags Friday, February 4, 2011
  40. 40. Tags @likes Scenario: This scenario is tagged by likes Given this scenario is tagged "@likes" Then the "likes" tag is on the scenario Friday, February 4, 2011
  41. 41. Tags % cucumber --tags @likes Friday, February 4, 2011
  42. 42. Tags % cucumber --tags ~@likes Friday, February 4, 2011
  43. 43. Tags % cucumber --tags @likes,@ratings Friday, February 4, 2011
  44. 44. Tags % cucumber --tags @likes --tags @ratings Friday, February 4, 2011
  45. 45. Confident! And Happy! Friday, February 4, 2011
  46. 46. Tagged Hooks Friday, February 4, 2011
  47. 47. Tagged Hooks 1 Before('@given_user') do 2 @user = "This is set" 3 end Friday, February 4, 2011
  48. 48. Tagged Hooks 1 Before('@given_user') do 2 @user = "This is set" 3 end Friday, February 4, 2011
  49. 49. World Object def log(*args) puts args.join(',') end When /^three (.*) mice$/ do |disability| log disability end When /^three blind (.*)$/ do |animal| log animal end Friday, February 4, 2011
  50. 50. World Object def log(*args) puts args.join(',') end When /^three (.*) mice$/ do |disability| log disability end When /^three blind (.*)$/ do |animal| log animal end Friday, February 4, 2011
  51. 51. World Object module Log def log(*args) puts args.join(',') end end World(Log) When /^three (.*) mice$/ do |disability| log disability end When /^three blind (.*)$/ do |animal| log animal end Friday, February 4, 2011
  52. 52. Tagged Hooks 1 Feature: Using Before and After with tags 2 In order to be more effective with cucumber 3 As a cucumberist 4 You can use Before and After with tags! 5 6 Scenario: Does not want the before step run 7 Then the user is not set 8 9 @given_user 0 Scenario: Does want the before step run 1 Then the user is set Friday, February 4, 2011
  53. 53. Tagged Hooks Then /^the user is( not)? set$/ do |negated| if negated @user.should_not be else @user.should be end end Friday, February 4, 2011
  54. 54. Tagged Hooks $ cucumber features/before_and_after_profile.feature Using the default profile... Feature: Using Before and After with tags In order to be more effective with cucumber As a cucumberist You can use Before and After with tags! Scenario: This scenario does not want the before step run # features/before_and_after_profile.featur Then the user is not set # features/step_definitions/user_steps.rb:1 @given_user Scenario: This scenario wants something set before it runs # features/before_and_after_profile.featur Then the user is set # features/step_definitions/user_steps.rb:1 2 scenarios (2 passed) 2 steps (2 passed) 0m1.525s Friday, February 4, 2011
  55. 55. Happy Friday, February 4, 2011
  56. 56. Not so Happy Friday, February 4, 2011
  57. 57. Tagged Hooks 5 Before('@reviews,@likes') do 6 puts "This is an OR" 7 end 8 9 Before ('@reviews', '@likes') do 10 puts "This is an AND" 11 end 12 13 Before ('~@reviews') do 14 puts "This is NOT" 15 end Friday, February 4, 2011
  58. 58. Not so Happy Friday, February 4, 2011
  59. 59. Special Tags Friday, February 4, 2011
  60. 60. @wip 1 @wip 2 Feature: Resolving ambiguity with --guess 3 4 Scenario: Ambiguous step definitions 5 Given three blind mice Friday, February 4, 2011
  61. 61. @wip % cucumber -p wip % rake cucumber:wip % alias wip=‘cucumber -p wip’ Friday, February 4, 2011
  62. 62. @wip <% rerun = File.file?('rerun.txt') ? IO.read('rerun rerun_opts = rerun.to_s.strip.empty? ? "--format std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || %> default: <%= std_opts %> features wip: --tags @wip:3 --wip features rerun: <%= rerun_opts %> --format rerun --out re Friday, February 4, 2011
  63. 63. @wip <% rerun = File.file?('rerun.txt') ? IO.read('rerun rerun_opts = rerun.to_s.strip.empty? ? "--format std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || %> default: <%= std_opts %> features wip: --tags @wip:3 --wip features rerun: <%= rerun_opts %> --format rerun --out re Friday, February 4, 2011
  64. 64. @wip % wip Using the wip profile... Feature: Using the path helper @wip Scenario: Using a regex with the path helper # features/paths.feature:7 Given the following movie: # features/step_definitions/fabrication_steps.rb:37 | title | Gone with the wind | | year | 1939 | When I go to the "Gone with the wind" movie page # features/step_definitions/web_steps.rb:30 Then I should see "Gone with the wind (1939)" # features/step_definitions/web_steps.rb:114 expected #has_content?("Gone with the wind (1939)") to return true, got false (RSpec::Expectations::ExpectationNotMetError) features/paths.feature:12:in `Then I should see "Gone with the wind (1939)"' Failing Scenarios: cucumber -p wip features/paths.feature:7 # Scenario: Using a regex with the path helper 1 scenario (1 failed) 3 steps (1 failed, 2 passed) 0m2.245s The --wip switch was used, so the failures were expected. All is good. Friday, February 4, 2011
  65. 65. Confident! And Happy! Friday, February 4, 2011
  66. 66. @no-txn @no-txn Scenario: Sending a message without pusher Given @philip_jay_fry signs in with Twitter And I start a conversation with @t_leela When I reply with "Hi Leela" Then the source is the browser Friday, February 4, 2011
  67. 67. @javascript @javascript @pusher Scenario: Starting a conversation with Pusher Given @philip_jay_fry signs in with Twitter When the browser has subscribed to Pusher And I start a conversation with"@t_leela" Then the source is pusher in the browser Friday, February 4, 2011
  68. 68. Happy Friday, February 4, 2011
  69. 69. Using path_to Friday, February 4, 2011
  70. 70. path_to Scenario: Context is king Given the following movie: | title | Gone with the wind | | year | 1939 | When the movie "Gone with the wind" has been liked 5 times And I go to ‘/’ Then I should see "5 likes" Friday, February 4, 2011
  71. 71. path_to Scenario: Context is king Given the following movie: | title | Gone with the wind | | year | 1939 | When the movie "Gone with the wind" has been liked 5 times And I go to ‘/’ Then I should see "5 likes" Scenario: Context is king Given the following movie: | title | Gone with the wind | | year | 1939 | When the movie "Gone with the wind" has been liked 5 times And I go to the home page Then I should see "5 likes" Friday, February 4, 2011
  72. 72. path_to When /^(?:|I )am on (.+)$/ do |page_name| visit path_to(page_name) end When /^(?:|I )go to (.+)$/ do |page_name| visit path_to(page_name) end Friday, February 4, 2011
  73. 73. path_to When /^(?:|I )am on (.+)$/ do |page_name| visit path_to(page_name) end When /^(?:|I )go to (.+)$/ do |page_name| visit path_to(page_name) end Friday, February 4, 2011
  74. 74. path_to When /^(?:|I )am on (.+)$/ do |page_name| visit path_to(page_name) end When /^(?:|I )go to (.+)$/ do |page_name| visit path_to(page_name) end Friday, February 4, 2011
  75. 75. path_to When /^(?:|I )am on (.+)$/ do |page_name| visit path_to(page_name) end When /^(?:|I )go to (.+)$/ do |page_name| visit path_to(page_name) end Friday, February 4, 2011
  76. 76. path_to When /^(?:|I )am on (.+)$/ do |page_name| visit path_to(page_name) end When /^(?:|I )go to (.+)$/ do |page_name| visit path_to(page_name) end Friday, February 4, 2011
  77. 77. path_to module NavigationHelpers def path_to(page_name) case page_name when /the homes?page/ '/' else begin page_name =~ /the (.*) page/ path_components = $1.split(/s+/) self.send(path_components.push('path').join('_').to_sym) rescue Object => e raise "Can't find mapping from "#{page_name}" to a path.n" + "Now, go and add a mapping in #{__FILE__}" end end end end World(NavigationHelpers) Friday, February 4, 2011
  78. 78. path_to module NavigationHelpers def path_to(page_name) case page_name when /the homes?page/ '/' else begin page_name =~ /the (.*) page/ path_components = $1.split(/s+/) self.send(path_components.push('path').join('_').to_sym) rescue Object => e raise "Can't find mapping from "#{page_name}" to a path.n" + "Now, go and add a mapping in #{__FILE__}" end end end end World(NavigationHelpers) Friday, February 4, 2011
  79. 79. path_to module NavigationHelpers def path_to(page_name) case page_name when /the homes?page/ '/' else begin page_name =~ /the (.*) page/ path_components = $1.split(/s+/) self.send(path_components.push('path').join('_').to_sym) rescue Object => e raise "Can't find mapping from "#{page_name}" to a path.n" + "Now, go and add a mapping in #{__FILE__}" end end end end World(NavigationHelpers) Friday, February 4, 2011
  80. 80. path_to module NavigationHelpers def path_to(page_name) case page_name when /the homes?page/ '/' else begin page_name =~ /the (.*) page/ path_components = $1.split(/s+/) self.send(path_components.push('path').join('_').to_sym) rescue Object => e raise "Can't find mapping from "#{page_name}" to a path.n" + "Now, go and add a mapping in #{__FILE__}" end end end end World(NavigationHelpers) Friday, February 4, 2011
  81. 81. path_to Scenario: Using a regex with the path helper Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the "Gone with the wind" movie page Then I should see "Gone with the wind (1939)" Friday, February 4, 2011
  82. 82. path_to Scenario: Using a regex with the path helper Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the "Gone with the wind" movie page Then I should see "Gone with the wind (1939)" Friday, February 4, 2011
  83. 83. path_to Scenario: Using a regex with the path helper Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the "Gone with the wind" movie page Then I should see "Gone with the wind (1939)" when /the "([^"]+)" movie page/ movie_path(Movie.find_by_title($1)) Friday, February 4, 2011
  84. 84. path_to Scenario: Using a regex with the path helper Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the "Gone with the wind" movie page Then I should see "Gone with the wind (1939)" when /the "([^"]+)" movie page/ movie_path(Movie.find_by_title($1)) Friday, February 4, 2011
  85. 85. Confident! And Happy! Friday, February 4, 2011
  86. 86. Building A Factory Friday, February 4, 2011
  87. 87. Specific Scenario: Domain specific, totally opaque Given a movie When I go to the home page Then I should see "Gone with the wind (1939)" Friday, February 4, 2011
  88. 88. Specific Scenario: Domain specific, totally opaque Given a movie When I go to the home page Then I should see "Gone with the wind (1939)" When /^a movie$/ do Movie.create(title: 'Gone with the wind', year: 1939) end Friday, February 4, 2011
  89. 89. Specific Scenario: Domain specific, totally opaque Given a movie When I go to the home page Then I should see "Gone with the wind (1939)" When /^a movie$/ do Movie.create(title: 'Gone with the wind', year: 1939) end Friday, February 4, 2011
  90. 90. Specific Scenario: Domain specific, totally opaque Given a movie When I go to the home page Then I should see "Gone with the wind (1939)" When /^a movie$/ do Movie.create(title: 'Gone with the wind', year: 1939) end Friday, February 4, 2011
  91. 91. Imperative Scenario: Way too granular Given a movie And I set the movie's title to "Gone with the wind" And I set the movie's year to "1939" And I save the movie When I go to the home page Then I should see "Gone with the wind (1939)" Friday, February 4, 2011
  92. 92. Imperative Scenario: Way too granular Given a movie And I set the movie's title to "Gone with the wind" And I set the movie's year to "1939" And I save the movie When I go to the home page Then I should see "Gone with the wind (1939)" Friday, February 4, 2011
  93. 93. Imperative Scenario: Way too granular Given a movie And I set the movie's title to "Gone with the wind" And I set the movie's year to "1939" And I save the movie When I go to the home page Then I should see "Gone with the wind (1939)" Friday, February 4, 2011
  94. 94. Too much like Code Scenario: Too much like code Given a movie exists with { "title" => "Gone with the wind", "year" => "1939" } When I go to the home page Then I should see "Gone with the wind (1939)" Friday, February 4, 2011
  95. 95. Too much like Code Scenario: Too much like code Given a movie exists with { "title" => "Gone with the wind", "year" => "1939" } When I go to the home page Then I should see "Gone with the wind (1939)" When /^a (.+) exists with ({.+})$/ do |model_name, attributes| model = dehumanize(model_name) model.create(eval(attributes)) end Friday, February 4, 2011
  96. 96. Too much like Code Scenario: Too much like code Given a movie exists with { "title" => "Gone with the wind", "year" => "1939" } When I go to the home page Then I should see "Gone with the wind (1939)" When /^a (.+) exists with ({.+})$/ do |model_name, attributes| model = dehumanize(model_name) model.create(eval(attributes)) end Yikes! Friday, February 4, 2011
  97. 97. Too much like Code Scenario: Too much like code Given a movie exists with { "title" => "Gone with the wind", "year" => "1939" } When I go to the home page Then I should see "Gone with the wind (1939)" ` When /^a (.+) exists with ({.+})$/ do |model_name, attributes| model = dehumanize(model_name) model.create(eval(attributes)) end Friday, February 4, 2011
  98. 98. Not so Happy Friday, February 4, 2011
  99. 99. Cleaned Up Scenario: Pickle Given a movie exists with title: "Gone with the wind", year: "1939" When I go to the home page Then I should see "Gone with the wind (1939)" Friday, February 4, 2011
  100. 100. Cleaned Up Scenario: Pickle Given a movie exists with title: "Gone with the wind", year: "1939" When I go to the home page Then I should see "Gone with the wind (1939)" Friday, February 4, 2011
  101. 101. Generic Factory Step Scenario: Generic factory step Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" Friday, February 4, 2011
  102. 102. Generic Factory Step Scenario: Generic factory step Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" Friday, February 4, 2011
  103. 103. Using Tables ruby-1.9.2-p0 :001 > table => | title | year | | Gone with the wind | 1939 | ruby-1.9.2-p0 :002 > table.headers => ["title", "year"] ruby-1.9.2-p0 :003 > table.rows => [["Gone with the wind", "1939"]] ruby-1.9.2-p0 :004 > table.hashes => [{"title"=>"Gone with the wind", "year"=>"1939"}] ruby-1.9.2-p0 :005 > table.rows_hash => {"title"=>"year", "Gone with the wind"=>"1939"} Friday, February 4, 2011
  104. 104. Using Tables ruby-1.9.2-p0 :001 > table => | title | year | | Gone with the wind | 1939 | ruby-1.9.2-p0 :002 > table.headers => ["title", "year"] ruby-1.9.2-p0 :003 > table.rows => [["Gone with the wind", "1939"]] ruby-1.9.2-p0 :004 > table.hashes => [{"title"=>"Gone with the wind", "year"=>"1939"}] ruby-1.9.2-p0 :005 > table.rows_hash => {"title"=>"year", "Gone with the wind"=>"1939"} Friday, February 4, 2011
  105. 105. Using Tables ruby-1.9.2-p0 :001 > table => | title | year | | Gone with the wind | 1939 | ruby-1.9.2-p0 :002 > table.headers => ["title", "year"] ruby-1.9.2-p0 :003 > table.rows => [["Gone with the wind", "1939"]] ruby-1.9.2-p0 :004 > table.hashes => [{"title"=>"Gone with the wind", "year"=>"1939"}] ruby-1.9.2-p0 :005 > table.rows_hash => {"title"=>"year", "Gone with the wind"=>"1939"} Friday, February 4, 2011
  106. 106. Using Tables ruby-1.9.2-p0 :001 > table => | title | year | | Gone with the wind | 1939 | ruby-1.9.2-p0 :002 > table.headers => ["title", "year"] ruby-1.9.2-p0 :003 > table.rows => [["Gone with the wind", "1939"]] ruby-1.9.2-p0 :004 > table.hashes => [{"title"=>"Gone with the wind", "year"=>"1939"}] ruby-1.9.2-p0 :005 > table.rows_hash => {"title"=>"year", "Gone with the wind"=>"1939"} Friday, February 4, 2011
  107. 107. Using Tables ruby-1.9.2-p0 :001 > table => | title | year | | Gone with the wind | 1939 | ruby-1.9.2-p0 :002 > table.headers => ["title", "year"] ruby-1.9.2-p0 :003 > table.rows => [["Gone with the wind", "1939"]] ruby-1.9.2-p0 :004 > table.hashes => [{"title"=>"Gone with the wind", "year"=>"1939"}] ruby-1.9.2-p0 :005 > table.rows_hash => {"title"=>"year", "Gone with the wind"=>"1939"} Friday, February 4, 2011
  108. 108. Generic Factory Step module FabricationMethods def create_from_table(model_name, table, extra = {}) fabricator_name = generate_fabricator_name(model_name) is_singular = model_name.to_s.singularize == model_name.to_s hashes = is_singular ? [table.rows_hash] : table.hashes @they = hashes.map do |hash| hash = hash.merge(extra).inject({}) {|h,(k,v)| h.update(k.gsub(/W+/,'_').to_sym => v) Fabricate(fabricator_name, hash) end if is_singular @it = @they.last instance_variable_set("@#{fabricator_name}", @it) end end def generate_fabricator_name(model_name) model_name.gsub(/W+/, '_').downcase.singularize.to_sym end end World(FabricationMethods) When /^the following ([^"]*):$/ do |model_name, table| create_from_table(model_name, table) end Friday, February 4, 2011
  109. 109. Generic Factory Step module FabricationMethods def create_from_table(model_name, table, extra = {}) fabricator_name = generate_fabricator_name(model_name) is_singular = model_name.to_s.singularize == model_name.to_s hashes = is_singular ? [table.rows_hash] : table.hashes @they = hashes.map do |hash| hash = hash.merge(extra).inject({}) {|h,(k,v)| h.update(k.gsub(/W+/,'_').to_sym => v) Fabricate(fabricator_name, hash) end if is_singular @it = @they.last instance_variable_set("@#{fabricator_name}", @it) end end def generate_fabricator_name(model_name) model_name.gsub(/W+/, '_').downcase.singularize.to_sym end end World(FabricationMethods) When /^the following ([^"]*):$/ do |model_name, table| create_from_table(model_name, table) end Friday, February 4, 2011
  110. 110. Generic Factory Step module FabricationMethods def create_from_table(model_name, table, extra = {}) fabricator_name = generate_fabricator_name(model_name) is_singular = model_name.to_s.singularize == model_name.to_s hashes = is_singular ? [table.rows_hash] : table.hashes @they = hashes.map do |hash| hash = hash.merge(extra).inject({}) {|h,(k,v)| h.update(k.gsub(/W+/,'_').to_sym => v) Fabricate(fabricator_name, hash) end if is_singular @it = @they.last instance_variable_set("@#{fabricator_name}", @it) end end def generate_fabricator_name(model_name) model_name.gsub(/W+/, '_').downcase.singularize.to_sym end end World(FabricationMethods) When /^the following ([^"]*):$/ do |model_name, table| create_from_table(model_name, table) end Friday, February 4, 2011
  111. 111. Generic Factory Step module FabricationMethods def create_from_table(model_name, table, extra = {}) fabricator_name = generate_fabricator_name(model_name) is_singular = model_name.to_s.singularize == model_name.to_s hashes = is_singular ? [table.rows_hash] : table.hashes @they = hashes.map do |hash| hash = hash.merge(extra).inject({}) {|h,(k,v)| h.update(k.gsub(/W+/,'_').to_sym => v) Fabricate(fabricator_name, hash) end if is_singular @it = @they.last instance_variable_set("@#{fabricator_name}", @it) end end def generate_fabricator_name(model_name) model_name.gsub(/W+/, '_').downcase.singularize.to_sym end end World(FabricationMethods) When /^the following ([^"]*):$/ do |model_name, table| create_from_table(model_name, table) end Friday, February 4, 2011
  112. 112. Generic Factory Step module FabricationMethods def create_from_table(model_name, table, extra = {}) fabricator_name = generate_fabricator_name(model_name) is_singular = model_name.to_s.singularize == model_name.to_s hashes = is_singular ? [table.rows_hash] : table.hashes @they = hashes.map do |hash| hash = hash.merge(extra).inject({}) {|h,(k,v)| h.update(k.gsub(/W+/,'_').to_sym => v) Fabricate(fabricator_name, hash) end if is_singular @it = @they.last instance_variable_set("@#{fabricator_name}", @it) end end def generate_fabricator_name(model_name) model_name.gsub(/W+/, '_').downcase.singularize.to_sym end end World(FabricationMethods) When /^the following ([^"]*):$/ do |model_name, table| create_from_table(model_name, table) end Friday, February 4, 2011
  113. 113. Easy Relationships Scenario: Has/Belongs to Relationships Given the following movie: | title | Gone with the wind | | year | 1939 | And that movie has the following role: | name | Scarlett O'Hara | And that role has the following actor: | name | Vivian Leigh | And that movie has the following role: | name | Rhett Butler | And that role has the following actor: | name | Clark Gable | When I go to the home page And I follow "Gone with the wind (1939)" Then I should see "Scarlett O'Hara - Vivian Leigh" And I should see "Rhett Butler - Clark Gable" Friday, February 4, 2011
  114. 114. Easy Relationships Scenario: Has/Belongs to Relationships Given the following movie: | title | Gone with the wind | | year | 1939 | And that movie has the following role: | name | Scarlett O'Hara | And that role has the following actor: | name | Vivian Leigh | And that movie has the following role: | name | Rhett Butler | And that role has the following actor: | name | Clark Gable | When I go to the home page And I follow "Gone with the wind (1939)" Then I should see "Scarlett O'Hara - Vivian Leigh" And I should see "Rhett Butler - Clark Gable" Friday, February 4, 2011
  115. 115. Easy Relationships Scenario: Has/Belongs to Relationships Given the following movie: | title | Gone with the wind | | year | 1939 | And that movie has the following role: | name | Scarlett O'Hara | And that role has the following actor: | name | Vivian Leigh | And that movie has the following role: | name | Rhett Butler | And that role has the following actor: | name | Clark Gable | When I go to the home page And I follow "Gone with the wind (1939)" Then I should see "Scarlett O'Hara - Vivian Leigh" And I should see "Rhett Butler - Clark Gable" Friday, February 4, 2011
  116. 116. Easy Relationships Scenario: Has/Belongs to Relationships Given the following movie: | title | Gone with the wind | | year | 1939 | And that movie has the following role: | name | Scarlett O'Hara | And that role has the following actor: | name | Vivian Leigh | And that movie has the following role: | name | Rhett Butler | And that role has the following actor: | name | Clark Gable | When I go to the home page And I follow "Gone with the wind (1939)" Then I should see "Scarlett O'Hara - Vivian Leigh" And I should see "Rhett Butler - Clark Gable" Friday, February 4, 2011
  117. 117. Confident! And Happy! Friday, February 4, 2011
  118. 118. Transforms Friday, February 4, 2011
  119. 119. Step Argument Transform Scenario: Step argument transforms Given the following movie: | title | Gone with the wind | | year | 1939 | When that movie has been liked 5 times And I go to the home page Then I should see "5 likes" Friday, February 4, 2011
  120. 120. Step Argument Transform Scenario: Step argument transforms Given the following movie: | title | Gone with the wind | | year | 1939 | When that movie has been liked 5 times And I go to the home page Then I should see "5 likes" Friday, February 4, 2011
  121. 121. Step Argument Transform When /^that movie has been liked (d+) times$/ do |count| count.to_i.times do |i| @movie.like! end end Friday, February 4, 2011
  122. 122. Step Argument Transform When /^that movie has been liked (d+) times$/ do |count| count.to_i.times do |i| @movie.like! end end Friday, February 4, 2011
  123. 123. Step Argument Transform # Convert 'a' or 'an' to the integer value 1 Transform /^(an?|-?d+)$/ do |amount| amount =~ /an?/ ? 1 : amount.to_i end When /^that movie has been liked (an?|-?d+) times$/ do |count| count.times do |i| @movie.like! end end Friday, February 4, 2011
  124. 124. Step Argument Transform # Convert 'a' or 'an' to the integer value 1 Transform /^(an?|-?d+)$/ do |amount| amount =~ /an?/ ? 1 : amount.to_i end When /^that movie has been liked (an?|-?d+) times$/ do |count| count.times do |i| @movie.like! end end Friday, February 4, 2011
  125. 125. Step Argument Transform # Convert 'a' or 'an' to the integer value 1 Transform /^(an?|-?d+)$/ do |amount| amount =~ /an?/ ? 1 : amount.to_i end When /^that movie has been liked (an?|-?d+) times$/ do |count| count.times do |i| @movie.like! end end Friday, February 4, 2011
  126. 126. Step Argument Transform # Convert 'a' or 'an' to the integer value 1 Transform /^(an?|-?d+)$/ do |amount| amount =~ /an?/ ? 1 : amount.to_i end When /^that movie has been liked (an?|-?d+) times$/ do |count| count.times do |i| @movie.like! end end Friday, February 4, 2011
  127. 127. Not so Happy Friday, February 4, 2011
  128. 128. Step Argument Transforms Scenario: Context is king Given the following movie: | title | Gone with the wind | | year | 1939 | When the movie "Gone with the wind" has been liked 5 times And I go to the home page Then I should see "5 likes" Friday, February 4, 2011
  129. 129. Step Argument Transforms Scenario: Context is king Given the following movie: | title | Gone with the wind | | year | 1939 | When the movie "Gone with the wind" has been liked 5 times And I go to the home page Then I should see "5 likes" Friday, February 4, 2011
  130. 130. Step Argument Transforms Transform /^movie "([^"]+)"$/ do |title| Movie.find_by_title(title) end When /^the (movie "(?:[^"]+)") has been liked (an?|-?d+) times$/ do |movie, count| count.times do |i| movie.like! end end Friday, February 4, 2011
  131. 131. Step Argument Transforms Transform /^movie "([^"]+)"$/ do |title| Movie.find_by_title(title) end When /^the (movie "(?:[^"]+)") has been liked (an?|-?d+) times$/ do |movie, count| count.times do |i| movie.like! end end Friday, February 4, 2011
  132. 132. Step Argument Transforms Transform /^movie "([^"]+)"$/ do |title| Movie.find_by_title(title) end When /^the (movie "(?:[^"]+)") has been liked (an?|-?d+) times$/ do |movie, count| count.times do |i| movie.like! end end Friday, February 4, 2011
  133. 133. Step Argument Transforms Transform /^movie "([^"]+)"$/ do |title| Movie.find_by_title(title) end When /^the (movie "(?:[^"]+)") has been liked (an?|-?d+) times$/ do |movie, count| count.times do |i| movie.like! end end Friday, February 4, 2011
  134. 134. Confident! And Happy! Friday, February 4, 2011
  135. 135. Table Transforms Transform /^table:name,actor$/ do |table| table.map_column!(:actor) do |actor_name| Actor.find_by_name(actor_name) end table end Friday, February 4, 2011
  136. 136. Table Transforms Transform /^table:name,actor$/ do |table| table.map_column!(:actor) do |actor_name| Actor.find_by_name(actor_name) end table end Friday, February 4, 2011
  137. 137. Table Transforms Transform /^table:name,actor$/ do |table| table.map_column!(:actor) do |actor_name| Actor.find_by_name(actor_name) end table end Friday, February 4, 2011
  138. 138. Table Transforms Transform /^table:name,actor$/ do |table| table.map_column!(:actor) do |actor_name| Actor.find_by_name(actor_name) end table end Friday, February 4, 2011
  139. 139. Table Transforms Scenario: Relationships using step transforms to transform the table Given the following movie: | title | Gone with the wind | | year | 1939 | And that movie has the following roles: | name | actor | | Scarlett O'Hara | Vivian Leigh | | Rhett Butler | Clark Gable | When I go to the home page And I follow "Gone with the wind (1939)" Then I should see "Scarlett O'Hara - Vivian Leigh" And I should see "Rhett Butler - Clark Gable" Friday, February 4, 2011
  140. 140. Table Transforms Scenario: Relationships using step transforms to transform the table Given the following movie: | title | Gone with the wind | | year | 1939 | And that movie has the following roles: | name | actor | | Scarlett O'Hara | Vivian Leigh | | Rhett Butler | Clark Gable | When I go to the home page And I follow "Gone with the wind (1939)" Then I should see "Scarlett O'Hara - Vivian Leigh" And I should see "Rhett Butler - Clark Gable" Friday, February 4, 2011
  141. 141. Table Transforms Transform /^table:name,actor$/ do |table| table.map_column!(:actor) do |actor_name| Fabricate(:actor, :name => actor_name) end table end Friday, February 4, 2011
  142. 142. Happy Friday, February 4, 2011
  143. 143. Not so Happy Friday, February 4, 2011
  144. 144. Better UI Scopes Friday, February 4, 2011
  145. 145. Shipped Web Steps Scenario: Viewing a group When @philip_jay_fry signs in And I go to the “Planet Express” group Then I should see "Planet_Express" within "//h2" And I should see "t_leela" within "//ul/li[@id='t_leela']" And I should see "dr_hjfarnsworth" within "//ul/li[@id='dr_hjfarnswor Friday, February 4, 2011
  146. 146. Shipped Web Steps Scenario: Viewing a group When @philip_jay_fry signs in And I go to the “Planet Express” group Then I should see "Planet_Express" within "//h2" And I should see "t_leela" within "//ul/li[@id='t_leela']" And I should see "dr_hjfarnsworth" within "//ul/li[@id='dr_hjfarnswor Friday, February 4, 2011
  147. 147. Shipped Web Steps module WithinHelpers 13def with_scope(locator) 14 locator ? within(locator) { yield } : yield 15end 16 end 17 World(WithinHelpers) 18 27 When /^(?:|I )press "([^"]*)"(?: within "([^"]*)")?$/ do |button, selector| 28 with_scope(selector) do 29 click_button(button) 30 end 31 end 32 33 When /^(?:|I )follow "([^"]*)"(?: within "([^"]*)")?$/ do |link, selector| 34 with_scope(selector) do 35 click_link(link) 36 end 37 end 38 39 When /^(?:|I )fill in "([^"]*)" with "([^"]*)"(?: within "([^"]*)")?$/ do |field 40 with_scope(selector) do 41 fill_in(field, :with => value) 42 end 43 end 44 Friday, February 4, 2011
  148. 148. Shipped Web Steps module WithinHelpers 13def with_scope(locator) 14 locator ? within(locator) { yield } : yield 15end 16 end 17 World(WithinHelpers) 18 27 When /^(?:|I )press "([^"]*)"(?: within "([^"]*)")?$/ do |button, selector| 28 with_scope(selector) do 29 click_button(button) 30 end 31 end 32 33 When /^(?:|I )follow "([^"]*)"(?: within "([^"]*)")?$/ do |link, selector| 34 with_scope(selector) do 35 click_link(link) 36 end 37 end 38 39 When /^(?:|I )fill in "([^"]*)" with "([^"]*)"(?: within "([^"]*)")?$/ do |field 40 with_scope(selector) do 41 fill_in(field, :with => value) 42 end 43 end 44 Friday, February 4, 2011
  149. 149. Shipped Web Steps module WithinHelpers 13def with_scope(locator) 14 locator ? within(locator) { yield } : yield 15end 16 end 17 World(WithinHelpers) 18 27 When /^(?:|I )press "([^"]*)"(?: within "([^"]*)")?$/ do |button, selector| 28 with_scope(selector) do 29 click_button(button) 30 end 31 end 32 33 When /^(?:|I )follow "([^"]*)"(?: within "([^"]*)")?$/ do |link, selector| 34 with_scope(selector) do 35 click_link(link) 36 end 37 end 38 39 When /^(?:|I )fill in "([^"]*)" with "([^"]*)"(?: within "([^"]*)")?$/ do |field 40 with_scope(selector) do 41 fill_in(field, :with => value) 42 end 43 end 44 Friday, February 4, 2011
  150. 150. Shipped Web Steps module WithinHelpers 13def with_scope(locator) 14 locator ? within(locator) { yield } : yield 15end 16 end 17 World(WithinHelpers) 18 27 When /^(?:|I )press "([^"]*)"(?: within "([^"]*)")?$/ do |button, selector| 28 with_scope(selector) do 29 click_button(button) 30 end 31 end 32 33 When /^(?:|I )follow "([^"]*)"(?: within "([^"]*)")?$/ do |link, selector| 34 with_scope(selector) do 35 click_link(link) 36 end 37 end 38 39 When /^(?:|I )fill in "([^"]*)" with "([^"]*)"(?: within "([^"]*)")?$/ do |field 40 with_scope(selector) do 41 fill_in(field, :with => value) 42 end 43 end 44 Friday, February 4, 2011
  151. 151. Shipped Web Steps module WithinHelpers 13def with_scope(locator) 14 locator ? within(locator) { yield } : yield 15end 16 end 17 World(WithinHelpers) 18 27 When /^(?:|I )press "([^"]*)"(?: within "([^"]*)")?$/ do |button, selector| 28 with_scope(selector) do 29 click_button(button) 30 end 31 end 32 33 When /^(?:|I )follow "([^"]*)"(?: within "([^"]*)")?$/ do |link, selector| 34 with_scope(selector) do 35 click_link(link) 36 end 37 end 38 39 When /^(?:|I )fill in "([^"]*)" with "([^"]*)"(?: within "([^"]*)")?$/ do |field 40 with_scope(selector) do 41 fill_in(field, :with => value) 42 end 43 end 44 Friday, February 4, 2011
  152. 152. Not so Happy Friday, February 4, 2011
  153. 153. Removing CSS/XPath Scenario: Simple and Direct Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" in the movie list Friday, February 4, 2011
  154. 154. Removing CSS/XPath Scenario: Simple and Direct Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" in the movie list Friday, February 4, 2011
  155. 155. Removing CSS/XPath Scenario: Simple and Direct Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" in the movie list { "in the movie list" => '.movies' }.each do |within, selector| When /^(.+) #{within}$/ do |step| with_scope(selector) do When step end end end Friday, February 4, 2011
  156. 156. Removing CSS/XPath Scenario: Simple and Direct Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" in the movie list { "in the movie list" => '.movies' }.each do |within, selector| When /^(.+) #{within}$/ do |step| with_scope(selector) do When step end end end Friday, February 4, 2011
  157. 157. Removing CSS/XPath Scenario: Simple and Direct Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" in the movie list { "in the movie list" => '.movies' }.each do |within, selector| When /^(.+) #{within}$/ do |step| with_scope(selector) do When step end end end Friday, February 4, 2011
  158. 158. Removing CSS/XPath Scenario: Simple and Direct Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" in the movie list { "in the movie list" => '.movies' }.each do |within, selector| When /^(.+) #{within}$/ do |step| with_scope(selector) do When step end end end Friday, February 4, 2011
  159. 159. Removing CSS/XPath Scenario: Simple and Direct Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" in the movie list { "in the movie list" => '.movies' }.each do |within, selector| When /^(.+) #{within}$/ do |step| with_scope(selector) do When step end end end Friday, February 4, 2011
  160. 160. Happy Friday, February 4, 2011
  161. 161. Removing CSS/XPath Scenario: Using selector_for Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" within the movie list Friday, February 4, 2011
  162. 162. Removing CSS/XPath Scenario: Using selector_for Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" within the movie list Friday, February 4, 2011
  163. 163. Removing CSS/XPath Scenario: Using selector_for Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" within the movie list When /^(.*) within ([^:"]+)$/ do |step, scope| with_scope(selector_for(scope)) do When step end end Friday, February 4, 2011
  164. 164. Removing CSS/XPath Scenario: Using selector_for Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" within the movie list When /^(.*) within ([^:"]+)$/ do |step, scope| with_scope(selector_for(scope)) do When step end end Friday, February 4, 2011
  165. 165. Removing CSS/XPath Scenario: Using selector_for Given the following movie: | title | Gone with the wind | | year | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" within the movie list When /^(.*) within ([^:"]+)$/ do |step, scope| with_scope(selector_for(scope)) do When step end end Friday, February 4, 2011
  166. 166. selector_for module HtmlSelectorsHelper def selector_for(scope) case scope when /the body/ "html > body" when /the movie list/ ".movies" else raise "Can't find mapping from "#{scope}" to a selector.n" + "Now, go and add a mapping in #{__FILE__}" end end end World(HtmlSelectorsHelper) Friday, February 4, 2011
  167. 167. Happy Friday, February 4, 2011
  168. 168. Persistent Scopes Scenario: Persistent scoping Given the following movies: | title | year | | Gone with the wind | 1939 | | The Wizard of Oz | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" within the movie list And also I should see "The Wizard of Oz (1939)" Friday, February 4, 2011
  169. 169. Persistent Scopes Scenario: Persistent scoping Given the following movies: | title | year | | Gone with the wind | 1939 | | The Wizard of Oz | 1939 | When I go to the home page Then I should see "Gone with the wind (1939)" within the movie list And also I should see "The Wizard of Oz (1939)" Friday, February 4, 2011
  170. 170. Persistent Scopes module PersistentScope def persistent_scope(scope = nil) if scope page.find(scope) @persistent_scope = scope end @persistent_scope end end World(PersistentScope) When /^(.*) within ([^:"]+)$/ do |step, scope| with_scope(persistent_scope(selector_for(scope))) do When step end end When /^also (.*)$/ do |step| fail unless persistent_scope with_scope(persistent_scope) do When step end end Friday, February 4, 2011
  171. 171. Persistent Scopes module PersistentScope def persistent_scope(scope = nil) if scope page.find(scope) @persistent_scope = scope end @persistent_scope end end World(PersistentScope) When /^(.*) within ([^:"]+)$/ do |step, scope| with_scope(persistent_scope(selector_for(scope))) do When step end end When /^also (.*)$/ do |step| fail unless persistent_scope with_scope(persistent_scope) do When step end end Friday, February 4, 2011
  172. 172. Persistent Scopes module PersistentScope def persistent_scope(scope = nil) if scope page.find(scope) @persistent_scope = scope end @persistent_scope end end World(PersistentScope) When /^(.*) within ([^:"]+)$/ do |step, scope| with_scope(persistent_scope(selector_for(scope))) do When step end end When /^also (.*)$/ do |step| fail unless persistent_scope with_scope(persistent_scope) do When step end end Friday, February 4, 2011
  173. 173. Persistent Scopes module PersistentScope def persistent_scope(scope = nil) if scope page.find(scope) @persistent_scope = scope end @persistent_scope end end World(PersistentScope) When /^(.*) within ([^:"]+)$/ do |step, scope| with_scope(persistent_scope(selector_for(scope))) do When step end end When /^also (.*)$/ do |step| fail unless persistent_scope with_scope(persistent_scope) do When step end end Friday, February 4, 2011
  174. 174. Persistent Scopes module PersistentScope def persistent_scope(scope = nil) if scope page.find(scope) @persistent_scope = scope end @persistent_scope end end World(PersistentScope) When /^(.*) within ([^:"]+)$/ do |step, scope| with_scope(persistent_scope(selector_for(scope))) do When step end end When /^also (.*)$/ do |step| fail unless persistent_scope with_scope(persistent_scope) do When step end end Friday, February 4, 2011
  175. 175. Confident! And Happy! Friday, February 4, 2011
  176. 176. Write Better Scenarios Friday, February 4, 2011
  177. 177. Imperative Scenario: Successful animal creation Given I'm on the animal creation page When I fill in Name with 'Alligator' And select Phylum as 'Chordata' And fill in Animal Class with 'Sauropsida' And fill in Order with 'Crocodilia' And fill in Family with 'Alligatoridae' And fill in Genus with 'Alligator' And check Lay Eggs And click the Create button Then I should see the notice 'Thank you for your animal submission!' And the page should include the animal's name, phylum, animal class, Friday, February 4, 2011
  178. 178. Declarative Scenario: Successful animal creation Given I'm on the animal creation page When I add a new animal Then I should see the page for my newly created animal And the notice 'Thank you for your animal submission!' Friday, February 4, 2011
  179. 179. UX Feature: Visitor uses advertiser carousel Background: Given the following advertiser: | name | Lowe's | And that advertiser has the following coupons: | description | | Free free free | And the following advertiser: | name | Home Depot | And that advertiser has the following coupons: | description | | Buy three storage bins and get the next 20 absolutely free for a limited time! | When I go to the homepage Scenario: populating the carousel Then I should see "Lowe's coupons" within the advertiser carousel And I should see "Free free free" within the advertiser carousel And I should see "Home Depot coupons" within the advertiser carousel And I should see "Buy three storage" within the advertiser carousel Scenario: truncating the coupon copy Then I should see "Buy three storage bins and get the next 20 absolute..." within the advertiser carousel Scenario: viewing all coupons for an advertiser When I follow "Lowe's coupons" within the advertiser carousel Then I should see "Coupons for Lowe's" And I should see "Free free free" And I should not see "Buy three storage bins" And I should not see "View all Lowe's coupons" Friday, February 4, 2011
  180. 180. Outside In Friday, February 4, 2011
  181. 181. Confident! And Happy! Friday, February 4, 2011
  182. 182. Cucumber: The Bad Parts Friday, February 4, 2011
  183. 183. Not so Happy Friday, February 4, 2011
  184. 184. Cucumber: The Good Part Friday, February 4, 2011
  185. 185. Happy Friday, February 4, 2011
  186. 186. Thank you! Friday, February 4, 2011

×