surveyor 0.22.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +1 -0
  3. data/CHANGELOG.md +83 -0
  4. data/Gemfile.rails_version +7 -1
  5. data/README.md +114 -16
  6. data/Rakefile +15 -9
  7. data/app/inputs/quiet_input.rb +5 -0
  8. data/app/inputs/surveyor_check_boxes_input.rb +35 -0
  9. data/app/inputs/surveyor_radio_input.rb +18 -0
  10. data/app/views/partials/_answer.html.haml +4 -4
  11. data/app/views/partials/_question.html.haml +7 -7
  12. data/app/views/partials/_question_group.html.haml +9 -8
  13. data/app/views/surveyor/export.json.rabl +28 -25
  14. data/app/views/surveyor/new.html.haml +8 -5
  15. data/app/views/surveyor/show.json.rabl +3 -2
  16. data/ci-exec.sh +13 -7
  17. data/cucumber.yml +3 -3
  18. data/doc/REPRESENTATIONS.md +34 -0
  19. data/doc/api_id_schema.json +7 -0
  20. data/doc/response_set_schema.json +54 -0
  21. data/doc/surveyor question combinations.png +0 -0
  22. data/doc/surveyor_timestamp_schema.json +9 -0
  23. data/features/ajax_submissions.feature +140 -0
  24. data/features/export_to_json.feature +182 -34
  25. data/features/no_duplicates.feature +110 -0
  26. data/features/show_survey.feature +1 -1
  27. data/features/step_definitions/parser_steps.rb +25 -2
  28. data/features/step_definitions/surveyor_steps.rb +145 -20
  29. data/features/step_definitions/ui_steps.rb +3 -0
  30. data/features/support/database_cleaner.rb +16 -0
  31. data/features/support/env.rb +21 -17
  32. data/features/support/simultaneous_ajax.rb +101 -0
  33. data/features/support/single_quit_selenium_driver.rb +23 -0
  34. data/features/support/slow_updates.rb +18 -0
  35. data/features/surveyor.feature +174 -44
  36. data/features/surveyor_dependencies.feature +80 -39
  37. data/features/surveyor_parser.feature +114 -20
  38. data/features/z_redcap_parser.feature +0 -1
  39. data/lib/{generators/surveyor/templates/public → assets}/images/surveyor/next.gif +0 -0
  40. data/lib/{generators/surveyor/templates/public → assets}/images/surveyor/prev.gif +0 -0
  41. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  42. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  43. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_glass_65_ffffff_1x400.png +0 -0
  44. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  45. data/lib/assets/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  46. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  47. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-icons_ef8c08_256x240.png +0 -0
  48. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-icons_ffffff_256x240.png +0 -0
  49. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery-ui-timepicker-addon.js +23 -23
  50. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery-ui.js +125 -125
  51. data/lib/assets/javascripts/surveyor/jquery.selectToUISlider.js +240 -0
  52. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery.surveyor.js +52 -57
  53. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery.tools.min.js +7 -7
  54. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/dateinput.css +13 -13
  55. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/jquery-ui-timepicker-addon.css +0 -0
  56. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/jquery-ui.custom.css +17 -17
  57. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/reset.css +1 -1
  58. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/results.css +0 -0
  59. data/lib/assets/stylesheets/surveyor/ui.slider.extras.css +110 -0
  60. data/lib/{generators/surveyor/templates/public/stylesheets/sass → assets/stylesheets}/surveyor.sass +15 -7
  61. data/lib/generators/surveyor/custom_generator.rb +3 -2
  62. data/lib/generators/surveyor/install_generator.rb +59 -17
  63. data/lib/generators/surveyor/templates/app/assets/javascripts/surveyor_all.js +5 -0
  64. data/lib/generators/surveyor/templates/app/assets/stylesheets/surveyor_all.css +9 -0
  65. data/lib/generators/surveyor/templates/app/controllers/surveyor_controller.rb +2 -1
  66. data/lib/generators/surveyor/templates/app/views/layouts/surveyor_custom.html.erb +1 -0
  67. data/lib/generators/surveyor/templates/config/locales/surveyor_es.yml +1 -0
  68. data/lib/generators/surveyor/templates/config/locales/surveyor_he.yml +1 -0
  69. data/lib/generators/surveyor/templates/db/migrate/add_api_id_to_question_groups.rb +1 -0
  70. data/lib/generators/surveyor/templates/db/migrate/add_api_ids.rb +1 -0
  71. data/lib/generators/surveyor/templates/db/migrate/add_api_ids_to_response_sets_and_responses.rb +1 -0
  72. data/lib/generators/surveyor/templates/db/migrate/add_correct_answer_id_to_questions.rb +1 -0
  73. data/lib/generators/surveyor/templates/db/migrate/add_default_value_to_answers.rb +1 -0
  74. data/lib/generators/surveyor/templates/db/migrate/add_display_order_to_surveys.rb +1 -0
  75. data/lib/generators/surveyor/templates/db/migrate/add_display_type_to_answers.rb +1 -0
  76. data/lib/generators/surveyor/templates/db/migrate/add_index_to_response_sets.rb +1 -0
  77. data/lib/generators/surveyor/templates/db/migrate/add_index_to_surveys.rb +1 -0
  78. data/lib/generators/surveyor/templates/db/migrate/add_section_id_to_responses.rb +1 -0
  79. data/lib/generators/surveyor/templates/db/migrate/add_unique_index_on_access_code_and_version_in_surveys.rb +10 -0
  80. data/lib/generators/surveyor/templates/db/migrate/add_unique_indicies.rb +3 -2
  81. data/lib/generators/surveyor/templates/db/migrate/add_version_to_surveys.rb +10 -0
  82. data/lib/generators/surveyor/templates/db/migrate/api_ids_must_be_unique.rb +23 -0
  83. data/lib/generators/surveyor/templates/db/migrate/create_answers.rb +6 -5
  84. data/lib/generators/surveyor/templates/db/migrate/create_dependencies.rb +2 -1
  85. data/lib/generators/surveyor/templates/db/migrate/create_dependency_conditions.rb +2 -1
  86. data/lib/generators/surveyor/templates/db/migrate/create_question_groups.rb +5 -4
  87. data/lib/generators/surveyor/templates/db/migrate/create_questions.rb +4 -3
  88. data/lib/generators/surveyor/templates/db/migrate/create_response_sets.rb +1 -0
  89. data/lib/generators/surveyor/templates/db/migrate/create_responses.rb +10 -9
  90. data/lib/generators/surveyor/templates/db/migrate/create_survey_sections.rb +5 -4
  91. data/lib/generators/surveyor/templates/db/migrate/create_surveys.rb +4 -3
  92. data/lib/generators/surveyor/templates/db/migrate/create_validation_conditions.rb +5 -4
  93. data/lib/generators/surveyor/templates/db/migrate/create_validations.rb +3 -2
  94. data/lib/generators/surveyor/templates/db/migrate/drop_unique_index_on_access_code_in_surveys.rb +10 -0
  95. data/lib/generators/surveyor/templates/db/migrate/update_blank_api_ids_on_question_group.rb +22 -0
  96. data/lib/generators/surveyor/templates/db/migrate/update_blank_versions_on_surveys.rb +13 -0
  97. data/lib/generators/surveyor/templates/surveys/date_survey.rb +1 -0
  98. data/lib/generators/surveyor/templates/surveys/kitchen_sink_survey.rb +54 -24
  99. data/lib/generators/surveyor/templates/surveys/quiz.rb +1 -0
  100. data/lib/generators/surveyor/templates/{public/stylesheets/sass → vendor/assets/stylesheets}/custom.sass +1 -1
  101. data/lib/surveyor/common.rb +16 -31
  102. data/lib/surveyor/engine.rb +2 -4
  103. data/lib/surveyor/helpers/asset_pipeline.rb +13 -0
  104. data/lib/surveyor/helpers/formtastic_custom_input.rb +17 -0
  105. data/lib/surveyor/helpers/surveyor_helper_methods.rb +10 -8
  106. data/lib/surveyor/models/answer_methods.rb +3 -0
  107. data/lib/surveyor/models/dependency_condition_methods.rb +27 -28
  108. data/lib/surveyor/models/dependency_methods.rb +3 -0
  109. data/lib/surveyor/models/question_group_methods.rb +3 -0
  110. data/lib/surveyor/models/question_methods.rb +10 -7
  111. data/lib/surveyor/models/response_methods.rb +16 -0
  112. data/lib/surveyor/models/response_set_methods.rb +71 -64
  113. data/lib/surveyor/models/survey_methods.rb +19 -28
  114. data/lib/surveyor/models/survey_section_methods.rb +3 -0
  115. data/lib/surveyor/models/validation_condition_methods.rb +4 -2
  116. data/lib/surveyor/models/validation_methods.rb +3 -0
  117. data/lib/surveyor/parser.rb +198 -148
  118. data/lib/surveyor/redcap_parser.rb +120 -80
  119. data/lib/surveyor/surveyor_controller_methods.rb +86 -37
  120. data/lib/surveyor/version.rb +2 -2
  121. data/lib/surveyor.rb +5 -6
  122. data/lib/tasks/surveyor_tasks.rake +19 -7
  123. data/spec/controllers/surveyor_controller_spec.rb +166 -92
  124. data/spec/factories.rb +33 -32
  125. data/spec/helpers/formtastic_custom_input_spec.rb +16 -0
  126. data/spec/lib/common_spec.rb +0 -39
  127. data/spec/lib/redcap_parser_spec.rb +24 -24
  128. data/spec/models/answer_spec.rb +12 -0
  129. data/spec/models/dependency_condition_spec.rb +279 -323
  130. data/spec/models/dependency_spec.rb +10 -0
  131. data/spec/models/question_group_spec.rb +12 -0
  132. data/spec/models/question_spec.rb +12 -0
  133. data/spec/models/response_set_spec.rb +189 -139
  134. data/spec/models/response_spec.rb +60 -0
  135. data/spec/models/survey_section_spec.rb +9 -0
  136. data/spec/models/survey_spec.rb +72 -9
  137. data/spec/models/validation_condition_spec.rb +9 -1
  138. data/spec/models/validation_spec.rb +10 -0
  139. data/spec/spec_helper.rb +25 -6
  140. data/surveyor.gemspec +5 -4
  141. metadata +332 -291
  142. data/features/step_definitions/common_steps.rb +0 -3
  143. data/lib/formtastic/surveyor_builder.rb +0 -82
  144. data/lib/generators/surveyor/templates/public/javascripts/surveyor/jquery.blockUI.js +0 -499
