surveyor 0.22.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +1 -0
  3. data/CHANGELOG.md +83 -0
  4. data/Gemfile.rails_version +7 -1
  5. data/README.md +114 -16
  6. data/Rakefile +15 -9
  7. data/app/inputs/quiet_input.rb +5 -0
  8. data/app/inputs/surveyor_check_boxes_input.rb +35 -0
  9. data/app/inputs/surveyor_radio_input.rb +18 -0
  10. data/app/views/partials/_answer.html.haml +4 -4
  11. data/app/views/partials/_question.html.haml +7 -7
  12. data/app/views/partials/_question_group.html.haml +9 -8
  13. data/app/views/surveyor/export.json.rabl +28 -25
  14. data/app/views/surveyor/new.html.haml +8 -5
  15. data/app/views/surveyor/show.json.rabl +3 -2
  16. data/ci-exec.sh +13 -7
  17. data/cucumber.yml +3 -3
  18. data/doc/REPRESENTATIONS.md +34 -0
  19. data/doc/api_id_schema.json +7 -0
  20. data/doc/response_set_schema.json +54 -0
  21. data/doc/surveyor question combinations.png +0 -0
  22. data/doc/surveyor_timestamp_schema.json +9 -0
  23. data/features/ajax_submissions.feature +140 -0
  24. data/features/export_to_json.feature +182 -34
  25. data/features/no_duplicates.feature +110 -0
  26. data/features/show_survey.feature +1 -1
  27. data/features/step_definitions/parser_steps.rb +25 -2
  28. data/features/step_definitions/surveyor_steps.rb +145 -20
  29. data/features/step_definitions/ui_steps.rb +3 -0
  30. data/features/support/database_cleaner.rb +16 -0
  31. data/features/support/env.rb +21 -17
  32. data/features/support/simultaneous_ajax.rb +101 -0
  33. data/features/support/single_quit_selenium_driver.rb +23 -0
  34. data/features/support/slow_updates.rb +18 -0
  35. data/features/surveyor.feature +174 -44
  36. data/features/surveyor_dependencies.feature +80 -39
  37. data/features/surveyor_parser.feature +114 -20
  38. data/features/z_redcap_parser.feature +0 -1
  39. data/lib/{generators/surveyor/templates/public → assets}/images/surveyor/next.gif +0 -0
  40. data/lib/{generators/surveyor/templates/public → assets}/images/surveyor/prev.gif +0 -0
  41. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  42. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  43. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_glass_65_ffffff_1x400.png +0 -0
  44. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  45. data/lib/assets/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  46. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  47. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-icons_ef8c08_256x240.png +0 -0
  48. data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-icons_ffffff_256x240.png +0 -0
  49. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery-ui-timepicker-addon.js +23 -23
  50. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery-ui.js +125 -125
  51. data/lib/assets/javascripts/surveyor/jquery.selectToUISlider.js +240 -0
  52. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery.surveyor.js +52 -57
  53. data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery.tools.min.js +7 -7
  54. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/dateinput.css +13 -13
  55. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/jquery-ui-timepicker-addon.css +0 -0
  56. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/jquery-ui.custom.css +17 -17
  57. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/reset.css +1 -1
  58. data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/results.css +0 -0
  59. data/lib/assets/stylesheets/surveyor/ui.slider.extras.css +110 -0
  60. data/lib/{generators/surveyor/templates/public/stylesheets/sass → assets/stylesheets}/surveyor.sass +15 -7
  61. data/lib/generators/surveyor/custom_generator.rb +3 -2
  62. data/lib/generators/surveyor/install_generator.rb +59 -17
  63. data/lib/generators/surveyor/templates/app/assets/javascripts/surveyor_all.js +5 -0
  64. data/lib/generators/surveyor/templates/app/assets/stylesheets/surveyor_all.css +9 -0
  65. data/lib/generators/surveyor/templates/app/controllers/surveyor_controller.rb +2 -1
  66. data/lib/generators/surveyor/templates/app/views/layouts/surveyor_custom.html.erb +1 -0
  67. data/lib/generators/surveyor/templates/config/locales/surveyor_es.yml +1 -0
  68. data/lib/generators/surveyor/templates/config/locales/surveyor_he.yml +1 -0
  69. data/lib/generators/surveyor/templates/db/migrate/add_api_id_to_question_groups.rb +1 -0
  70. data/lib/generators/surveyor/templates/db/migrate/add_api_ids.rb +1 -0
  71. data/lib/generators/surveyor/templates/db/migrate/add_api_ids_to_response_sets_and_responses.rb +1 -0
  72. data/lib/generators/surveyor/templates/db/migrate/add_correct_answer_id_to_questions.rb +1 -0
  73. data/lib/generators/surveyor/templates/db/migrate/add_default_value_to_answers.rb +1 -0
  74. data/lib/generators/surveyor/templates/db/migrate/add_display_order_to_surveys.rb +1 -0
  75. data/lib/generators/surveyor/templates/db/migrate/add_display_type_to_answers.rb +1 -0
  76. data/lib/generators/surveyor/templates/db/migrate/add_index_to_response_sets.rb +1 -0
  77. data/lib/generators/surveyor/templates/db/migrate/add_index_to_surveys.rb +1 -0
  78. data/lib/generators/surveyor/templates/db/migrate/add_section_id_to_responses.rb +1 -0
  79. data/lib/generators/surveyor/templates/db/migrate/add_unique_index_on_access_code_and_version_in_surveys.rb +10 -0
  80. data/lib/generators/surveyor/templates/db/migrate/add_unique_indicies.rb +3 -2
  81. data/lib/generators/surveyor/templates/db/migrate/add_version_to_surveys.rb +10 -0
  82. data/lib/generators/surveyor/templates/db/migrate/api_ids_must_be_unique.rb +23 -0
  83. data/lib/generators/surveyor/templates/db/migrate/create_answers.rb +6 -5
  84. data/lib/generators/surveyor/templates/db/migrate/create_dependencies.rb +2 -1
  85. data/lib/generators/surveyor/templates/db/migrate/create_dependency_conditions.rb +2 -1
  86. data/lib/generators/surveyor/templates/db/migrate/create_question_groups.rb +5 -4
  87. data/lib/generators/surveyor/templates/db/migrate/create_questions.rb +4 -3
  88. data/lib/generators/surveyor/templates/db/migrate/create_response_sets.rb +1 -0
  89. data/lib/generators/surveyor/templates/db/migrate/create_responses.rb +10 -9
  90. data/lib/generators/surveyor/templates/db/migrate/create_survey_sections.rb +5 -4
  91. data/lib/generators/surveyor/templates/db/migrate/create_surveys.rb +4 -3
  92. data/lib/generators/surveyor/templates/db/migrate/create_validation_conditions.rb +5 -4
  93. data/lib/generators/surveyor/templates/db/migrate/create_validations.rb +3 -2
  94. data/lib/generators/surveyor/templates/db/migrate/drop_unique_index_on_access_code_in_surveys.rb +10 -0
  95. data/lib/generators/surveyor/templates/db/migrate/update_blank_api_ids_on_question_group.rb +22 -0
  96. data/lib/generators/surveyor/templates/db/migrate/update_blank_versions_on_surveys.rb +13 -0
  97. data/lib/generators/surveyor/templates/surveys/date_survey.rb +1 -0
  98. data/lib/generators/surveyor/templates/surveys/kitchen_sink_survey.rb +54 -24
  99. data/lib/generators/surveyor/templates/surveys/quiz.rb +1 -0
  100. data/lib/generators/surveyor/templates/{public/stylesheets/sass → vendor/assets/stylesheets}/custom.sass +1 -1
  101. data/lib/surveyor/common.rb +16 -31
  102. data/lib/surveyor/engine.rb +2 -4
  103. data/lib/surveyor/helpers/asset_pipeline.rb +13 -0
  104. data/lib/surveyor/helpers/formtastic_custom_input.rb +17 -0
  105. data/lib/surveyor/helpers/surveyor_helper_methods.rb +10 -8
  106. data/lib/surveyor/models/answer_methods.rb +3 -0
  107. data/lib/surveyor/models/dependency_condition_methods.rb +27 -28
  108. data/lib/surveyor/models/dependency_methods.rb +3 -0
  109. data/lib/surveyor/models/question_group_methods.rb +3 -0
  110. data/lib/surveyor/models/question_methods.rb +10 -7
  111. data/lib/surveyor/models/response_methods.rb +16 -0
  112. data/lib/surveyor/models/response_set_methods.rb +71 -64
  113. data/lib/surveyor/models/survey_methods.rb +19 -28
  114. data/lib/surveyor/models/survey_section_methods.rb +3 -0
  115. data/lib/surveyor/models/validation_condition_methods.rb +4 -2
  116. data/lib/surveyor/models/validation_methods.rb +3 -0
  117. data/lib/surveyor/parser.rb +198 -148
  118. data/lib/surveyor/redcap_parser.rb +120 -80
  119. data/lib/surveyor/surveyor_controller_methods.rb +86 -37
  120. data/lib/surveyor/version.rb +2 -2
  121. data/lib/surveyor.rb +5 -6
  122. data/lib/tasks/surveyor_tasks.rake +19 -7
  123. data/spec/controllers/surveyor_controller_spec.rb +166 -92
  124. data/spec/factories.rb +33 -32
  125. data/spec/helpers/formtastic_custom_input_spec.rb +16 -0
  126. data/spec/lib/common_spec.rb +0 -39
  127. data/spec/lib/redcap_parser_spec.rb +24 -24
  128. data/spec/models/answer_spec.rb +12 -0
  129. data/spec/models/dependency_condition_spec.rb +279 -323
  130. data/spec/models/dependency_spec.rb +10 -0
  131. data/spec/models/question_group_spec.rb +12 -0
  132. data/spec/models/question_spec.rb +12 -0
  133. data/spec/models/response_set_spec.rb +189 -139
  134. data/spec/models/response_spec.rb +60 -0
  135. data/spec/models/survey_section_spec.rb +9 -0
  136. data/spec/models/survey_spec.rb +72 -9
  137. data/spec/models/validation_condition_spec.rb +9 -1
  138. data/spec/models/validation_spec.rb +10 -0
  139. data/spec/spec_helper.rb +25 -6
  140. data/surveyor.gemspec +5 -4
  141. metadata +332 -291
  142. data/features/step_definitions/common_steps.rb +0 -3
  143. data/lib/formtastic/surveyor_builder.rb +0 -82
  144. data/lib/generators/surveyor/templates/public/javascripts/surveyor/jquery.blockUI.js +0 -499
