cukesalad 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. data/.gitignore +1 -0
  2. data/Examples/Calculator/Gemfile +1 -1
  3. data/Examples/Calculator/Gemfile.lock +26 -25
  4. data/Examples/Calculator/README.md +17 -0
  5. data/Examples/Calculator/features/addition.feature +0 -1
  6. data/Examples/Calculator/features/complex_calculations.feature +0 -1
  7. data/Examples/Calculator/features/lib/alternative/roles/calculating_web_user.rb +4 -21
  8. data/Examples/Calculator/features/lib/default/roles/calculating_individual.rb +4 -0
  9. data/Examples/Calculator/features/lib/default/tasks/arithmetic/calculate.rb +1 -1
  10. data/Examples/Calculator/features/subtraction.feature +0 -1
  11. data/Examples/Calculator/features/typical_workflow.feature +0 -1
  12. data/Gemfile.lock +31 -11
  13. data/Rakefile +21 -1
  14. data/VERSION +1 -1
  15. data/cucumber.yml +2 -0
  16. data/cukesalad.gemspec +7 -1
  17. data/features/README.markdown +299 -0
  18. data/features/{a_new_cukesalad_project.feature → creating_a_new_project.feature} +1 -1
  19. data/features/define_a_role.feature +0 -1
  20. data/features/define_a_task.feature +1 -2
  21. data/features/define_a_task_with_arguments.feature +15 -7
  22. data/features/expressing_expectations.feature +39 -0
  23. data/features/lib/roles/step_free_cuker.rb +15 -0
  24. data/features/lib/tasks/create_a_new_cukesalad_project.rb +2 -10
  25. data/features/lib/tasks/create_a_task.rb +9 -9
  26. data/features/prepare_the_actor_for_the_role.feature +36 -0
  27. data/features/remember_information_between_steps.feature +0 -1
  28. data/lib/cukesalad.rb +1 -1
  29. data/lib/cukesalad/actor.rb +7 -1
  30. data/lib/cukesalad/{cucumber.rb → cucumber_steps.rb} +5 -1
  31. data/lib/cukesalad/director.rb +2 -2
  32. data/lib/cukesalad/specifics.rb +2 -2
  33. data/lib/cukesalad/version.rb +1 -1
  34. data/spec/cukesalad/specifics_spec.rb +30 -23
  35. metadata +26 -8
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  .rvmrc
2
2
  *.swp
3
+ *.swo
3
4
  .DS_Store
4
5
 
5
6
  #rdoc generated
@@ -1,5 +1,5 @@
1
1
  source :rubygems
2
- gem "cukesalad", ">=0.6"
2
+ gem "cukesalad", ">=0.7"
3
3
  gem "rspec", ">=2.5.0", :require => 'spec'
4
4
  gem "capybara", ">=0.4.1.2"
5
5
  gem "sinatra", ">=1.2.0"
@@ -1,56 +1,57 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- aruba (0.3.6)
5
- childprocess (>= 0.1.7)
6
- cucumber (>= 0.10.0)
7
- rspec (>= 2.5.0)
4
+ aruba (0.4.3)
5
+ bcat (>= 0.6.1)
6
+ childprocess (>= 0.1.9)
7
+ cucumber (>= 0.10.7)
8
+ rdiscount (>= 1.6.8)
9
+ rspec (>= 2.6.0)
10
+ bcat (0.6.1)
11
+ rack (~> 1.0)
8
12
  builder (3.0.0)
9
- capybara (0.4.1.2)
10
- celerity (>= 0.7.9)
11
- culerity (>= 0.2.4)
13
+ capybara (1.0.0)
12
14
  mime-types (>= 1.16)
13
15
  nokogiri (>= 1.3.3)
14
16
  rack (>= 1.0.0)
15
17
  rack-test (>= 0.5.4)
16
- selenium-webdriver (>= 0.0.27)
17
- xpath (~> 0.1.3)
18
- celerity (0.8.9)
18
+ selenium-webdriver (~> 0.2.0)
19
+ xpath (~> 0.1.4)
19
20
  childprocess (0.1.9)
20
21
  ffi (~> 1.0.6)
21
- cucumber (0.10.2)
22
+ cucumber (1.0.0)
22
23
  builder (>= 2.1.2)
23
24
  diff-lcs (>= 1.1.2)
24
- gherkin (>= 2.3.5)
25
+ gherkin (~> 2.4.1)
25
26
  json (>= 1.4.6)
26
27
  term-ansicolor (>= 1.0.5)
27
- cukesalad (0.6.1)
28
+ cukesalad (0.7.0)
28
29
  aruba (>= 0.3.5)
29
30
  cucumber (>= 0.10.0)
30
31
  rspec (>= 2.5.0)
31
- culerity (0.2.15)
32
32
  diff-lcs (1.1.2)
33
- ffi (1.0.8)
34
- gherkin (2.3.8)
33
+ ffi (1.0.9)
34
+ gherkin (2.4.1)
35
35
  json (>= 1.4.6)
36
- json (1.5.1)
37
- json_pure (1.5.1)
36
+ json (1.5.3)
37
+ json_pure (1.5.3)
38
38
  mime-types (1.16)
39
- nokogiri (1.4.4)
40
- rack (1.2.2)
39
+ nokogiri (1.5.0)
40
+ rack (1.3.0)
41
41
  rack-test (0.6.0)
42
42
  rack (>= 1.0)
43
+ rdiscount (1.6.8)
43
44
  rspec (2.6.0)
44
45
  rspec-core (~> 2.6.0)
45
46
  rspec-expectations (~> 2.6.0)
46
47
  rspec-mocks (~> 2.6.0)
47
- rspec-core (2.6.0)
48
+ rspec-core (2.6.4)
48
49
  rspec-expectations (2.6.0)
49
50
  diff-lcs (~> 1.1.2)
50
51
  rspec-mocks (2.6.0)
51
52
  rubyzip (0.9.4)
