gaq 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +15 -0
  3. data/Guardfile +23 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +102 -0
  6. data/Rakefile +1 -0
  7. data/gaq.gemspec +22 -0
  8. data/lib/gaq.rb +3 -0
  9. data/lib/gaq/class_cache.rb +32 -0
  10. data/lib/gaq/command_language.rb +145 -0
  11. data/lib/gaq/configuration.rb +159 -0
  12. data/lib/gaq/controller_facade.rb +15 -0
  13. data/lib/gaq/controller_handle.rb +93 -0
  14. data/lib/gaq/dsl_target.rb +51 -0
  15. data/lib/gaq/dsl_target_factory.rb +64 -0
  16. data/lib/gaq/flash_commands_adapter.rb +48 -0
  17. data/lib/gaq/interprets_config.rb +13 -0
  18. data/lib/gaq/railtie.rb +40 -0
  19. data/lib/gaq/snippet_renderer.rb +44 -0
  20. data/lib/gaq/version.rb +3 -0
  21. data/spec-dummy/.rspec +1 -0
  22. data/spec-dummy/README.rdoc +261 -0
  23. data/spec-dummy/Rakefile +7 -0
  24. data/spec-dummy/app/assets/images/rails.png +0 -0
  25. data/spec-dummy/app/assets/javascripts/application.js +13 -0
  26. data/spec-dummy/app/assets/stylesheets/application.css +13 -0
  27. data/spec-dummy/app/controllers/application_controller.rb +3 -0
  28. data/spec-dummy/app/controllers/integration_spec_controller.rb +22 -0
  29. data/spec-dummy/app/helpers/application_helper.rb +2 -0
  30. data/spec-dummy/app/views/integration_spec/view.erb +0 -0
  31. data/spec-dummy/app/views/layouts/application.html.erb +16 -0
  32. data/spec-dummy/config.ru +4 -0
  33. data/spec-dummy/config/application.rb +64 -0
  34. data/spec-dummy/config/boot.rb +6 -0
  35. data/spec-dummy/config/environment.rb +5 -0
  36. data/spec-dummy/config/environments/development.rb +26 -0
  37. data/spec-dummy/config/environments/production.rb +51 -0
  38. data/spec-dummy/config/environments/test_dynamic.rb +39 -0
  39. data/spec-dummy/config/environments/test_static.rb +38 -0
  40. data/spec-dummy/config/initializers/backtrace_silencers.rb +7 -0
  41. data/spec-dummy/config/initializers/inflections.rb +15 -0
  42. data/spec-dummy/config/initializers/mime_types.rb +5 -0
  43. data/spec-dummy/config/initializers/secret_token.rb +7 -0
  44. data/spec-dummy/config/initializers/session_store.rb +8 -0
  45. data/spec-dummy/config/initializers/wrap_parameters.rb +10 -0
  46. data/spec-dummy/config/locales/en.yml +5 -0
  47. data/spec-dummy/config/routes.rb +8 -0
  48. data/spec-dummy/db/seeds.rb +7 -0
  49. data/spec-dummy/public/404.html +26 -0
  50. data/spec-dummy/public/422.html +26 -0
  51. data/spec-dummy/public/500.html +25 -0
  52. data/spec-dummy/public/favicon.ico +0 -0
  53. data/spec-dummy/public/robots.txt +5 -0
  54. data/spec-dummy/script/rails +6 -0
  55. data/spec-dummy/spec/common_spec_methods.rb +88 -0
  56. data/spec-dummy/spec/features/dynamic_config_spec.rb +21 -0
  57. data/spec-dummy/spec/features/next_request_spec.rb +27 -0
  58. data/spec-dummy/spec/features/presence_spec.rb +64 -0
  59. data/spec-dummy/spec/spec_helper.rb +41 -0
  60. data/spec/lib/gaq/class_cache_spec.rb +62 -0
  61. data/spec/lib/gaq/command_language_spec.rb +267 -0
  62. data/spec/lib/gaq/configuration_spec.rb +233 -0
  63. data/spec/lib/gaq/controller_facade_spec.rb +29 -0
  64. data/spec/lib/gaq/controller_handle_spec.rb +510 -0
  65. data/spec/lib/gaq/dsl_target_factory_spec.rb +163 -0
  66. data/spec/lib/gaq/dsl_target_spec.rb +87 -0
  67. data/spec/lib/gaq/flash_commands_adapter_spec.rb +116 -0
  68. data/spec/lib/gaq/interprets_config_spec.rb +37 -0
  69. data/spec/lib/gaq/snippet_renderer_spec.rb +60 -0
  70. metadata +159 -0
