affectiva-surveyor 1.5.0.pre.disco.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (212) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +28 -0
  3. data/.rspec +1 -0
  4. data/CHANGELOG.md +198 -0
  5. data/Gemfile +5 -0
  6. data/Gemfile.rails_version +27 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +166 -0
  9. data/Rakefile +106 -0
  10. data/app/controllers/surveyor_controller.rb +5 -0
  11. data/app/helpers/results_helper.rb +20 -0
  12. data/app/helpers/survey_form_builder.rb +37 -0
  13. data/app/helpers/surveyor_helper.rb +3 -0
  14. data/app/inputs/quiet_input.rb +5 -0
  15. data/app/inputs/surveyor_check_boxes_input.rb +35 -0
  16. data/app/inputs/surveyor_radio_input.rb +18 -0
  17. data/app/models/answer.rb +3 -0
  18. data/app/models/dependency.rb +3 -0
  19. data/app/models/dependency_condition.rb +3 -0
  20. data/app/models/question.rb +3 -0
  21. data/app/models/question_group.rb +4 -0
  22. data/app/models/response.rb +4 -0
  23. data/app/models/response_set.rb +3 -0
  24. data/app/models/survey.rb +3 -0
  25. data/app/models/survey_section.rb +4 -0
  26. data/app/models/survey_section_sweeper.rb +15 -0
  27. data/app/models/survey_translation.rb +4 -0
  28. data/app/models/validation.rb +3 -0
  29. data/app/models/validation_condition.rb +3 -0
  30. data/app/views/layouts/results.html.erb +13 -0
  31. data/app/views/layouts/surveyor_default.html.erb +12 -0
  32. data/app/views/partials/_answer.html.haml +25 -0
  33. data/app/views/partials/_dependents.html.haml +5 -0
  34. data/app/views/partials/_question.html.haml +28 -0
  35. data/app/views/partials/_question_group.html.haml +44 -0
  36. data/app/views/partials/_section.html.haml +12 -0
  37. data/app/views/partials/_section_menu.html.haml +12 -0
  38. data/app/views/surveyor/edit.html.haml +24 -0
  39. data/app/views/surveyor/export.json.rabl +85 -0
  40. data/app/views/surveyor/new.html.haml +24 -0
  41. data/app/views/surveyor/show.html.haml +74 -0
  42. data/app/views/surveyor/show.json.rabl +15 -0
  43. data/ci-exec.sh +56 -0
  44. data/config/routes.rb +8 -0
  45. data/cucumber.yml +10 -0
  46. data/doc/REPRESENTATIONS.md +34 -0
  47. data/doc/api_id_schema.json +7 -0
  48. data/doc/question types.png +0 -0
  49. data/doc/response_set_schema.json +54 -0
  50. data/doc/surveyor question combinations.png +0 -0
  51. data/doc/surveyor reject or delete decision matrix.png +0 -0
  52. data/doc/surveyor_models.png +0 -0
  53. data/doc/surveyor_models2.png +0 -0
  54. data/doc/surveyor_timestamp_schema.json +9 -0
  55. data/features/ajax_submissions.feature +140 -0
  56. data/features/export_to_json.feature +344 -0
  57. data/features/internationalization.feature +121 -0
  58. data/features/no_duplicates.feature +110 -0
  59. data/features/show_survey.feature +71 -0
  60. data/features/step_definitions/parser_steps.rb +145 -0
  61. data/features/step_definitions/surveyor_steps.rb +325 -0
  62. data/features/step_definitions/ui_steps.rb +25 -0
  63. data/features/step_definitions/web_steps.rb +225 -0
  64. data/features/support/REDCapDemoDatabase_DataDictionary.csv +127 -0
  65. data/features/support/database_cleaner.rb +16 -0
  66. data/features/support/env.rb +56 -0
  67. data/features/support/hooks.rb +4 -0
  68. data/features/support/paths.rb +39 -0
  69. data/features/support/redcap_new_headers.csv +1 -0
  70. data/features/support/redcap_siblings.csv +1 -0
  71. data/features/support/redcap_whitespace.csv +1 -0
  72. data/features/support/selectors.rb +39 -0
  73. data/features/support/simultaneous_ajax.rb +101 -0
  74. data/features/support/single_quit_selenium_driver.rb +23 -0
  75. data/features/support/slow_updates.rb +18 -0
  76. data/features/surveyor.feature +895 -0
  77. data/features/surveyor_dependencies.feature +476 -0
  78. data/features/surveyor_parser.feature +504 -0
  79. data/features/z_redcap_parser.feature +62 -0
  80. data/lib/assets/images/surveyor/next.gif +0 -0
  81. data/lib/assets/images/surveyor/prev.gif +0 -0
  82. data/lib/assets/images/surveyor/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  83. data/lib/assets/images/surveyor/ui-bg_flat_75_ffffff_40x100.png +0 -0
  84. data/lib/assets/images/surveyor/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  85. data/lib/assets/images/surveyor/ui-bg_glass_65_ffffff_1x400.png +0 -0
  86. data/lib/assets/images/surveyor/ui-bg_glass_75_dadada_1x400.png +0 -0
  87. data/lib/assets/images/surveyor/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  88. data/lib/assets/images/surveyor/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  89. data/lib/assets/images/surveyor/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  90. data/lib/assets/images/surveyor/ui-icons_222222_256x240.png +0 -0
  91. data/lib/assets/images/surveyor/ui-icons_2e83ff_256x240.png +0 -0
  92. data/lib/assets/images/surveyor/ui-icons_454545_256x240.png +0 -0
  93. data/lib/assets/images/surveyor/ui-icons_888888_256x240.png +0 -0
  94. data/lib/assets/images/surveyor/ui-icons_cd0a0a_256x240.png +0 -0
  95. data/lib/assets/javascripts/surveyor/jquery-1.9.0.js +9555 -0
  96. data/lib/assets/javascripts/surveyor/jquery-ui-1.10.0.custom.js +14850 -0
  97. data/lib/assets/javascripts/surveyor/jquery-ui-timepicker-addon.js +1919 -0
  98. data/lib/assets/javascripts/surveyor/jquery.maskedinput.js +338 -0
  99. data/lib/assets/javascripts/surveyor/jquery.selectToUISlider.js +240 -0
  100. data/lib/assets/javascripts/surveyor/jquery.surveyor.js +154 -0
  101. data/lib/assets/stylesheets/surveyor.sass +132 -0
  102. data/lib/assets/stylesheets/surveyor/jquery-ui-1.10.0.custom.css +1174 -0
  103. data/lib/assets/stylesheets/surveyor/jquery-ui-timepicker-addon.css +11 -0
  104. data/lib/assets/stylesheets/surveyor/reset.css +48 -0
  105. data/lib/assets/stylesheets/surveyor/results.css +125 -0
  106. data/lib/assets/stylesheets/surveyor/ui.slider.extras.css +110 -0
  107. data/lib/generators/surveyor/custom_generator.rb +18 -0
  108. data/lib/generators/surveyor/install_generator.rb +101 -0
  109. data/lib/generators/surveyor/templates/app/assets/javascripts/surveyor_all.js +6 -0
  110. data/lib/generators/surveyor/templates/app/assets/stylesheets/surveyor_all.css +9 -0
  111. data/lib/generators/surveyor/templates/app/controllers/surveyor_controller.rb +40 -0
  112. data/lib/generators/surveyor/templates/app/views/layouts/surveyor_custom.html.erb +13 -0
  113. data/lib/generators/surveyor/templates/config/locales/surveyor_en.yml +19 -0
  114. data/lib/generators/surveyor/templates/config/locales/surveyor_es.yml +19 -0
  115. data/lib/generators/surveyor/templates/config/locales/surveyor_he.yml +19 -0
  116. data/lib/generators/surveyor/templates/config/locales/surveyor_ko.yml +19 -0
  117. data/lib/generators/surveyor/templates/db/migrate/add_api_id_to_question_groups.rb +10 -0
  118. data/lib/generators/surveyor/templates/db/migrate/add_api_ids.rb +14 -0
  119. data/lib/generators/surveyor/templates/db/migrate/add_api_ids_to_response_sets_and_responses.rb +12 -0
  120. data/lib/generators/surveyor/templates/db/migrate/add_correct_answer_id_to_questions.rb +10 -0
  121. data/lib/generators/surveyor/templates/db/migrate/add_default_value_to_answers.rb +10 -0
  122. data/lib/generators/surveyor/templates/db/migrate/add_display_order_to_surveys.rb +10 -0
  123. data/lib/generators/surveyor/templates/db/migrate/add_display_type_to_answers.rb +14 -0
  124. data/lib/generators/surveyor/templates/db/migrate/add_index_to_response_sets.rb +10 -0
  125. data/lib/generators/surveyor/templates/db/migrate/add_index_to_surveys.rb +10 -0
  126. data/lib/generators/surveyor/templates/db/migrate/add_input_mask_attributes_to_answer.rb +12 -0
  127. data/lib/generators/surveyor/templates/db/migrate/add_section_id_to_responses.rb +12 -0
  128. data/lib/generators/surveyor/templates/db/migrate/add_unique_index_on_access_code_and_version_in_surveys.rb +10 -0
  129. data/lib/generators/surveyor/templates/db/migrate/add_unique_indicies.rb +18 -0
  130. data/lib/generators/surveyor/templates/db/migrate/add_version_to_surveys.rb +10 -0
  131. data/lib/generators/surveyor/templates/db/migrate/api_ids_must_be_unique.rb +23 -0
  132. data/lib/generators/surveyor/templates/db/migrate/create_answers.rb +38 -0
  133. data/lib/generators/surveyor/templates/db/migrate/create_dependencies.rb +23 -0
  134. data/lib/generators/surveyor/templates/db/migrate/create_dependency_conditions.rb +30 -0
  135. data/lib/generators/surveyor/templates/db/migrate/create_question_groups.rb +28 -0
  136. data/lib/generators/surveyor/templates/db/migrate/create_questions.rb +37 -0
  137. data/lib/generators/surveyor/templates/db/migrate/create_response_sets.rb +23 -0
  138. data/lib/generators/surveyor/templates/db/migrate/create_responses.rb +34 -0
  139. data/lib/generators/surveyor/templates/db/migrate/create_survey_sections.rb +30 -0
  140. data/lib/generators/surveyor/templates/db/migrate/create_survey_translations.rb +19 -0
  141. data/lib/generators/surveyor/templates/db/migrate/create_surveys.rb +32 -0
  142. data/lib/generators/surveyor/templates/db/migrate/create_validation_conditions.rb +33 -0
  143. data/lib/generators/surveyor/templates/db/migrate/create_validations.rb +21 -0
  144. data/lib/generators/surveyor/templates/db/migrate/drop_unique_index_on_access_code_in_surveys.rb +10 -0
  145. data/lib/generators/surveyor/templates/db/migrate/update_blank_api_ids_on_question_group.rb +22 -0
  146. data/lib/generators/surveyor/templates/db/migrate/update_blank_versions_on_surveys.rb +13 -0
  147. data/lib/generators/surveyor/templates/surveys/EXTENDING_SURVEYOR.md +52 -0
  148. data/lib/generators/surveyor/templates/surveys/MODIFYING_SURVEYOR.md +91 -0
  149. data/lib/generators/surveyor/templates/surveys/date_survey.rb +16 -0
  150. data/lib/generators/surveyor/templates/surveys/kitchen_sink_survey.rb +280 -0
  151. data/lib/generators/surveyor/templates/surveys/languages.rb +14 -0
  152. data/lib/generators/surveyor/templates/surveys/quiz.rb +11 -0
  153. data/lib/generators/surveyor/templates/surveys/translations/languages.es.yml +18 -0
  154. data/lib/generators/surveyor/templates/surveys/translations/languages.he.yml +18 -0
  155. data/lib/generators/surveyor/templates/surveys/translations/languages.ko.yml +18 -0
  156. data/lib/generators/surveyor/templates/vendor/assets/stylesheets/custom.sass +5 -0
  157. data/lib/surveyor.rb +13 -0
  158. data/lib/surveyor/acts_as_response.rb +17 -0
  159. data/lib/surveyor/common.rb +59 -0
  160. data/lib/surveyor/engine.rb +10 -0
  161. data/lib/surveyor/helpers/asset_pipeline.rb +18 -0
  162. data/lib/surveyor/helpers/formtastic_custom_input.rb +13 -0
  163. data/lib/surveyor/helpers/surveyor_helper_methods.rb +103 -0
  164. data/lib/surveyor/models/answer_methods.rb +83 -0
  165. data/lib/surveyor/models/dependency_condition_methods.rb +69 -0
  166. data/lib/surveyor/models/dependency_methods.rb +57 -0
  167. data/lib/surveyor/models/question_group_methods.rb +58 -0
  168. data/lib/surveyor/models/question_methods.rb +112 -0
  169. data/lib/surveyor/models/response_methods.rb +126 -0
  170. data/lib/surveyor/models/response_set_methods.rb +188 -0
  171. data/lib/surveyor/models/survey_methods.rb +100 -0
  172. data/lib/surveyor/models/survey_section_methods.rb +53 -0
  173. data/lib/surveyor/models/survey_translation_methods.rb +30 -0
  174. data/lib/surveyor/models/validation_condition_methods.rb +54 -0
  175. data/lib/surveyor/models/validation_methods.rb +45 -0
  176. data/lib/surveyor/mustache_context.rb +11 -0
  177. data/lib/surveyor/parser.rb +427 -0
  178. data/lib/surveyor/redcap_parser.rb +288 -0
  179. data/lib/surveyor/surveyor_controller_methods.rb +236 -0
  180. data/lib/surveyor/unparser.rb +147 -0
  181. data/lib/surveyor/version.rb +3 -0
  182. data/lib/tasks/surveyor_tasks.rake +88 -0
  183. data/rails/init.rb +1 -0
  184. data/spec/controllers/surveyor_controller_spec.rb +311 -0
  185. data/spec/factories.rb +166 -0
  186. data/spec/helpers/formtastic_custom_input_spec.rb +15 -0
  187. data/spec/helpers/surveyor_helper_spec.rb +117 -0
  188. data/spec/lib/benchmark_spec.rb +22 -0
  189. data/spec/lib/chinese_survey.rb +14 -0
  190. data/spec/lib/common_spec.rb +34 -0
  191. data/spec/lib/parser_spec.rb +204 -0
  192. data/spec/lib/rake_kitchen_sink.rb +40 -0
  193. data/spec/lib/redcap_parser_spec.rb +75 -0
  194. data/spec/lib/tasks_spec.rake +26 -0
  195. data/spec/lib/unparser_spec.rb +126 -0
  196. data/spec/models/answer_spec.rb +174 -0
  197. data/spec/models/dependency_condition_spec.rb +439 -0
  198. data/spec/models/dependency_spec.rb +101 -0
  199. data/spec/models/question_group_spec.rb +93 -0
  200. data/spec/models/question_spec.rb +206 -0
  201. data/spec/models/response_set_spec.rb +477 -0
  202. data/spec/models/response_spec.rb +218 -0
  203. data/spec/models/survey_section_spec.rb +85 -0
  204. data/spec/models/survey_spec.rb +191 -0
  205. data/spec/models/validation_condition_spec.rb +113 -0
  206. data/spec/models/validation_spec.rb +74 -0
  207. data/spec/rcov.opts +2 -0
  208. data/spec/spec.opts +4 -0
  209. data/spec/spec_helper.rb +56 -0
  210. data/stacktests.sh +65 -0
  211. data/surveyor.gemspec +53 -0
  212. metadata +601 -0