@@ -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
- page.should have_selector %(fieldset[name="#{text}"][class!="q_hidden"])
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
- Then /^there should be a datetime response with today's date$/ do
78
- # Response.datetime_value returns ActiveSupport::TimeWithZone
79
- # so we call .to_date on it for the comparison with Date.today
80
- Response.all.one?{|x| x.datetime_value.to_date == Date.today}.should be_true
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
- Then /^the json for "([^"]*)" should be$/ do |title, string|
92
- visit "/surveys/#{Survey.find_by_title(title).access_code}.json"
93
- puts page.find('body').text
94
- Surveyor::Common.equal_json_excluding_wildcards(page.find('body').text, string).should == true
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 /^the json for the last response set for "([^"]*)" should be$/ do |title, string|
98
- (survey = Survey.find_by_title(title)).should_not be_nil
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
- Given /^I have survey context of "([^"]*)"$/ do |context|
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
- When /^I follow today's date$/ do
138
- steps %Q{
139
- When I follow "#{Date.today.strftime('%d').to_i}"
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,3 @@
1
+ When /^I select "([^"]+)" as the datepicker's (\w+)$/ do |selection, type|
2
+ page.find(".ui-datepicker-#{type}").select(selection)
3
+ 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
@@ -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
- # Remove/comment out the lines below if your app doesn't have a database.
33
- # For some databases (like MongoDB and CouchDB) you may need to use :truncation instead.
34
- begin
35
- DatabaseCleaner.strategy = :transaction
36
- rescue NameError
37
- raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it."
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
- # You may also want to configure DatabaseCleaner to use different strategies for certain features and scenarios.
41
- # See the DatabaseCleaner documentation for details. Example:
42
- #
43
- # Before('@no-txn,@selenium,@culerity,@celerity,@javascript') do
44
- # DatabaseCleaner.strategy = :truncation, {:except => %w[widgets]}
45
- # end
46
- #
47
- # Before('~@no-txn', '~@selenium', '~@culerity', '~@celerity', '~@javascript') do
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