bacchanalytics 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/.travis.yml +11 -0
- data/Appraisals +14 -0
- data/CHANGELOG.md +53 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +20 -0
- data/README.md +94 -0
- data/Rakefile +17 -0
- data/bacchanalytics.gemspec +27 -0
- data/gemfiles/2.3.gemfile +8 -0
- data/gemfiles/2.3.gemfile.lock +31 -0
- data/gemfiles/3.0.gemfile +8 -0
- data/gemfiles/3.0.gemfile.lock +33 -0
- data/gemfiles/3.1.gemfile +8 -0
- data/gemfiles/3.1.gemfile.lock +33 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/bacchanalytics.rb +5 -0
- data/lib/bacchanalytics/adwords_conversion.rb +95 -0
- data/lib/bacchanalytics/analytics.rb +62 -0
- data/lib/bacchanalytics/google_analytics.rb +74 -0
- data/lib/bacchanalytics/version.rb +3 -0
- data/lib/bacchanalytics/website_optimizer.rb +204 -0
- data/test/integration/bacchanalytics_integration_test.rb +64 -0
- data/test/test_helper.rb +8 -0
- data/test/unit/adwords_conversion_test.rb +96 -0
- data/test/unit/bacchanalytics_test.rb +70 -0
- data/test/unit/website_optimizer_test.rb +63 -0
- metadata +190 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
module Bacchanalytics
|
2
|
+
class Analytics
|
3
|
+
include GoogleAnalytics::Base
|
4
|
+
include GoogleAnalytics::TrackingCode
|
5
|
+
|
6
|
+
def initialize(app, options = {})
|
7
|
+
@app = app
|
8
|
+
@web_property_id = options[:web_property_id] || "UA-XXXXX-X"
|
9
|
+
@domain_name = options[:domain_name]
|
10
|
+
@ignored_organic = options[:ignored_organic]
|
11
|
+
@skip_ga_src = options[:skip_ga_src] || false
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
status, headers, response = @app.call(env)
|
16
|
+
|
17
|
+
if should_instrument?(headers) && (source = response_source(response))
|
18
|
+
@skip_ga_src = true if env["bacchanlytics.loaded_ga_src"]
|
19
|
+
tracking_code = google_analytics_tracking_code(@web_property_id, @domain_name)
|
20
|
+
|
21
|
+
env["bacchanalytics.loaded_ga_src"] = true
|
22
|
+
new_body = source.sub /<[hH][eE][aA][dD]\s*>/, "<head>\n\n#{tracking_code}"
|
23
|
+
headers["Content-Length"] = new_body.length.to_s
|
24
|
+
Rack::Response.new(new_body, status, headers).finish
|
25
|
+
else
|
26
|
+
[status, headers, response]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.track_page_view_code(page)
|
31
|
+
"_gaq.push(['_trackPageview', '#{page}'])"
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.track_page_view_script(page)
|
35
|
+
<<-SCRIPT
|
36
|
+
<script type="text/javascript">
|
37
|
+
#{track_page_view_code(page)};
|
38
|
+
</script>
|
39
|
+
SCRIPT
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.track_event(category, action, opt_label = nil, opt_value = nil, options = {})
|
43
|
+
if opt_label.blank? || opt_value.blank?
|
44
|
+
track_event_code = "_gaq.push(['_trackEvent', '#{category}', '#{action}'])"
|
45
|
+
else
|
46
|
+
track_event_code = "_gaq.push(['_trackEvent', '#{category}', '#{action}', '#{opt_label}', '#{opt_value}'])"
|
47
|
+
end
|
48
|
+
|
49
|
+
timeout = options[:timeout] rescue nil
|
50
|
+
if timeout
|
51
|
+
"#{track_event_code};setTimeout('void(0)', #{timeout});"
|
52
|
+
else
|
53
|
+
track_event_code
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def load_ga_src
|
59
|
+
@skip_ga_src ? "" : super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Bacchanalytics
|
2
|
+
module GoogleAnalytics
|
3
|
+
|
4
|
+
module Base
|
5
|
+
# Return the javascript code used to load google analytics code
|
6
|
+
def load_ga_src
|
7
|
+
<<-SCRIPT
|
8
|
+
(function() {
|
9
|
+
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
10
|
+
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
11
|
+
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
12
|
+
})();
|
13
|
+
SCRIPT
|
14
|
+
end
|
15
|
+
|
16
|
+
# headers["Content-Type"] will be nil if the status of the response is 304 (Not Modified)
|
17
|
+
# From the HTTP Status Code Definitions:
|
18
|
+
# If the client has performed a conditional GET request and access is allowed,
|
19
|
+
# but the document has not been modified, the server SHOULD respond with this status code.
|
20
|
+
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
21
|
+
def should_instrument?(headers)
|
22
|
+
!headers["Content-Type"].nil? && (headers["Content-Type"].include? "text/html")
|
23
|
+
end
|
24
|
+
|
25
|
+
def response_source(response)
|
26
|
+
source = nil
|
27
|
+
response.each { |fragment| (source) ? (source << fragment) : (source = fragment) }
|
28
|
+
source
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module TrackingCode
|
33
|
+
# Construct the new asynchronous version of the Google Analytics code.
|
34
|
+
# http://code.google.com/apis/analytics/docs/tracking/asyncTracking.html
|
35
|
+
def google_analytics_tracking_code(web_property_id, domain_name = nil)
|
36
|
+
<<-SCRIPT
|
37
|
+
<script type="text/javascript">
|
38
|
+
|
39
|
+
var _gaq = _gaq || [];
|
40
|
+
_gaq.push(['_setAccount', '#{web_property_id}']);
|
41
|
+
if ('#{domain_name}' !== ''){
|
42
|
+
_gaq.push(['_setDomainName', '#{domain_name}']);
|
43
|
+
}
|
44
|
+
#{ignored_organic_script}
|
45
|
+
_gaq.push(['_trackPageview']);
|
46
|
+
|
47
|
+
#{load_ga_src}
|
48
|
+
</script>
|
49
|
+
SCRIPT
|
50
|
+
end
|
51
|
+
|
52
|
+
def ignored_organic_script
|
53
|
+
script = ""
|
54
|
+
|
55
|
+
begin
|
56
|
+
if @ignored_organic.is_a?(Array)
|
57
|
+
@ignored_organic.each { |item|
|
58
|
+
script << <<-CODE
|
59
|
+
_gaq.push(['_addIgnoredOrganic', '#{item}']);
|
60
|
+
CODE
|
61
|
+
}
|
62
|
+
elsif @ignored_organic.is_a?(String)
|
63
|
+
script << <<-CODE
|
64
|
+
_gaq.push(['_addIgnoredOrganic', '#{@ignored_organic}']);
|
65
|
+
CODE
|
66
|
+
end
|
67
|
+
rescue
|
68
|
+
end
|
69
|
+
|
70
|
+
script
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
module Bacchanalytics
|
2
|
+
|
3
|
+
module WebsiteOptimizerTrackingCode
|
4
|
+
|
5
|
+
# Construct the Google WebSite Optimizer tracking code.
|
6
|
+
def website_optimizer_tracking_code(page, account_id, ab)
|
7
|
+
if page.blank? || account_id.blank? || !ab_options_valid?(ab)
|
8
|
+
return ""
|
9
|
+
end
|
10
|
+
|
11
|
+
gotc = ""
|
12
|
+
begin
|
13
|
+
goal_keys = [] # Multiple tests and the same goal
|
14
|
+
for key in ab.keys do
|
15
|
+
value = ab[key]
|
16
|
+
next unless value[:locales].nil? || [value[:locales]].flatten.map(&:to_s).include?(I18n.locale.to_s)
|
17
|
+
|
18
|
+
a = value[:a].to_a
|
19
|
+
b = value[:b].to_a
|
20
|
+
g = value[:goal].to_a
|
21
|
+
|
22
|
+
# Check the requested page, to include the A, B or goal tracking code.
|
23
|
+
if a.include?(page)
|
24
|
+
gotc = a_tracking_code(account_id, key)
|
25
|
+
break
|
26
|
+
elsif b.include?(page)
|
27
|
+
gotc = b_tracking_code(account_id, key)
|
28
|
+
break
|
29
|
+
elsif g.include?(page)
|
30
|
+
goal_keys << key
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
if gotc.blank?
|
35
|
+
gotc = goal_tracking_code(account_id, goal_keys)
|
36
|
+
end
|
37
|
+
|
38
|
+
rescue Exception => e
|
39
|
+
logger.debug "#{e.message}"
|
40
|
+
gotc = ""
|
41
|
+
end
|
42
|
+
|
43
|
+
gotc
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# Returns the A (original page) website optimizer code. Look at the html comments before
|
49
|
+
# the javascript code: use them to pass W3C validations.
|
50
|
+
def a_tracking_code(account_id, track_page_id)
|
51
|
+
result = <<-SCRIPT
|
52
|
+
<!-- Google Website Optimizer Control Script -->
|
53
|
+
<script type="text/javascript">
|
54
|
+
<!--
|
55
|
+
function utmx_section(){}function utmx(){}
|
56
|
+
(function(){var k='#{track_page_id}',d=document,l=d.location,c=d.cookie;function f(n){
|
57
|
+
if(c){var i=c.indexOf(n+'=');if(i>-1){var j=c.indexOf(';',i);return escape(c.substring(i+n.
|
58
|
+
length+1,j<0?c.length:j))}}}var x=f('__utmx'),xx=f('__utmxx'),h=l.hash;
|
59
|
+
d.write('<sc'+'ript src="'+
|
60
|
+
'http'+(l.protocol=='https:'?'s://ssl':'://www')+'.google-analytics.com'
|
61
|
+
+'/siteopt.js?v=1&utmxkey='+k+'&utmx='+(x?x:'')+'&utmxx='+(xx?xx:'')+'&utmxtime='
|
62
|
+
+new Date().valueOf()+(h?'&utmxhash='+escape(h.substr(1)):'')+
|
63
|
+
'" type="text/javascript" charset="utf-8"></sc'+'ript>')})();
|
64
|
+
-->
|
65
|
+
</script>
|
66
|
+
<script type="text/javascript">utmx("url",'A/B');</script>
|
67
|
+
<!-- End of Google Website Optimizer Control Script -->
|
68
|
+
|
69
|
+
<!-- Google Website Optimizer Tracking Script -->
|
70
|
+
<script type="text/javascript">
|
71
|
+
var _gaq = _gaq || [];
|
72
|
+
_gaq.push(['gwo._setAccount', '#{account_id}']);
|
73
|
+
_gaq.push(['gwo._trackPageview', '/#{track_page_id}/test']);
|
74
|
+
#{load_ga_src}
|
75
|
+
</script>
|
76
|
+
<!-- End of Google Website Optimizer Tracking Script -->
|
77
|
+
SCRIPT
|
78
|
+
|
79
|
+
return result
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns the B (variation page) website optimizer code.
|
83
|
+
def b_tracking_code(account_id, track_page_id)
|
84
|
+
<<-SCRIPT
|
85
|
+
<!-- Google Website Optimizer Tracking Script -->
|
86
|
+
<script type="text/javascript">
|
87
|
+
var _gaq = _gaq || [];
|
88
|
+
_gaq.push(['gwo._setAccount', '#{account_id}']);
|
89
|
+
_gaq.push(['gwo._trackPageview', '/#{track_page_id}/test']);
|
90
|
+
#{load_ga_src}
|
91
|
+
</script>
|
92
|
+
<!-- End of Google Website Optimizer Tracking Script -->
|
93
|
+
SCRIPT
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns the goal (conversion page) website optimizer code.
|
97
|
+
def goal_tracking_code(account_id, track_page_ids = [])
|
98
|
+
track_page_ids = Array(track_page_ids)
|
99
|
+
|
100
|
+
<<-SCRIPT
|
101
|
+
<!-- Google Website Optimizer Tracking Script -->
|
102
|
+
<script type="text/javascript">
|
103
|
+
var _gaq = _gaq || [];
|
104
|
+
_gaq.push(['gwo._setAccount', '#{account_id}']);
|
105
|
+
#{track_page_ids.map { |id| "_gaq.push(['gwo._trackPageview', '/#{id}/goal']);" }.join("\n")}
|
106
|
+
#{load_ga_src}
|
107
|
+
</script>
|
108
|
+
<!-- End of Google Website Optimizer Tracking Script -->
|
109
|
+
SCRIPT
|
110
|
+
end
|
111
|
+
|
112
|
+
# Are the options of the tests valid?
|
113
|
+
def ab_options_valid?(ab_options = {})
|
114
|
+
return false if ab_options.empty?
|
115
|
+
|
116
|
+
unless ab_options_is_complete?(ab_options)
|
117
|
+
logger.info "**********************************************************************************************************"
|
118
|
+
logger.info "[Bachanalytics] You need to specify the :a, b: and :goal options for your WebSiteOptimizer tests, aborting"
|
119
|
+
logger.info "**********************************************************************************************************"
|
120
|
+
return false
|
121
|
+
end
|
122
|
+
|
123
|
+
unless uniq_goals?(ab_options)
|
124
|
+
logger.info "**************************************************************************************************"
|
125
|
+
logger.info "[Bachanalytics] You can't specify a :goal as part of the your WebSiteOptimizer tests, not applying"
|
126
|
+
logger.info "**************************************************************************************************"
|
127
|
+
return false
|
128
|
+
end
|
129
|
+
|
130
|
+
true
|
131
|
+
end
|
132
|
+
|
133
|
+
# Each A/B test need at least: :a, :b and :goal keys
|
134
|
+
def ab_options_is_complete?(ab_options = {})
|
135
|
+
result = true
|
136
|
+
|
137
|
+
ab_options.values.each do |value|
|
138
|
+
# Has :a, :b and :goal options
|
139
|
+
result = value.keys.size >= 3 && value.key?(:a) && value.key?(:b) && value.key?(:goal)
|
140
|
+
break if result == false
|
141
|
+
end
|
142
|
+
|
143
|
+
result
|
144
|
+
end
|
145
|
+
|
146
|
+
# One goal option must not be an a or b option
|
147
|
+
# A conversion (goal) option can't ba an a or b option (a original or test option)
|
148
|
+
def uniq_goals?(ab_options = {})
|
149
|
+
uniq_goals, test_pages = [], []
|
150
|
+
|
151
|
+
ab_options.values.each do |value|
|
152
|
+
uniq_goals << value[:goal]
|
153
|
+
test_pages << value[:a]
|
154
|
+
test_pages << value[:b]
|
155
|
+
end
|
156
|
+
|
157
|
+
uniq_goals.flatten!.uniq!
|
158
|
+
test_pages.flatten!.uniq!
|
159
|
+
|
160
|
+
(uniq_goals & test_pages).size == 0
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class WebsiteOptimizer
|
165
|
+
include GoogleAnalytics::Base
|
166
|
+
include WebsiteOptimizerTrackingCode
|
167
|
+
|
168
|
+
def initialize(app, options = {})
|
169
|
+
@app = app
|
170
|
+
@account_id = options[:account_id] || "UA-XXXXX-X"
|
171
|
+
@ab = options[:ab] || {}
|
172
|
+
@skip_ga_src = options[:skip_ga_src] || false
|
173
|
+
end
|
174
|
+
|
175
|
+
def call(env)
|
176
|
+
status, headers, response = @app.call(env)
|
177
|
+
|
178
|
+
if should_instrument?(headers) && (source = response_source(response))
|
179
|
+
page = env['REQUEST_URI']
|
180
|
+
page.gsub!(/\?.*/, '') if page #remove url parameters
|
181
|
+
|
182
|
+
@skip_ga_src = true if env["bacchanalytics.loaded_ga_src"]
|
183
|
+
tracking_code = website_optimizer_tracking_code(page, @account_id, @ab)
|
184
|
+
return [status, headers, response] if tracking_code.to_s == ""
|
185
|
+
|
186
|
+
env["bacchanalytics.loaded_ga_src"] = true
|
187
|
+
new_body = source.sub /<[hH][eE][aA][dD]\s*>/, "<head>\n\n#{tracking_code}"
|
188
|
+
headers["Content-Length"] = new_body.length.to_s
|
189
|
+
Rack::Response.new(new_body, status, headers).finish
|
190
|
+
else
|
191
|
+
[status, headers, response]
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
private
|
196
|
+
def logger
|
197
|
+
defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
|
198
|
+
end
|
199
|
+
|
200
|
+
def load_ga_src
|
201
|
+
@skip_ga_src ? "" : super
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'bacchanalytics'
|
3
|
+
|
4
|
+
ENV['RACK_ENV'] = 'test'
|
5
|
+
|
6
|
+
class BacchanalyticsIntegrationTest < Test::Unit::TestCase
|
7
|
+
include Rack::Test::Methods
|
8
|
+
include Bacchanalytics::GoogleAnalytics::Base
|
9
|
+
|
10
|
+
def app
|
11
|
+
response = Rack::Response.new(HTML_DOCUMENT)
|
12
|
+
mock_app = lambda do |env|
|
13
|
+
[200, {'Content-Type' => 'text/html'}, response]
|
14
|
+
end
|
15
|
+
|
16
|
+
Rack::Builder.new do
|
17
|
+
use Bacchanalytics::WebsiteOptimizer, {:account_id => 'UA-20891683-1',
|
18
|
+
:ab => {'1924712694' => {:a => ["/"],
|
19
|
+
:b => ["/home"],
|
20
|
+
:goal => ["/welcome"]}}
|
21
|
+
}
|
22
|
+
use Bacchanalytics::Analytics, :web_property_id => "UA-12345-6"
|
23
|
+
run mock_app
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_must_include_google_analytics_src_only_once
|
28
|
+
get "/", {}, {"REQUEST_URI" => "/"}
|
29
|
+
|
30
|
+
times = 0
|
31
|
+
last_response.body.gsub(load_ga_src){|m| times += 1}
|
32
|
+
|
33
|
+
assert_equal 1, times
|
34
|
+
end
|
35
|
+
|
36
|
+
HTML_DOCUMENT = <<-HTML
|
37
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
38
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
39
|
+
|
40
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
41
|
+
<head>
|
42
|
+
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
|
43
|
+
<title>Listing things</title>
|
44
|
+
</head>
|
45
|
+
<body>
|
46
|
+
|
47
|
+
<h1>Listing things</h1>
|
48
|
+
|
49
|
+
<table>
|
50
|
+
<tr>
|
51
|
+
<th>Name</th>
|
52
|
+
</tr>
|
53
|
+
<tr>
|
54
|
+
<td>Thing 1</td>
|
55
|
+
</tr>
|
56
|
+
<tr>
|
57
|
+
<td>Thing 2</td>
|
58
|
+
</tr>
|
59
|
+
</table>
|
60
|
+
|
61
|
+
</body>
|
62
|
+
</html>
|
63
|
+
HTML
|
64
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'bacchanalytics'
|
3
|
+
|
4
|
+
ENV['RACK_ENV'] = 'test'
|
5
|
+
|
6
|
+
class AdwordsConversionTest < Test::Unit::TestCase
|
7
|
+
include Rack::Test::Methods
|
8
|
+
|
9
|
+
WEB_PROPERTY_ID = "UA-12345-6"
|
10
|
+
|
11
|
+
def app
|
12
|
+
response = Rack::Response.new(HTML_DOCUMENT)
|
13
|
+
mock_app = lambda do |env|
|
14
|
+
[200, {'Content-Type' => 'text/html'}, response]
|
15
|
+
end
|
16
|
+
@options ||= [
|
17
|
+
{:id => '1062298921', :label=>'oIiDCJe__QEQqcrF-gM', :language=>'es', :format=>3, :value=>3.5, :description=>'',
|
18
|
+
:pages=>["/welcome", "/en/welcome", "/es/welcome", "/ca/welcome", "/eu/welcome"]
|
19
|
+
}
|
20
|
+
]
|
21
|
+
Bacchanalytics::AdwordsConversion.new(mock_app, @options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_must_not_include_any_code_if_no_page
|
25
|
+
get "/", {}, {'REQUEST_URI' => nil}
|
26
|
+
|
27
|
+
assert_equal last_response.body, HTML_DOCUMENT
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_must_not_include_any_code_if_the_page_is_not_a_goal
|
31
|
+
get "/", {}, {'REQUEST_URI' => nil}
|
32
|
+
|
33
|
+
assert_equal last_response.body, HTML_DOCUMENT
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_must_include_conversion_code_if_the_page_is_a_goal
|
37
|
+
get "/welcome", {}, {'REQUEST_URI' => "/welcome"}
|
38
|
+
|
39
|
+
assert last_response.body.include?("Google Code for Conversion Page "), last_response.body
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_must_include_conversion_values_in_the_code
|
43
|
+
get "/welcome", {}, {'REQUEST_URI' => "/welcome"}
|
44
|
+
|
45
|
+
conversion = @options.first
|
46
|
+
assert last_response.body.include?("google_conversion_id = #{conversion[:id]}"), last_response.body
|
47
|
+
assert last_response.body.include?("google_conversion_language = \"#{conversion[:language]}\""), last_response.body
|
48
|
+
assert last_response.body.include?("google_conversion_format = \"#{conversion[:format]}\""), last_response.body
|
49
|
+
assert last_response.body.include?("google_conversion_label = \"#{conversion[:label]}\""), last_response.body
|
50
|
+
assert last_response.body.include?("google_conversion_value = #{conversion[:value]}"), last_response.body
|
51
|
+
end
|
52
|
+
|
53
|
+
def text_must_not_include_any_code_if_not_a_valid_conversion
|
54
|
+
invalid_conversion = {}
|
55
|
+
@options = [invalid_conversion]
|
56
|
+
get "/", {}, {'REQUEST_URI' => "/request_uri"}
|
57
|
+
|
58
|
+
assert_equal last_response.body, HTML_DOCUMENT
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_must_not_include_any_code_if_no_conversion
|
62
|
+
@options = []
|
63
|
+
get "/", {}, {'REQUEST_URI' => "/request_uri"}
|
64
|
+
|
65
|
+
assert_equal last_response.body, HTML_DOCUMENT
|
66
|
+
end
|
67
|
+
|
68
|
+
HTML_DOCUMENT = <<-HTML
|
69
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
70
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
71
|
+
|
72
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
73
|
+
<head>
|
74
|
+
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
|
75
|
+
<title>Listing things</title>
|
76
|
+
</head>
|
77
|
+
<body>
|
78
|
+
|
79
|
+
<h1>Listing things</h1>
|
80
|
+
|
81
|
+
<table>
|
82
|
+
<tr>
|
83
|
+
<th>Name</th>
|
84
|
+
</tr>
|
85
|
+
<tr>
|
86
|
+
<td>Thing 1</td>
|
87
|
+
</tr>
|
88
|
+
<tr>
|
89
|
+
<td>Thing 2</td>
|
90
|
+
</tr>
|
91
|
+
</table>
|
92
|
+
|
93
|
+
</body>
|
94
|
+
</html>
|
95
|
+
HTML
|
96
|
+
end
|