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
@@ -4,6 +4,31 @@ When /^I start the "([^"]*)" survey$/ do |name|
|
|
4
4
|
Then I should see "#{name}\"
|
5
5
|
When I press "Take it"
|
6
6
|
}
|
7
|
+
@survey_code = current_path.split("/")[2] # /surveys/:survey_code/:response_set_code/take
|
8
|
+
@response_set_code = current_path.split("/")[3] # /surveys/:survey_code/:response_set_code/take
|
9
|
+
end
|
10
|
+
|
11
|
+
When /^I start the survey$/ do
|
12
|
+
steps %Q{
|
13
|
+
When I go to the surveys page
|
14
|
+
And I press "Take it"
|
15
|
+
}
|
16
|
+
@survey_code = current_path.split("/")[2] # /surveys/:survey_code/:response_set_code/take
|
17
|
+
@response_set_code = current_path.split("/")[3] # /surveys/:survey_code/:response_set_code/take
|
18
|
+
end
|
19
|
+
|
20
|
+
# When I fill in the (nth) (string) for "(ref_id)" with "(value to fill)"
|
21
|
+
When /^I fill in the (\d+[a-z]{0,2} )?(\w+) for "([^"]+)" with "([^"]+)"$/ do |index, type, answer_reference_id, value|
|
22
|
+
answer = Answer.where(:reference_identifier => answer_reference_id).first
|
23
|
+
fail "No answer with ref ID #{answer_reference_id.inspect}" unless answer
|
24
|
+
|
25
|
+
i = index ? index.to_i - 1 : 0
|
26
|
+
|
27
|
+
answer_input_id = page.all("input[@value='#{answer.id}']").
|
28
|
+
select { |x| x['id'] =~ /answer_id/ }[i]['id']
|
29
|
+
ordinal = answer_input_id.scan(/r_(\d+)/).first.first
|
30
|
+
value_input_id = "r_#{ordinal}_#{type}_value"
|
31
|
+
page.fill_in(value_input_id, :with => value)
|
7
32
|
end
|
8
33
|
|
9
34
|
Then /^there should be (\d+) response set with (\d+) responses? with:$/ do |rs_num, r_num, table|
|
@@ -66,7 +91,13 @@ Then /^there should be (\d+) text areas$/ do |count|
|
|
66
91
|
end
|
67
92
|
|
68
93
|
Then /^the question "([^"]*)" should be triggered$/ do |text|
|
69
|
-
|
94
|
+
q = Question.find_by_text(text)
|
95
|
+
page.should_not have_css %(fieldset#q_#{q.id}.q_hidden)
|
96
|
+
end
|
97
|
+
|
98
|
+
Then /^the question "(.*?)" should be hidden$/ do |text|
|
99
|
+
q = Question.find_by_text(text)
|
100
|
+
page.should have_css %(fieldset#q_#{q.id}.q_hidden)
|
70
101
|
end
|
71
102
|
|
72
103
|
Then /^there should be (\d+) response with answer "([^"]*)"$/ do |count, answer_text|
|
@@ -74,34 +105,91 @@ Then /^there should be (\d+) response with answer "([^"]*)"$/ do |count, answer_
|
|
74
105
|
Response.find_by_answer_id(Answer.find_by_text(answer_text)).should_not be_blank
|
75
106
|
end
|
76
107
|
|
77
|
-
|
78
|
-
#
|
79
|
-
|
80
|
-
|
108
|
+
When /^I choose row (\d+), column (\d+) of the grid$/ do |row, col|
|
109
|
+
find(".g_grid").find("tr:nth-child(#{row.to_i + 1})").find("td:nth-child(#{col.to_i + 1})").find("input").set(true)
|
110
|
+
end
|
111
|
+
|
112
|
+
Then /^there should (not )?be a (\w+ )?response(?: for answer "([^"]+)")?(?: with value "([^"]+)")?(?: on question "([^"]+)")?$/ do |neg, type, answer_reference_id, value, question_reference_id|
|
113
|
+
conditions = []
|
114
|
+
values = []
|
115
|
+
expected_count = neg.blank? ? 1 : 0
|
116
|
+
if type
|
117
|
+
attribute = case type.strip
|
118
|
+
when 'date'
|
119
|
+
'datetime_value'
|
120
|
+
when 'time'
|
121
|
+
'datetime_value'
|
122
|
+
else
|
123
|
+
"#{type.strip}_value"
|
124
|
+
end
|
125
|
+
if value
|
126
|
+
case type.strip
|
127
|
+
when 'date'
|
128
|
+
# Work around deficient SQLite date handling
|
129
|
+
conditions << "date(#{attribute}) = date(?)"
|
130
|
+
values << value
|
131
|
+
else
|
132
|
+
conditions << "#{attribute} = ?"
|
133
|
+
values << value
|
134
|
+
end
|
135
|
+
else
|
136
|
+
conditions << "#{attribute} IS NOT NULL"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
if question_reference_id
|
141
|
+
question = Question.where(:reference_identifier => question_reference_id).first
|
142
|
+
fail "No question with ref ID #{question_reference_id}" unless question
|
143
|
+
conditions << 'question_id = ?'
|
144
|
+
values << question
|
145
|
+
end
|
146
|
+
|
147
|
+
if answer_reference_id
|
148
|
+
a_conds = { :reference_identifier => answer_reference_id }
|
149
|
+
if question
|
150
|
+
a_conds[:question_id] = question
|
151
|
+
end
|
152
|
+
answer = Answer.where(a_conds).first
|
153
|
+
fail "No answer with ref ID #{answer_reference_id}" unless answer
|
154
|
+
conditions << 'answer_id = ?'
|
155
|
+
values << answer
|
156
|
+
end
|
157
|
+
|
158
|
+
Response.where(conditions.join(' AND '), *values).count.should == expected_count
|
81
159
|
end
|
82
160
|
|
83
161
|
Then /^I should see the image "([^"]*)"$/ do |src|
|
84
162
|
page.should have_selector %(img[src^="#{src}"])
|
85
163
|
end
|
86
164
|
|
165
|
+
Then /^I click elsewhere$/ do
|
166
|
+
page.find('.survey_title').click
|
167
|
+
end
|
168
|
+
|
87
169
|
Then /^(\d+) responses should exist$/ do |response_count|
|
88
170
|
Response.count.should == response_count.to_i
|
89
171
|
end
|
90
172
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
173
|
+
## JSON
|
174
|
+
|
175
|
+
def last_json
|
176
|
+
page.find('body').text
|
177
|
+
end
|
178
|
+
|
179
|
+
When /^I visit "(.*?)"$/ do |path|
|
180
|
+
visit path
|
95
181
|
end
|
96
182
|
|
97
|
-
Then /^
|
98
|
-
|
99
|
-
(response_set = ResponseSet.last(:conditions => {:survey_id => survey.id})).should_not be_nil
|
100
|
-
visit "/surveys/#{survey.access_code}/#{response_set.access_code}.json"
|
101
|
-
puts page.find('body').text
|
102
|
-
Surveyor::Common.equal_json_excluding_wildcards(page.find('body').text, string).should == true
|
183
|
+
Then /^I export the response set$/ do
|
184
|
+
visit "/surveys/#{@survey_code}/#{@response_set_code}.json"
|
103
185
|
end
|
104
186
|
|
187
|
+
Then /^the JSON response at "(.*?)" should correspond to an answer with text "(.*?)"$/ do |path, text|
|
188
|
+
last_json.should be_json_eql(JsonSpec.remember("\"#{Answer.find_by_text(text).api_id}\"")).at_path(path)
|
189
|
+
end
|
190
|
+
|
191
|
+
## Hidden and shown elements
|
192
|
+
|
105
193
|
Then /the element "([^\"]*)" should be hidden$/ do |selector|
|
106
194
|
wait_until do
|
107
195
|
its_hidden = page.evaluate_script("$('#{selector}').is(':hidden');")
|
@@ -117,7 +205,10 @@ Then /the element "([^\"]*)" should not be hidden$/ do |selector|
|
|
117
205
|
(its_not_hidden && its_in_dom).should be_true
|
118
206
|
end
|
119
207
|
end
|
120
|
-
|
208
|
+
|
209
|
+
## Context and substitution with mustache
|
210
|
+
|
211
|
+
Given /^I have survey context of "(.*)"$/ do |context|
|
121
212
|
class SurveyorController < ApplicationController
|
122
213
|
require 'mustache'
|
123
214
|
class FakeMustacheContext < ::Mustache
|
@@ -134,8 +225,42 @@ Given /^I have survey context of "([^"]*)"$/ do |context|
|
|
134
225
|
end
|
135
226
|
end
|
136
227
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
228
|
+
## Various input elements
|
229
|
+
|
230
|
+
Then /^I should see (\d+) textareas on the page$/ do |i|
|
231
|
+
page.has_css?('textarea', :count => i)
|
232
|
+
end
|
233
|
+
|
234
|
+
Then /^I should see (\d+) "(.*?)" input on the page$/ do |i, css_class|
|
235
|
+
page.has_css?("input.#{css_class}", :count => i)
|
236
|
+
end
|
237
|
+
|
238
|
+
Then /^I should see (\d+) select on the page$/ do |i|
|
239
|
+
page.has_css?("select", :count => i)
|
240
|
+
end
|
241
|
+
|
242
|
+
Then /^the checkbox for "(.*?)" should be (dis|en)abled$/ do |text, dis_or_en|
|
243
|
+
a = Answer.find_by_text(text)
|
244
|
+
a.should_not be_nil
|
245
|
+
element = find("input[value='#{a.id}']")
|
246
|
+
if dis_or_en == 'dis'
|
247
|
+
element['disabled'].should == 'true'
|
248
|
+
else
|
249
|
+
[nil, 'false'].should include(element['disabled'])
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# see support/simultaneous_ajax.rb
|
254
|
+
Then /^I wait for things to settle out( longer)?$/ do |longer|
|
255
|
+
if @simultaneous_ajax
|
256
|
+
Capybara.timeout(longer ? 120 : 10, page.driver, "waiting for all AJAX requests timed out") do
|
257
|
+
page.evaluate_script("window.surveyorIntegratedTestsRequestsOutstanding <= 0")
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
## quizzes
|
263
|
+
|
264
|
+
Then /^the question "(.*?)" should have correct answer "(.*?)"$/ do |q, a|
|
265
|
+
Question.find_by_reference_identifier(q).correct_answer.reference_identifier.should == a
|
141
266
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'database_cleaner'
|
2
|
+
|
3
|
+
# Use transaction cleaning for performance by default
|
4
|
+
DatabaseCleaner.strategy = :transaction
|
5
|
+
# Clean to start so that manual testing data in testbed can't cause false
|
6
|
+
# test results.
|
7
|
+
DatabaseCleaner.clean_with(:truncation)
|
8
|
+
|
9
|
+
# use truncation to allow direct DB checks in Selenium tests
|
10
|
+
Before('@javascript') do
|
11
|
+
DatabaseCleaner.strategy = :truncation
|
12
|
+
end
|
13
|
+
|
14
|
+
After('@javascript') do
|
15
|
+
DatabaseCleaner.strategy = :transaction
|
16
|
+
end
|
data/features/support/env.rb
CHANGED
@@ -5,6 +5,8 @@
|
|
5
5
|
# files.
|
6
6
|
ENV["RAILS_ROOT"] ||= File.expand_path(File.dirname(__FILE__) + '/../../testbed')
|
7
7
|
require 'cucumber/rails'
|
8
|
+
|
9
|
+
|
8
10
|
# require File.expand_path(File.dirname(__FILE__) + '/../../testbed/config/environment.rb')
|
9
11
|
# Capybara defaults to XPath selectors rather than Webrat's default of CSS3. In
|
10
12
|
# order to ease the transition to Capybara we set the default here. If you'd
|
@@ -29,22 +31,24 @@ Capybara.default_selector = :css
|
|
29
31
|
#
|
30
32
|
ActionController::Base.allow_rescue = false
|
31
33
|
|
32
|
-
#
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
# Use chrome for Selenium by default. This is because of Firefox's bug 566671
|
35
|
+
# (https://bugzilla.mozilla.org/show_bug.cgi?id=566671): any test that relies on
|
36
|
+
# blur or change events will not work when Firefox is in the background.
|
37
|
+
ENV['SELENIUM_BROWSER'] ||= 'chrome'
|
38
|
+
|
39
|
+
# Wait for AJAX requests to complete in selenium
|
40
|
+
# n.b.: Capybara 2.0 will change the way this works.
|
41
|
+
# http://groups.google.com/group/ruby-capybara/browse_thread/thread/6d955173ce413b0a/d0682d47a915dfbd
|
42
|
+
Capybara.register_driver :selenium do |app|
|
43
|
+
SingleQuitSeleniumDriver.new(app, :browser => ENV['SELENIUM_BROWSER'].to_sym,
|
44
|
+
:resynchronize => true)
|
38
45
|
end
|
39
46
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
# DatabaseCleaner.strategy = :transaction
|
49
|
-
# end
|
50
|
-
#
|
47
|
+
Before do |scenario|
|
48
|
+
Rails.logger.info "\n\nBeginning scenario #{scenario.file_colon_line} \"#{scenario.title}\""
|
49
|
+
end
|
50
|
+
|
51
|
+
require "json_spec/cucumber"
|
52
|
+
JsonSpec.configure do
|
53
|
+
exclude_keys "id", "created_at", "updated_at", "uuid", "modified_at", "completed_at"
|
54
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'selenium/webdriver'
|
2
|
+
|
3
|
+
Before('~@simultaneous_ajax') do
|
4
|
+
evict_capybara_session :selenium_nowait
|
5
|
+
end
|
6
|
+
|
7
|
+
Before('@simultaneous_ajax') do
|
8
|
+
evict_capybara_session :selenium
|
9
|
+
|
10
|
+
@simultaneous_ajax = true
|
11
|
+
Capybara.javascript_driver = Capybara.current_driver = :selenium_nowait
|
12
|
+
end
|
13
|
+
|
14
|
+
After('@simultaneous_ajax') do
|
15
|
+
Capybara.javascript_driver = nil
|
16
|
+
@simultaneous_ajax = false
|
17
|
+
end
|
18
|
+
|
19
|
+
# Evict the "other" selenium driver's session when switching drivers because it
|
20
|
+
# seems that WebDriver can't handle having two different browsers open
|
21
|
+
# simultaneously. Specifically, if you:
|
22
|
+
#
|
23
|
+
# * Run a test with driver A, then
|
24
|
+
# * Run set of tests with driver B, then
|
25
|
+
# * Attempt to run a test with driver A
|
26
|
+
#
|
27
|
+
# ... the final driver A test will hang attempting to contact some piece of the
|
28
|
+
# webdriver infrastructure. It will hang until it times out, or until you
|
29
|
+
# kill the browser associated with driver B. This code does the killing for
|
30
|
+
# you.
|
31
|
+
def evict_capybara_session(driver_name)
|
32
|
+
Capybara.instance_eval do
|
33
|
+
key = session_pool.keys.grep(/^#{driver_name}\:/).first
|
34
|
+
if key
|
35
|
+
session = session_pool.delete(key)
|
36
|
+
puts "key=#{key} evicting session #{session.object_id} driver #{session.driver.object_id}"
|
37
|
+
session.driver.quit
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class AjaxRefCountListener < Selenium::WebDriver::Support::AbstractEventListener
|
43
|
+
def refcount_key
|
44
|
+
'surveyorIntegratedTestsRequestsOutstanding'
|
45
|
+
end
|
46
|
+
|
47
|
+
def call(*args)
|
48
|
+
event = args.unshift
|
49
|
+
driver = args.pop
|
50
|
+
unless event.to_s =~ /script/ # prevent infinite recursion
|
51
|
+
enable_ajax_call_refcount_if_necessary(driver)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def enable_ajax_call_refcount_if_necessary(driver)
|
56
|
+
unless driver.execute_script "return window.hasOwnProperty('#{refcount_key}')"
|
57
|
+
enable_ajax_call_refcount(driver)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Taken wholesale from the resynchronize code in Capybara's Selenium driver.
|
62
|
+
# Replicated here because the Capybara implementation doesn't let you make
|
63
|
+
# multiple interactions issuing separate AJAX calls and then wait for all of
|
64
|
+
# them — it can only wait for those AJAX requests issued in the context of a
|
65
|
+
# single interaction (a click, etc.).
|
66
|
+
def enable_ajax_call_refcount(driver)
|
67
|
+
driver.execute_script <<-JS
|
68
|
+
window.#{refcount_key} = 0;
|
69
|
+
(function() { // Overriding XMLHttpRequest
|
70
|
+
var oldXHR = window.XMLHttpRequest;
|
71
|
+
|
72
|
+
function newXHR() {
|
73
|
+
var realXHR = new oldXHR();
|
74
|
+
|
75
|
+
window.#{refcount_key}++;
|
76
|
+
realXHR.addEventListener("readystatechange", function() {
|
77
|
+
if( realXHR.readyState == 4 ) {
|
78
|
+
setTimeout( function() {
|
79
|
+
window.#{refcount_key}--;
|
80
|
+
if(window.#{refcount_key} < 0) {
|
81
|
+
window.#{refcount_key} = 0;
|
82
|
+
}
|
83
|
+
}, 500 );
|
84
|
+
}
|
85
|
+
}, false);
|
86
|
+
|
87
|
+
return realXHR;
|
88
|
+
}
|
89
|
+
|
90
|
+
window.XMLHttpRequest = newXHR;
|
91
|
+
})();
|
92
|
+
JS
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Provides an alternative selenium driver with resync off and refcounting on.
|
97
|
+
# This allows for simulation of competing AJAX requests.
|
98
|
+
Capybara.register_driver :selenium_nowait do |app|
|
99
|
+
SingleQuitSeleniumDriver.new(app, :browser => ENV['SELENIUM_BROWSER'].to_sym,
|
100
|
+
:resynchronize => false, :listener => AjaxRefCountListener.new)
|
101
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'capybara/selenium/driver'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Subclass of `Capybara::Selenium::Driver` that ensures that quit is only called
|
5
|
+
# once.
|
6
|
+
#
|
7
|
+
# This is necessary because there is code in the Firefox selenium bridge which
|
8
|
+
# reacts poorly if its quit method is called more than once. The
|
9
|
+
# @simultaneous_ajax support for the duplicate check features requires a
|
10
|
+
# separate selenium driver instance, which in turn requires directly calling
|
11
|
+
# quit on the running driver when switching between them.
|
12
|
+
#
|
13
|
+
# Capybara::Selenium::Driver registers an `at_exit` hook which isn't removed
|
14
|
+
# when you call quit. This results in quit being called twice for some driver
|
15
|
+
# instances, provoking the issue with Firefox.
|
16
|
+
class SingleQuitSeleniumDriver < Capybara::Selenium::Driver
|
17
|
+
def quit
|
18
|
+
unless @already_quit
|
19
|
+
super
|
20
|
+
@already_quit = true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# It appears that rails caches the filter chain after the first request. We
|
2
|
+
# can't wait and only apply this if any @slow_updates scenarios are executed.
|
3
|
+
class SurveyorController
|
4
|
+
before_filter(:only => :update) do
|
5
|
+
if $delay_updates
|
6
|
+
Rails.logger.info "Slowing things down."
|
7
|
+
sleep 2
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
Before('@slow_updates') do
|
13
|
+
$delay_updates = true
|
14
|
+
end
|
15
|
+
|
16
|
+
After('@slow_updates') do
|
17
|
+
$delay_updates = false
|
18
|
+
end
|