@@ -11,11 +11,6 @@ describe SurveyorController do
11
11
  # end
12
12
 
13
13
  describe "available surveys: GET /surveys" do
14
- before(:each) do
15
- @survey = Factory(:survey)
16
- Survey.stub!(:find).and_return([@survey])
17
- end
18
-
19
14
  def do_get
20
15
  get :new
21
16
  end
@@ -26,33 +21,37 @@ describe SurveyorController do
26
21
  response.should render_template('new')
27
22
  end
28
23
 
29
- it "should find all surveys" do
30
- Survey.should_receive(:find).with(:all).and_return([@survey])
31
- do_get
32
- end
33
-
34
- it "should assign the found surveys for the view" do
24
+ it "should list codes and survey_versions for all surveys" do
25
+ original = Factory(:survey, :title => "Foo", :access_code => 'foo')
26
+ supplant = Factory(:survey, :title => "Foo", :access_code => 'foo', :survey_version => 1)
27
+ hash = {"foo"=>{"title"=>"Foo", "survey_versions"=>[0, 1]}}
35
28
  do_get
36
- assigns[:surveys].should == [@survey]
29
+ assigns(:codes).should eq hash
37
30
  end
38
31
  end
39
32
 
40
33
  describe "take survey: POST /surveys/xyz" do
