hssc_surveyor 1.4.1.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. data/.gitignore +28 -0
  2. data/.rspec +1 -0
  3. data/CHANGELOG.md +198 -0
  4. data/Gemfile +5 -0
  5. data/Gemfile.rails_version +19 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +186 -0
  8. data/Rakefile +105 -0
  9. data/app/controllers/surveyor_controller.rb +6 -0
  10. data/app/helpers/results_helper.rb +20 -0
  11. data/app/helpers/survey_form_builder.rb +37 -0
  12. data/app/helpers/surveyor_helper.rb +3 -0
  13. data/app/inputs/quiet_input.rb +5 -0
  14. data/app/inputs/surveyor_check_boxes_input.rb +35 -0
  15. data/app/inputs/surveyor_radio_input.rb +18 -0
  16. data/app/models/answer.rb +4 -0
  17. data/app/models/dependency.rb +4 -0
  18. data/app/models/dependency_condition.rb +4 -0
  19. data/app/models/question.rb +4 -0
  20. data/app/models/question_group.rb +5 -0
  21. data/app/models/response.rb +5 -0
  22. data/app/models/response_set.rb +4 -0
  23. data/app/models/survey.rb +4 -0
  24. data/app/models/survey_section.rb +5 -0
  25. data/app/models/survey_section_sweeper.rb +15 -0
  26. data/app/models/survey_translation.rb +5 -0
  27. data/app/models/validation.rb +4 -0
  28. data/app/models/validation_condition.rb +4 -0
  29. data/app/views/layouts/results.html.erb +13 -0
  30. data/app/views/layouts/surveyor_default.html.erb +12 -0
  31. data/app/views/partials/_answer.html.haml +25 -0
  32. data/app/views/partials/_dependents.html.haml +5 -0
  33. data/app/views/partials/_question.html.haml +41 -0
  34. data/app/views/partials/_question_group.html.haml +44 -0
  35. data/app/views/partials/_section.html.haml +12 -0
  36. data/app/views/partials/_section_menu.html.haml +12 -0
  37. data/app/views/surveyor/edit.html.haml +24 -0
  38. data/app/views/surveyor/export.json.rabl +85 -0
  39. data/app/views/surveyor/new.html.haml +24 -0
  40. data/app/views/surveyor/show.html.haml +74 -0
  41. data/app/views/surveyor/show.json.rabl +15 -0
  42. data/ci-exec.sh +56 -0
  43. data/config/routes.rb +8 -0
  44. data/cucumber.yml +10 -0
  45. data/doc/REPRESENTATIONS.md +34 -0
  46. data/doc/api_id_schema.json +7 -0
  47. data/doc/question types.png +0 -0
  48. data/doc/response_set_schema.json +54 -0
  49. data/doc/surveyor question combinations.png +0 -0
  50. data/doc/surveyor reject or delete decision matrix.png +0 -0
  51. data/doc/surveyor_models.png +0 -0
  52. data/doc/surveyor_models2.png +0 -0
  53. data/doc/surveyor_timestamp_schema.json +9 -0
  54. data/features/ajax_submissions.feature +140 -0
  55. data/features/export_to_json.feature +344 -0
  56. data/features/internationalization.feature +121 -0
  57. data/features/no_duplicates.feature +110 -0
  58. data/features/show_survey.feature +71 -0
  59. data/features/step_definitions/parser_steps.rb +145 -0
  60. data/features/step_definitions/surveyor_steps.rb +325 -0
  61. data/features/step_definitions/ui_steps.rb +25 -0
  62. data/features/step_definitions/web_steps.rb +225 -0
  63. data/features/support/REDCapDemoDatabase_DataDictionary.csv +127 -0
  64. data/features/support/database_cleaner.rb +16 -0
  65. data/features/support/env.rb +56 -0
  66. data/features/support/hooks.rb +4 -0
  67. data/features/support/paths.rb +39 -0
  68. data/features/support/redcap_new_headers.csv +1 -0
  69. data/features/support/redcap_siblings.csv +1 -0
  70. data/features/support/redcap_whitespace.csv +1 -0
  71. data/features/support/selectors.rb +39 -0
  72. data/features/support/simultaneous_ajax.rb +101 -0
  73. data/features/support/single_quit_selenium_driver.rb +23 -0
  74. data/features/support/slow_updates.rb +18 -0
  75. data/features/surveyor.feature +895 -0
  76. data/features/surveyor_dependencies.feature +476 -0
  77. data/features/surveyor_parser.feature +504 -0
  78. data/features/z_redcap_parser.feature +62 -0
  79. data/lib/assets/images/surveyor/next.gif +0 -0
  80. data/lib/assets/images/surveyor/prev.gif +0 -0
  81. data/lib/assets/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  82. data/lib/assets/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  83. data/lib/assets/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  84. data/lib/assets/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  85. data/lib/assets/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  86. data/lib/assets/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  87. data/lib/assets/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  88. data/lib/assets/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  89. data/lib/assets/images/ui-icons_222222_256x240.png +0 -0
  90. data/lib/assets/images/ui-icons_2e83ff_256x240.png +0 -0
  91. data/lib/assets/images/ui-icons_454545_256x240.png +0 -0
  92. data/lib/assets/images/ui-icons_888888_256x240.png +0 -0
  93. data/lib/assets/images/ui-icons_cd0a0a_256x240.png +0 -0
  94. data/lib/assets/javascripts/surveyor/jquery-1.9.0.js +9555 -0
  95. data/lib/assets/javascripts/surveyor/jquery-ui-1.10.0.custom.js +14850 -0
  96. data/lib/assets/javascripts/surveyor/jquery-ui-timepicker-addon.js +1919 -0
  97. data/lib/assets/javascripts/surveyor/jquery.maskedinput.js +338 -0
  98. data/lib/assets/javascripts/surveyor/jquery.selectToUISlider.js +240 -0
  99. data/lib/assets/javascripts/surveyor/jquery.surveyor.js +156 -0
  100. data/lib/assets/stylesheets/surveyor/jquery-ui-1.10.0.custom.css +1174 -0
  101. data/lib/assets/stylesheets/surveyor/jquery-ui-timepicker-addon.css +11 -0
  102. data/lib/assets/stylesheets/surveyor/reset.css +48 -0
  103. data/lib/assets/stylesheets/surveyor/results.css +125 -0
  104. data/lib/assets/stylesheets/surveyor/ui.slider.extras.css +110 -0
  105. data/lib/assets/stylesheets/surveyor.sass +132 -0
  106. data/lib/generators/surveyor/custom_generator.rb +18 -0
  107. data/lib/generators/surveyor/install_generator.rb +102 -0
  108. data/lib/generators/surveyor/templates/app/assets/javascripts/surveyor_all.js +6 -0
  109. data/lib/generators/surveyor/templates/app/assets/stylesheets/surveyor_all.css +9 -0
  110. data/lib/generators/surveyor/templates/app/controllers/surveyor_controller.rb +40 -0
  111. data/lib/generators/surveyor/templates/app/views/layouts/surveyor_custom.html.erb +13 -0
  112. data/lib/generators/surveyor/templates/config/locales/surveyor_en.yml +19 -0
  113. data/lib/generators/surveyor/templates/config/locales/surveyor_es.yml +19 -0
  114. data/lib/generators/surveyor/templates/config/locales/surveyor_he.yml +19 -0
  115. data/lib/generators/surveyor/templates/config/locales/surveyor_ko.yml +19 -0
  116. data/lib/generators/surveyor/templates/db/migrate/add_api_id_to_question_groups.rb +10 -0
  117. data/lib/generators/surveyor/templates/db/migrate/add_api_ids.rb +14 -0
  118. data/lib/generators/surveyor/templates/db/migrate/add_api_ids_to_response_sets_and_responses.rb +12 -0
  119. data/lib/generators/surveyor/templates/db/migrate/add_attachment_to_response.rb +5 -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 +284 -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/acts_as_response.rb +17 -0
  158. data/lib/surveyor/common.rb +59 -0
  159. data/lib/surveyor/engine.rb +10 -0
  160. data/lib/surveyor/helpers/asset_pipeline.rb +13 -0
  161. data/lib/surveyor/helpers/formtastic_custom_input.rb +13 -0
  162. data/lib/surveyor/helpers/surveyor_helper_methods.rb +103 -0
  163. data/lib/surveyor/models/answer_methods.rb +86 -0
  164. data/lib/surveyor/models/dependency_condition_methods.rb +72 -0
  165. data/lib/surveyor/models/dependency_methods.rb +60 -0
  166. data/lib/surveyor/models/question_group_methods.rb +61 -0
  167. data/lib/surveyor/models/question_methods.rb +115 -0
  168. data/lib/surveyor/models/response_methods.rb +132 -0
  169. data/lib/surveyor/models/response_set_methods.rb +196 -0
  170. data/lib/surveyor/models/survey_methods.rb +103 -0
  171. data/lib/surveyor/models/survey_section_methods.rb +56 -0
  172. data/lib/surveyor/models/survey_translation_methods.rb +33 -0
  173. data/lib/surveyor/models/validation_condition_methods.rb +56 -0
  174. data/lib/surveyor/models/validation_methods.rb +48 -0
  175. data/lib/surveyor/mustache_context.rb +11 -0
  176. data/lib/surveyor/parser.rb +428 -0
  177. data/lib/surveyor/redcap_parser.rb +289 -0
  178. data/lib/surveyor/surveyor_controller_methods.rb +237 -0
  179. data/lib/surveyor/unparser.rb +147 -0
  180. data/lib/surveyor/version.rb +3 -0
  181. data/lib/surveyor.rb +13 -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 +307 -0
  185. data/spec/factories.rb +161 -0
  186. data/spec/helpers/formtastic_custom_input_spec.rb +15 -0
  187. data/spec/helpers/surveyor_helper_spec.rb +118 -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 +175 -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 +207 -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 +45 -0
  212. metadata +657 -0