@@ -0,0 +1,40 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'rake'
3
+
4
+ describe Surveyor::Parser do
5
+ before do
6
+ @rake = Rake::Application.new
7
+ Rake.application = @rake
8
+ Rake.application.rake_require "lib/tasks/surveyor_tasks"
9
+ Rake::Task.define_task(:environment)
10
+ end
11
+ it "should return properly parse the kitchen sink survey" do
12
+ ENV["FILE"]="surveys/kitchen_sink_survey.rb"
13
+ @rake["surveyor"].invoke
14
+
15
+ Survey.count.should == 1
16
+ SurveySection.count.should == 2
17
+ Question.count.should == 44
18
+ Answer.count.should == 241
19
+ Dependency.count.should == 6
20
+ DependencyCondition.count.should == 9
21
+ QuestionGroup.count.should == 6
22
+
23
+ Survey.all.map(&:destroy)
24
+ end
25
+ it "should return properly parse a UTF8 survey" do
26
+ ENV["FILE"]="../spec/lib/chinese_survey.rb"
27
+ @rake["surveyor"].invoke
28
+
29
+ Survey.count.should == 1
30
+ SurveySection.count.should == 1
31
+ Question.count.should == 3
32
+ Answer.count.should == 15
33
+ Dependency.count.should == 0
34
+ DependencyCondition.count.should == 0
35
+ QuestionGroup.count.should == 1
36
+
37
+ Survey.all.map(&:destroy)
38
+ end
39
+
40
+ end
@@ -0,0 +1,75 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Surveyor::RedcapParser do
4
+ before(:each) do
5
+ # @parser = Surveyor::Parser.new
6
+ end
7
+ it "should require specific columns" do
8
+ # with standard fields
9
+ x = %w(field_units choices_or_calculations text_validation_type variable__field_name form_name section_header field_type field_label field_note text_validation_min text_validation_max identifier branching_logic_show_field_only_if required_field)
10
+ Surveyor::RedcapParser.new.missing_columns(x).should be_blank
11
+ # without field_units
12
+ y = %w(choices_or_calculations text_validation_type variable__field_name form_name section_header field_type field_label field_note text_validation_min text_validation_max identifier branching_logic_show_field_only_if required_field)
13
+ Surveyor::RedcapParser.new.missing_columns(y).should be_blank
14
+ # choices_or_calculations => choices_calculations_or_slider_labels
15
+ z = %w(field_units choices_calculations_or_slider_labels text_validation_type variable__field_name form_name section_header field_type field_label field_note text_validation_min text_validation_max identifier branching_logic_show_field_only_if required_field)
16
+ Surveyor::RedcapParser.new.missing_columns(z).should be_blank
17
+ # text_validation_type => text_validation_type_or_show_slider_number
18
+ a = %w(field_units choices_or_calculations text_validation_type_or_show_slider_number variable__field_name form_name section_header field_type field_label field_note text_validation_min text_validation_max identifier branching_logic_show_field_only_if required_field)
19
+ Surveyor::RedcapParser.new.missing_columns(a).should be_blank
20
+ end
21
+ it "should decompose dependency rules" do
22
+ # basic
23
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[f1_q12]="1"').should == {:rule => "A", :components => ['[f1_q12]="1"']}
24
+ # spacing
25
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[f1_q9] = "1"').should == {:rule => "A", :components => ['[f1_q9] = "1"']}
26
+ # and
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
+ # or
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
+ # or and
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
+ # and or
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
+ # or or or
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
+ # and with different operator
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
+ end
39
+ it "should decompose nested dependency rules" do
40
+ # external parenthesis
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
+ # internal parenthesis
43
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[f1_q10(4)]="1"').should == {:rule => "A", :components => ['[f1_q10(4)]="1"']}
44
+ # internal and external parenthesis
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
+ end
47
+ it "should decompose shortcut dependency rules" do
48
+ # 'or' on the right of the operator
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
+ # multiple 'or' on the right
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
+ # commas on the right
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
+ # multiple internal parenthesis on the left
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
+ end
57
+ it "should decompose components" do
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
+ # basic, blanks
62
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component("[f1_q15]=''").should == {:question_reference => 'f1_q15', :operator => '==', :answer_reference => ''}
63
+ # basic, negatives
64
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component("[f1_q15]='-2'").should == {:question_reference => 'f1_q15', :operator => '==', :answer_reference => '-2'}
65
+ # internal parenthesis
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
+ end
70
+ it "should return a survey object" do
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
+ Surveyor::RedcapParser.new.parse(x, "redcaptest").is_a?(Survey).should be_true
73
+ end
74
+
75
+ end
@@ -0,0 +1,26 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require "rake"
3
+
4
+ describe "app rake tasks" do
5
+ before(:all) do # do this only once before all tests
6
+ @rake = Rake::Application.new
7
+ Rake.application = @rake
8
+ Rake.application.rake_require "lib/tasks/surveyor_tasks"
9
+ Rake::Task.define_task(:environment)
10
+ end
11
+ after do # after every test
12
+ @rake[@task_name].reenable if @task_name
13
+ end
14
+
15
+ it "should have 'environment' as a prereq" do
16
+ @task_name = "surveyor:parse"
17
+ @rake[@task_name].prerequisites.should include("environment")
18
+ end
19
+ it "should should trace" do
20
+ ENV["FILE"]="surveys/kitchen_sink_survey.rb"
21
+ @task_name = "surveyor:parse"
22
+ @rake.options.trace = true
23
+ Surveyor::Parser.should_receive(:parse).with(File.read(File.join(Rails.root, ENV["FILE"])), {:trace => true})
24
+ @rake[@task_name].invoke
25
+ end
26
+ end
@@ -0,0 +1,126 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Surveyor::Unparser do
4
+ before(:each) do
5
+ @survey = Survey.new(:title => "Simple survey", :description => "very simple")
6
+ @section = @survey.sections.build(:title => "Simple section")
7
+ end
8
+
9
+ it "should unparse a basic survey, section, and question" do
10
+ q1 = @section.questions.build(:text => "What is your favorite color?", :reference_identifier => 1, :pick => :one)
11
+ a11 = q1.answers.build(:text => "red", :response_class => "answer", :reference_identifier => 1, :question => q1)
12
+ a12 = q1.answers.build(:text => "green", :response_class => "answer", :reference_identifier => 2, :question => q1)
13
+ a13 = q1.answers.build(:text => "blue", :response_class => "answer", :reference_identifier => 3, :question => q1)
14
+ a14 = q1.answers.build(:text => "Other", :response_class => "string", :reference_identifier => 4, :question => q1)
15
+ a15 = q1.answers.build(:text => "Omit", :reference_identifier => 5, :question => q1, :is_exclusive => true)
16
+ q2 = @section.questions.build(:text => "What is your name?", :reference_identifier => 2, :pick => :none)
17
+ a21 = q2.answers.build(:response_class => "string", :reference_identifier => 1, :question => q2)
18
+ Surveyor::Unparser.unparse(@survey).should ==
19
+ <<-dsl
20
+ survey "Simple survey", :description=>"very simple" do
21
+ section "Simple section" do
22
+
23
+ q_1 "What is your favorite color?", :pick=>"one"
24
+ a_1 "red"
25
+ a_2 "green"
26
+ a_3 "blue"
27
+ a_4 :other, :string
28
+ a_5 :omit
29
+
30
+ q_2 "What is your name?"
31
+ a_1 :string
32
+ end
33
+ end
34
+ dsl
35
+ end
36
+
37
+ it "should unparse groups" do
38
+ q3 = @section.questions.build(:text => "Happy?")
39
+ a31 = q3.answers.build(:text => "Yes", :question => q3)
40
+ a32 = q3.answers.build(:text => "Maybe", :question => q3)
41
+ a33 = q3.answers.build(:text => "No", :question => q3)
42
+
43
+ q4 = @section.questions.build(:text => "Energized?")
44
+ a41 = q4.answers.build(:text => "Yes", :question => q4)
45
+ a42 = q4.answers.build(:text => "Maybe", :question => q4)
46
+ a43 = q4.answers.build(:text => "No", :question => q4)
47
+
48
+ g1 = q3.build_question_group(:text => "How are you feeling?", :display_type => "grid")
49
+ q4.question_group = g1
50
+ g1.questions = [q3, q4]
51
+
52
+ q5 = @section.questions.build(:text => "Model")
53
+ a51 = q5.answers.build(:response_class => "string", :question => q3)
54
+
55
+ g2 = q5.build_question_group(:text => "Tell us about the cars you own", :display_type => "repeater")
56
+ g2.questions = [q5]
57
+
58
+ Surveyor::Unparser.unparse(@survey).should ==
59
+ <<-dsl
60
+ survey "Simple survey", :description=>"very simple" do
61
+ section "Simple section" do
62
+
63
+ grid "How are you feeling?" do
64
+ a "Yes"
65
+ a "Maybe"
66
+ a "No"
67
+ q "Happy?"
68
+ q "Energized?"
69
+ end
70
+
71
+ repeater "Tell us about the cars you own" do
72
+ q "Model"
73
+ a :string
74
+ end
75
+ end
76
+ end
77
+ dsl
78
+ end
79
+
80
+ it "should unparse a basic survey, section, and question" do
81
+ q6 = @section.questions.build(:text => "What... is your name? (e.g. It is 'Arthur', King of the Britons)", :reference_identifier => "montypython3")
82
+ a61 = q6.answers.build(:response_class => "string", :reference_identifier => 1, :question => q6)
83
+
84
+ q7 = @section.questions.build(:text => "What... is your quest? (e.g. To seek the Holy Grail)", :display_type => "label")
85
+ d1 = q7.build_dependency(:rule => "A", :question => q7)
86
+ dc1 = d1.dependency_conditions.build(:dependency => d1, :question => q6, :answer => a61, :operator => "==", :string_value => "It is 'Arthur', King of the Britons", :rule_key => "A")
87
+
88
+ q8 = @section.questions.build(:text => "How many pets do you own?")
89
+ a81 = q8.answers.build(:response_class => "integer", :question => q8)
90
+ v1 = a81.validations.build(:rule => "A", :answer => a81)
91
+ vc1 = v1.validation_conditions.build(:operator => ">=", :integer_value => 0, :validation => v1, :rule_key => "A")
92
+
93
+ q9 = @section.questions.build(:text => "Pick your favorite date AND time", :custom_renderer => "/partials/custom_question")
94
+ a91 = q9.answers.build(:response_class => "datetime", :question => q9)
95
+
96
+ q10 = @section.questions.build(:text => "What time do you usually take a lunch break?", :reference_identifier => "time_lunch")
97
+ a101 = q10.answers.build(:response_class => "time", :reference_identifier => 1, :question => q10)
98
+
99
+ Surveyor::Unparser.unparse(@survey).should ==
100
+ <<-dsl
101
+ survey "Simple survey", :description=>"very simple" do
102
+ section "Simple section" do
103
+
104
+ q_montypython3 "What... is your name? (e.g. It is 'Arthur', King of the Britons)"
105
+ a_1 :string
106
+
107
+ label "What... is your quest? (e.g. To seek the Holy Grail)"
108
+ dependency :rule=>"A"
109
+ condition_A :q_montypython3, "==", {:string_value=>"It is 'Arthur', King of the Britons", :answer_reference=>"1"}
110
+
111
+ q "How many pets do you own?"
112
+ a :integer
113
+ validation :rule=>"A"
114
+ condition_A ">=", :integer_value=>0
115
+
116
+ q "Pick your favorite date AND time", :custom_renderer=>"/partials/custom_question"
117
+ a :datetime
118
+
119
+ q_time_lunch "What time do you usually take a lunch break?"
120
+ a_1 :time
121
+ end
122
+ end
123
+ dsl
124
+ end
125
+ end
126
+
@@ -0,0 +1,174 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
3
+
4
+ describe Answer do
5
+ let(:answer){ FactoryGirl.create(:answer) }
6
+
7
+ context "when creating" do
8
+ it { answer.should be_valid }
9
+ it "deletes validation when deleted" do
10
+ v_id = FactoryGirl.create(:validation, :answer => answer).id
11
+ answer.destroy
12
+ Validation.find_by_id(v_id).should be_nil
13
+ end
14
+ it "protects #api_id" do
15
+ saved_attrs = answer.attributes
16
+ if defined? ActiveModel::MassAssignmentSecurity::Error
17
+ expect { answer.update_attributes(:api_id => "NEW") }.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
18
+ else
19
+ answer.attributes = {:api_id => "NEW"} # Rails doesn't return false, but this will be checked in the comparison to saved_attrs
20
+ end
21
+ answer.attributes.should == saved_attrs
22
+ end
23
+ it "protects #created_at" do
24
+ saved_attrs = answer.attributes
25
+ if defined? ActiveModel::MassAssignmentSecurity::Error
26
+ expect { answer.update_attributes(:created_at => 3.days.ago) }.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
27
+ else
28
+ answer.attributes = {:created_at => 3.days.ago} # Rails doesn't return false, but this will be checked in the comparison to saved_attrs
29
+ end
30
+ answer.attributes.should == saved_attrs
31
+ end
32
+ it "protects #updated_at" do
33
+ saved_attrs = answer.attributes
34
+ if defined? ActiveModel::MassAssignmentSecurity::Error
35
+ expect { answer.update_attributes(:updated_at => 3.hours.ago) }.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
36
+ else
37
+ answer.attributes = {:updated_at => 3.hours.ago} # Rails doesn't return false, but this will be checked in the comparison to saved_attrs
38
+ end
39
+ answer.attributes.should == saved_attrs
40
+ end
41
+ end
42
+
43
+ context "with mustache text substitution" do
44
+ require 'mustache'
45
+ let(:mustache_context){ Class.new(::Mustache){ def site; "Northwestern"; end; def foo; "bar"; end } }
46
+ it "subsitutes Mustache context variables" do
47
+ answer.text = "You are in {{site}}"
48
+ answer.in_context(answer.text, mustache_context).should == "You are in Northwestern"
49
+ answer.text_for(nil, mustache_context).should == "You are in Northwestern"
50
+
51
+ answer.help_text = "{{site}} is your site"
52
+ answer.in_context(answer.help_text, mustache_context).should == "Northwestern is your site"
53
+ answer.help_text_for(mustache_context).should == "Northwestern is your site"
54
+
55
+ answer.default_value = "{{site}}"
56
+ answer.in_context(answer.default_value, mustache_context).should == "Northwestern"
57
+ answer.default_value_for(mustache_context).should == "Northwestern"
58
+ end
59
+ end
60
+
61
+ context "with translations" do
62
+ require 'yaml'
63
+ let(:survey){ FactoryGirl.create(:survey) }
64
+ let(:survey_section){ FactoryGirl.create(:survey_section) }
65
+ let(:survey_translation){
66
+ FactoryGirl.create(:survey_translation, :locale => :es, :translation => {
67
+ :questions => {
68
+ :name => {
69
+ :answers => {
70
+ :name => {
71
+ :help_text => "Mi nombre es..."
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }.to_yaml)
77
+ }
78
+ let(:question){ FactoryGirl.create(:question, :reference_identifier => "name") }
79
+ before do
80
+ answer.reference_identifier = "name"
81
+ answer.help_text = "My name is..."
82
+ answer.text = nil
83
+ answer.question = question
84
+ question.survey_section = survey_section
85
+ survey_section.survey = survey
86
+ survey.translations << survey_translation
87
+ end
88
+ it "returns its own translation" do
89
+ answer.translation(:es)[:help_text].should == "Mi nombre es..."
90
+ end
91
+ it "returns translations in views" do
92
+ answer.help_text_for(nil, :es).should == "Mi nombre es..."
93
+ end
94
+ it "returns its own default values" do
95
+ answer.translation(:de).should == {"text" => nil, "help_text" => "My name is...", "default_value" => nil}
96
+ end
97
+ it "returns default values in views" do
98
+ answer.help_text_for(nil, :de).should == "My name is..."
99
+ end
100
+ end
101
+
102
+ context "handling strings" do
103
+ it "#split preserves strings" do
104
+ answer.split(answer.text).should == "My favorite color is clear"
105
+ end
106
+ it "#split(:pre) preserves strings" do
107
+ answer.split(answer.text, :pre).should == "My favorite color is clear"
108
+ end
109
+ it "#split(:post) preserves strings" do
110
+ answer.split(answer.text, :post).should == ""
111
+ end
112
+ it "#split splits strings" do
113
+ answer.text = "before|after|extra"
114
+ answer.split(answer.text).should == "before|after|extra"
115
+ end
116
+ it "#split(:pre) splits strings" do
117
+ answer.text = "before|after|extra"
118
+ answer.split(answer.text, :pre).should == "before"
119
+ end
120
+ it "#split(:post) splits strings" do
121
+ answer.text = "before|after|extra"
122
+ answer.split(answer.text, :post).should == "after|extra"
123
+ end
124
+ end
125
+
126
+ context "for views" do
127
+ before do
128
+ ActionController::Base.helpers.config.assets_dir = "public" unless asset_pipeline_enabled?
129
+ end
130
+ it "#text_for with #display_type == image" do
131
+ answer.text = "rails.png"
132
+ answer.display_type = :image
133
+ answer.text_for.should == %(<img alt="Rails" src="/images/rails.png" />)
134
+ end
135
+ it "#text_for with #display_type == hidden_label" do
136
+ answer.text = "Red"
137
+ answer.text_for.should == "Red"
138
+ answer.display_type = "hidden_label"
139
+ answer.text_for.should == ""
140
+ end
141
+ it "#default_value_for"
142
+ it "#help_text_for"
143
+ it "reports DOM ready #css_class from #custom_class" do
144
+ answer.custom_class = "foo bar"
145
+ answer.css_class.should == "foo bar"
146
+ end
147
+ it "reports DOM ready #css_class from #custom_class and #is_exclusive" do
148
+ answer.custom_class = "foo bar"
149
+ answer.is_exclusive = true
150
+ answer.css_class.should == "exclusive foo bar"
151
+ end
152
+ it "#text_for preserves strings" do
153
+ answer.text_for.should == "My favorite color is clear"
154
+ end
155
+ it "#text_for(:pre) preserves strings" do
156
+ answer.text_for(:pre).should == "My favorite color is clear"
157
+ end
158
+ it "#text_for(:post) preserves strings" do
159
+ answer.text_for(:post).should == ""
160
+ end
161
+ it "#text_for splits strings" do
162
+ answer.text = "before|after|extra"
163
+ answer.text_for.should == "before|after|extra"
164
+ end
165
+ it "#text_for(:pre) splits strings" do
166
+ answer.text = "before|after|extra"
167
+ answer.text_for(:pre).should == "before"
168
+ end
169
+ it "#text_for(:post) splits strings" do
170
+ answer.text = "before|after|extra"
171
+ answer.text_for(:post).should == "after|extra"
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,439 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+
4
+ describe DependencyCondition do
5
+ it "should have a list of operators" do
6
+ %w(== != < > <= >=).each do |operator|
7
+ DependencyCondition.operators.include?(operator).should be_true
8
+ end
9
+ end
10
+
11
+ describe "instance" do
12
+ before(:each) do
13
+ @dependency_condition = DependencyCondition.new(
14
+ :dependency_id => 1, :question_id => 45, :operator => "==",
15
+ :answer_id => 23, :rule_key => "A")
16
+ end
17
+
18
+ it "should be valid" do
19
+ @dependency_condition.should be_valid
20
+ end
21
+
22
+ it "should be invalid without a parent dependency_id, question_id" do
23
+ # this causes issues with building and saving
24
+ # @dependency_condition.dependency_id = nil
25
+ # @dependency_condition.should have(1).errors_on(:dependency_id)
26
+ # @dependency_condition.question_id = nil
27
+ # @dependency_condition.should have(1).errors_on(:question_id)
28
+ end
29
+
30
+ it "should be invalid without an operator" do
31
+ @dependency_condition.operator = nil
32
+ @dependency_condition.should have(2).errors_on(:operator)
33
+ end
34
+
35
+ it "should be invalid without a rule_key" do
36
+ @dependency_condition.should be_valid
37
+ @dependency_condition.rule_key = nil
38
+ @dependency_condition.should_not be_valid
39
+ @dependency_condition.should have(1).errors_on(:rule_key)
40
+ end
41
+
42
+ it "should have unique rule_key within the context of a dependency" do
43
+ @dependency_condition.should be_valid
44
+ DependencyCondition.create(
45
+ :dependency_id => 2, :question_id => 46, :operator => "==",
46
+ :answer_id => 14, :rule_key => "B")
47
+ @dependency_condition.rule_key = "B" # rule key uniquness is scoped by dependency_id
48
+ @dependency_condition.dependency_id = 2
49
+ @dependency_condition.should_not be_valid
50
+ @dependency_condition.should have(1).errors_on(:rule_key)
51
+ end
52
+
53
+ it "should have an operator in DependencyCondition.operators" do
54
+ DependencyCondition.operators.each do |o|
55
+ @dependency_condition.operator = o
56
+ @dependency_condition.should have(0).errors_on(:operator)
57
+ end
58
+ @dependency_condition.operator = "#"
59
+ @dependency_condition.should have(1).error_on(:operator)
60
+ end
61
+ it "should have a properly formed count operator" do
62
+ %w(count>1 count<1 count>=1 count<=1 count==1 count!=1).each do |o|
63
+ @dependency_condition.operator = o
64
+ @dependency_condition.should have(0).errors_on(:operator)
65
+ end
66
+ %w(count> count< count>= count<= count== count!=).each do |o|
67
+ @dependency_condition.operator = o
68
+ @dependency_condition.should have(1).errors_on(:operator)
69
+ end
70
+ %w(count=1 count><1 count<>1 count!1 count!!1 count=>1 count=<1).each do |o|
71
+ @dependency_condition.operator = o
72
+ @dependency_condition.should have(1).errors_on(:operator)
73
+ end
74
+ %w(count= count>< count<> count! count!! count=> count=< count> count< count>= count<= count== count!=).each do |o|
75
+ @dependency_condition.operator = o
76
+ @dependency_condition.should have(1).errors_on(:operator)
77
+ end
78
+ end
79
+
80
+ it "should protect timestamps" do
81
+ saved_attrs = @dependency_condition.attributes
82
+ if defined? ActiveModel::MassAssignmentSecurity::Error
83
+ lambda {@dependency_condition.update_attributes(:created_at => 3.days.ago, :updated_at => 3.hours.ago)}.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
84
+ else
85
+ @dependency_condition.attributes = {:created_at => 3.days.ago, :updated_at => 3.hours.ago} # automatically protected by Rails
86
+ end
87
+ @dependency_condition.attributes.should == saved_attrs
88
+ end
89
+
90
+ end
91
+
92
+ it "returns true for != with no responses" do
93
+ question = FactoryGirl.create(:question)
94
+ dependency_condition = FactoryGirl.create(:dependency_condition, :rule_key => "C", :question => question)
95
+ rs = FactoryGirl.create(:response_set)
96
+ dependency_condition.to_hash(rs).should == {:C => false}
97
+ end
98
+
99
+
100
+ it "should not assume that Response#as is not nil" do
101
+ # q_HEIGHT_FT "Portion of height in whole feet (e.g., 5)",
102
+ # :pick=>:one
103
+ # a :integer
104
+ # a_neg_1 "Refused"
105
+ # a_neg_2 "Don't know"
106
+ # label "Provided value is outside of the suggested range (4 to 7 feet). This value is admissible, but you may wish to verify."
107
+ # dependency :rule=>"A or B"
108
+ # condition_A :q_HEIGHT_FT, "<", {:integer_value => "4"}
109
+ # condition_B :q_HEIGHT_FT, ">", {:integer_value => "7"}
110
+
111
+ answer = FactoryGirl.create(:answer, :response_class => :integer)
112
+ @dependency_condition = DependencyCondition.new(
113
+ :dependency => FactoryGirl.create(:dependency),
114
+ :question => answer.question,
115
+ :answer => answer,
116
+ :operator => ">",
117
+ :integer_value => 4,
118
+ :rule_key => "A")
119
+
120
+ response = FactoryGirl.create(:response, :answer => answer, :question => answer.question)
121
+ response_set = response.response_set
122
+ response.integer_value.should == nil
123
+
124
+ @dependency_condition.to_hash(response_set).should == {:A => false}
125
+ end
126
+
127
+ describe "evaluate '==' operator" do
128
+ before(:each) do
129
+ @a = FactoryGirl.create(:answer, :response_class => "answer")
130
+ @b = FactoryGirl.create(:answer, :question => @a.question)
131
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a)
132
+ @rs = @r.response_set
133
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => "==", :rule_key => "D")
134
+ @dc.as(:answer).should == @r.as(:answer)
135
+ end
136
+
137
+ it "with checkbox/radio type response" do
138
+ @dc.to_hash(@rs).should == {:D => true}
139
+ @dc.answer = @b
140
+ @dc.to_hash(@rs).should == {:D => false}
141
+ end
142
+
143
+ it "with string value response" do
144
+ @a.update_attributes(:response_class => "string")
145
+ @r.update_attributes(:string_value => "hello123")
146
+ @dc.string_value = "hello123"
147
+ @dc.to_hash(@rs).should == {:D => true}
148
+ @r.update_attributes(:string_value => "foo_abc")
149
+ @dc.to_hash(@rs).should == {:D => false}
150
+ end
151
+
152
+ it "with a text value response" do
153
+ @a.update_attributes(:response_class => "text")
154
+ @r.update_attributes(:text_value => "hello this is some text for comparison")
155
+ @dc.text_value = "hello this is some text for comparison"
156
+ @dc.to_hash(@rs).should == {:D => true}
157
+ @r.update_attributes(:text_value => "Not the same text")
158
+ @dc.to_hash(@rs).should == {:D => false}
159
+ end
160
+
161
+ it "with an integer value response" do
162
+ @a.update_attributes(:response_class => "integer")
163
+ @r.update_attributes(:integer_value => 10045)
164
+ @dc.integer_value = 10045
165
+ @dc.to_hash(@rs).should == {:D => true}
166
+ @r.update_attributes(:integer_value => 421)
167
+ @dc.to_hash(@rs).should == {:D => false}
168
+ end
169
+
170
+ it "with a float value response" do
171
+ @a.update_attributes(:response_class => "float")
172
+ @r.update_attributes(:float_value => 121.1)
173
+ @dc.float_value = 121.1
174
+ @dc.to_hash(@rs).should == {:D => true}
175
+ @r.update_attributes(:float_value => 130.123)
176
+ @dc.to_hash(@rs).should == {:D => false}
177
+ end
178
+ end
179
+
180
+ describe "evaluate '!=' operator" do
181
+ before(:each) do
182
+ @a = FactoryGirl.create(:answer)
183
+ @b = FactoryGirl.create(:answer, :question => @a.question)
184
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a)
185
+ @rs = @r.response_set
186
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => "!=", :rule_key => "E")
187
+ @dc.as(:answer).should == @r.as(:answer)
188
+ end
189
+
190
+ it "with checkbox/radio type response" do
191
+ @dc.to_hash(@rs).should == {:E => false}
192
+ @dc.answer_id = @a.id.to_i+1
193
+ @dc.to_hash(@rs).should == {:E => true}
194
+ end
195
+
196
+ it "with string value response" do
197
+ @a.update_attributes(:response_class => "string")
198
+ @r.update_attributes(:string_value => "hello123")
199
+ @dc.string_value = "hello123"
200
+ @dc.to_hash(@rs).should == {:E => false}
201
+ @r.update_attributes(:string_value => "foo_abc")
202
+ @dc.to_hash(@rs).should == {:E => true}
203
+ end
204
+
205
+ it "with a text value response" do
206
+ @a.update_attributes(:response_class => "text")
207
+ @r.update_attributes(:text_value => "hello this is some text for comparison")
208
+ @dc.text_value = "hello this is some text for comparison"
209
+ @dc.to_hash(@rs).should == {:E => false}
210
+ @r.update_attributes(:text_value => "Not the same text")
211
+ @dc.to_hash(@rs).should == {:E => true}
212
+ end
213
+
214
+ it "with an integer value response" do
215
+ @a.update_attributes(:response_class => "integer")
216
+ @r.update_attributes(:integer_value => 10045)
217
+ @dc.integer_value = 10045
218
+ @dc.to_hash(@rs).should == {:E => false}
219
+ @r.update_attributes(:integer_value => 421)
220
+ @dc.to_hash(@rs).should == {:E => true}
221
+ end
222
+
223
+ it "with a float value response" do
224
+ @a.update_attributes(:response_class => "float")
225
+ @r.update_attributes(:float_value => 121.1)
226
+ @dc.float_value = 121.1
227
+ @dc.to_hash(@rs).should == {:E => false}
228
+ @r.update_attributes(:float_value => 130.123)
229
+ @dc.to_hash(@rs).should == {:E => true}
230
+ end
231
+ end
232
+
233
+ describe "evaluate the '<' operator" do
234
+ before(:each) do
235
+ @a = FactoryGirl.create(:answer)
236
+ @b = FactoryGirl.create(:answer, :question => @a.question)
237
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a)
238
+ @rs = @r.response_set
239
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => "<", :rule_key => "F")
240
+ @dc.as(:answer).should == @r.as(:answer)
241
+ end
242
+
243
+ it "with an integer value response" do
244
+ @a.update_attributes(:response_class => "integer")
245
+ @r.update_attributes(:integer_value => 50)
246
+ @dc.integer_value = 100
247
+ @dc.to_hash(@rs).should == {:F => true}
248
+ @r.update_attributes(:integer_value => 421)
249
+ @dc.to_hash(@rs).should == {:F => false}
250
+ end
251
+
252
+ it "with a float value response" do
253
+ @a.update_attributes(:response_class => "float")
254
+ @r.update_attributes(:float_value => 5.1)
255
+ @dc.float_value = 121.1
256
+ @dc.to_hash(@rs).should == {:F => true}
257
+ @r.update_attributes(:float_value => 130.123)
258
+ @dc.to_hash(@rs).should == {:F => false}
259
+ end
260
+ end
261
+
262
+ describe "evaluate the '<=' operator" do
263
+ before(:each) do
264
+ @a = FactoryGirl.create(:answer)
265
+ @b = FactoryGirl.create(:answer, :question => @a.question)
266
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a)
267
+ @rs = @r.response_set
268
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => "<=", :rule_key => "G")
269
+ @dc.as(:answer).should == @r.as(:answer)
270
+ end
271
+
272
+ it "with an integer value response" do
273
+ @a.update_attributes(:response_class => "integer")
274
+ @r.update_attributes(:integer_value => 50)
275
+ @dc.integer_value = 100
276
+ @dc.to_hash(@rs).should == {:G => true}
277
+ @r.update_attributes(:integer_value => 100)
278
+ @dc.to_hash(@rs).should == {:G => true}
279
+ @r.update_attributes(:integer_value => 421)
280
+ @dc.to_hash(@rs).should == {:G => false}
281
+ end
282
+
283
+ it "with a float value response" do
284
+ @a.update_attributes(:response_class => "float")
285
+ @r.update_attributes(:float_value => 5.1)
286
+ @dc.float_value = 121.1
287
+ @dc.to_hash(@rs).should == {:G => true}
288
+ @r.update_attributes(:float_value => 121.1)
289
+ @dc.to_hash(@rs).should == {:G => true}
290
+ @r.update_attributes(:float_value => 130.123)
291
+ @dc.to_hash(@rs).should == {:G => false}
292
+ end
293
+
294
+ end
295
+
296
+ describe "evaluate the '>' operator" do
297
+ before(:each) do
298
+ @a = FactoryGirl.create(:answer)
299
+ @b = FactoryGirl.create(:answer, :question => @a.question)
300
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a)
301
+ @rs = @r.response_set
302
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => ">", :rule_key => "H")
303
+ @dc.as(:answer).should == @r.as(:answer)
304
+ end
305
+
306
+ it "with an integer value response" do
307
+ @a.update_attributes(:response_class => "integer")
308
+ @r.update_attributes(:integer_value => 50)
309
+ @dc.integer_value = 100
310
+ @dc.to_hash(@rs).should == {:H => false}
311
+ @r.update_attributes(:integer_value => 421)
312
+ @dc.to_hash(@rs).should == {:H => true}
313
+ end
314
+
315
+ it "with a float value response" do
316
+ @a.update_attributes(:response_class => "float")
317
+ @r.update_attributes(:float_value => 5.1)
318
+ @dc.float_value = 121.1
319
+ @dc.to_hash(@rs).should == {:H => false}
320
+ @r.update_attributes(:float_value => 130.123)
321
+ @dc.to_hash(@rs).should == {:H => true}
322
+ end
323
+ end
324
+
325
+ describe "evaluate the '>=' operator" do
326
+ before(:each) do
327
+ @a = FactoryGirl.create(:answer)
328
+ @b = FactoryGirl.create(:answer, :question => @a.question)
329
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a)
330
+ @rs = @r.response_set
331
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => ">=", :rule_key => "I")
332
+ @dc.as(:answer).should == @r.as(:answer)
333
+ end
334
+
335
+ it "with an integer value response" do
336
+ @a.update_attributes(:response_class => "integer")
337
+ @r.update_attributes(:integer_value => 50)
338
+ @dc.integer_value = 100
339
+ @dc.to_hash(@rs).should == {:I => false}
340
+ @r.update_attributes(:integer_value => 100)
341
+ @dc.to_hash(@rs).should == {:I => true}
342
+ @r.update_attributes(:integer_value => 421)
343
+ @dc.to_hash(@rs).should == {:I => true}
344
+ end
345
+
346
+ it "with a float value response" do
347
+ @a.update_attributes(:response_class => "float")
348
+ @r.update_attributes(:float_value => 5.1)
349
+ @dc.float_value = 121.1
350
+ @dc.to_hash(@rs).should == {:I => false}
351
+ @r.update_attributes(:float_value => 121.1)
352
+ @dc.to_hash(@rs).should == {:I => true}
353
+ @r.update_attributes(:float_value => 130.123)
354
+ @dc.to_hash(@rs).should == {:I => true}
355
+ end
356
+ end
357
+
358
+ describe "evaluating with response_class string" do
359
+ it "should compare answer ids when the dependency condition string_value is nil" do
360
+ @a = FactoryGirl.create(:answer, :response_class => "string")
361
+ @b = FactoryGirl.create(:answer, :question => @a.question)
362
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a, :string_value => "")
363
+ @rs = @r.response_set
364
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => "==", :rule_key => "J")
365
+ @dc.to_hash(@rs).should == {:J => true}
366
+ end
367
+
368
+ it "should compare strings when the dependency condition string_value is not nil, even if it is blank" do
369
+ @a = FactoryGirl.create(:answer, :response_class => "string")
370
+ @b = FactoryGirl.create(:answer, :question => @a.question)
371
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a, :string_value => "foo")
372
+ @rs = @r.response_set
373
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => "==", :rule_key => "K", :string_value => "foo")
374
+ @dc.to_hash(@rs).should == {:K => true}
375
+
376
+ @r.update_attributes(:string_value => "")
377
+ @dc.string_value = ""
378
+ @dc.to_hash(@rs).should == {:K => true}
379
+ end
380
+ end
381
+
382
+ describe "evaluate 'count' operator" do
383
+ before(:each) do
384
+ @q = FactoryGirl.create(:question)
385
+ @dc = DependencyCondition.new(:operator => "count>2", :rule_key => "M", :question => @q)
386
+ @as = []
387
+ 3.times do
388
+ @as << FactoryGirl.create(:answer, :question => @q, :response_class => "answer")
389
+ end
390
+ @rs = FactoryGirl.create(:response_set)
391
+ @as.slice(0,2).each do |a|
392
+ FactoryGirl.create(:response, :question => @q, :answer => a, :response_set => @rs)
393
+ end
394
+ @rs.save
395
+ end
396
+
397
+ it "with operator with >" do
398
+ @dc.to_hash(@rs).should == {:M => false}
399
+ FactoryGirl.create(:response, :question => @q, :answer => @as.last, :response_set => @rs)
400
+ @rs.reload.responses.count.should == 3
401
+ @dc.to_hash(@rs.reload).should == {:M => true}
402
+ end
403
+
404
+ it "with operator with <" do
405
+ @dc.operator = "count<2"
406
+ @dc.to_hash(@rs).should == {:M => false}
407
+ @dc.operator = "count<3"
408
+ @dc.to_hash(@rs).should == {:M => true}
409
+ end
410
+
411
+ it "with operator with <=" do
412
+ @dc.operator = "count<=1"
413
+ @dc.to_hash(@rs).should == {:M => false}
414
+ @dc.operator = "count<=2"
415
+ @dc.to_hash(@rs).should == {:M => true}
416
+ @dc.operator = "count<=3"
417
+ @dc.to_hash(@rs).should == {:M => true}
418
+ end
419
+
420
+ it "with operator with >=" do
421
+ @dc.operator = "count>=1"
422
+ @dc.to_hash(@rs).should == {:M => true}
423
+ @dc.operator = "count>=2"
424
+ @dc.to_hash(@rs).should == {:M => true}
425
+ @dc.operator = "count>=3"
426
+ @dc.to_hash(@rs).should == {:M => false}
427
+ end
428
+
429
+ it "with operator with !=" do
430
+ @dc.operator = "count!=1"
431
+ @dc.to_hash(@rs).should == {:M => true}
432
+ @dc.operator = "count!=2"
433
+ @dc.to_hash(@rs).should == {:M => false}
434
+ @dc.operator = "count!=3"
435
+ @dc.to_hash(@rs).should == {:M => true}
436
+ end
437
+ end
438
+
439
+ end