41
34
  before(:each) do
42
35
  @survey = Factory(:survey, :title => "xyz", :access_code => "xyz")
36
+ @newsurvey = Factory(:survey, :title => "xyz", :access_code => "xyz", :survey_version => 1)
43
37
  @response_set = Factory(:response_set, :access_code => "pdq")
44
38
  ResponseSet.stub!(:create).and_return(@response_set)
45
- Survey.stub!(:find_by_access_code).and_return(@survey)
46
39
  end
47
40
 
48
41
  describe "with success" do
49
42
  def do_post
50
43
  post :create, :survey_code => "xyz"
51
44
  end
52
- it "should look for the survey" do
53
- Survey.should_receive(:find_by_access_code).with("xyz").and_return(@survey)
45
+ it "should look for the latest survey_version of the survey if survey_version is not explicitely provided" do
54
46
  do_post
47
+ assigns(:survey).should eq(@newsurvey)
55
48
  end
49
+
50
+ it "should look for the partculer survey_version of the survey if it is provided" do
51
+ post :create, :survey_code => "xyz", :survey_version => 0
52
+ assigns(:survey).should eq(@survey)
53
+ end
54
+
56
55
  it "should create a new response_set" do
57
56
  ResponseSet.should_receive(:create).and_return(@response_set)
58
57
  do_post
@@ -71,8 +70,7 @@ describe SurveyorController do
71
70
  response.should redirect_to(available_surveys_url)
72
71
  end
73
72
  it "should re-redirect to 'new' if Survey failed find" do
74
- Survey.should_receive(:find_by_access_code).and_return(nil)
75
- post :create, :survey_code => "XYZ"
73
+ post :create, :survey_code => "ABC"
76
74
  response.should redirect_to(available_surveys_url)
77
75
  end
78
76
  end
@@ -125,6 +123,25 @@ describe SurveyorController do
125
123
  get :show, :survey_code => "xyz", :response_set_code => "DIFFERENT"
126
124
  response.should redirect_to(available_surveys_url)
127
125
  end
126
+
127
+ it "should render correct survey survey_version" do
128
+ supplant = Factory(:survey, :title => "xyz", :access_code => 'xyz', :survey_version => 1)
129
+ supplant_section = Factory(:survey_section, :survey => supplant)
130
+ supplant_response_set = Factory(:response_set, :access_code => "rst", :survey => supplant)
131
+
132
+ get :show, :survey_code => "xyz", :response_set_code => "pdq"
133
+ response.should be_success
134
+ response.should render_template('show')
135
+ assigns[:response_set].should == @response_set
136
+ assigns[:survey].should == @survey
137
+
138
+ get :show, :survey_code => "xyz", :response_set_code => "rst"
139
+ response.should be_success
140
+ response.should render_template('show')
141
+ assigns[:response_set].should == supplant_response_set
142
+ assigns[:survey].should == supplant
143
+ end
144
+
128
145
  end
129
146
 
130
147
  describe "edit my survey: GET /surveys/XYZ/PDQ/take" do
@@ -169,99 +186,156 @@ describe SurveyorController do
169
186
  assigns[:dependents].should be_empty
170
187
  session[:surveyor_javascript].should == "enabled"
171
188
  end
189
+
190
+ it "should render correct survey survey_version" do
191
+ supplant = Factory(:survey, :title => "XYZ", :access_code => 'XYZ', :survey_version => 1)
192
+ supplant_section = Factory(:survey_section, :survey => supplant)
193
+ supplant_response_set = Factory(:response_set, :access_code => "RST", :survey => supplant)
194
+
195
+ get :edit, :survey_code => "XYZ", :response_set_code => "PDQ"
196
+ response.should be_success
197
+ response.should render_template('edit')
198
+ assigns[:response_set].should == @response_set
199
+ assigns[:survey].should == @survey
200
+
201
+ get :edit, :survey_code => "XYZ", :response_set_code => "RST"
202
+ response.should be_success
203
+ response.should render_template('edit')
204
+ assigns[:response_set].should == supplant_response_set
205
+ assigns[:survey].should == supplant
206
+ end
172
207
  end
173
208
 
174
209
  describe "update my survey: PUT /surveys/XYZ/PDQ" do
