datashift_journey 0.1.2

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 (167) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +391 -0
  4. data/Rakefile +26 -0
  5. data/app/assets/stylesheets/datashift_journey/partials/_state_jumper_toolbar.scss.erb +27 -0
  6. data/app/controllers/concerns/datashift_journey/error_renderer.rb +9 -0
  7. data/app/controllers/concerns/datashift_journey/review_renderer.rb +21 -0
  8. data/app/controllers/concerns/datashift_journey/token_based_access.rb +23 -0
  9. data/app/controllers/concerns/datashift_journey/validate_state.rb +59 -0
  10. data/app/controllers/datashift_journey/abandon_enrollments_controller.rb +14 -0
  11. data/app/controllers/datashift_journey/abandonments_controller.rb +17 -0
  12. data/app/controllers/datashift_journey/api/v1/states_controller.rb +49 -0
  13. data/app/controllers/datashift_journey/application_controller.rb +53 -0
  14. data/app/controllers/datashift_journey/errors_controller.rb +24 -0
  15. data/app/controllers/datashift_journey/journey_ends_controller.rb +19 -0
  16. data/app/controllers/datashift_journey/journey_plans_controller.rb +166 -0
  17. data/app/controllers/datashift_journey/page_states_controller.rb +49 -0
  18. data/app/controllers/datashift_journey/reviews_controller.rb +32 -0
  19. data/app/controllers/datashift_journey/state_jumper_controller.rb +51 -0
  20. data/app/factories/datashift_journey/form_object_factory.rb +68 -0
  21. data/app/forms/datashift_journey/collector/base_collector_form.rb +60 -0
  22. data/app/forms/datashift_journey/concerns/form_mixin.rb +64 -0
  23. data/app/forms/datashift_journey/null_form.rb +20 -0
  24. data/app/helpers/datashift_journey/application_helper.rb +50 -0
  25. data/app/helpers/datashift_journey/back_link_helper.rb +9 -0
  26. data/app/models/datashift_journey/collector/data_node.rb +18 -0
  27. data/app/models/datashift_journey/collector/form_definition.rb +35 -0
  28. data/app/models/datashift_journey/collector/form_field.rb +61 -0
  29. data/app/models/datashift_journey/journey_review.rb +65 -0
  30. data/app/models/datashift_journey/review_data_section.rb +32 -0
  31. data/app/serializers/datashift_journey/collector/page_state_serializer.rb +9 -0
  32. data/app/serializers/state_machines/state/state_serializer.rb +5 -0
  33. data/app/views/datashift_journey/collector/_generic_form.html.erb +14 -0
  34. data/app/views/datashift_journey/errors/401.html.erb +13 -0
  35. data/app/views/datashift_journey/errors/403.html.erb +13 -0
  36. data/app/views/datashift_journey/errors/404.html.erb +13 -0
  37. data/app/views/datashift_journey/errors/422.html.erb +11 -0
  38. data/app/views/datashift_journey/errors/500.html.erb +13 -0
  39. data/app/views/datashift_journey/errors/503.html.erb +11 -0
  40. data/app/views/datashift_journey/errors/invalid_authenticity_token.html.erb +14 -0
  41. data/app/views/datashift_journey/journey_ends/new.html.erb +5 -0
  42. data/app/views/datashift_journey/journey_ends/show.html.erb +5 -0
  43. data/app/views/datashift_journey/journey_plans/_form.html.erb +14 -0
  44. data/app/views/datashift_journey/journey_plans/_render_fields.html.erb +24 -0
  45. data/app/views/datashift_journey/journey_plans/edit.html.erb +6 -0
  46. data/app/views/datashift_journey/journey_plans/new.html.erb +6 -0
  47. data/app/views/datashift_journey/shared/_default_actions.html.erb +6 -0
  48. data/app/views/datashift_journey/shared/_errors.html.erb +19 -0
  49. data/app/views/datashift_journey/shared/_submit_action.html.erb +5 -0
  50. data/app/views/datashift_journey/state_jumper/_toolbar.html.erb +16 -0
  51. data/config/brakeman.ignore +42 -0
  52. data/config/i18n-tasks.yml +103 -0
  53. data/config/initializers/exceptions_app.rb +3 -0
  54. data/config/initializers/mime_types.rb +1 -0
  55. data/config/initializers/rswag-api.rb +14 -0
  56. data/config/initializers/rswag-ui.rb +9 -0
  57. data/config/locales/en.yml +39 -0
  58. data/config/routes.rb +44 -0
  59. data/lib/datashift_journey/collector/field_snippet.rb +12 -0
  60. data/lib/datashift_journey/collector/page_state_snippet.rb +12 -0
  61. data/lib/datashift_journey/collector/snippet.rb +14 -0
  62. data/lib/datashift_journey/configuration.rb +103 -0
  63. data/lib/datashift_journey/engine.rb +38 -0
  64. data/lib/datashift_journey/exceptions.rb +26 -0
  65. data/lib/datashift_journey/helpers/back_link.rb +58 -0
  66. data/lib/datashift_journey/journey/machine_builder.rb +59 -0
  67. data/lib/datashift_journey/prepare_data_for_review.rb +219 -0
  68. data/lib/datashift_journey/reference_generator.rb +51 -0
  69. data/lib/datashift_journey/state_machines/branch_sequence_map.rb +35 -0
  70. data/lib/datashift_journey/state_machines/extensions.rb +40 -0
  71. data/lib/datashift_journey/state_machines/planner.rb +206 -0
  72. data/lib/datashift_journey/state_machines/sequence.rb +86 -0
  73. data/lib/datashift_journey/state_machines/state_machine_core_ext.rb +72 -0
  74. data/lib/datashift_journey/version.rb +3 -0
  75. data/lib/datashift_journey.rb +57 -0
  76. data/lib/generators/datashift_journey/collector/collector_generator.rb +43 -0
  77. data/lib/generators/datashift_journey/collector/install_collector_generator.rb +61 -0
  78. data/lib/generators/datashift_journey/collector/install_mongo_collector_generator.rb +44 -0
  79. data/lib/generators/datashift_journey/collector/templates/collector_concern.rb.tt +34 -0
  80. data/lib/generators/datashift_journey/collector/templates/collector_migration.rb.tt +46 -0
  81. data/lib/generators/datashift_journey/forms_generator.rb +45 -0
  82. data/lib/generators/datashift_journey/generate_common.rb +33 -0
  83. data/lib/generators/datashift_journey/setup/USAGE +12 -0
  84. data/lib/generators/datashift_journey/setup/setup_generator.rb +44 -0
  85. data/lib/generators/datashift_journey/setup/templates/initializer.rb.tt +17 -0
  86. data/lib/generators/datashift_journey/setup/templates/model_concern.rb.tt +37 -0
  87. data/lib/generators/datashift_journey/templates/base_form.rb.tt +7 -0
  88. data/lib/generators/datashift_journey/templates/collector_form.rb.tt +18 -0
  89. data/lib/generators/datashift_journey/templates/collector_view.rb.tt +15 -0
  90. data/lib/generators/datashift_journey/templates/journey_plan_form.rb.tt +23 -0
  91. data/lib/generators/datashift_journey/templates/journey_plan_view.rb.tt +15 -0
  92. data/lib/generators/datashift_journey/views_generator.rb +35 -0
  93. data/lib/tasks/state_machine.thor +48 -0
  94. data/spec/datashift_journey/complex_journey_spec.rb +132 -0
  95. data/spec/datashift_journey/machine_builder_spec.rb +268 -0
  96. data/spec/datashift_journey/planner_spec.rb +129 -0
  97. data/spec/datashift_journey/sequence_spec.rb +20 -0
  98. data/spec/dummy/Rakefile +6 -0
  99. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  100. data/spec/dummy/app/assets/stylesheets/application.css +16 -0
  101. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  102. data/spec/dummy/app/forms/base_form.rb +8 -0
  103. data/spec/dummy/app/forms/business_details_form.rb +17 -0
  104. data/spec/dummy/app/forms/business_type_form.rb +17 -0
  105. data/spec/dummy/app/forms/contact_details_form.rb +17 -0
  106. data/spec/dummy/app/forms/enter_reg_number_form.rb +17 -0
  107. data/spec/dummy/app/forms/new_or_renew_form.rb +17 -0
  108. data/spec/dummy/app/forms/postal_address_form.rb +17 -0
  109. data/spec/dummy/app/forms/question1_form.rb +9 -0
  110. data/spec/dummy/app/forms/question2_form.rb +12 -0
  111. data/spec/dummy/app/forms/sole_trader_name_form.rb +17 -0
  112. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  113. data/spec/dummy/app/models/payment.rb +5 -0
  114. data/spec/dummy/app/services/datashift_journey/models/collector_journey.rb +51 -0
  115. data/spec/dummy/app/views/_question1.html.erb +13 -0
  116. data/spec/dummy/app/views/_question2.html.erb +9 -0
  117. data/spec/dummy/app/views/layouts/alternative.html.erb +14 -0
  118. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  119. data/spec/dummy/app/views/pages/home.html.erb +6 -0
  120. data/spec/dummy/app/views/pages/start.html.erb +5 -0
  121. data/spec/dummy/bin/bundle +3 -0
  122. data/spec/dummy/bin/rails +4 -0
  123. data/spec/dummy/bin/rake +4 -0
  124. data/spec/dummy/bin/setup +29 -0
  125. data/spec/dummy/config/application.rb +39 -0
  126. data/spec/dummy/config/boot.rb +5 -0
  127. data/spec/dummy/config/database.yml +22 -0
  128. data/spec/dummy/config/environment.rb +5 -0
  129. data/spec/dummy/config/environments/development.rb +47 -0
  130. data/spec/dummy/config/environments/production.rb +79 -0
  131. data/spec/dummy/config/environments/test.rb +47 -0
  132. data/spec/dummy/config/initializers/assets.rb +11 -0
  133. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  134. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  135. data/spec/dummy/config/initializers/datashift_journey.rb +6 -0
  136. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  137. data/spec/dummy/config/initializers/inflections.rb +16 -0
  138. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  139. data/spec/dummy/config/initializers/session_store.rb +3 -0
  140. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  141. data/spec/dummy/config/locales/en.yml +27 -0
  142. data/spec/dummy/config/routes.rb +28 -0
  143. data/spec/dummy/config/secrets.yml +22 -0
  144. data/spec/dummy/config.ru +4 -0
  145. data/spec/dummy/db/migrate/20160101091218_create_dummy_checkout.rb +43 -0
  146. data/spec/dummy/db/migrate/20161221100703_datashift_journey_create_collector.rb +56 -0
  147. data/spec/dummy/db/schema.rb +142 -0
  148. data/spec/dummy/lib/version.rb +1 -0
  149. data/spec/factories/collector_factory.rb +16 -0
  150. data/spec/factories/collector_snippet_factory.rb +9 -0
  151. data/spec/factories/collector_state_page_factory.rb +12 -0
  152. data/spec/factories/data_node_factory.rb +9 -0
  153. data/spec/factories/form_factory.rb +6 -0
  154. data/spec/features/basic_navigation_spec.rb +125 -0
  155. data/spec/helpers/application_helper_spec.rb +40 -0
  156. data/spec/integration/collector/page_state_spec.rb +45 -0
  157. data/spec/models/collector/collector_spec.rb +100 -0
  158. data/spec/models/collector/page_state_spec.rb +30 -0
  159. data/spec/rails_helper.rb +73 -0
  160. data/spec/requests/collector/api/v1/page_state_spec.rb +85 -0
  161. data/spec/requests/collector/api/v1/states_spec.rb +28 -0
  162. data/spec/spec_helper.rb +63 -0
  163. data/spec/support/asserts.rb +27 -0
  164. data/spec/support/mailer_macros.rb +25 -0
  165. data/spec/support/page_objects/base_page_object.rb +77 -0
  166. data/spec/swagger_helper.rb +25 -0
  167. metadata +425 -0