@@ -0,0 +1,218 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Response, "when saving a response" do
4
+ before(:each) do
5
+ # @response = Response.new(:question_id => 314, :response_set_id => 159, :answer_id => 1)
6
+ @response = Factory(:response, :question => Factory(:question), :answer => Factory(:answer))
7
+ end
8
+
9
+ it "should be valid" do
10
+ @response.should be_valid
11
+ end
12
+
13
+ it "should be invalid without a question" do
14
+ @response.question_id = nil
15
+ @response.should have(1).error_on(:question_id)
16
+ end
17
+
18
+ it "should be correct if the question has no correct_answer_id" do
19
+ @response.question.correct_answer_id.should be_nil
20
+ @response.correct?.should be_true
21
+ end
22
+
23
+ it "should be correct if the answer's response class != answer" do
24
+ @response.answer.response_class.should_not == "answer"
25
+ @response.correct?.should be_true
26
+ end
27
+
28
+ it "should be (in)correct if answer_id is (not) equal to question's correct_answer_id" do
29
+ @answer = Factory(:answer, :response_class => "answer")
30
+ @question = Factory(:question, :correct_answer => @answer)
31
+ @response = Factory(:response, :question => @question, :answer => @answer)
32
+ @response.correct?.should be_true
33
+ @response.answer = Factory(:answer, :response_class => "answer").tap { |a| a.id = 143 }
34
+ @response.correct?.should be_false
35
+ end
36
+
37
+ it "should be in order by created_at" do
38
+ @response.response_set.should_not be_nil
39
+ response2 = Factory(:response, :question => Factory(:question), :answer => Factory(:answer), :response_set => @response.response_set, :created_at => (@response.created_at + 1))
40
+ Response.all.should == [@response, response2]
41
+ end
42
+
43
+ it "should protect api_id, timestamps" do
44
+ saved_attrs = @response.attributes
45
+ if defined? ActiveModel::MassAssignmentSecurity::Error
46
+ lambda {@response.update_attributes(:created_at => 3.days.ago, :updated_at => 3.hours.ago)}.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
47
+ lambda {@response.update_attributes(:api_id => "NEW")}.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
48
+ else
49
+ @response.attributes = {:created_at => 3.days.ago, :updated_at => 3.hours.ago} # automatically protected by Rails
50
+ @response.attributes = {:api_id => "NEW"} # Rails doesn't return false, but this will be checked in the comparison to saved_attrs
51
+ end
52
+ @response.attributes.should == saved_attrs
53
+ end
54
+
55
+
56
+ describe "returns the response as the type requested" do
57
+ it "returns 'string'" do
58
+ @response.string_value = "blah"
59
+ @response.as("string").should == "blah"
60
+ @response.as(:string).should == "blah"
61
+ end
62
+
63
+ it "returns 'integer'" do
64
+ @response.integer_value = 1001
65
+ @response.as(:integer).should == 1001
66
+ end
67
+
68
+ it "returns 'float'" do
69
+ @response.float_value = 3.14
70
+ @response.as(:float).should == 3.14
71
+ end
72
+
73
+ it "returns 'answer'" do
74
+ @response.answer_id = 14
75
+ @response.as(:answer).should == 14
76
+ end
77
+
78
+ it "default returns answer type if not specified" do
79
+ @response.answer_id =18
80
+ @response.as(:stuff).should == 18
81
+ end
82
+
83
+ it "returns empty elements if the response is cast as a type that is not present" do
84
+ resp = Response.new(:question_id => 314, :response_set_id => 156)
85
+ resp.as(:string).should == nil
86
+ resp.as(:integer).should == nil
87
+ resp.as(:float).should == nil
88
+ resp.as(:answer).should == nil
89
+ resp.as(:stuff).should == nil
90
+ end
91
+ end
92
+ end
93
+
94
+ describe Response, "applicable_attributes" do
95
+ before(:each) do
96
+ @who = Factory(:question, :text => "Who rules?")
97
+ @odoyle = Factory(:answer, :text => "Odoyle", :response_class => "answer")
98
+ @other = Factory(:answer, :text => "Other", :response_class => "string")
99
+ end
100
+
101
+ it "should have string_value if response_type is string" do
102
+ good = {"question_id" => @who.id, "answer_id" => @other.id, "string_value" => "Frank"}
103
+ Response.applicable_attributes(good).should == good
104
+ end
105
+
106
+ it "should not have string_value if response_type is answer" do
107
+ bad = {"question_id"=>@who.id, "answer_id"=>@odoyle.id, "string_value"=>"Frank"}
108
+ Response.applicable_attributes(bad).
109
+ should == {"question_id" => @who.id, "answer_id"=> @odoyle.id}
110
+ end
111
+
112
+ it "should have string_value if response_type is string and answer_id is an array (in the case of checkboxes)" do
113
+ good = {"question_id"=>@who.id, "answer_id"=>["", @odoyle.id], "string_value"=>"Frank"}
114
+ Response.applicable_attributes(good).
115
+ should == {"question_id" => @who.id, "answer_id"=> ["", @odoyle.id]}
116
+ end
117
+
118
+ it "should have ignore attribute if missing answer_id" do
119
+ ignore = {"question_id"=>@who.id, "answer_id"=>"", "string_value"=>"Frank"}
120
+ Response.applicable_attributes(ignore).
121
+ should == {"question_id"=>@who.id, "answer_id"=>"", "string_value"=>"Frank"}
122
+ end
123
+
124
+ it "should have ignore attribute if missing answer_id is an array" do
125
+ ignore = {"question_id"=>@who.id, "answer_id"=>[""], "string_value"=>"Frank"}
126
+ Response.applicable_attributes(ignore).
127
+ should == {"question_id"=>@who.id, "answer_id"=>[""], "string_value"=>"Frank"}
128
+ end
129
+ end
130
+
131
+ describe Response, '#to_formatted_s' do
132
+ context "when datetime" do
133
+ let(:r) { Response.new(:answer => Answer.new(:response_class => 'datetime')) }
134
+
135
+ it 'returns "" when nil' do
136
+ r.datetime_value = nil
137
+
138
+ r.to_formatted_s.should == ""
139
+ end
140
+ end
141
+ end
142
+
143
+ describe Response, '#json_value' do
144
+ context "when integer" do
145
+ let(:r) {Response.new(:integer_value => 2, :answer => Answer.new(:response_class => 'integer'))}
146
+ it "should be 2" do
147
+ r.json_value.should == 2
148
+ end
149
+ end
150
+
151
+ context "when float" do
152
+ let(:r) {Response.new(:float_value => 3.14, :answer => Answer.new(:response_class => 'float'))}
153
+ it "should be 3.14" do
154
+ r.json_value.should == 3.14
155
+ end
156
+ end
157
+
158
+ context "when string" do
159
+ let(:r) {Response.new(:string_value => 'bar', :answer => Answer.new(:response_class => 'string'))}
160
+ it "should be 'bar'" do
161
+ r.json_value.should == 'bar'
162
+ end
163
+ end
164
+
165
+ context "when datetime" do
166
+ let(:r) {Response.new(:datetime_value => DateTime.strptime('2010-04-08T10:30+00:00', '%Y-%m-%dT%H:%M%z'), :answer => Answer.new(:response_class => 'datetime'))}
167
+ it "should be '2010-04-08T10:30+00:00'" do
168
+ r.json_value.should == '2010-04-08T10:30+00:00'
169
+ r.json_value.to_json.should == '"2010-04-08T10:30+00:00"'
170
+ end
171
+ end
172
+
173
+ context "when date" do
174
+ let(:r) {Response.new(:datetime_value => DateTime.strptime('2010-04-08', '%Y-%m-%d'), :answer => Answer.new(:response_class => 'date'))}
175
+ it "should be '2010-04-08'" do
176
+ r.json_value.should == '2010-04-08'
177
+ r.json_value.to_json.should == '"2010-04-08"'
178
+ end
179
+ end
180
+
181
+ context "when time" do
182
+ let(:r) {Response.new(:datetime_value => DateTime.strptime('10:30', '%H:%M'), :answer => Answer.new(:response_class => 'time'))}
183
+ it "should be '10:30'" do
184
+ r.json_value.should == '10:30'
185
+ r.json_value.to_json.should == '"10:30"'
186
+ end
187
+ end
188
+ end
189
+
190
+ describe Response, 'value methods' do
191
+ let(:response) { Response.new }
192
+
193
+ describe '#date_value=' do
194
+ it 'accepts a parseable date string' do
195
+ response.date_value = '2010-01-15'
196
+ response.datetime_value.strftime('%Y %m %d').should == '2010 01 15'
197
+ end
198
+
199
+ it 'clears when given nil' do
200
+ response.datetime_value = Time.new
201
+ response.date_value = nil
202
+ response.datetime_value.should be_nil
203
+ end
204
+ end
205
+
206
+ describe 'time_value=' do
207
+ it 'accepts a parseable time string' do
208
+ response.time_value = '11:30'
209
+ response.datetime_value.strftime('%H %M %S').should == '11 30 00'
210
+ end
211
+
212
+ it 'clears when given nil' do
213
+ response.datetime_value = Time.new
214
+ response.time_value = nil
215
+ response.datetime_value.should be_nil
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,85 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
3
+
4
+ describe SurveySection do
5
+ let(:survey_section){ Factory(:survey_section) }
6
+
7
+ context "when creating" do
8
+ it "is invalid without #title" do
9
+ survey_section.title = nil
10
+ survey_section.should have(1).error_on(:title)
11
+ end
12
+ it "protects #api_id" do
13
+ saved_attrs = survey_section.attributes
14
+ if defined? ActiveModel::MassAssignmentSecurity::Error
15
+ expect { survey_section.update_attributes(:api_id => "NEW") }.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
16
+ else
17
+ survey_section.attributes = {:api_id => "NEW"} # Rails doesn't return false, but this will be checked in the comparison to saved_attrs
18
+ end
19
+ survey_section.attributes.should == saved_attrs
20
+ end
21
+ it "protects #created_at" do
22
+ saved_attrs = survey_section.attributes
23
+ if defined? ActiveModel::MassAssignmentSecurity::Error
24
+ expect { survey_section.update_attributes(:created_at => 3.days.ago) }.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
25
+ else
26
+ survey_section.attributes = {:created_at => 3.days.ago} # Rails doesn't return false, but this will be checked in the comparison to saved_attrs
27
+ end
28
+ survey_section.attributes.should == saved_attrs
29
+ end
30
+ it "protects #updated_at" do
31
+ saved_attrs = survey_section.attributes
32
+ if defined? ActiveModel::MassAssignmentSecurity::Error
33
+ expect { survey_section.update_attributes(:updated_at => 3.hours.ago) }.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
34
+ else
35
+ survey_section.attributes = {:updated_at => 3.hours.ago} # Rails doesn't return false, but this will be checked in the comparison to saved_attrs
36
+ end
37
+ survey_section.attributes.should == saved_attrs
38
+ end
39
+ end
40
+
41
+ context "with questions" do
42
+ let(:question_1){ Factory(:question, :survey_section => survey_section, :display_order => 3, :text => "Peep")}
43
+ let(:question_2){ Factory(:question, :survey_section => survey_section, :display_order => 1, :text => "Little")}
44
+ let(:question_3){ Factory(:question, :survey_section => survey_section, :display_order => 2, :text => "Bo")}
45
+ before do
46
+ [question_1, question_2, question_3].each{|q| survey_section.questions << q }
47
+ end
48
+ it{ survey_section.should have(3).questions}
49
+ it "gets questions in order" do
50
+ survey_section.questions.should == [question_2, question_3, question_1]
51
+ survey_section.questions.map(&:display_order).should == [1,2,3]
52
+ end
53
+ it "deletes child questions when deleted" do
54
+ question_ids = survey_section.questions.map(&:id)
55
+ survey_section.destroy
56
+ question_ids.each{|id| Question.find_by_id(id).should be_nil}
57
+ end
58
+ end
59
+
60
+ context "with translations" do
61
+ require 'yaml'
62
+ let(:survey){ Factory(:survey) }
63
+ let(:survey_translation){
64
+ Factory(:survey_translation, :locale => :es, :translation => {
65
+ :survey_sections => {
66
+ :one => {
67
+ :title => "Uno"
68
+ }
69
+ }
70
+ }.to_yaml)
71
+ }
72
+ before do
73
+ survey_section.reference_identifier = "one"
74
+ survey_section.survey = survey
75
+ survey.translations << survey_translation
76
+ end
77
+ it "returns its own translation" do
78
+ YAML.load(survey_translation.translation).should_not be_nil
79
+ survey_section.translation(:es)[:title].should == "Uno"
80
+ end
81
+ it "returns its own default values" do
82
+ survey_section.translation(:de).should == {"title" => survey_section.title, "description" => survey_section.description}
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,191 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
3
+
4
+ describe Survey do
5
+ let(:survey){ Factory(:survey) }
6
+
7
+ context "when creating" do
8
+ it "is invalid without #title" do
9
+ survey.title = nil
10
+ survey.should have(1).error_on :title
11
+ end
12
+ it "adjust #survey_version" do
13
+ original = Survey.new(:title => "Foo")
14
+ original.save.should be_true
15
+ original.survey_version.should == 0
16
+ imposter = Survey.new(:title => "Foo")
17
+ imposter.save.should be_true
18
+ imposter.title.should == "Foo"
19
+ imposter.survey_version.should == 1
20
+ bandwagoneer = Survey.new(:title => "Foo")
21
+ bandwagoneer.save.should be_true
22
+ bandwagoneer.title.should == "Foo"
23
+ bandwagoneer.survey_version.should == 2
24
+ end
25
+ it "prevents duplicate #survey_version" do
26
+ original = Survey.new(:title => "Foo")
27
+ original.save.should be_true
28
+ imposter = Survey.new(:title => "Foo")
29
+ imposter.save.should be_true
30
+ imposter.survey_version = 0
31
+ imposter.save.should be_false
32
+ imposter.should have(1).error_on(:survey_version)
33
+ end
34
+ it "doesn't adjust #title when" do
35
+ original = Factory(:survey, :title => "Foo")
36
+ original.save.should be_true
37
+ original.update_attributes(:title => "Foo")
38
+ original.title.should == "Foo"
39
+ end
40
+ it "has #api_id with 36 characters by default" do
41
+ survey.api_id.length.should == 36
42
+ end
43
+ it "protects #access_code" do
44
+ saved_attrs = survey.attributes
45
+ if defined? ActiveModel::MassAssignmentSecurity::Error
46
+ expect { survey.update_attributes(:access_code => "NEW") }.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
47
+ else
48
+ survey.attributes = {:access_code => "NEW"} # Rails doesn't return false, but this will be checked in the comparison to saved_attrs
49
+ end
50
+ survey.attributes.should == saved_attrs
51
+ end
52
+ it "protects #api_id" do
53
+ saved_attrs = survey.attributes
54
+ if defined? ActiveModel::MassAssignmentSecurity::Error
55
+ expect { survey.update_attributes(:api_id => "NEW") }.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
56
+ else
57
+ survey.attributes = {:api_id => "NEW"} # Rails doesn't return false, but this will be checked in the comparison to saved_attrs
58
+ end
59
+ survey.attributes.should == saved_attrs
60
+ end
61
+ it "protects #created_at" do
62
+ saved_attrs = survey.attributes
63
+ if defined? ActiveModel::MassAssignmentSecurity::Error
64
+ expect { survey.update_attributes(:created_at => 3.days.ago) }.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
65
+ else
66
+ survey.attributes = {:created_at => 3.days.ago} # Rails doesn't return false, but this will be checked in the comparison to saved_attrs
67
+ end
68
+ survey.attributes.should == saved_attrs
69
+ end
70
+ it "protects #updated_at" do
71
+ saved_attrs = survey.attributes
72
+ if defined? ActiveModel::MassAssignmentSecurity::Error
73
+ expect { survey.update_attributes(:updated_at => 3.hours.ago) }.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
74
+ else
75
+ survey.attributes = {:updated_at => 3.hours.ago} # Rails doesn't return false, but this will be checked in the comparison to saved_attrs
76
+ end
77
+ survey.attributes.should == saved_attrs
78
+ end
79
+ end
80
+
81
+ context "activating" do
82
+ it { survey.active?.should }
83
+ it "both #inactive_at and #active_at == nil by default" do
84
+ survey.active_at.should be_nil
85
+ survey.inactive_at.should be_nil
86
+ end
87
+ it "#active_at on a certain date/time" do
88
+ survey.inactive_at = 2.days.from_now
89
+ survey.active_at = 2.days.ago
90
+ survey.active?.should be_true
91
+ end
92
+ it "#inactive_at on a certain date/time" do
93
+ survey.active_at = 3.days.ago
94
+ survey.inactive_at = 1.days.ago
95
+ survey.active?.should be_false
96
+ end
97
+ it "#activate! and #deactivate!" do
98
+ survey.activate!
99
+ survey.active?.should be_true
100
+ survey.deactivate!
101
+ survey.active?.should be_false
102
+ end
103
+ it "nils out past values of #inactive_at on #activate!" do
104
+ survey.inactive_at = 5.days.ago
105
+ survey.active?.should be_false
106
+ survey.activate!
107
+ survey.active?.should be_true
108
+ survey.inactive_at.should be_nil
109
+ end
110
+ it "nils out pas values of #active_at on #deactivate!" do
111
+ survey.active_at = 5.days.ago
112
+ survey.active?.should be_true
113
+ survey.deactivate!
114
+ survey.active?.should be_false
115
+ survey.active_at.should be_nil
116
+ end
117
+ end
118
+
119
+ context "with survey_sections" do
120
+ let(:s1){ Factory(:survey_section, :survey => survey, :title => "wise", :display_order => 2)}
121
+ let(:s2){ Factory(:survey_section, :survey => survey, :title => "er", :display_order => 3)}
122
+ let(:s3){ Factory(:survey_section, :survey => survey, :title => "bud", :display_order => 1)}
123
+ let(:q1){ Factory(:question, :survey_section => s1, :text => "what is wise?", :display_order => 2)}
124
+ let(:q2){ Factory(:question, :survey_section => s2, :text => "what is er?", :display_order => 4)}
125
+ let(:q3){ Factory(:question, :survey_section => s2, :text => "what is mill?", :display_order => 3)}
126
+ let(:q4){ Factory(:question, :survey_section => s3, :text => "what is bud?", :display_order => 1)}
127
+ before do
128
+ [s1, s2, s3].each{|s| survey.sections << s }
129
+ s1.questions << q1
130
+ s2.questions << q2
131
+ s2.questions << q3
132
+ s3.questions << q4
133
+ end
134
+
135
+ it{ survey.should have(3).sections}
136
+ it "gets survey_sections in order" do
137
+ survey.sections.should == [s3, s1, s2]
138
+ survey.sections.map(&:display_order).should == [1,2,3]
139
+ end
140
+ it "gets survey_sections_with_questions in order" do
141
+ survey.sections_with_questions.map(&:questions).flatten.should have(4).questions
142
+ survey.sections_with_questions.map(&:questions).flatten.should == [q4,q1,q3,q2]
143
+ end
144
+ it "deletes child survey_sections when deleted" do
145
+ survey_section_ids = survey.sections.map(&:id)
146
+ survey.destroy
147
+ survey_section_ids.each{|id| SurveySection.find_by_id(id).should be_nil}
148
+ end
149
+ end
150
+
151
+ context "serialization" do
152
+ let(:s1){ Factory(:survey_section, :survey => survey, :title => "wise") }
153
+ let(:s2){ Factory(:survey_section, :survey => survey, :title => "er") }
154
+ let(:q1){ Factory(:question, :survey_section => s1, :text => "what is wise?") }
155
+ let(:q2){ Factory(:question, :survey_section => s2, :text => "what is er?") }
156
+ let(:q3){ Factory(:question, :survey_section => s2, :text => "what is mill?") }
157
+ before do
158
+ [s1, s2].each{|s| survey.sections << s }
159
+ s1.questions << q1
160
+ s2.questions << q2
161
+ s2.questions << q3
162
+ end
163
+
164
+ it "includes title, sections, and questions" do
165
+ actual = survey.as_json
166
+ actual[:title].should == 'Simple survey'
167
+ actual[:sections].size.should == 2
168
+ actual[:sections][0][:questions_and_groups].size.should == 1
169
+ actual[:sections][1][:questions_and_groups].size.should == 2
170
+ end
171
+ end
172
+
173
+ context "with translations" do
174
+ require 'yaml'
175
+ let(:survey_translation){
176
+ Factory(:survey_translation, :locale => :es, :translation => {
177
+ :title => "Un idioma nunca es suficiente"
178
+ }.to_yaml)
179
+ }
180
+ before do
181
+ survey.translations << survey_translation
182
+ end
183
+ it "returns its own translation" do
184
+ YAML.load(survey_translation.translation).should_not be_nil
185
+ survey.translation(:es)[:title].should == "Un idioma nunca es suficiente"
186
+ end
187
+ it "returns its own default values" do
188
+ survey.translation(:de).should == {"title" => survey.title, "description" => survey.description}
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,113 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe ValidationCondition, "Class methods" do
4
+ it "should have a list of operators" do
5
+ %w(== != < > <= >= =~).each{|operator| ValidationCondition.operators.include?(operator).should be_true }
6
+ end
7
+ end
8
+
9
+ describe ValidationCondition do
10
+ before(:each) do
11
+ @validation_condition = Factory(:validation_condition)
12
+ end
13
+
14
+ it "should be valid" do
15
+ @validation_condition.should be_valid
16
+ end
17
+ # this causes issues with building and saving
18
+ # it "should be invalid without a parent validation_id" do
19
+ # @validation_condition.validation_id = nil
20
+ # @validation_condition.should have(1).errors_on(:validation_id)
21
+ # end
22
+
23
+ it "should be invalid without an operator" do
24
+ @validation_condition.operator = nil
25
+ @validation_condition.should have(2).errors_on(:operator)
26
+ end
27
+
28
+ it "should be invalid without a rule_key" do
29
+ @validation_condition.should be_valid
30
+ @validation_condition.rule_key = nil
31
+ @validation_condition.should_not be_valid
32
+ @validation_condition.should have(1).errors_on(:rule_key)
33
+ end
34
+
35
+ it "should have unique rule_key within the context of a validation" do
36
+ @validation_condition.should be_valid
37
+ Factory(:validation_condition, :validation_id => 2, :rule_key => "2")
38
+ @validation_condition.rule_key = "2" #rule key uniquness is scoped by validation_id
39
+ @validation_condition.validation_id = 2
40
+ @validation_condition.should_not be_valid
41
+ @validation_condition.should have(1).errors_on(:rule_key)
42
+ end
43
+
44
+ it "should have an operator in ValidationCondition.operators" do
45
+ ValidationCondition.operators.each do |o|
46
+ @validation_condition.operator = o
47
+ @validation_condition.should have(0).errors_on(:operator)
48
+ end
49
+ @validation_condition.operator = "#"
50
+ @validation_condition.should have(1).error_on(:operator)
51
+ end
52
+ it "should protect timestamps" do
53
+ saved_attrs = @validation_condition.attributes
54
+ if defined? ActiveModel::MassAssignmentSecurity::Error
55
+ lambda {@validation_condition.update_attributes(:created_at => 3.days.ago, :updated_at => 3.hours.ago)}.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
56
+ else
57
+ @validation_condition.attributes = {:created_at => 3.days.ago, :updated_at => 3.hours.ago} # automatically protected by Rails
58
+ end
59
+ @validation_condition.attributes.should == saved_attrs
60
+ end
61
+ end
62
+
63
+ describe ValidationCondition, "validating responses" do
64
+ def test_var(vhash, ahash, rhash)
65
+ v = Factory(:validation_condition, vhash)
66
+ a = Factory(:answer, ahash)
67
+ r = Factory(:response, {:answer => a, :question => a.question}.merge(rhash))
68
+ return v.is_valid?(r)
69
+ end
70
+
71
+ it "should validate a response by regexp" do
72
+ test_var({:operator => "=~", :regexp => /^[a-z]{1,6}$/}, {:response_class => "string"}, {:string_value => "clear"}).should be_true
73
+ test_var({:operator => "=~", :regexp => /^[a-z]{1,6}$/}, {:response_class => "string"}, {:string_value => "foobarbaz"}).should be_false
74
+ end
75
+ it "should validate a response by integer comparison" do
76
+ test_var({:operator => ">", :integer_value => 3}, {:response_class => "integer"}, {:integer_value => 4}).should be_true
77
+ test_var({:operator => "<=", :integer_value => 256}, {:response_class => "integer"}, {:integer_value => 512}).should be_false
78
+ end
79
+ it "should validate a response by (in)equality" do
80
+ test_var({:operator => "!=", :datetime_value => Date.today + 1}, {:response_class => "date"}, {:datetime_value => Date.today}).should be_true
81
+ test_var({:operator => "==", :string_value => "foo"}, {:response_class => "string"}, {:string_value => "foo"}).should be_true
82
+ end
83
+ it "should represent itself as a hash" do
84
+ @v = Factory(:validation_condition, :rule_key => "A")
85
+ @v.stub!(:is_valid?).and_return(true)
86
+ @v.to_hash("foo").should == {:A => true}
87
+ @v.stub!(:is_valid?).and_return(false)
88
+ @v.to_hash("foo").should == {:A => false}
89
+ end
90
+ end
91
+
92
+ describe ValidationCondition, "validating responses by other responses" do
93
+ def test_var(v_hash, a_hash, r_hash, ca_hash, cr_hash)
94
+ ca = Factory(:answer, ca_hash)
95
+ cr = Factory(:response, cr_hash.merge(:answer => ca, :question => ca.question))
96
+ v = Factory(:validation_condition, v_hash.merge({:question_id => ca.question.id, :answer_id => ca.id}))
97
+ a = Factory(:answer, a_hash)
98
+ r = Factory(:response, r_hash.merge(:answer => a, :question => a.question))
99
+ return v.is_valid?(r)
100
+ end
101
+ it "should validate a response by integer comparison" do
102
+ test_var({:operator => ">"}, {:response_class => "integer"}, {:integer_value => 4}, {:response_class => "integer"}, {:integer_value => 3}).should be_true
103
+ test_var({:operator => "<="}, {:response_class => "integer"}, {:integer_value => 512}, {:response_class => "integer"}, {:integer_value => 4}).should be_false
104
+ end
105
+ it "should validate a response by (in)equality" do
106
+ test_var({:operator => "!="}, {:response_class => "date"}, {:datetime_value => Date.today}, {:response_class => "date"}, {:datetime_value => Date.today + 1}).should be_true
107
+ test_var({:operator => "=="}, {:response_class => "string"}, {:string_value => "donuts"}, {:response_class => "string"}, {:string_value => "donuts"}).should be_true
108
+ end
109
+ it "should not validate a response by regexp" do
110
+ test_var({:operator => "=~"}, {:response_class => "date"}, {:datetime_value => Date.today}, {:response_class => "date"}, {:datetime_value => Date.today + 1}).should be_false
111
+ test_var({:operator => "=~"}, {:response_class => "string"}, {:string_value => "donuts"}, {:response_class => "string"}, {:string_value => "donuts"}).should be_false
112
+ end
113
+ end
@@ -0,0 +1,74 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Validation do
4
+ before(:each) do
5
+ @validation = Factory(:validation)
6
+ end
7
+
8
+ it "should be valid" do
9
+ @validation.should be_valid
10
+ end
11
+
12
+ it "should be invalid without a rule" do
13
+ @validation.rule = nil
14
+ @validation.should have(2).errors_on(:rule)
15
+ @validation.rule = " "
16
+ @validation.should have(1).errors_on(:rule)
17
+ end
18
+
19
+ # this causes issues with building and saving
20
+ # it "should be invalid without a answer_id" do
21
+ # @validation.answer_id = nil
22
+ # @validation.should have(1).error_on(:answer_id)
23
+ # end
24
+
25
+ it "should be invalid unless rule composed of only references and operators" do
26
+ @validation.rule = "foo"
27
+ @validation.should have(1).error_on(:rule)
28
+ @validation.rule = "1 to 2"
29
+ @validation.should have(1).error_on(:rule)
30
+ @validation.rule = "a and b"
31
+ @validation.should have(1).error_on(:rule)
32
+ end
33
+ it "should protect timestamps" do
34
+ saved_attrs = @validation.attributes
35
+ if defined? ActiveModel::MassAssignmentSecurity::Error
36
+ lambda {@validation.update_attributes(:created_at => 3.days.ago, :updated_at => 3.hours.ago)}.should raise_error(ActiveModel::MassAssignmentSecurity::Error)
37
+ else
38
+ @validation.attributes = {:created_at => 3.days.ago, :updated_at => 3.hours.ago} # automatically protected by Rails
39
+ @validation.attributes = {:created_at => 3.days.ago, :updated_at => 3.hours.ago} # automatically protected by Rails
40
+ end
41
+ @validation.attributes.should == saved_attrs
42
+ end
43
+ end
44
+ describe Validation, "reporting its status" do
45
+ def test_var(vhash, vchashes, ahash, rhash)
46
+ a = Factory(:answer, ahash)
47
+ v = Factory(:validation, {:answer => a, :rule => "A"}.merge(vhash))
48
+ vchashes.each do |vchash|
49
+ Factory(:validation_condition, {:validation => v, :rule_key => "A"}.merge(vchash))
50
+ end
51
+ rs = Factory(:response_set)
52
+ r = Factory(:response, {:answer => a, :question => a.question}.merge(rhash))
53
+ rs.responses << r
54
+ return v.is_valid?(rs)
55
+ end
56
+
57
+ it "should validate a response by integer comparison" do
58
+ test_var({:rule => "A and B"}, [{:operator => ">=", :integer_value => 0}, {:rule_key => "B", :operator => "<=", :integer_value => 120}], {:response_class => "integer"}, {:integer_value => 48}).should be_true
59
+ end
60
+ it "should validate a response by regexp" do
61
+ test_var({}, [{:operator => "=~", :regexp => /^[a-z]{1,6}$/}], {:response_class => "string"}, {:string_value => ""}).should be_false
62
+ end
63
+ end
64
+ describe Validation, "with conditions" do
65
+ it "should destroy conditions when destroyed" do
66
+ @validation = Factory(:validation)
67
+ Factory(:validation_condition, :validation => @validation, :rule_key => "A")
68
+ Factory(:validation_condition, :validation => @validation, :rule_key => "B")
69
+ Factory(:validation_condition, :validation => @validation, :rule_key => "C")
70
+ v_ids = @validation.validation_conditions.map(&:id)
71
+ @validation.destroy
72
+ v_ids.each{|id| DependencyCondition.find_by_id(id).should == nil}
73
+ end
74
+ end
data/spec/rcov.opts ADDED
@@ -0,0 +1,2 @@
1
+ --exclude "spec/*,gems/*"
2
+ --rails