175
- before(:each) do
176
- @survey = Factory(:survey, :title => "XYZ", :access_code => "XYZ")
177
- @section = Factory(:survey_section, :survey => @survey)
178
- @response_set = Factory(:response_set, :access_code => "PDQ", :survey => @survey)
179
- # @response_set.stub!(:update_attributes).and_return(true)
180
- # @response_set.stub!(:complete!).and_return(Time.now)
181
- # @response_set.stub!(:save).and_return(true)
182
- end
210
+ let(:survey_code) { 'XYZ' }
211
+ let!(:survey) { Factory(:survey, :title => survey_code, :access_code => survey_code) }
183
212
 
184
- def do_put
185
- put :update, :survey_code => "XYZ", :response_set_code => "PDQ"
186
- end
213
+ let(:response_set_code) { 'PDQ' }
214
+ let!(:response_set) { Factory(:response_set, :access_code => response_set_code, :survey => survey) }
215
+
216
+ let(:responses_ui_hash) { {} }
187
217
 
188
- def do_put_with_finish
189
- responses = {
190
- "6"=>{"question_id"=>"6", "answer_id" => "6", "string_value"=>"saf"}, #string
191
- "7"=>{"question_id"=>"7", "answer_id" => "11", "text_value"=>"foo"}, #text
192
- "1"=>{"question_id"=>"1", "answer_id"=>"1", "string_value"=>"bar"}, #radio+txt
193
- "2"=>{"question_id"=>"2", "answer_id"=>"6"}, #radio
194
- "3"=>{"question_id"=>"3", "answer_id"=>"10"}, #radio
195
- "4"=>{"question_id"=>"4", "answer_id"=>"15"}, #check
196
- "5"=>{"question_id"=>"5", "answer_id"=>"16", "string_value"=>""} #check+txt
218
+ let(:params) {
219
+ {
220
+ :survey_code => survey_code,
221
+ :response_set_code => response_set_code,
222
+ :r => responses_ui_hash.empty? ? nil : responses_ui_hash
197
223
  }
198
- put :update, :survey_code => "XYZ", :response_set_code => "PDQ", :finish => "finish", :r => responses
199
- end
224
+ }
200
225
 
201
- it "should find the response set requested" do
202
- ResponseSet.should_receive(:find_by_access_code).and_return(@response_set)
203
- do_put
226
+ def a_ui_response(hash)
227
+ { 'api_id' => 'something' }.merge(hash)
204
228
  end
205
229
 
206
- it "should redirect to 'edit' without params" do
207
- do_put
208
- response.should redirect_to(:action => :edit)
209
- end
230
+ shared_examples 'common update behaviors' do
231
+ it "should find the response set requested" do
232
+ ResponseSet.should_receive(:find_by_access_code).and_return(response_set)
233
+ do_put
234
+ end
210
235
 
211
- it "should complete the found response set on finish" do
212
- do_put_with_finish
213
- flash[:notice].should == "Completed survey"
214
- end
236
+ it 'applies any provided responses to the response set' do
237
+ ResponseSet.stub!(:find_by_access_code).and_return(response_set)
215
238
 
216
- it "should redirect to available surveys if :response_code not found" do
217
- put :update, :survey_code => "XYZ", :response_set_code => "DIFFERENT"
218
- response.should redirect_to(available_surveys_url)
219
- flash[:notice].should == "Unable to find your responses to the survey"
220
- end
221
- end
239
+ responses_ui_hash['11'] = a_ui_response('answer_id' => '56', 'question_id' => '9')
240
+ response_set.should_receive(:update_from_ui_hash).with(responses_ui_hash)
241
+ do_put
242
+ end
222
243
 
223
- describe "update my survey with ajax" do
224
- before(:each) do
225
- @survey = Factory(:survey, :title => "XYZ", :access_code => "XYZ")
226
- @section = Factory(:survey_section, :survey => @survey)
227
- @response_set = Factory(:response_set, :access_code => "PDQ", :survey => @survey)
228
- ResponseSet.stub!(:find_by_access_code).and_return(@response_set)
229
- end
244
+ it 'does not fail when there are no responses' do
245
+ lambda { do_put }.should_not raise_error
246
+ end
230
247
 
231
- def do_ajax_put(r)
232
- xhr :put, :update, :survey_code => "XYZ", :response_set_code => "PDQ", :r => r
233
- end
248
+ describe 'when updating the response set produces a exception' do
249
+ before do
250
+ responses_ui_hash['11'] = a_ui_response('answer_id' => '56', 'question_id' => '9')
251
+
252
+ ResponseSet.stub!(:find_by_access_code).and_return(response_set)
253
+ end
254
+
255
+ it 'retries the update on a constraint violation' do
256
+ response_set.should_receive(:update_from_ui_hash).ordered.
257
+ with(responses_ui_hash).and_raise(ActiveRecord::StatementInvalid)
258
+ response_set.should_receive(:update_from_ui_hash).ordered.with(responses_ui_hash)
234
259
 
235
- it "should return an id for new responses" do
236
- do_ajax_put({
237
- "2"=>{"question_id"=>"4", "answer_id"=>"14"}
238
- })
239
- JSON.parse(response.body).
240
- should == {"ids" => {"2" => 1}, "remove" => {}, "show" => [], "hide" => []}
241
- do_ajax_put({
242
- "4"=>{"question_id"=>"4", "answer_id"=>"15"}
243
- })
244
- JSON.parse(response.body).
245
- should == {"ids" => {"4" => 2}, "remove" => {}, "show" => [], "hide" => []}
260
+ lambda { do_put }.should_not raise_error
261
+ end
262
+
263
+ it 'only retries three times' do
264
+ response_set.should_receive(:update_from_ui_hash).exactly(3).times.
265
+ with(responses_ui_hash).and_raise(ActiveRecord::StatementInvalid)
266
+
267
+ lambda { do_put }.should raise_error(ActiveRecord::StatementInvalid)
268
+ end
269
+
270
+ it 'does not retry for other errors' do
271
+ response_set.should_receive(:update_from_ui_hash).once.
272
+ with(responses_ui_hash).and_raise('Bad news')
273
+
274
+ lambda { do_put }.should raise_error('Bad news')
275
+ end
276
+ end
246
277
  end
247
278
 
248
- it "should return a delete for when responses are removed" do
249
- r = @response_set.responses.create(:question_id => 4, :answer_id => 14)
250
- do_ajax_put({
251
- "2"=>{"question_id"=>"4", "answer_id"=>"", "id" => r.id} # uncheck
252
- })
253
- # r.id is a String with AR 3.0 and an int with AR 3.1
254
- JSON.parse(response.body)['remove']['2'].to_s.should == r.id.to_s
279
+ describe 'via full cycle form submission' do
280
+ def do_put
281
+ put :update, params
282
+ end
283
+
284
+ include_examples 'common update behaviors'
285
+
286
+ it "should redirect to 'edit' without params" do
287
+ do_put
288
+ response.should redirect_to(:action => :edit)
289
+ end
290
+
291
+ describe 'on finish' do
292
+ before do
293
+ params[:finish] = 'finish'
294
+ do_put
295
+ end
296
+
297
+ it "completes the found response set" do
298
+ response_set.reload.should be_complete
299
+ end
300
+
301
+ it 'flashes completion' do
302
+ flash[:notice].should == "Completed survey"
303
+ end
304
+ end
305
+
306
+ it "should redirect to available surveys if :response_code not found" do
307
+ params[:response_set_code] = "DIFFERENT"
308
+ do_put
309
+ response.should redirect_to(available_surveys_url)
310
+ flash[:notice].should == "Unable to find your responses to the survey"
311
+ end
255
312
  end
256
313
 
257
- it "should return dependencies" do
258
- @response_set.should_receive(:all_dependencies).
259
- and_return({"show" => ['q_1'], "hide" => ['q_2']})
260
- do_ajax_put({
261
- "4"=>{"question_id"=>"9", "answer_id"=>"12"} #check
262
- })
263
- JSON.parse(response.body).
264
- should == {"ids" => {"4" => 1}, "remove" => {}, "show" => ['q_1'], "hide" => ["q_2"]}
314
+ describe 'via ajax' do
315
+ def do_put
316
+ xhr :put, :update, params
317
+ end
318
+
319
+ include_examples 'common update behaviors'
320
+
321
+ it "should return dependencies" do
322
+ ResponseSet.stub!(:find_by_access_code).and_return(response_set)
323
+
324
+ response_set.should_receive(:all_dependencies).
325
+ and_return({"show" => ['q_1'], "hide" => ['q_2']})
326
+
327
+ responses_ui_hash['4'] = a_ui_response("question_id"=>"9", "answer_id"=>"12") # check
328
+ do_put
329
+
330
+ JSON.parse(response.body).
331
+ should == {"show" => ['q_1'], "hide" => ["q_2"]}
332
+ end
333
+
334
+ it '404s if the response set does not exist' do
335
+ params[:response_set_code] = 'ELSE'
336
+ do_put
337
+ response.status.should == 404
338
+ end
265
339
  end
266
340
  end
267
341
  end
data/spec/factories.rb CHANGED
@@ -5,12 +5,13 @@ require 'factory_girl'
5
5
  Factory.sequence(:unique_survey_access_code){|n| "simple_survey" << n.to_s }
6
6
 
7
7
  Factory.define :survey do |s|
8
- s.title {"Simple survey"}
9
- s.description {"A simple survey for testing"}
10
- s.access_code {Factory.next :unique_survey_access_code}
11
- s.active_at {Time.now}
12
- s.inactive_at {}
13
- s.css_url {}
8
+ s.title {"Simple survey"}
9
+ s.description {"A simple survey for testing"}
10
+ s.access_code {Factory.next :unique_survey_access_code}
11
+ s.active_at {Time.now}
12
+ s.inactive_at {}
13
+ s.css_url {}
14
+ s.survey_version {0}
14
15
  end
15
16
 
16
17
  Factory.sequence(:survey_section_display_order){|n| n }
@@ -28,20 +29,20 @@ Factory.sequence(:question_display_order){|n| n }
28
29
 
29
30
  Factory.define :question do |q|
30
31
  q.association :survey_section # s.survey_section_id {}
31
- q.question_group_id {}
32
- q.text {"What is your favorite color?"}
33
- q.short_text {"favorite_color"}
34
- q.help_text {"just write it in the box"}
35
- q.pick {:none}
32
+ # q.question_group_id {}
33
+ q.text "What is your favorite color?"
34
+ q.short_text "favorite_color"
35
+ q.help_text "just write it in the box"
36
+ q.pick :none
36
37
  q.reference_identifier {|me| "q_#{me.object_id}"}
37
- q.data_export_identifier {}
38
- q.common_namespace {}
39
- q.common_identifier {}
40
- q.display_order {Factory.next :question_display_order}
41
- q.display_type {} # nil is default
42
- q.is_mandatory {false}
43
- q.display_width {}
44
- q.correct_answer_id {nil}
38
+ # q.data_export_identifier {}
39
+ # q.common_namespace {}
40
+ # q.common_identifier {}
41
+ q.display_order Factory.next :question_display_order
42
+ # q.display_type {} # nil is default
43
+ q.is_mandatory false
44
+ # q.display_width {}
45
+ q.correct_answer_id nil
45
46
  end
46
47
 
47
48
  Factory.define :question_group do |g|
@@ -60,21 +61,21 @@ Factory.sequence(:answer_display_order){|n| n }
60
61
 
61
62
  Factory.define :answer do |a|
62
63
  a.association :question # a.question_id {}
63
- a.text {"My favorite color is clear"}
64
- a.short_text {"clear"}
65
- a.help_text {"Clear is the absense of color"}
66
- a.weight {}
67
- a.response_class {"String"}
68
- a.reference_identifier {}
69
- a.data_export_identifier {}
70
- a.common_namespace {}
71
- a.common_identifier {}
64
+ a.text "My favorite color is clear"
65
+ a.short_text "clear"
66
+ a.help_text "Clear is the absense of color"
67
+ # a.weight
68
+ a.response_class "string"
69
+ # a.reference_identifier {}
70
+ # a.data_export_identifier {}
71
+ # a.common_namespace {}
72
+ # a.common_identifier {}
72
73
  a.display_order {Factory.next :answer_display_order}
73
- a.is_exclusive {}
74
+ # a.is_exclusive {}
74
75
  a.display_type "default"
75
- a.display_length {}
76
- a.custom_class {}
77
- a.custom_renderer {}
76
+ # a.display_length {}
77
+ # a.custom_class {}
78
+ # a.custom_renderer {}
78
79
  end
79
80
 
80
81
  Factory.define :dependency do |d|
@@ -0,0 +1,16 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../lib/surveyor/helpers/formtastic_custom_input')
3
+
4
+ describe Surveyor::Helpers::FormtasticCustomInput do
5
+ context "input helpers" do
6
+ it "should translate response class into attribute" do
7
+ helper.response_class_to_method(:string).should == :string_value
8
+ helper.response_class_to_method(:other_and_string).should == :string_value
9
+ helper.response_class_to_method(:integer).should == :integer_value
10
+ helper.response_class_to_method(:float).should == :float_value
11
+ helper.response_class_to_method(:datetime).should == :datetime_value
12
+ helper.response_class_to_method(:date).should == :datetime_value
13
+ helper.response_class_to_method(:time).should == :datetime_value
14
+ end
15
+ end
16
+ end
@@ -18,45 +18,6 @@ describe Surveyor::Common, "" do
18
18
  "In general, you would say your health is:" => "you_would_say_your_health"
19
19
  }.each{|k, v| Surveyor::Common.to_normalized_string(k).should == v}