52
- selenium-webdriver (0.2.0)
53
- childprocess (>= 0.1.7)
53
+ selenium-webdriver (0.2.2)
54
+ childprocess (>= 0.1.9)
54
55
  ffi (>= 1.0.7)
55
56
  json_pure
56
57
  rubyzip
@@ -58,7 +59,7 @@ GEM
58
59
  rack (~> 1.1)
59
60
  tilt (< 2.0, >= 1.2.2)
60
61
  term-ansicolor (1.0.5)
61
- tilt (1.3)
62
+ tilt (1.3.2)
62
63
  xpath (0.1.4)
63
64
  nokogiri (~> 1.3)
64
65
 
@@ -67,6 +68,6 @@ PLATFORMS
67
68
 
68
69
  DEPENDENCIES
69
70
  capybara (>= 0.4.1.2)
70
- cukesalad (>= 0.6)
71
+ cukesalad (>= 0.7)
71
72
  rspec (>= 2.5.0)
72
73
  sinatra (>= 1.2.0)
@@ -0,0 +1,17 @@
1
+ ## Calculator Example
2
+
3
+ In the current folder...
4
+
5
+ Setup the environment:
6
+
7
+ bundle install
8
+
9
+ Then you can run the examples:
10
+
11
+ bundle exec cucumber
12
+
13
+ To run these via a web front end, use:
14
+
15
+ bundle exec cucumber -p alternative
16
+
17
+ Enjoy!!
@@ -5,7 +5,6 @@ Feature: Addition
5
5
 
6
6
  Scenario Outline: Find the sum of two numbers
7
7
  Given I am a Calculating Individual
8
- And I was able to switch on the calculator
9
8
  When I attempt to add: the number '<first_number>' and the number '<second_number>'
10
9
  Then I should see the answer '<result>'
11
10
 
@@ -5,7 +5,6 @@ Feature: Complex Calculations
5
5
 
6
6
  Scenario Outline: Get the result of a more complex calculation
7
7
  Given I am a calculating individual
8
- And I was able to switch on the calculator
9
8
  When I attempt to calculate, with the following '<calculation>'
10
9
  Then I should see the answer '<correct_result>'
11
10
 
@@ -10,10 +10,11 @@ Capybara.default_driver = :rack_test
10
10
 
11
11
  module CalculatingIndividual
12
12
 
13
- # Uses a Browser to perform its tasks
14
13
  include Capybara
15
- # This is not a page object. It is a WebUser, specialised to our application
16
- # See the comments in the 'enter' and 'press' method for an explanation of why
14
+
15
+ def role_preparation
16
+ switch_on_the_calculator
17
+ end
17
18
 
18
19
  def switch_on_the_calculator
19
20
  visit '/'
@@ -21,28 +22,10 @@ module CalculatingIndividual
21
22
 
22
23
  def enter value
23
24
  fill_in 'number', :with => value
24
- # Because the calculator is a simple web_app with only
25
- # one page we can put the id for the field here. In a more complex web-app
26
- # we would pass in the id of the thing to enter
27
- # The task would express the action as: enter( :the_number, "10")
28
- # Because we can change the id used in our application,
29
- # we would just change the id in the application to be 'the_number'.
30
- # If we were on a project where we could not change the id
31
- # in the application we would write an 'ApplicationExpert'
32
- # to map :the_number to the id for the field 'number'. Our CalculatingIndividual
33
- # would ask the ApplicationExpert for the id of the symbol :the_number
34
- # See 'press' below for am explanation of how an ApplicationExpert
35
- # would work
36
25
  end
37
26
 
38
27
  def press operator
39
28
  click_button operator.to_s
40
- # Here, our 'operator' happens to also match the id for the button.
41
- # If we were on a project where we could not choose what the id
42
- # was for buttons, we would write an ApplicationExpert to map
43
- # the symbol used in the task to the symbol for that button.
44
- # The ApplicationExpert would only need to know what page it
45
- # was on to find a mappings class (or yml file) for the fields on that page.
46
29
  end
47
30
 
48
31
  def look_at_the_display
@@ -2,6 +2,10 @@ require 'calculator'
2
2
 
3
3
  module CalculatingIndividual
4
4
 
5
+ def role_preparation
6
+ switch_on_the_calculator
7
+ end
8
+
5
9
  def switch_on_the_calculator
6
10
  @calculator = Calculator.new
