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