20
20
  end
21
- it "should deep compare json objects" do
22
- a = {"a" => "b"}.to_json
23
- b = '{"a": "b"}'
24
- Surveyor::Common.equal_json_excluding_wildcards(a,b).should be_true
25
-
26
- a = {"y" => "x"}.to_json
27
- b = {:y => "x"}
28
- Surveyor::Common.equal_json_excluding_wildcards(a,b).should be_true
29
-
30
- a = [{"y" => "x"}, {"j" => "b"}].to_json
31
- b = '[{"y": "x"}]'
32
- Surveyor::Common.equal_json_excluding_wildcards(a,b).should be_false
33
-
34
- a = [{"y" => "x"}, {"uuid" => "*"}].to_json
35
- b = '[{"y": "x"}, {"uuid": "12312312312123"}]'
36
- Surveyor::Common.equal_json_excluding_wildcards(a,b).should be_true
37
-
38
- a = %({"survey": {
39
- "title":"Simple survey",
40
- "uuid":"72888670-9151-012e-9ec1-00254bc472f4",
41
- "sections":[{
42
- "title":"Basic questions"
43
- }]
44
- }
45
- })
46
- b = %({"survey": {"title": "Simple survey","uuid": "*","sections": [{"title": "Basic questions"}]}})
47
- Surveyor::Common.equal_json_excluding_wildcards(a,b).should be_true
48
-
49
- a = %({"survey": {
50
- "title":"Simple survey",
51
- "uuid":"72888670-9151-012e-9ec1-00254bc472f4",
52
- "sections":[{
53
- "title":"Different"
54
- }]
55
- }
56
- })
57
- b = %({"survey": {"title": "Simple survey","uuid": "*","sections": [{"title": "Basic questions"}]}})
58
- Surveyor::Common.equal_json_excluding_wildcards(a,b).should be_false
59
- end
60
21
  describe '#generate_api_id' do