7
11
  @operate_with = {
@@ -1,4 +1,4 @@
1
1
  in_order_to "Calculate", with_the_following: :calculation do
2
- see_how_to_do Calculations
2
+ see_how_to Calculations
3
3
  follow_the_steps_for the( :calculation ).split(" ")
4
4
  end
@@ -5,7 +5,6 @@ Feature: Subtraction
5
5
 
6
6
  Scenario Outline: Find the result of subtracting two numbers
7
7
  Given I am a Calculating Individual
8
- And I was able to switch on the calculator
9
8
  When I attempt to subtract, the number '<subtractor>' from the number '<subtractee>'
10
9
  Then I should see the answer '<result>'
11
10
 
@@ -5,7 +5,6 @@ Feature: Typical Calculator Workflow
5
5
 
6
6
  Scenario Outline: See what I expect based on specific interactions
7
7
  Given I am a calculating individual
8
- And I was able to switch on the calculator
9
8
  When I attempt to calculate: with the following '<interactions>'
10
9
  Then I should see the answer '<expected>'
11
10
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cukesalad (0.6.1)
4
+ cukesalad (0.7.0)
5
5
  aruba (>= 0.3.5)
6
6
  cucumber (>= 0.10.0)
7
7
  rspec (>= 2.5.0)
@@ -9,39 +9,59 @@ PATH
9
9
  GEM
10
10
  remote: http://rubygems.org/
11
11
  specs:
12
- aruba (0.3.6)
13
- childprocess (>= 0.1.7)
14
- cucumber (>= 0.10.0)
15
- rspec (>= 2.5.0)
12
+ archive-tar-minitar (0.5.2)
13
+ aruba (0.4.3)
14
+ bcat (>= 0.6.1)
15
+ childprocess (>= 0.1.9)
16
+ cucumber (>= 0.10.7)
17
+ rdiscount (>= 1.6.8)
18
+ rspec (>= 2.6.0)
19
+ bcat (0.6.1)
20
+ rack (~> 1.0)
16
21
  builder (3.0.0)
17
22
  childprocess (0.1.9)
18
23
  ffi (~> 1.0.6)
19
- cucumber (0.10.2)
24
+ cucumber (1.0.0)
20
25
  builder (>= 2.1.2)
21
26
  diff-lcs (>= 1.1.2)
22
- gherkin (>= 2.3.5)
27
+ gherkin (~> 2.4.1)
23
28
  json (>= 1.4.6)
24
29
  term-ansicolor (>= 1.0.5)
25
30
  diff-lcs (1.1.2)
26
- ffi (1.0.8)
27
- gherkin (2.3.8)
31
+ ffi (1.0.9)
32
+ ffi (1.0.9-java)
33
+ gherkin (2.4.1)
34
+ json (>= 1.4.6)
35
+ gherkin (2.4.1-java)
28
36
  json (>= 1.4.6)
29
- json (1.5.1)
37
+ json (1.4.6)
38
+ json (1.4.6-java)
39
+ mime-types (1.16)
40
+ rack (1.3.0)
30
41
  rake (0.8.7)
42
+ rdiscount (1.6.8)
43
+ relish (0.3.0)
44
+ archive-tar-minitar (~> 0.5.2)
45
+ json (~> 1.4.6)
46
+ rest-client (~> 1.6.1)
47
+ rest-client (1.6.3)
48
+ mime-types (>= 1.16)
31
49
  rspec (2.6.0)
32
50
  rspec-core (~> 2.6.0)
33
51
  rspec-expectations (~> 2.6.0)
34
52
  rspec-mocks (~> 2.6.0)
35
- rspec-core (2.6.0)
53
+ rspec-core (2.6.4)
36
54
  rspec-expectations (2.6.0)
37
55
  diff-lcs (~> 1.1.2)
38
56
  rspec-mocks (2.6.0)
39
57
  term-ansicolor (1.0.5)
40
58
 
41
59
  PLATFORMS
60
+ java
42
61
  ruby
43
62
 
44
63
  DEPENDENCIES
45
64
  bundler (~> 1.0.0)
46
65
  cukesalad!
47
66
  rake (~> 0.8.7)
67
+ relish (= 0.3.0)
data/Rakefile CHANGED
@@ -19,8 +19,11 @@ end
19
19
 
20
20
  require 'cucumber/rake/task'
21
21
  Cucumber::Rake::Task.new(:cucumber)
22
+ Cucumber::Rake::Task.new(:wip) do |wip|
23
+ wip.cucumber_opts = "-p wip"
24
+ end
22
25
 
23
- task :default => :spec
26
+ task :default => [:spec, :cucumber]
24
27
 
25
28
  require 'rake/rdoctask'
26
29
  Rake::RDocTask.new do |rdoc|
@@ -31,3 +34,20 @@ Rake::RDocTask.new do |rdoc|
31
34
  rdoc.rdoc_files.include('README*')
32
35
  rdoc.rdoc_files.include('lib/**/*.rb')
33
36
  end
37
+
38
+ require 'relish/command'
39
+ namespace :relish do
40
+ task :create do
41
+ `relish projects:add RiverGlide/CukeSalad`
42
+ end
43
+
44
+ task :push do
45
+ `relish push CukeSalad:#{CukeSalad::VERSION}`
46
+ end
47
+
48
+ namespace :version do
49
+ task :add do
50
+ `relish versions:add CukeSalad:#{CukeSalad::VERSION}`
51
+ end
52
+ end
53
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.0
1
+ 0.7.0
data/cucumber.yml CHANGED
@@ -1,4 +1,6 @@
1
1
  default: --tags ~@wip --tags ~@todo
2
+ wip: --tags @wip
3
+ todo: --tags @todo
2
4
  all: features
3
5
  autotest: --color
4
6
  autotest-all: --color
data/cukesalad.gemspec CHANGED
@@ -10,7 +10,12 @@ Gem::Specification.new do |s|
10
10
  s.email = ["talktous@riverglide.com"]
11
11
  s.homepage = "https://github.com/RiverGlide/CukeSalad"
12
12
  s.summary = %q{Friction Free BDD/ATDD with cucumber}
13
- s.description = %q{CukeSalad allows you to focus on the tasks at hand - expressing examples, the roles involved in those examples and the tasks that those roles need to perform with the product under development.}
13
+ s.description = %q{CukeSalad allows you to focus on the tasks at hand - expressing examples, the roles involved in those examples and the tasks that those roles need to perform with the product under development.
14
+ New in this version:
15
+ - Single or double quotes can be used for parameters
16
+ - Single or double quotes can be used for expectations
17
+ - You can initialise the actor by having a role_preparation method in your role
18
+ }
14
19
 
15
20
  s.licenses = ["MIT"]
16
21
 
@@ -26,4 +31,5 @@ Gem::Specification.new do |s|
26
31
 
27
32
  s.add_development_dependency "bundler", "~> 1.0.0"
28
33
  s.add_development_dependency "rake", "~> 0.8.7"
34
+ s.add_development_dependency "relish", "= 0.3.0"
29
35
  end
@@ -0,0 +1,299 @@
1
+ # Cuke Salad
2
+
3
+ _Cucumber, washed and ready to eat for Friction-free ATDD/BDD_
4
+
5
+ **This is a work in progress - feedback welcome**
6
+ e-mail feedback to <talktous@riverglide.com>
7
+
8
+ You can see our current list of intended features under issues:
9
+ [https://github.com/RiverGlide/CukeSalad/issues?labels=Feature](https://github.com/RiverGlide/CukeSalad/issues?labels=Feature)
10
+
11
+
12
+ ## This project has step-free access!
13
+
14
+ CukeSalad allows you to focus on the task at hand - expressing examples, the roles involved in those examples and what those roles can do with the product under development.
15
+
16
+ With CukeSalad you don't need to write step-definitions.
17
+
18
+ Of course, you still have to write some code - but only the code that expresses:
19
+
20
+ * the roles and the actions they can perform
21
+ * the tasks and the actions involved in completing that task
22
+
23
+ ## Goals->Tasks->Actions
24
+ The terms *"actions"* and *"tasks"* above come from Task Analysis, as used in User Centred Design (UCD) of Human Computer Interfaces (HCI) a.k.a. User Experience (UX):
25
+
26
+ * *Goal:* What we’re trying to achieve which has one or more…
27
+ * *Tasks:* The high-level work-item that we complete to fulfil the goal, each having one or more…
28
+ * *Actions:* The specific steps or interactions we execute to complete the task.
29
+
30
+ More information about Goals, Tasks and Actions can be found in this [blog post](http://antonymarcano.com/blog/2011/03/goals-tasks-action/)
31
+
32
+ Let's see how this works with a simple example...
33
+
34
+ ## Install
35
+
36
+ gem install cukesalad
37
+
38
+ ## Let's Get started
39
+
40
+ Create a new project called Calculator:
41
+
42
+ cukesalad Calculator
43
+
44
+ Or, if you have an existing cucumber project that you want to configure to use cukesalad, you can type:
45
+
46
+ cukesalad
47
+
48
+ ## Write Features
49
+
50
+ In `features/`, let's create our first feature file - `a_place_to_start.feature`:
51
+
52
+ Feature: A Place To Start
53
+ As Callie, a calculating individual
54
+ I want to know when my calculator is on
55
+ So that I know when I can start calculating
56
+
57
+ Scenario: Let's Begin
58
+ Given I am a Calculating Individual
59
+ When I attempt to switch on the calculator
60
+ Then I should see the answer '0'
61
+
62
+ Let's take a moment to understand this scenario:
63
+
64
+ Scenario: Let's Begin
65
+ Given I am a <some role>
66
+ When I attempt to <do some task>
67
+ Then I should <ask some question> '<expected answer>'
68
+
69
+ To get this working, we don't need to write any steps. Instead, we describe tasks...
70
+
71
+ ## Create Tasks
72
+
73
+ Explaining how to do a _task_ is easy:
74
+ Create a new file, `features/lib/tasks/switch_on_the_calculator.rb`
75
+
76
+ Remember the step `When I attempt to switch on the calculator`
77
+
78
+ in_order_to "switch on the calculator" do
79
+ switch_on_the_calculator
80
+ end
81
+
82
+ Remember the step `Then I should see the answer '0'`
83
+ Now we need `task/see_the_answer.rb`
84
+
85
+ in_order_to "see the answer" do
86
+ look_at_the_display
87
+ end
88
+
89
+ Now we've explained the _tasks_, we need to define the _role_ that performs them. In
90
+ this example, we need to explain how the `CalculatingIndividual` _role_ works...
91
+
92
+ ## Create Roles
93
+
94
+ Remember the step `Given I am a Calculating Individual`?
95
+
96
+ We explain a _role_ by creating a new file
97
+ called `features/lib/roles/calculating_individual.rb`
98
+
99
+ module CalculatingIndividual
100
+
101
+ def switch_on_the_calculator
102
+ @calculator = Calculator.new
103
+ end
104
+
105
+ def look_at_the_display
106
+ @calculator.display
107
+ end
108
+ end
109
+
110
+ You'll need a class called Calculator on the load path of course, but that's it.
111
+
112
+ From your project folder, run (_note: '%' is our command prompt_)
113
+
114
+ % cucumber
115
+
116
+ We now have our first passing Feature, without creating a single step definition!
117
+
118
+ ## Wash, rinse, repeat
119
+
120
+ Let's try another scenario...
121
+
122
+ Scenario Outline: Find the sum of two numbers
123
+ Given I am a Calculating Individual
124
+ And I was able to switch on the calculator
125
+ When I attempt to add: the number '10' and the number '10'
126
+ Then I should see the answer '20'
127
+
128
+ Notice that we've reused 'switch on the calculator'.
129
+
130
+ The new _When_ step has a slightly different layout.
131
+ Let's examine that for a second... See below. Notice the ':' (colon) after `<do something>` followed by the name-value pairs:
132
+
133
+ When I attempt to <do something>: <name> '<value>' <name> '<value>'
134
+
135
+ You can also use a ',' (comma) in situations where a colon wouldn't quite work:
136
+
137
+ When I attempt to <do something>, <name> '<value>' <name> '<value>'
138
+
139
+ `<do something>` can be as many words as you like. You can have as many name-value pairs as you like.
140
+
141
+ For this to work we need a task called `tasks/add.rb` that explains the individual actions required to complete the task:
142
+
143
+ in_order_to "add" do
144
+ enter @value_of(:the_number)
145
+ press :plus
146
+ enter @value_of(:and_the_number)
147
+ press :equals
148
+ end
149
+
150
+ Notice how the `value_of` lines use symbols that correspond to the wording `'the number '10' to the number '10'` in the "When" step.
151
+
152
+ There is some 'syntactic sugar' that we can use to dress this up a little and make it read nicer... a simple attribute mapping (using Ruby 1.9.x syntax):
153
+
154
+ in_order_to "add", the_number: :first_number, to_the_number: :second_number do
155
+ enter the :first_number
156
+ press :plus
157
+ enter the :second_number
158
+ press :equals
159
+ end
160
+
161
+ All we've done is mapped `:the_number` to `:first_number` and `:to_the_number` to `:second_number`. There is a special method called "the" that allows you to reference the mapped values rather than the symbols derived from the scenario.
162
+
163
+ Now all we need to do is create the corresponding methods in `calculating_individual.rb`.
164
+
165
+ module CalculatingIndividual
166
+
167
+ def switch_on_the_calculator
168
+ @calculator = Calculator.new
169
+ @operate_with = {
170
+ plus: :+,
171
+ minus: :-
172
+ }
173
+ end
174
+
175
+ def enter value
176
+ @calculator.enter value.to_i
177
+ end
178
+
179
+ def press next_operator
180
+ if next_operator == :equals
181
+ equals
182
+ else
183
+ @calculator.get_ready_to @operate_with[next_operator]
184
+ end
185
+ end
186
+
187
+ def equals
188
+ @calculator.equals
189
+ end
190
+
191
+ def look_at_the_display
192
+ @calculator.display
193
+ end
194
+ end
195
+
196
+ Of course you'll have to [implement the calculator too](https://github.com/RiverGlide/CukeSalad/blob/master/Examples/Calculator/lib/calculator.rb)
197
+
198
+ Now, you can run cucumber again:
199
+
200
+ % cucumber
201
+
202
+ There's no need to write `step_definitions`...
203
+ Simply express the _roles_ and the _tasks_ in clear,
204
+ concise, easy to read classes.
205
+
206
+ If we want to know what things we can say, instead of trawling through a step-def ruby file, we can look in our tasks folder:
207
+
208
+ features/lib/default/tasks/
209
+ ├── add.rb
210
+ ├── calculate.rb
211
+ ├── calculations.rb
212
+ ├── see_the_answer.rb
213
+ ├── subtract.rb
214
+ └── switch_on_the_calculator.rb
215
+
216
+ You can structure your tasks as you see fit. For example, as the project grows, it might end up looking like this:
217
+
218
+ features/lib/default/tasks/
219
+ ├── all_purpose
220
+ | └── calculate.rb
221
+ ├── arithmetic
222
+ | ├── add.rb
223
+ | ├── divide.rb
224
+ | ├── multiply.rb
225
+ | └── subtract.rb
226
+ ├── extras
227
+ | ├── recall_from_memory.rb
228
+ | ├── store_in_memory.rb
229
+ | └── switch_on_the_calculator.rb
230
+ ├── questions
231
+ | ├── see_the_answer.rb
232
+ | ├── see_the_following_indicators.rb
233
+ | └── switch_on_the_calculator.rb
234
+ ├── trigonometry
235
+ | ├── sine.rb
236
+ | ├── cosine.rb
237
+ | └── tangent.rb
238
+ └── reference_material
239
+ └── calculations.rb
240
+
241
+ ## Alternative Roles
242
+
243
+ As our features _describe the value of a calculator application and not its
244
+ implementation_, we have the opportunity to reuse them.
245
+
246
+ In the Calculator example, we create a new _role_ in
247
+ `./features/lib/alternative/roles/calculating_web_user.rb`, which we can swap
248
+ into our tests using a Cucumber profile defined in `features/cucumber.yml`:
249
+
250
+ default --exclude features/lib/alternative/
251
+ alternative -r features/lib/alternative/ -r features/support/env.rb -r features/lib/default/tasks/
252
+
253
+ We can run our alternative configuration like so:
254
+
255
+ `%cucumber --profile alternative`
256
+
257
+ The Calculating Web User masquerades as the Calculating Individual from our
258
+ previous example, and provides the same API, allowing us to reuse all of our
259
+ existing features and _tasks_.
260
+
261
+ The alternative, `./lib/web_calculator.rb`, implementation is a simple [Sinatra](http://www.sinatrarb.com) application,
262
+ which we drive with the [Capybara](http://github.com/jnicklas/capybara) web testing framework.
263
+
264
+ By writing a single new _role_ class we're able to reuse all of our existing features,
265
+ _tasks_ and even the `Calculator` itself, which the web calculator delegates to in order to do its calculations.
266
+
267
+ After adding some more scenarios and tasks and an alternative "Calculating Individual" (see below for why), our Calculator directory structure currently looks like this...
268
+
269
+ ├── cucumber.yml
270
+ ├── features
271
+ │   ├── A_PlaceToStart.feature
272
+ │   ├── Addition.feature
273
+ │   ├── Complex_calculations.feature
274
+ │   ├── Subtraction.feature
275
+ │   ├── Typical_workflow.feature
276
+ │   ├── lib
277
+ │   │   ├── alternative
278
+ │   │   │   ├── roles
279
+ │   │   │   │   └── calculating_web_user.rb
280
+ │   │   │   └── tasks
281
+ │   │   └── default
282
+ │   │   ├── roles
283
+ │   │   │   └── calculating_individual.rb
284
+ │   │   └── tasks
285
+ │   │   ├── add.rb
286
+ │   │   ├── perform.rb
287
+ │   │   ├── see_the_answer.rb
288
+ │   │   ├── subtract.rb
289
+ │   │   └── switch_on_the_calculator.rb
290
+ │   └── support
291
+ │   └── env.rb
292
+ ├── lib
293
+ │   ├── calculator.rb
294
+ │   └── web_calculator.rb
295
+ └── spec
296
+ ├── calculator_spec.rb
297
+ └── web_calculator_spec.rb
298
+
299
+ Take a look around the examples and the code to see how it all works. We hope you enjoy reading as much as we enjoyed writing it.
@@ -1,4 +1,4 @@
1
- Feature: Cuke Salad
1
+ Feature: Creating a New Project
2
2
  As a Step Free Cuker
3
3
  You want to set up your project to use use Cuke Salad
4
4
  So that you can start writing scenarios without step definitions
@@ -5,7 +5,6 @@ Feature: Define the Role
5
5
 
6
6
  Background:
7
7
  Given you are a Step Free Cuker
8
- And you were able to create a new Cuke Salad project
9
8
 
10
9
  Scenario: We'll tell you what you need to do to establish the role
11
10
  Given you did not create a role: called 'NewCustomer'
@@ -6,11 +6,10 @@ Feature: Define the Task
6
6
 
7
7
  Background:
8
8
  Given you are a Step Free Cuker
9
- And you were able to create a new Cuke Salad project
10
9
  And you were able to create a role: called 'NewCustomer'
11
10
 
12
11
  Scenario Outline: Once you've created the task, you see the step pass
13
- Given you did create a task: called 'do something'
12
+ Given you were able to create a task: called 'do something'
14
13
  When you attempt to run a scenario: containing
15
14
  """
16
15
  Given I am a New Customer
@@ -6,25 +6,33 @@ Feature: Define a Task with arguments
6
6
 
7
7
  Background:
8
8
  Given you are a Step Free Cuker
9
- And you were able to create a new Cuke Salad project
10
9
  And you were able to create a role: called 'NewCustomer'
11
- And you were able to create a task: called 'do something'
12
10
 
13
11
  Scenario Outline: A task can accept arguments
12
+ Given you were able to create a task, called 'do some task' containing
13
+ """
14
+ in_order_to 'do some task', with: :detail do
15
+ raise "Expected 'information' in the detail
16
+ got '#{the :detail}'" unless the( :detail ) == 'information'
17
+ end
18
+ """
14
19
  When you attempt to run a scenario: called 'something' containing
15
20
  """
16
21
  Given I am a New Customer
17
- <the_step>
22
+ When I attempt to <task_with_argument>
18
23
  """
19
24
  Then you should see it has 'passed'
20
25
 
21
26
  Examples:
22
- | the_step |
23
- | When I attempt to do something: with 'information' |
24
- | When I attempt to do something, with 'information' |
27
+ | task_with_argument |
28
+ | do some task: with 'information' |
29
+ | do some task, with 'information' |
30
+ | do some task: with "information" |
31
+ | do some task, with "information" |
25
32
 
26
33
  Scenario: A task accepts tables
27
- When you attempt to run a scenario, called 'something' containing
34
+ Given you were able to create a task: called 'do something'
35
+ When you attempt to run a scenario, called 'something' containing
28
36
  """
29
37
  Given I am a New Customer
30
38
  When I attempt to do something, with
@@ -0,0 +1,39 @@
1
+ Feature: Expressing expectations
2
+ As a Step Free Cuker
3
+ You want to express expectations
4
+ So that you know whether your scenario has passed
5
+
6
+ Background:
7
+ Given you are a Step Free Cuker
8
+ And you were able to create a file, at 'features/roles/role_with_answers.rb' containing
9
+ """
10
+ module RoleWithAnswers
11
+ def see_the_answer
12
+ get_the_answer_from_the_application
13
+ end
14
+
15
+ def get_the_answer_from_the_application
16
+ # Pretending to get the answer from the application
17
+ return 'correct answer'
18
+ end
19
+ end
20
+ """
21
+ And you were able to create a task, called 'see the answer is' containing
22
+ """
23
+ in_order_to 'see the answer is' do
24
+ see_the_answer
25
+ end
26
+ """
27
+
28
+ Scenario Outline: Simple Expectation
29
+ When you attempt to run a scenario, containing
30
+ """
31
+ Given I am a Role With Answers
32
+ Then I should see the answer is <answer>
33
+ """
34
+ Then you should see it has 'passed'
35
+
36
+ Examples:
37
+ | answer |
38
+ | 'correct answer' |
39
+ | "correct answer" |
@@ -2,4 +2,19 @@ require 'aruba/api'
2
2
  #TODO: Consider wrapping Aruba
3
3
  module StepFreeCuker
4
4
  include Aruba::Api
5
+
6
+ def role_preparation
7
+ create_a_new_cuke_salad_project
8
+ end
9
+
10
+ def create_a_new_cuke_salad_project
11
+ create_dir 'features'
12
+ create_dir 'features/support'
13
+ write_file 'features/support/env.rb', "$:.unshift(File.dirname(__FILE__) + '/../../../../lib') #where to find CukeSalad
14
+
15
+ require 'cukesalad'"
16
+ create_dir 'features/lib'
17
+ create_dir 'features/lib/roles'
18
+ create_dir 'features/lib/tasks'
19
+ end
5
20
  end
@@ -1,11 +1,3 @@
1
- #TOOO: consider doing this as a feature since it's part of how you set up the project.
2
- in_order_to "CreateANewCukeSaladProject" do
3
- create_dir 'features'
4
- create_dir 'features/support'
5
- write_file 'features/support/env.rb', "$:.unshift(File.dirname(__FILE__) + '/../../../../lib') #where to find CukeSalad
6
-
7
- require 'cukesalad'"
8
- create_dir 'features/lib'
9
- create_dir 'features/lib/roles'
10
- create_dir 'features/lib/tasks'
1
+ in_order_to 'create a new cukesalad project' do
2
+ create_a_new_cuke_salad_project
11
3
  end
@@ -1,12 +1,12 @@
1
1
  in_order_to 'create a task', called: :name_of_task, containing: :code do
2
- file_name = the( :name_of_task ).gsub(" ", "_")
3
-
4
- default_content =
5
- "in_order_to '#{the :name_of_task}' do
6
- #nothing
7
- end"
8
- content = the :code
9
- content = default_content if content.nil?
2
+ file_name = the( :name_of_task ).gsub(" ", "_")
3
+
4
+ default_content =
5
+ "in_order_to '#{the :name_of_task}' do
6
+ #nothing
7
+ end"
8
+ content = the :code
9
+ content = default_content if content.nil?
10
10
 
11
- write_file "features/lib/tasks/#{file_name}.rb", content
11
+ write_file "features/lib/tasks/#{file_name}.rb", content
12
12
  end
@@ -0,0 +1,36 @@
1
+ Feature: Prepare the actor for the role
2
+ As a Step Free Cuker
3
+ You want the role to initialize some things
4
+ So that you can define the things that the role always does at the start
5
+
6
+ Scenario: Initialization
7
+ Given you are a Step Free Cuker
8
+ And you were able to create a file, at 'features/roles/role_with_prep.rb' containing
9
+ """
10
+ module RoleWithPrep
11
+ def role_preparation
12
+ do_something_important
13
+ end
14
+
15
+ def do_something_important
16
+ @something = 'something important was done'
17
+ end
18
+
19
+ def see_what_was_done
20
+ @something
21
+ end
22
+ end
23
+ """
24
+ And you were able to create a task, called 'see that' containing
25
+ """
26
+ in_order_to 'see that' do
27
+ see_what_was_done
28
+ end
29
+ """
30
+ When you attempt to run a scenario, containing
31
+ """
32
+ Given I am a Role With Prep
33
+ Then I should see that 'something important was done'
34
+ """
35
+ Then you should see it has 'passed'
36
+
@@ -5,7 +5,6 @@ Feature: Remember information between steps
5
5
 
6
6
  Background:
7
7
  Given you are a Step Free Cuker
8
- And you were able to create a new Cuke Salad project
9
8
  And you were able to create a role, called 'NewCustomer'
10
9
 
11
10
  Scenario: You can reuse information
data/lib/cukesalad.rb CHANGED
@@ -1,2 +1,2 @@
1
1
  require 'cukesalad/salad'
2
- require 'cukesalad/cucumber'
2
+ require 'cukesalad/cucumber_steps'
@@ -5,8 +5,8 @@ module CukeSalad
5
5
 
6
6
  def initialize this_type_of_role, directed_by=Director.new
7
7
  @director = directed_by
8
- get_into_character_for this_type_of_role
9
8
  @note_pad = {}
9
+ get_into_character_for this_type_of_role
10
10
  end
11
11
 
12
12
  def perform described_task, details = {}
@@ -19,6 +19,7 @@ module CukeSalad
19
19
  def get_into_character_for described_role
20
20
  the_role = @director.explain_the_role described_role
21
21
  see_how_to_do the_role
22
+ role_preparation
22
23
  end
23
24
 
24
25
  def get_ready_to_perform something
@@ -29,6 +30,11 @@ module CukeSalad
29
30
  def see_how_to_do something
30
31
  extend something
31
32
  end
33
+ alias :see_how_to :see_how_to_do
34
+
35
+ def role_preparation
36
+ # Does nothing unless you override it from your role
37
+ end
32
38
 
33
39
  def value_of(symbol)
34
40
  @info[symbol]
@@ -26,7 +26,11 @@ Then /^(?:I|you) should ([^':]*)(?: '([^']*)')$/ do | this_question, expect_valu
26
26
  @actor.answer( this_question ).to_s.should == expect_value
27
27
  end
28
28
 
29
- Then /^(?:I|you) should ([^':]+)$/ do | this_question |
29
+ Then /^(?:I|you) should ([^':]*)(?: "([^"]*)")$/ do | this_question, expect_value |
30
+ @actor.answer( this_question ).to_s.should == expect_value
31
+ end
32
+
33
+ Then /^(?:I|you) should ([^'^":]+)$/ do | this_question |
30
34
  @actor.answer( this_question )
31
35
  end
32
36
 
@@ -10,7 +10,7 @@ module CukeSalad
10
10
  begin
11
11
  find_directives_for name
12
12
  rescue NameError
13
- raise "I can't find a role called '#{ name }'. Have you created it?\ne.g.\n module #{ name }\n end"
13
+ raise "I can't find a role called '#{ name }'. Have you created it?\ne.g.\n module #{ name }\n end\n\n"
14
14
  end
15
15
  end
16
16
 
@@ -19,7 +19,7 @@ module CukeSalad
19
19
  begin
20
20
  find_directives_for name
21
21
  rescue NameError
22
- raise "I can't find a task called '#{ something }'. Have you created it?\ne.g.\n in_order_to '#{ something }' do\n # the actions\n end"
22
+ raise "I can't find a task called '#{ something }'. Have you created it?\ne.g.\n in_order_to '#{ something }' do\n # the actions\n end\n\n"
23
23
  end
24
24
  end
25
25
 
@@ -21,7 +21,7 @@ module CukeSalad
21
21
  end
22
22
 
23
23
  def names_and_values_in details
24
- specifics_pattern = /('[^']+')/
24
+ specifics_pattern = /('[^']+'|"[^"]+")/
25
25
  details.split(specifics_pattern)
26
26
  end
27
27
 
@@ -30,7 +30,7 @@ module CukeSalad
30
30
  end
31
31
 
32
32
  def the_value_from_the item
33
- item.gsub(/(^'|'$)/, '') unless item.nil?
33
+ item.gsub(/((?:^'|'$)|(?:^"|"$))/, '') unless item.nil?
34
34
  end
35
35
  end
36
36
  end
@@ -1,3 +1,3 @@
1
1
  module CukeSalad
2
- VERSION = "0.6.1"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -7,31 +7,38 @@ end
7
7
  module CukeSalad
8
8
  describe Specifics do
9
9
 
10
- it "has an item of specific information" do
11
- something = NeedingSpecifics.new
12
- something.understand_the "specific 'information'"
13
- something.value_of(:specific).should == "information"
14
- end
10
+ context 'name value pairs' do
15
11
 
16
- it "has items of specific information" do
17
- something = NeedingSpecifics.new
18
- something.understand_the "first 'item' second 'another'"
19
- something.value_of(:first).should == "item"
20
- something.value_of(:second).should == "another"
21
- end
12
+ [ "specific 'information'",
13
+ 'specific "information"'
14
+ ].each do | specifics |
15
+ it "can be found in: #{specifics}" do
16
+ something = NeedingSpecifics.new
17
+ something.understand_the specifics
18
+ something.value_of(:specific).should == "information"
19
+ end
20
+ end
21
+
22
+ it "can have more than one name-value pair" do
23
+ something = NeedingSpecifics.new
24
+ something.understand_the "first 'item' second 'another'"
25
+ something.value_of(:first).should == "item"
26
+ something.value_of(:second).should == "another"
27
+ end
22
28
 
23
- it "copes with names having more than one word" do
24
- something = NeedingSpecifics.new
25
- something.understand_the "first thing 'item' second thing 'another'"
26
- something.value_of(:first_thing).should == "item"
27
- something.value_of(:second_thing).should == "another"
28
- end
29
-
30
- it "should cope with values having more than one word" do
31
- something = NeedingSpecifics.new
32
- something.understand_the "first thing 'item' second thing 'another thing'"
33
- something.value_of(:first_thing).should == "item"
34
- something.value_of(:second_thing).should == "another thing"
29
+ it "can have more than one word as the name" do
30
+ something = NeedingSpecifics.new
31
+ something.understand_the "first thing 'item' second thing 'another'"
32
+ something.value_of(:first_thing).should == "item"
33
+ something.value_of(:second_thing).should == "another"
34
+ end
35
+
36
+ it "can have more than one word as the name or value" do
37
+ something = NeedingSpecifics.new
38
+ something.understand_the "first thing 'item' second thing 'another thing'"
39
+ something.value_of(:first_thing).should == "item"
40
+ something.value_of(:second_thing).should == "another thing"
41
+ end
35
42
  end
36
43
 
37
44
  context 'the last item' do
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: cukesalad
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.6.1
5
+ version: 0.7.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - RiverGlide
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-05-19 00:00:00 +01:00
13
+ date: 2011-07-03 00:00:00 +01:00
14
14
  default_executable: cukesalad
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -68,7 +68,18 @@ dependencies:
68
68
  type: :development
69
69
  prerelease: false
70
70
  version_requirements: *id005
71
- description: CukeSalad allows you to focus on the tasks at hand - expressing examples, the roles involved in those examples and the tasks that those roles need to perform with the product under development.
71
+ - !ruby/object:Gem::Dependency
72
+ name: relish
73
+ requirement: &id006 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - "="
77
+ - !ruby/object:Gem::Version
78
+ version: 0.3.0
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: *id006
82
+ description: "CukeSalad allows you to focus on the tasks at hand - expressing examples, the roles involved in those examples and the tasks that those roles need to perform with the product under development.\n New in this version:\n - Single or double quotes can be used for parameters\n - Single or double quotes can be used for expectations\n - You can initialise the actor by having a role_preparation method in your role\n "
72
83
  email:
73
84
  - talktous@riverglide.com
74
85
  executables:
@@ -82,6 +93,7 @@ files:
82
93
  - .rspec
83
94
  - Examples/Calculator/Gemfile
84
95
  - Examples/Calculator/Gemfile.lock
96
+ - Examples/Calculator/README.md
85
97
  - Examples/Calculator/cucumber.yml
86
98
  - Examples/Calculator/features/LOOK_MA_NO_STEP_DEFS.txt
87
99
  - Examples/Calculator/features/a_place_to_start.feature
@@ -112,10 +124,12 @@ files:
112
124
  - bin/cukesalad
113
125
  - cucumber.yml
114
126
  - cukesalad.gemspec
115
- - features/a_new_cukesalad_project.feature
127
+ - features/README.markdown
128
+ - features/creating_a_new_project.feature
116
129
  - features/define_a_role.feature
117
130
  - features/define_a_task.feature
118
131
  - features/define_a_task_with_arguments.feature
132
+ - features/expressing_expectations.feature
119
133
  - features/lib/roles/step_free_cuker.rb
120
134
  - features/lib/tasks/create_a_file.rb
121
135
  - features/lib/tasks/create_a_new_cukesalad_project.rb
@@ -129,13 +143,14 @@ files:
129
143
  - features/lib/tasks/run_a_scenario.rb
130
144
  - features/lib/tasks/see_a_reply.rb
131
145
  - features/lib/tasks/see_the_step_has.rb
146
+ - features/prepare_the_actor_for_the_role.feature
132
147
  - features/remember_information_between_steps.feature
133
148
  - features/support/env.rb
134
149
  - lib/cukesalad.rb
135
150
  - lib/cukesalad/actor.rb
136
151
  - lib/cukesalad/cli.rb
137
152
  - lib/cukesalad/codify/const_name.rb
138
- - lib/cukesalad/cucumber.rb
153
+ - lib/cukesalad/cucumber_steps.rb
139
154
  - lib/cukesalad/director.rb
140
155
  - lib/cukesalad/salad.rb
141
156
  - lib/cukesalad/specifics.rb
@@ -162,7 +177,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
162
177
  requirements:
163
178
  - - ">="
164
179
  - !ruby/object:Gem::Version
165
- hash: -3357820802428433430
180
+ hash: 3570330056691710889
166
181
  segments:
167
182
  - 0
168
183
  version: "0"
@@ -171,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
171
186
  requirements:
172
187
  - - ">="
173
188
  - !ruby/object:Gem::Version
174
- hash: -3357820802428433430
189
+ hash: 3570330056691710889
175
190
  segments:
176
191
  - 0
177
192
  version: "0"
@@ -183,10 +198,12 @@ signing_key:
183
198
  specification_version: 3
184
199
  summary: Friction Free BDD/ATDD with cucumber
185
200
  test_files:
186
- - features/a_new_cukesalad_project.feature
201
+ - features/README.markdown
202
+ - features/creating_a_new_project.feature
187
203
  - features/define_a_role.feature
188
204
  - features/define_a_task.feature
189
205
  - features/define_a_task_with_arguments.feature
206
+ - features/expressing_expectations.feature
190
207
  - features/lib/roles/step_free_cuker.rb
191
208
  - features/lib/tasks/create_a_file.rb
192
209
  - features/lib/tasks/create_a_new_cukesalad_project.rb
@@ -200,6 +217,7 @@ test_files:
200
217
  - features/lib/tasks/run_a_scenario.rb
201
218
  - features/lib/tasks/see_a_reply.rb
202
219
  - features/lib/tasks/see_the_step_has.rb
220
+ - features/prepare_the_actor_for_the_role.feature
203
221
  - features/remember_information_between_steps.feature
204
222
  - features/support/env.rb
205
223
  - spec/cukesalad/actor_spec.rb