@@ -0,0 +1,233 @@
1
+ require 'gaq/configuration'
2
+
3
+ require 'gaq/interprets_config'
4
+ require 'active_support/string_inquirer'
5
+
6
+ module Gaq
7
+ describe Configuration do
8
+ let(:rails) { subject.rails_config }
9
+
10
+ describe "rails exposed config object" do
11
+ describe ".declare_variable" do
12
+ describe "variable setup" do
13
+ let(:variable) do
14
+ subject.variables.should have(1).variable
15
+ subject.variables["my_variable"]
16
+ end
17
+
18
+ it "sets up variable name and slot correctly" do
19
+ rails.declare_variable("my_variable", slot: 2)
20
+
21
+ variable.name.should be == "my_variable"
22
+ variable.slot.should be 2
23
+ end
24
+
25
+ it "default to scope code 3" do
26
+ rails.declare_variable("my_variable", slot: 1)
27
+
28
+ variable.scope.should be 3
29
+ end
30
+
31
+ it "correctly sets up scope, given as symbol" do
32
+ rails.declare_variable("my_variable", slot: 1, scope: :visitor)
33
+
34
+ variable.scope.should be 1
35
+ end
36
+
37
+ it "correctly sets up scope, given as Fixnum" do
38
+ rails.declare_variable("my_variable", slot: 1, scope: 1)
39
+
40
+ variable.scope.should be 1
41
+ end
42
+
43
+ it "complains about unknown scope" do
44
+ expect do
45
+ rails.declare_variable("my_variable", slot: 1, scope: //)
46
+ end.to raise_exception(Configuration::VariableException)
47
+ end
48
+ end
49
+
50
+ it "complains about an invalid slot"
51
+
52
+ it "complains about a taken slot" do
53
+ rails.declare_variable("my_variable", slot: 1)
54
+ expect do
55
+ rails.declare_variable("my_other_variable", slot: 1)
56
+ end.to raise_exception Configuration::VariableException
57
+ end
58
+
59
+ it "complains about a taken name" do
60
+ rails.declare_variable("my_variable", slot: 1)
61
+ expect do
62
+ rails.declare_variable("my_variable", slot: 2)
63
+ end.to raise_exception Configuration::VariableException
64
+ end
65
+ end
66
+
67
+ describe ".additional_trackers=" do
68
+ it "complains about bad tracker names" do
69
+ pending "could not find out about that in the docs"
70
+ # probably, leading underscore is bad
71
+ end
72
+
73
+ it "makes tracker configs available" do
74
+ rails.additional_trackers = [:foo, :bar]
75
+
76
+ rails.tracker(:foo).should_not be rails.tracker(:bar)
77
+ end
78
+
79
+ it "tolerates symbols as well as strings" do
80
+ rails.additional_trackers = [:foo, "bar"]
81
+
82
+ rails.tracker(:foo).should_not be rails.tracker(:bar)
83
+ end
84
+
85
+ it "complains about name collision" do
86
+ expect { rails.additional_trackers = ["foo", "foo"] }.to raise_exception
87
+ end
88
+
89
+ it "complains about name collision even if symbol and string is used" do
90
+ expect { rails.additional_trackers = [:foo, "foo"] }.to raise_exception
91
+ end
92
+ end
93
+
94
+ def find_tracker_config_by_name(name)
95
+ name = name.to_s unless name.nil?
96
+ result = subject.tracker_config(name)
97
+ result.should_not be_nil
98
+ result
99
+ end
100
+
101
+ describe ".tracker" do
102
+ it "complains about a non-existent tracker name" do
103
+ expect{ rails.tracker(:nonexistent) }.to raise_exception
104
+ end
105
+
106
+ it "returns something for a valid tracker name" do
107
+ rails.additional_trackers = [:foo]
108
+
109
+ rails.tracker(:foo).should_not be_nil
110
+ end
111
+
112
+ it "tolerates symbols as well as strings" do
113
+ rails.additional_trackers = [:foo]
114
+
115
+ rails.tracker(:foo).should be rails.tracker("foo")
116
+ end
117
+
118
+ it "delegates tracker setting setters to tracker configs" do
119
+ rails.additional_trackers = [:foo]
120
+
121
+ rails_tracker = rails.tracker(:foo)
122
+ rails_tracker.web_property_id = "web property id"
123
+ rails_tracker.track_pageview = :production
124
+
125
+ tracker_config = find_tracker_config_by_name(:foo)
126
+ tracker_config.web_property_id.should be == "web property id"
127
+ tracker_config.track_pageview.should be :production
128
+ end
129
+ end
130
+
131
+ it "delegates tracker setting setters to default tracker rails config object" do
132
+ rails.web_property_id = "bla"
133
+
134
+ default_tracker_config = find_tracker_config_by_name(nil)
135
+
136
+ default_tracker_config.web_property_id.should be == "bla"
137
+ end
138
+ end
139
+
140
+ describe "tracker_config" do
141
+ it "returns nil when no tracker config by given name" do
142
+ subject.tracker_config('bogus').should be_nil
143
+ end
144
+
145
+ it "returns a different object per tracker name" do
146
+ rails.additional_trackers = [:foo, :bar]
147
+
148
+ subject.tracker_config('foo').should_not be subject.tracker_config('bar')
149
+ end
150
+
151
+ it "only accepts tracker names as strings" do
152
+ rails.additional_trackers = [:foo, :bar]
153
+
154
+ subject.tracker_config(:foo).should be_nil
155
+ end
156
+ end
157
+
158
+ describe "tracker_names" do
159
+ it "initially is [nil]" do
160
+ subject.tracker_names.should be == [nil]
161
+ end
162
+
163
+ context "after gaq.additional_trackers = [:foo, :bar]" do
164
+ before do
165
+ rails.additional_trackers = [:foo, :bar]
166
+ end
167
+
168
+ it "contains nil, 'foo' and 'bar'" do
169
+ subject.tracker_names.should have(3).tracker_names
170
+ subject.tracker_names.should include(nil)
171
+ subject.tracker_names.should include('foo')
172
+ subject.tracker_names.should include('bar')
173
+ end
174
+ end
175
+ end
176
+
177
+ describe "#render_ga_js?" do
178
+ let(:environment) do
179
+ ActiveSupport::StringInquirer.new(example.metadata[:env].to_s)
180
+ end
181
+
182
+ def result
183
+ subject.render_ga_js?(environment)
184
+ end
185
+
186
+
187
+ describe "this StringInquirer setup" do
188
+ it "works as expected", env: :development do
189
+ environment.should be_development
190
+ environment.should_not be_production
191
+ end
192
+ end
193
+
194
+ context "with rails_config.render_ga_js = true or false" do
195
+ it "reports those values " do
196
+ rails.render_ga_js = true
197
+ result.should be == true
198
+
199
+ rails.render_ga_js = false
200
+ result.should be == false
201
+ end
202
+ end
203
+
204
+ context "with rails_config.render_ga_ja = some symbol" do
205
+ it "compares with given environment", env: :production do
206
+ rails.render_ga_js = :production
207
+ result.should be == true
208
+
209
+ rails.render_ga_js = :development
210
+ result.should be == false
211
+ end
212
+ end
213
+
214
+ context "with rails_config.render_ga_ja = Array of symbols" do
215
+ it "compares with given environment", env: :production do
216
+ rails.render_ga_js = [:production, :test]
217
+ result.should be == true
218
+
219
+ rails.render_ga_js = [:development, :test]
220
+ result.should be == false
221
+ end
222
+ end
223
+
224
+ context "with rails_config.render_ga_ja = callable object (for interpret_config)" do
225
+ it "passes it through" do
226
+ lmbda = ->{}
227
+ rails.render_ga_js = lmbda
228
+ result.should be lmbda
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,29 @@
1
+ require 'gaq/controller_facade'
2
+
3
+ module Gaq
4
+ describe ControllerFacade do
5
+ let(:flash) { Object.new }
6
+ let(:controller) do
7
+ Struct.new(:flash).new(flash)
8
+ end
9
+
10
+ subject do
11
+ described_class.new(controller)
12
+ end
13
+
14
+ describe '#flash' do
15
+ it "returns controller's flash" do
16
+ subject.flash.should be(flash)
17
+ end
18
+ end
19
+
20
+ describe '#evaluate_config_lambda' do
21
+ it "calls the lambda, passing controller, returning lambda result" do
22
+ lmbda = ->(*args) { args }
23
+ result = subject.evaluate_config_lambda(lmbda)
24
+ result.should have(1).item
25
+ result.first.should be(controller)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,510 @@
1
+ require 'gaq/controller_handle'
2
+ require 'gaq/configuration'
3
+ require 'gaq/class_cache'
4
+
5
+ RSpec::Matchers.define :command_segments do
6
+ match do |segments|
7
+ next false unless segments.is_a?(Array) and segments.length >= 1
8
+ first_segment = segments.first
9
+ command, tracker = first_segment.split('.', 2).reverse
10
+
11
+ (!@first_segment || first_segment == @first_segment) &&
12
+ (!@tracker_name || tracker.to_s == @tracker_name)
13
+ end
14
+
15
+ chain :starting_with do |first_segment|
16
+ @first_segment = first_segment
17
+ end
18
+
19
+ chain :for_tracker do |tracker_name|
20
+ @tracker_name = tracker_name.to_s
21
+ end
22
+
23
+ chain :for_default_tracker do
24
+ @tracker_name = ''
25
+ end
26
+ end
27
+
28
+ describe "custom matcher" do
29
+ it "detects commands" do
30
+ [['foo']].should include(command_segments.starting_with('foo'))
31
+ [['foo']].should_not include(command_segments.starting_with('bar'))
32
+ [['tracker.foo']].should_not include(command_segments.starting_with('foo'))
33
+ [['tracker.foo']].should include(command_segments.starting_with('tracker.foo'))
34
+
35
+ [['foo']].should include(command_segments.for_default_tracker)
36
+ [['foo']].should_not include(command_segments.for_tracker('tracker'))
37
+ [['tracker.foo']].should include(command_segments.for_tracker('tracker'))
38
+ [['tracker.foo']].should_not include(command_segments.for_default_tracker)
39
+ end
40
+ end
41
+
42
+ module Gaq
43
+ describe ControllerHandle do
44
+ describe "#finalized_commands_as_segments" do
45
+ # This is more like an integration test. Many things are stubbed by real representatives,
46
+ # e.g. a real CommandLanguage object (because that's easier).
47
+ # The purpose of these examples is to assert what commands are rendered and what are
48
+ # placed in the flash, under different circumstances, namely,
49
+ # configurations and relevant data initially found in flash.
50
+
51
+ # Assertions are made against
52
+ # * #finalized_commands_as_segments results (see let(:result)) and
53
+ # * commands_pushed_to_flash
54
+ # Initial flash input is placed in
55
+ # * commands_from_flash
56
+ # For test convenience, all three use the same format, namely an array of commands
57
+ # represented as command segments. The language is used for the necessary conversion.
58
+ # Emptiness of commands_pushed_to_flash is automatically asserted unless the
59
+ # push_to_flash: true metadata is present.
60
+
61
+ # The result array is implicitly checked to comply to certain ordering assumptions (fex.
62
+ # a tracker must be initialized with _setAccount before anything else is done with it).
63
+ # See let(:result) for details.
64
+
65
+ let(:stubbed_controller_facade) { double "controller facade" }
66
+
67
+ let(:stubbed_language) do
68
+ # we use the actual implementation, given that this spec is at the highest level of all
69
+ result = CommandLanguage.new
70
+ CommandLanguage.declare_language_on(result)
71
+ CommandLanguage.define_transformations_on(result)
72
+ result
73
+ end
74
+
75
+ let(:stubbed_configuration) do
76
+ Configuration.new
77
+ end
78
+
79
+ # convenience. think "config.gaq", so rails_config.track_event would be config.gaq.track_event in the real world
80
+ let(:rails_config) { stubbed_configuration.rails_config }
81
+
82
+ let(:commands_from_flash) { [] }
83
+ let(:commands_pushed_to_flash) { [] }
84
+
85
+ let(:stubbed_flash_commands_adapter) do
86
+ result = double("flash_commands_adapter")
87
+
88
+ # for test convenience, convert between plain "segments" arrays and proper command objects
89
+ # this depends on features from language
90
+
91
+ result.stub(:commands_from_flash) do
92
+ stubbed_language.commands_from_flash_items(commands_from_flash)
93
+ end
94
+
95
+ result.stub(:<<) do |item|
96
+ item = stubbed_language.commands_to_flash_items([item]).first
97
+ commands_pushed_to_flash << item
98
+ end
99
+
100
+ result
101
+ end
102
+
103
+ after do
104
+ # automatically assert that nothing got pushed to flash storage unless metadata tagged
105
+ commands_pushed_to_flash.should be_empty \
106
+ unless example.metadata[:push_to_flash]
107
+ end
108
+
109
+ subject do
110
+ described_class.new(stubbed_controller_facade, stubbed_language, ClassCache.new, stubbed_configuration).tap do |result|
111
+ result.flash_commands_adapter = stubbed_flash_commands_adapter
112
+ end
113
+ end
114
+
115
+ let(:result) do
116
+ subject.finalized_commands_as_segments
117
+ end
118
+
119
+ let(:order_asserter_class) do
120
+ # We need to do a bunch of assertions that are hard to express in rspec proper.
121
+ # This class does the heavy lifting. It is tested below!
122
+ Class.new do
123
+ def initialize(result, context)
124
+ @result = result
125
+ @context = context
126
+ end
127
+
128
+ def do_the_assertions
129
+ ## overall order assumptions
130
+
131
+ # renders _gat._anonymizeIp before any _trackPageview, _setCustomVar or _trackEvent"
132
+ any_occurrence_of('_anonymizeIp').should \
133
+ precede(any_occurrence_of('_trackPageview'))
134
+ any_occurrence_of('_anonymizeIp').should \
135
+ precede(any_occurrence_of('_setCustomVar'))
136
+ any_occurrence_of('_anonymizeIp').should \
137
+ precede(any_occurrence_of('_trackEvent'))
138
+
139
+ ## per tracker order assumptions
140
+
141
+ # it rendes _setAccount before _trackPageview, _setCustomVar or _trackEvent
142
+ tracker_occurrences_of('_setAccount').should \
143
+ precede(tracker_occurrences_of('_trackPageview'))
144
+ tracker_occurrences_of('_setAccount').should \
145
+ precede(tracker_occurrences_of('_setCustomVar'))
146
+ tracker_occurrences_of('_setAccount').should \
147
+ precede(tracker_occurrences_of('_trackEvent'))
148
+
149
+ # it renders _setCustomVar before _trackEvent
150
+ tracker_occurrences_of('_setCustomVar').should \
151
+ precede(tracker_occurrences_of('_trackEvent'))
152
+ end
153
+
154
+ def self_test
155
+ @result = [
156
+ ['cmd1'], ['cmd2'], ['tracker.cmd1'], ['tracker.cmd2'],
157
+ ['cmd3'], ['cmd4'], ['tracker.cmd5']
158
+ ]
159
+
160
+ tracker_occurrences_of('cmd1').should \
161
+ precede(tracker_occurrences_of('cmd2'))
162
+
163
+ tracker_occurrences_of('cmd2').should_not \
164
+ precede(tracker_occurrences_of('cmd1'))
165
+
166
+ any_occurrence_of('cmd3').should \
167
+ precede(any_occurrence_of('cmd4'))
168
+
169
+ any_occurrence_of('cmd4').should_not \
170
+ precede(any_occurrence_of('cmd3'))
171
+
172
+ any_occurrence_of('cmd3').should \
173
+ precede(any_occurrence_of('cmd5'))
174
+
175
+ any_occurrence_of('cmd5').should_not \
176
+ precede(any_occurrence_of('cmd3'))
177
+ end
178
+
179
+ private
180
+
181
+ def precede(following)
182
+ @context.satisfy do |preceding|
183
+ preceding.all? do |tracker_name, indices|
184
+ following_indices = following[tracker_name]
185
+ [*indices, -1].max < [*following_indices, 2**31].min
186
+ end
187
+ end
188
+ end
189
+
190
+ def tracker_occurrences_of(command_name)
191
+ command_occurrences_with_matching_tracker_name(command_name) do |tracker_name|
192
+ tracker_name != :any
193
+ end
194
+ # preprocessed_result[command_name].select { |tracker_name, _| tracker_name != :any }
195
+ end
196
+
197
+ def any_occurrence_of(command_name)
198
+ command_occurrences_with_matching_tracker_name(command_name) do |tracker_name|
199
+ tracker_name == :any
200
+ end
201
+ # preprocessed_result[command_name].select { |tracker_name, _| tracker_name == :any }
202
+ end
203
+
204
+ def command_occurrences_with_matching_tracker_name(command_name)
205
+ analyzed_result[command_name].select { |tracker_name, _| yield tracker_name }
206
+ end
207
+
208
+ def analyzed_result
209
+ ar = Hash.new do |hash, command_name|
210
+ hash[command_name] = Hash.new { |h, tracker_name| h[tracker_name] = [] }
211
+ end
212
+
213
+ @result.map(&:first).each_with_index do |first_segment, index|
214
+ command_name, tracker_name = first_segment.split('.', 2).reverse
215
+
216
+ per_command = ar[command_name]
217
+ per_command[tracker_name] << index
218
+ per_command[:any] << index
219
+ end
220
+
221
+ ar
222
+ end
223
+ end
224
+ end
225
+
226
+ describe "order asserter mechanism" do
227
+ # the order_asserter_class does heavy lifting and deserves being tested!
228
+
229
+ it "does what we expect" do
230
+ order_asserter_class.new(nil, self).self_test
231
+ end
232
+ end
233
+
234
+ after do
235
+ # automatically assert order assumptions for each example
236
+ order_asserter_class.new(result, self).do_the_assertions
237
+ # this way we don't forget.
238
+ end
239
+
240
+ let(:root_target) { subject.root_target }
241
+
242
+ shared_context declare_var: true do
243
+ before do
244
+ rails_config.declare_variable :var, scope: 3, slot: 0
245
+ end
246
+ end
247
+
248
+ context "without further configuration" do
249
+ it "returns _setAccount (unset wpi) and _trackPageview for default tracker" do
250
+ result.should have(2).items
251
+ result.should include(["_setAccount", "UA-XUNSET-S"])
252
+ result.should include(["_trackPageview"])
253
+ end
254
+ end
255
+
256
+ context "with config.gaq.anonymize_ip = true" do
257
+ before do
258
+ rails_config.anonymize_ip = true
259
+ end
260
+
261
+ it "renders '_gat._anonymizeIp" do
262
+ result.should include(["_gat._anonymizeIp"])
263
+ end
264
+ end
265
+
266
+ describe "default tracker configuration effect" do
267
+ before do
268
+ rails_config.web_property_id = 'UA-TEST23-5'
269
+ end
270
+
271
+ it "renders a correct _setAccount" do
272
+ result.should include(["_setAccount", 'UA-TEST23-5'])
273
+ end
274
+
275
+ it "renders a _trackPageview by default" do
276
+ result.should include(["_trackPageview"])
277
+ end
278
+
279
+ context "config.gaq.track_pageview = false" do
280
+ before do
281
+ rails_config.track_pageview = false
282
+ end
283
+
284
+ it "does not render anything for default tracker", pending: true do
285
+ # Currently, the default tracker _setAccount is always rendered.
286
+ # I'm undecided about that.
287
+ # If the implementation stays this way, I should assert
288
+ # _setAccount presence here instead.
289
+ result.should_not include command_segments.for_default_tracker
290
+ end
291
+
292
+ context "with a tracker command" do
293
+ before do
294
+ # this is really an arbitrary tracker command.
295
+ root_target.track_event 'category', 'action', 'label'
296
+ end
297
+
298
+ it "renders the _setAccount, but not a _trackPageview" do
299
+ result.should include(["_setAccount", 'UA-TEST23-5'])
300
+ result.should_not include(command_segments.starting_with('_trackPageview'))
301
+ end
302
+ end
303
+ end
304
+ end
305
+
306
+ describe "effect of tracker commands issued on default tracker" do
307
+ describe "gaq.track_event 'category', 'action', 'label'" do
308
+ before do
309
+ root_target.track_event 'category', 'action', 'label'
310
+ end
311
+
312
+ it "renders the _trackEvent" do
313
+ result.should include(["_trackEvent", "category", "action", "label"])
314
+ end
315
+ end
316
+
317
+ # more here when implemented...
318
+ end
319
+
320
+ describe "effect of tracker commands issued on default tracker .next_request" do
321
+ context "gaq.next_request.track_event 'category', 'action', 'label'", push_to_flash: true do
322
+ before do
323
+ root_target.next_request.track_event 'category', 'action', 'label'
324
+ end
325
+
326
+ it "does not render the _trackEvent" do
327
+ result.should_not include command_segments.starting_with('_trackEvent')
328
+ end
329
+
330
+ it "pushes the _trackEvent to flash storage" do
331
+ commands_pushed_to_flash.should be == [
332
+ ["_trackEvent", "category", "action", "label"]
333
+ ]
334
+ end
335
+ end
336
+
337
+ # more here when implemented...
338
+ end
339
+
340
+ context "with a variable declared", declare_var: true do
341
+ it "returns nothing in addition" do
342
+ result.should have(2).items
343
+ result.should include(["_setAccount", "UA-XUNSET-S"])
344
+ result.should include(["_trackPageview"])
345
+ end
346
+
347
+ context "after assigning to variable" do
348
+ before do
349
+ root_target.var = "blah"
350
+ end
351
+
352
+ it "renders the _setCustomVar" do
353
+ result.should include(["_setCustomVar", 0, "var", "blah", 3])
354
+ end
355
+
356
+ context "gaq.track_event 'category', 'action', 'label'" do
357
+ before(:each) do
358
+ root_target.track_event 'category', 'action', 'label'
359
+ end
360
+
361
+ it "renders the _trackEvent in addition, maintaining correct order" do
362
+ # order assertion is not here, see preamble.
363
+ result.should include(["_setCustomVar", 0, "var", "blah", 3])
364
+ result.should include(["_trackEvent", "category", "action", "label"])
365
+ end
366
+ end
367
+ end
368
+
369
+ context "after assigning to variable on gaq.next_request", push_to_flash: true do
370
+ before do
371
+ root_target.next_request.var = "blah"
372
+ end
373
+
374
+ it "does not render a _setCustomVar" do
375
+ result.should_not include(command_segments.starting_with('_setCustomVar'))
376
+ end
377
+
378
+ it "pushes the _setCustomVar onto the flash storage" do
379
+ commands_pushed_to_flash.should be == [
380
+ ["_setCustomVar", 0, "var", "blah", 3]
381
+ ]
382
+ end
383
+ end
384
+ end
385
+
386
+ context "with commands stored in flash" do
387
+ before do
388
+ commands_from_flash << ["_trackEvent", "last_cat", "last_action", "last_label"]
389
+ commands_from_flash << ["_setCustomVar", 0, "var", "blah", 3]
390
+ end
391
+
392
+ it "renders these" do
393
+ result.should include(["_setCustomVar", 0, "var", "blah", 3])
394
+ result.should include(["_trackEvent", "last_cat", "last_action", "last_label"])
395
+ end
396
+
397
+ context "gaq.next_request.track_event 'category', 'action', 'label'", push_to_flash: true do
398
+ before(:each) do
399
+ root_target.next_request.track_event 'category', 'action', 'label'
400
+ end
401
+
402
+ it "does not render that in addition" do
403
+ result.should_not include(["_trackEvent", "category", "action", "label"])
404
+ end
405
+
406
+ it "pushes that on the flash store instead" do
407
+ commands_pushed_to_flash.should be == [
408
+ ["_trackEvent", "category", "action", "label"]
409
+ ]
410
+ end
411
+ end
412
+
413
+ context "with a variable declared", declare_var: true do
414
+ context "after assigning to same variable again" do
415
+ before do
416
+ root_target.var = "blubb"
417
+ end
418
+
419
+ it "renders both _setCustomVar, in order" do
420
+ first_set_custom_var = ["_setCustomVar", 0, "var", "blah", 3]
421
+ second_set_custom_var = ["_setCustomVar", 0, "var", "blubb", 3]
422
+
423
+ result.should include(first_set_custom_var)
424
+ result.should include(second_set_custom_var)
425
+ result.index(first_set_custom_var).should be <
426
+ result.index(second_set_custom_var)
427
+ end
428
+ end
429
+
430
+ end
431
+ end
432
+
433
+ context "with a custom tracker" do
434
+ before do
435
+ rails_config.additional_trackers = ["foo"]
436
+ end
437
+
438
+ context "configured not to track pageviews" do
439
+ before do
440
+ rails_config.tracker(:foo).track_pageview = false
441
+ end
442
+
443
+ it "does not render any command for the additional tracker" do
444
+ result.should_not include(command_segments.for_tracker('foo'))
445
+ end
446
+
447
+ context "after gaq[:foo].track_event 'category', 'action', 'label'" do
448
+ before do
449
+ root_target["foo"].track_event 'category', 'action', 'label'
450
+ end
451
+
452
+ it "renders the proper _setAccount" do
453
+ result.should include(["foo._setAccount", "UA-XUNSET-S"])
454
+ end
455
+
456
+ it "does not render a _trackPageview for the tracker" do
457
+ result.should_not include(["foo._trackPageview"])
458
+ end
459
+
460
+ it "renders the _trackEvent" do
461
+ result.should include(["foo._trackEvent", "category", "action", "label"])
462
+ end
463
+ end
464
+ end
465
+
466
+ it "renders a _setAccount for the additional tracker" do
467
+ result.should include(['foo._setAccount', "UA-XUNSET-S"])
468
+ end
469
+
470
+ it "renders a _trackPageview for the tracker" do
471
+ result.should include(["foo._trackPageview"])
472
+ end
473
+
474
+ context "after gaq[:foo].track_event 'category', 'action', 'label'" do
475
+ before do
476
+ root_target["foo"].track_event 'category', 'action', 'label'
477
+ end
478
+
479
+ it "renders a _setAccount for the additional tracker" do
480
+ result.should include(['foo._setAccount', "UA-XUNSET-S"])
481
+ end
482
+
483
+ it "renders a _trackPageview for the tracker" do
484
+ result.should include(["foo._trackPageview"])
485
+ end
486
+
487
+ it "renders the _trackEvent" do
488
+ result.should include(["foo._trackEvent", "category", "action", "label"])
489
+ end
490
+ end
491
+ end
492
+
493
+ it "fails when an undeclared tracker is accessed from a target like gaq[:bogus]" do
494
+ expect {
495
+ root_target[:bogus]
496
+ }.to raise_exception
497
+ end
498
+
499
+ context "with commands stored in flash referencing a nonexistend tracker" do
500
+ before do
501
+ commands_from_flash << ["nonexistent._trackEvent", "last_cat", "last_action", "last_label"]
502
+ end
503
+
504
+ it "does not raise an exception" do
505
+ result
506
+ end
507
+ end
508
+ end
509
+ end
510
+ end