61
22
  def generate
62
23
  Surveyor::Common.generate_api_id
@@ -20,56 +20,56 @@ describe Surveyor::RedcapParser do
20
20
  end
21
21
  it "should decompose dependency rules" do
22
22
  # basic
23
- Dependency.decompose_rule('[f1_q12]="1"').should == {:rule => "A", :components => ['[f1_q12]="1"']}
23
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[f1_q12]="1"').should == {:rule => "A", :components => ['[f1_q12]="1"']}
24
24
  # spacing
25
- Dependency.decompose_rule('[f1_q9] = "1"').should == {:rule => "A", :components => ['[f1_q9] = "1"']}
25
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[f1_q9] = "1"').should == {:rule => "A", :components => ['[f1_q9] = "1"']}
26
26
  # and
27
- Dependency.decompose_rule('[pre_q88]="1" and [pre_q90]="1"').should == {:rule => "A and B", :components => ['[pre_q88]="1"', '[pre_q90]="1"']}
27
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[pre_q88]="1" and [pre_q90]="1"').should == {:rule => "A and B", :components => ['[pre_q88]="1"', '[pre_q90]="1"']}
28
28
  # or
29
- Dependency.decompose_rule('[second_q111]="1" or [second_q111]="3"').should == {:rule => "A or B", :components => ['[second_q111]="1"', '[second_q111]="3"']}
29
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[second_q111]="1" or [second_q111]="3"').should == {:rule => "A or B", :components => ['[second_q111]="1"', '[second_q111]="3"']}
30
30
  # or and