@@ -0,0 +1,268 @@
1
+ require 'rails_helper'
2
+
3
+ module DatashiftJourney
4
+
5
+ module Journey
6
+
7
+ # These tests split out testing diff elements, into separate tests, so for each
8
+ # we need a clean empty class as the State Machines are held at the class level
9
+ # and this is easier than trying to manage diff state machines in one class
10
+
11
+ class Checkout < ActiveRecord::Base
12
+ belongs_to :bill_address, class_name: 'Address'
13
+ belongs_to :ship_address, class_name: 'Address'
14
+ belongs_to :payment
15
+
16
+ def payment_card
17
+ payment.present? ? payment.card : ''
18
+ end
19
+ end
20
+
21
+ class CheckoutEmpty < ActiveRecord::Base; end
22
+ class CheckoutA < ActiveRecord::Base; end
23
+ class CheckoutB < ActiveRecord::Base; end
24
+ class CheckoutC < ActiveRecord::Base; end
25
+
26
+ class Payment < ActiveRecord::Base
27
+ end
28
+
29
+ RSpec.describe MachineBuilder do
30
+ describe 'DSL' do
31
+ before(:all) do
32
+ # Only required as we are testing multiple machines using same Class here
33
+ # under normal circumstances build 1 journey per class
34
+ ::StateMachines::Machine.ignore_method_conflicts = true
35
+ end
36
+
37
+ context 'CheckoutEmpty - Simple MachineBuilder with no transitions' do
38
+ let(:klass) { DatashiftJourney.journey_plan_class }
39
+
40
+ before(:all) do
41
+ DatashiftJourney.journey_plan_class = 'DatashiftJourney::Journey::CheckoutEmpty'
42
+ end
43
+
44
+ it 'does not break the extended Rails class' do
45
+ expect(klass.new).to be
46
+ expect(klass.create).to be
47
+ end
48
+
49
+ it 'can build an empty machine with a given name' do
50
+ machine = Journey::MachineBuilder.create_journey_plan(machine_name: :checkout_empty)
51
+
52
+ expect(machine).to be_a ::StateMachines::Machine
53
+
54
+ expect(machine.name).to eq :checkout_empty
55
+ end
56
+ end
57
+
58
+ context 'CheckoutA - Simple Sequence' do
59
+ before(:all) do
60
+ DatashiftJourney.journey_plan_class = 'DatashiftJourney::Journey::CheckoutA'
61
+
62
+ @machine = MachineBuilder.create_journey_plan(machine_name: :checkout_a, initial: :page1) do
63
+ sequence :page1, :page2, :page3, :page4
64
+ end
65
+ end
66
+
67
+ let(:klass) { DatashiftJourney.journey_plan_class }
68
+
69
+ it 'enables a sequential journey to be planned via list' do
70
+ expect(@machine).to be_a ::StateMachines::Machine
71
+
72
+ expect(@machine).to eq CheckoutA.state_machines[:checkout_a]
73
+
74
+ checkout = CheckoutA.new
75
+
76
+ # Methods are generated based on the StateMachine name, so where the default is state
77
+ # you get the current column name from 'state_name' so for our tests becomes checkout_a_name
78
+ expect(checkout.checkout_a_name).to eq :page1
79
+ expect(checkout.checkout_a).to eq 'page1'
80
+ expect(checkout.page1?).to eq true
81
+ expect(checkout.page4?).to eq false
82
+
83
+ # SO StateMachines::PathCollection seems to map related transitions together, and
84
+ # progressively, so by the last it reports all 6 possible transitions (back/next )
85
+ # not sure based on what yet
86
+ expect(checkout.checkout_a_paths.last.size).to eq 6
87
+
88
+ # events is a StateMachines::EventCollection
89
+ # puts CheckoutA.state_machines[:checkout_a].events.each{|e| puts e.inspect }
90
+ expect(CheckoutA.state_machines[:checkout_a].events.keys.sort).to eq [:back, :next]
91
+
92
+ # puts CheckoutA.state_machines[:checkout_a].events[:back].known_states.inspect
93
+ expect(
94
+ CheckoutA.state_machines[:checkout_a].events[:back].known_states.sort
95
+ ).to eq [:page1, :page2, :page3, :page4]
96
+ end
97
+
98
+ it 'creates back & next transitions for sequential journey' do
99
+ checkout = CheckoutA.new
100
+
101
+ # initially can only go fwd
102
+ # puts CheckoutA.state_machines[:checkout_a].events.transitions_for(checkout).inspect
103
+
104
+ expect(checkout.checkout_a_events.size).to eq 1
105
+ expect(checkout.checkout_a_transitions.size).to eq 1
106
+
107
+ expect(checkout.can_back?).to eq false
108
+ expect(checkout.can_next?).to eq true
109
+ checkout.next!
110
+
111
+ # puts checkout.methods.sort.grep( /trans/ ).inspect
112
+
113
+ # now we should be able to go back and fwd
114
+ expect(checkout.checkout_a_events.size).to eq 2
115
+ expect(checkout.checkout_a_transitions.size).to eq 2
116
+
117
+ expect(checkout.can_back?).to eq true
118
+ expect(checkout.can_next?).to eq true
119
+ end
120
+ end
121
+
122
+ context 'CheckoutB - Simple Sequence as Array' do
123
+ before(:all) do
124
+ DatashiftJourney.journey_plan_class = 'DatashiftJourney::Journey::CheckoutB'
125
+
126
+ @machine = MachineBuilder.create_journey_plan(machine_name: :checkout_b, initial: :array1) do
127
+ sequence [:array1, :array2, :array3]
128
+ end
129
+ end
130
+
131
+ let(:klass) { DatashiftJourney.journey_plan_class }
132
+
133
+ it 'enables a sequential journey to be planned via array' do
134
+ expect(
135
+ CheckoutB.state_machines[:checkout_b].events[:back].known_states.sort
136
+ ).to eq [:array1, :array2, :array3]
137
+
138
+ # If I add in This line :
139
+ # puts CheckoutB.state_machine.states.map(&:name).inspect
140
+ #
141
+ # It causes the next line to then fail - but it passes ok once outs is commented out !
142
+ # NoMethodError:
143
+ # undefined method `state=' for #<CheckoutB:0x00000005489018>
144
+ # Did you mean? state?
145
+ # ruby-2.3.1/gems/activemodel-4.2.6/lib/active_model/attribute_methods.rb:433:in `method_missing'
146
+ # ruby-2.3.1/gems/state_machines-0.4.0/lib/state_machines/machine.rb:1074:in `write'
147
+
148
+ checkout = klass.new
149
+
150
+ expect(checkout.checkout_b_name).to eq :array1
151
+ expect(checkout.checkout_b).to eq 'array1'
152
+
153
+ expect(checkout.array2?).to eq false
154
+
155
+ expect(checkout.can_back?).to eq false
156
+ expect(checkout.can_next?).to eq true
157
+ checkout.next!
158
+ expect(checkout.can_back?).to eq true
159
+ expect(checkout.can_next?).to eq true
160
+ checkout.next!
161
+ expect(checkout.can_back?).to eq true
162
+ expect(checkout.can_next?).to eq false
163
+ end
164
+ end
165
+
166
+ context 'Checkout - Complete API' do
167
+ # See
168
+ before(:all) do
169
+ DatashiftJourney.journey_plan_class = 'DatashiftJourney::Journey::Checkout'
170
+
171
+ [:visa, :mastercard, :paypal].each { |p| Payment.create(name: p) }
172
+ end
173
+
174
+ let(:payment_types) { [:visa, :mastercard, :paypal] }
175
+
176
+ # STATE ENGINE DEFINITION
177
+
178
+ it 'enables a complete journey to be planned via simple DSL', duff: true do
179
+ MachineBuilder.create_journey_plan(initial: :ship_address) do
180
+ sequence [:ship_address, :bill_address]
181
+
182
+ # first define the sequences
183
+ branch_sequence :visa_sequence, [:page_visa_1, :page_visa_2]
184
+
185
+ branch_sequence :mastercard_sequence, [:page_mastercard_1, :page_mastercard_2, :page_mastercard_3]
186
+
187
+ branch_sequence :paypal_sequence, []
188
+
189
+ # now define the parent state and the routing criteria to each sequence
190
+
191
+ split_on_equality(:payment,
192
+ 'payment_card', # Helper method on Checkout that returns card type from Payment
193
+ visa_sequence: 'visa',
194
+ mastercard_sequence: 'mastercard',
195
+ paypal_sequence: 'paypal')
196
+ # byebug
197
+ sequence [:review, :complete]
198
+ end
199
+
200
+ checkout = DatashiftJourney.journey_plan_class.new
201
+
202
+ # puts checkout.state_names.inspect
203
+ # puts Checkout.state_machine.states.map(&:name).inspect
204
+
205
+ expect(checkout.state?(:ship_address)).to eq true
206
+ expect(checkout.can_back?).to eq false # this is the initial state
207
+ expect(checkout.can_next?).to eq true
208
+ checkout.next!
209
+
210
+ expect_state_canback_cannext_and_next!(checkout, :bill_address)
211
+
212
+ expect_state_matches(checkout, :payment)
213
+ # But non of the conditions to move on from payment have been met yet so cannot next
214
+ expect(checkout.can_next?).to eq false
215
+ expect(checkout.can_back?).to eq true
216
+
217
+ # TODO: - should we also create an Event per state ?
218
+ # Implications to how to manage acceptable transitions to that event
219
+ # expect(checkout.payment?).to eq true
220
+
221
+ checkout.create_payment!(card: :mastercard)
222
+
223
+ # now the conditions should have been met - one block (mastercard_page) should match payment_card value
224
+ expect(checkout.can_next?).to eq true
225
+ checkout.next!
226
+
227
+ expect_state_canback_cannext_and_next!(checkout, :page_mastercard_1)
228
+ expect_state_canback_cannext_and_next!(checkout, :page_mastercard_2)
229
+ expect_state_canback_cannext_and_next!(checkout, :page_mastercard_3)
230
+
231
+ expect_state_matches(checkout, :review)
232
+
233
+ expect(checkout.can_next?).to eq true
234
+
235
+ # We should go back based on same conditions
236
+ expect(checkout.can_back?).to eq true
237
+ checkout.back!
238
+
239
+ expect_state_canback_cannext_and_next!(checkout, :page_mastercard_3)
240
+
241
+ expect_state_matches(checkout, :review)
242
+
243
+ checkout.next!
244
+
245
+ expect_state_matches(checkout, :complete)
246
+ # End point so no next
247
+ expect(checkout.can_next?).to eq false
248
+ expect(checkout.can_back?).to eq true
249
+
250
+ # TODO: Implement BACK with event transitions - reverse criteria of the start of split next
251
+
252
+ # Now go all way back to split point and try another path
253
+ # checkout.back until(!checkout.can_back? || checkout.payment?)
254
+ #
255
+ # expect_state_matches( checkout, :payment )
256
+ #
257
+ # checkout.payment.update(card: :paypal)
258
+ #
259
+ # checkout.next!
260
+ #
261
+ # check_state_and_next!(checkout, :paypal_page )
262
+ # check_state_and_next!(checkout, :review )
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end
@@ -0,0 +1,129 @@
1
+ require 'rails_helper'
2
+
3
+ module DatashiftJourney
4
+
5
+ module StateMachines
6
+
7
+ RSpec.describe Planner do
8
+ describe 'DSL' do
9
+ context 'Complex Sequences' do
10
+ it 'enables a split sequence to be first item', duff: true do
11
+ class SomeJourney
12
+ attr_accessor :new_or_renew_value
13
+ end
14
+
15
+ DatashiftJourney.journey_plan_class = 'DatashiftJourney::StateMachines::SomeJourney'
16
+
17
+ Journey::MachineBuilder.create_journey_plan_for(SomeJourney, initial: :new_or_renew) do
18
+ # first define the sequences
19
+ branch_sequence :new_sequence, [:new_sequence_start_page]
20
+
21
+ branch_sequence :renew_sequence, [:renew_sequence_start_page, :enter_reg_number]
22
+
23
+ # now define the parent state and the routing criteria to each sequence
24
+ split_on_equality(:new_or_renew,
25
+ 'new_or_renew_value', # Helper method on Collector
26
+ new_sequence: 'new',
27
+ renew_sequence: 'renew')
28
+ end
29
+
30
+ journey = DatashiftJourney.journey_plan_class.new
31
+
32
+ # First branch
33
+ journey.new_or_renew_value = 'new'
34
+
35
+ expect_state_matches(journey, :new_or_renew)
36
+ expect(journey.can_back?).to eq false # this is the initial state
37
+ expect(journey.can_next?).to eq true
38
+ journey.next!
39
+
40
+ expect_state_matches(journey, :new_sequence_start_page)
41
+ expect(journey.can_back?).to eq true
42
+ expect(journey.can_next?).to eq false # the end
43
+
44
+ journey.back!
45
+ expect_state_matches(journey, :new_or_renew)
46
+
47
+ # Other branch
48
+ journey = DatashiftJourney.journey_plan_class.new
49
+
50
+ journey.new_or_renew_value = 'renew'
51
+
52
+ expect_state_matches(journey, :new_or_renew)
53
+ expect(journey.can_back?).to eq false # this is the initial state
54
+ expect(journey.can_next?).to eq true
55
+ journey.next!
56
+
57
+ expect_state_canback_cannext_and_next!(journey, :renew_sequence_start_page)
58
+
59
+ expect_state_matches(journey, :enter_reg_number)
60
+ expect(journey.can_back?).to eq true
61
+ expect(journey.can_next?).to eq false # the end
62
+ end
63
+ end
64
+
65
+ it 'enables consecutive split sequences which all self terminate' do
66
+ class SomeJourney1
67
+ attr_accessor :new_or_renew_value
68
+ attr_accessor :business_type_value
69
+ end
70
+
71
+ DatashiftJourney.journey_plan_class = 'DatashiftJourney::StateMachines::SomeJourney1'
72
+
73
+ Journey::MachineBuilder.create_journey_plan(initial: :new_or_renew) do
74
+ # test the empty sequences - next should hit split state of next sequence ()
75
+ branch_sequence :new_sequence, []
76
+
77
+ branch_sequence :renew_sequence, [:renew_sequence_start_page, :enter_reg_number]
78
+
79
+ # now define the parent state and the routing criteria to each sequence
80
+ split_on_equality(:new_or_renew,
81
+ 'new_or_renew_value', # Helper method on Collector
82
+ new_sequence: 'new',
83
+ renew_sequence: 'renew')
84
+
85
+ branch_sequence :sole_trader_sequence, [:sole_trader_start]
86
+ branch_sequence :partnership_sequence, [:partnership_start]
87
+ branch_sequence :limited_company_sequence, [:limited_company_start]
88
+
89
+ split_on_equality(:business_type,
90
+ 'business_type_value',
91
+ sole_trader_sequence: 'sole_trader',
92
+ partnership_sequence: 'partnership',
93
+ limited_company_sequence: 'limited_company')
94
+ end
95
+
96
+ journey = DatashiftJourney.journey_plan_class.new
97
+
98
+ # First branch
99
+ journey.new_or_renew_value = 'new'
100
+
101
+ expect_state_matches(journey, :new_or_renew)
102
+ expect(journey.can_back?).to eq false # this is the initial state
103
+
104
+ # new sequence is empty - use case where we just stored the split value and moved onto next sequence
105
+ # which is :business_type
106
+ expect(journey.can_next?).to eq true
107
+
108
+ journey.next!
109
+
110
+ expect_state_matches(journey, :business_type)
111
+ expect(journey.can_back?).to eq true
112
+
113
+ # We don't yet have a valid value for business_type_value so should not be able to proceed
114
+ expect(journey.can_next?).to eq false
115
+
116
+ journey.business_type_value = 'partnership'
117
+
118
+ expect(journey.can_next?).to eq true
119
+
120
+ journey.next!
121
+
122
+ expect_state_matches(journey, :partnership_start)
123
+ expect(journey.can_back?).to eq true
124
+ expect(journey.can_next?).to eq false
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,20 @@
1
+ require 'rails_helper'
2
+
3
+ module DatashiftJourney
4
+ module StateMachines
5
+
6
+ RSpec.describe Sequence do
7
+ let(:list) { [:review, :complete] }
8
+
9
+ describe 'intializing an instance' do
10
+ it 'should create a simple list of states' do
11
+ sequence = Sequence.new(list.flatten)
12
+
13
+ expect(sequence).to be_a Sequence
14
+ expect(sequence.states).to eq list
15
+ end
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,16 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
16
+
@@ -0,0 +1,5 @@
1
+ class ApplicationController < ::DatashiftJourney::ApplicationController
2
+ # Prevent CSRF attacks by raising an exception.
3
+ # For APIs, you may want to use :null_session instead.
4
+ protect_from_forgery with: :exception
5
+ end
@@ -0,0 +1,8 @@
1
+ class BaseForm < DatashiftJourney::BaseForm
2
+
3
+ def initialize(model, journey_plan = nil)
4
+ super(model, journey_plan)
5
+ end
6
+
7
+ end
8
+
@@ -0,0 +1,17 @@
1
+ class BusinessDetailForm < DatashiftJourney::Collector::BaseCollectorForm
2
+
3
+ def params_key
4
+ :business_details
5
+ end
6
+
7
+ # Data collection performed by base class but if you need custom processing, the forms
8
+ # fields are represented as a series of DataNode objects, with the actual data saved in :field_value
9
+ #
10
+ # collection :data_nodes do
11
+ # property :field_value
12
+ # end
13
+
14
+ # Example basic validation - has field been filled in :
15
+ # validates :field_value, presence: true
16
+
17
+ end
@@ -0,0 +1,17 @@
1
+ class BusinessTypeForm < DatashiftJourney::Collector::BaseCollectorForm
2
+
3
+ def params_key
4
+ :business_type
5
+ end
6
+
7
+ # Data collection performed by base class but if you need custom processing, the forms
8
+ # fields are represented as a series of DataNode objects, with the actual data saved in :field_value
9
+ #
10
+ # collection :data_nodes do
11
+ # property :field_value
12
+ # end
13
+
14
+ # Example basic validation - has field been filled in :
15
+ # validates :field_value, presence: true
16
+
17
+ end
@@ -0,0 +1,17 @@
1
+ class ContactDetailForm < DatashiftJourney::Collector::BaseCollectorForm
2
+
3
+ def params_key
4
+ :contact_details
5
+ end
6
+
7
+ # Data collection performed by base class but if you need custom processing, the forms
8
+ # fields are represented as a series of DataNode objects, with the actual data saved in :field_value
9
+ #
10
+ # collection :data_nodes do
11
+ # property :field_value
12
+ # end
13
+
14
+ # Example basic validation - has field been filled in :
15
+ # validates :field_value, presence: true
16
+
17
+ end
@@ -0,0 +1,17 @@
1
+ class EnterRegNumberForm < DatashiftJourney::Collector::BaseCollectorForm
2
+
3
+ def params_key
4
+ :enter_reg_number
5
+ end
6
+
7
+ # Data collection performed by base class but if you need custom processing, the forms
8
+ # fields are represented as a series of DataNode objects, with the actual data saved in :field_value
9
+ #
10
+ # collection :data_nodes do
11
+ # property :field_value
12
+ # end
13
+
14
+ # Example basic validation - has field been filled in :
15
+ # validates :field_value, presence: true
16
+
17
+ end
@@ -0,0 +1,17 @@
1
+ class NewOrRenewForm < DatashiftJourney::Collector::BaseCollectorForm
2
+
3
+ def params_key
4
+ :new_or_renew
5
+ end
6
+
7
+ # Data collection performed by base class but if you need custom processing, the forms
8
+ # fields are represented as a series of DataNode objects, with the actual data saved in :field_value
9
+ #
10
+ collection :data_nodes do
11
+ property :field
12
+
13
+ # Example basic validation - has field been filled in :
14
+ validates :field, presence: true
15
+ end
16
+
17
+ end
@@ -0,0 +1,17 @@
1
+ class PostalAddressForm < DatashiftJourney::Collector::BaseCollectorForm
2
+
3
+ def params_key
4
+ :postal_address
5
+ end
6
+
7
+ # Data collection performed by base class but if you need custom processing, the forms
8
+ # fields are represented as a series of DataNode objects, with the actual data saved in :field_value
9
+ #
10
+ # collection :data_nodes do
11
+ # property :field_value
12
+ # end
13
+
14
+ # Example basic validation - has field been filled in :
15
+ # validates :field_value, presence: true
16
+
17
+ end
@@ -0,0 +1,9 @@
1
+ class Question1Form < DatashiftJourney::BaseCollectorForm
2
+
3
+ def params_key
4
+ :question1
5
+ end
6
+
7
+ property :field_value
8
+ validates :field_value, presence: true
9
+ end
@@ -0,0 +1,12 @@
1
+ class Question2Form < DatashiftJourney::BaseCollectorForm
2
+
3
+ def params_key
4
+ :question2
5
+ end
6
+
7
+ property :field_value
8
+ validate :field_value, presence: true
9
+
10
+
11
+ end
12
+
@@ -0,0 +1,17 @@
1
+ class SoleTraderNameForm < DatashiftJourney::Collector::BaseCollectorForm
2
+
3
+ def params_key
4
+ :sole_trader_name
5
+ end
6
+
7
+ # Data collection performed by base class but if you need custom processing, the forms
8
+ # fields are represented as a series of DataNode objects, with the actual data saved in :field_value
9
+ #
10
+ # collection :data_nodes do
11
+ # property :field_value
12
+ # end
13
+
14
+ # Example basic validation - has field been filled in :
15
+ # validates :field_value, presence: true
16
+
17
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,5 @@
1
+ class Payment < ActiveRecord::Base
2
+ #belongs_to :bill_address, class_name: "Address"
3
+ #belongs_to :ship_address, class_name: "Address"
4
+ #belongs_to :payment
5
+ end