31
- Dependency.decompose_rule('[second_q100]="1" or [second_q100]="3" and [second_q101]="1"').should == {:rule => "A or B and C", :components => ['[second_q100]="1"', '[second_q100]="3"', '[second_q101]="1"']}
31
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[second_q100]="1" or [second_q100]="3" and [second_q101]="1"').should == {:rule => "A or B and C", :components => ['[second_q100]="1"', '[second_q100]="3"', '[second_q101]="1"']}
32
32
  # and or
33
- Dependency.decompose_rule('[second_q4]="1" and [second_q11]="1" or [second_q11]="98"').should == {:rule => "A and B or C", :components => ['[second_q4]="1"', '[second_q11]="1"', '[second_q11]="98"']}
33
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[second_q4]="1" and [second_q11]="1" or [second_q11]="98"').should == {:rule => "A and B or C", :components => ['[second_q4]="1"', '[second_q11]="1"', '[second_q11]="98"']}
34
34
  # or or or
35
- Dependency.decompose_rule('[pre_q74]="1" or [pre_q74]="2" or [pre_q74]="4" or [pre_q74]="5"').should == {:rule => "A or B or C or D", :components => ['[pre_q74]="1"', '[pre_q74]="2"', '[pre_q74]="4"', '[pre_q74]="5"']}
35
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[pre_q74]="1" or [pre_q74]="2" or [pre_q74]="4" or [pre_q74]="5"').should == {:rule => "A or B or C or D", :components => ['[pre_q74]="1"', '[pre_q74]="2"', '[pre_q74]="4"', '[pre_q74]="5"']}
36
36
  # and with different operator
37
- Dependency.decompose_rule('[f1_q15] >= 21 and [f1_q28] ="1"').should == {:rule => "A and B", :components => ['[f1_q15] >= 21', '[f1_q28] ="1"']}
37
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[f1_q15] >= 21 and [f1_q28] ="1"').should == {:rule => "A and B", :components => ['[f1_q15] >= 21', '[f1_q28] ="1"']}
38
38
  end
39
39
  it "should decompose nested dependency rules" do
40
40
  # external parenthesis
41
- Dependency.decompose_rule('([pre_q74]="1" or [pre_q74]="2" or [pre_q74]="4" or [pre_q74]="5") and [pre_q76]="2"').should == {:rule => "(A or B or C or D) and E", :components => ['[pre_q74]="1"', '[pre_q74]="2"', '[pre_q74]="4"', '[pre_q74]="5"', '[pre_q76]="2"']}
41
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('([pre_q74]="1" or [pre_q74]="2" or [pre_q74]="4" or [pre_q74]="5") and [pre_q76]="2"').should == {:rule => "(A or B or C or D) and E", :components => ['[pre_q74]="1"', '[pre_q74]="2"', '[pre_q74]="4"', '[pre_q74]="5"', '[pre_q76]="2"']}
42
42
  # internal parenthesis
43
- Dependency.decompose_rule('[f1_q10(4)]="1"').should == {:rule => "A", :components => ['[f1_q10(4)]="1"']}
43
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[f1_q10(4)]="1"').should == {:rule => "A", :components => ['[f1_q10(4)]="1"']}
44
44
  # internal and external parenthesis
45
- Dependency.decompose_rule('([f1_q7(11)] = "1" or [initial_52] = "1") and [pre_q76]="2"').should == {:rule => "(A or B) and C", :components => ['[f1_q7(11)] = "1"', '[initial_52] = "1"', '[pre_q76]="2"']}
45
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('([f1_q7(11)] = "1" or [initial_52] = "1") and [pre_q76]="2"').should == {:rule => "(A or B) and C", :components => ['[f1_q7(11)] = "1"', '[initial_52] = "1"', '[pre_q76]="2"']}
46
46
  end
47
47
  it "should decompose shortcut dependency rules" do
48
48
  # 'or' on the right of the operator
49
- Dependency.decompose_rule('[initial_108] = "1" or "2"').should == {:rule => "A or B", :components => ['[initial_108] = "1"', '[initial_108] = "2"']}
49
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[initial_108] = "1" or "2"').should == {:rule => "A or B", :components => ['[initial_108] = "1"', '[initial_108] = "2"']}
50
50
  # multiple 'or' on the right
51
- Dependency.decompose_rule('[initial_52] = "1" or "2" or "3"').should == {:rule => "A or B or C", :components => ['[initial_52] = "1"', '[initial_52] = "2"', '[initial_52] = "3"']}
51
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[initial_52] = "1" or "2" or "3"').should == {:rule => "A or B or C", :components => ['[initial_52] = "1"', '[initial_52] = "2"', '[initial_52] = "3"']}
52
52
  # commas on the right
53
- Dependency.decompose_rule('[initial_189] = "1, 2, 3"').should == {:rule => "(A and B and C)", :components => ['[initial_189] = "1"', '[initial_189] = "2"', '[initial_189] = "3"']}
53
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[initial_189] = "1, 2, 3"').should == {:rule => "(A and B and C)", :components => ['[initial_189] = "1"', '[initial_189] = "2"', '[initial_189] = "3"']}
54
54
  # multiple internal parenthesis on the left
55
- Dependency.decompose_rule('[initial_119(1)(2)(3)(4)(5)] = "1"').should == {:rule => "(A and B and C and D and E)", :components => ['[initial_119(1)] = "1"', '[initial_119(2)] = "1"', '[initial_119(3)] = "1"', '[initial_119(4)] = "1"', '[initial_119(5)] = "1"']}
55
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[initial_119(1)(2)(3)(4)(5)] = "1"').should == {:rule => "(A and B and C and D and E)", :components => ['[initial_119(1)] = "1"', '[initial_119(2)] = "1"', '[initial_119(3)] = "1"', '[initial_119(4)] = "1"', '[initial_119(5)] = "1"']}
56
56
  end
57
57
  it "should decompose components" do
58
- Dependency.decompose_component('[initial_52] = "1"').should == {:question_reference => 'initial_52', :operator => '==', :answer_reference => '1'}
59
- Dependency.decompose_component('[initial_119(2)] = "1"').should == {:question_reference => 'initial_119', :operator => '==', :answer_reference => '2'}
60
- Dependency.decompose_component('[f1_q15] >= 21').should == {:question_reference => 'f1_q15', :operator => '>=', :integer_value => '21'}
58
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component('[initial_52] = "1"').should == {:question_reference => 'initial_52', :operator => '==', :answer_reference => '1'}
59
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component('[initial_119(2)] = "1"').should == {:question_reference => 'initial_119', :operator => '==', :answer_reference => '2'}
60
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component('[f1_q15] >= 21').should == {:question_reference => 'f1_q15', :operator => '>=', :integer_value => '21'}
61
61
  # basic, blanks
62
- Dependency.decompose_component("[f1_q15]=''").should == {:question_reference => 'f1_q15', :operator => '==', :answer_reference => ''}
62
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component("[f1_q15]=''").should == {:question_reference => 'f1_q15', :operator => '==', :answer_reference => ''}
63
63
  # basic, negatives
64
- Dependency.decompose_component("[f1_q15]='-2'").should == {:question_reference => 'f1_q15', :operator => '==', :answer_reference => '-2'}
64
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component("[f1_q15]='-2'").should == {:question_reference => 'f1_q15', :operator => '==', :answer_reference => '-2'}
65
65
  # internal parenthesis
66
- Dependency.decompose_component("[hiprep_heat2(97)] = '1'").should == {:question_reference => 'hiprep_heat2', :operator => '==', :answer_reference => '97'}
67
- Dependency.decompose_component("[hi_event1_type] <> ''").should == {:question_reference => 'hi_event1_type', :operator => '!=', :answer_reference => ''}
68
-
66
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component("[hiprep_heat2(97)] = '1'").should == {:question_reference => 'hiprep_heat2', :operator => '==', :answer_reference => '97'}
67
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component("[hi_event1_type] <> ''").should == {:question_reference => 'hi_event1_type', :operator => '!=', :answer_reference => ''}
68
+
69
69
  end
70
70
  it "should return a survey object" do
71
71
  x = %("Variable / Field Name","Form Name","Field Units","Section Header","Field Type","Field Label","Choices OR Calculations","Field Note","Text Validation Type","Text Validation Min","Text Validation Max",Identifier?,"Branching Logic (Show field only if...)","Required Field?"\nstudy_id,demographics,,,text,"Study ID",,,,,,,,)
72
72
  Surveyor::RedcapParser.new.parse(x, "redcaptest").is_a?(Survey).should be_true
73
73
  end
74
-
74
+
75
75
  end
@@ -42,6 +42,18 @@ describe Answer, "when creating a new answer" do
42
42
  @answer.api_id.length.should == 36
43
43
  end
44
44
 
45
+ it "should protect api_id, timestamps" do
46
+ saved_attrs = @answer.attributes
47
+ if defined? ActiveModel::MassAssignmentSecurity::Error
48
+ lambda {@answer.update_attributes(:created_at => 3.days.ago, :updated_at => 3.hours.ago)}.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
49
+ lambda {@answer.update_attributes(:api_id => "NEW")}.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
50
+ else
51
+ @answer.attributes = {:created_at => 3.days.ago, :updated_at => 3.hours.ago} # automatically protected by Rails
52
+ @answer.attributes = {:api_id => "NEW"} # Rails doesn't return false, but this will be checked in the comparison to saved_attrs
53
+ end
54
+ @answer.attributes.should == saved_attrs
55
+ end
56
+
45
57
  require 'mustache'
46
58
  class FakeMustacheContext < ::Mustache
47
59
  def site