govuk_ab_testing 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +25 -8
- data/lib/govuk_ab_testing/ab_test.rb +4 -4
- data/lib/govuk_ab_testing/acceptance_tests/active_support.rb +41 -0
- data/lib/govuk_ab_testing/acceptance_tests/capybara.rb +46 -0
- data/lib/govuk_ab_testing/configuration.rb +36 -0
- data/lib/govuk_ab_testing/minitest_helpers.rb +13 -9
- data/lib/govuk_ab_testing/requested_variant.rb +6 -6
- data/lib/govuk_ab_testing/rspec_helpers.rb +40 -0
- data/lib/govuk_ab_testing/version.rb +1 -1
- data/lib/govuk_ab_testing.rb +13 -1
- metadata +6 -3
- data/lib/govuk_ab_testing/rspec_capybara_helpers.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 74c9eed0d845a4d1139c618e74bbc6cab109f2c6
|
4
|
+
data.tar.gz: bca89588653315d83e4a3a8387695685bd21558d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 694cddcacc3320548b0d558dfc7b10dd5f14f4dabc559e2edc006e14e33ce718acdaa7b4bc8c0db859ffaf3bcb78f8d71bc67e6b4ddcc79c88da33e59e7ba8a8
|
7
|
+
data.tar.gz: f9a25ff78213c7012fd5ef5e5145b3ff3db28fa8b34f0e89b9654560bb5b60a789a0fc4cc309f49a0549a68ff4d02e5cd558716d0bdafc11ea47918e30df2e5f
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
## 1.0.0
|
2
|
+
|
3
|
+
* Pass in request headers instead of the actual request to RequestedVariant.
|
4
|
+
This is a breaking change.
|
5
|
+
* Slit testing framework helpers from acceptance test frameworks. This means we
|
6
|
+
can now use a combination of RSpec/Minitest and Capybara/ActiveSupport test
|
7
|
+
cases.
|
8
|
+
|
1
9
|
## 0.2.0
|
2
10
|
|
3
11
|
* Include RSpec + Capybara helper class
|
data/README.md
CHANGED
@@ -33,7 +33,21 @@ To enable testing in the app, your Rails app needs:
|
|
33
33
|
the dimension to use in Google Analytics
|
34
34
|
3. A response HTTP header that tells Fastly you're doing an A/B test
|
35
35
|
|
36
|
-
|
36
|
+
Start by defining which acceptance testing framework you will use. This gem
|
37
|
+
supports both Capybara and ActiveSupport. In order to configure it, add this to
|
38
|
+
your test helper file:
|
39
|
+
|
40
|
+
```
|
41
|
+
GovukAbTesting.configure do |config|
|
42
|
+
config.acceptance_test_framework = :capybara # or :active_support
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
If we use capybara, the gem expects `page` to be defined in the scope of the
|
47
|
+
test cases. If we use ActiveSupport, the gem expects `@request` to be defined in
|
48
|
+
the scope of the test cases.
|
49
|
+
|
50
|
+
Now, let's say you have this controller:
|
37
51
|
|
38
52
|
```ruby
|
39
53
|
# app/controllers/party_controller.rb
|
@@ -123,15 +137,15 @@ class PartyControllerTest < ActionController::TestCase
|
|
123
137
|
end
|
124
138
|
```
|
125
139
|
|
126
|
-
##### RSpec
|
140
|
+
##### RSpec
|
127
141
|
|
128
|
-
It is also possible to use `with_variant` in RSpec tests
|
129
|
-
|
142
|
+
It is also possible to use `with_variant` in RSpec tests. Here is an example of
|
143
|
+
a spec file:
|
130
144
|
|
131
145
|
```ruby
|
132
146
|
# spec/features/ab_testing_spec.rb
|
133
147
|
feature "Viewing a page with an A/B test" do
|
134
|
-
include GovukAbTesting::
|
148
|
+
include GovukAbTesting::RspecHelpers
|
135
149
|
|
136
150
|
scenario "viewing the B version of the page" do
|
137
151
|
with_variant your_ab_test_name: 'B' do
|
@@ -144,15 +158,18 @@ feature "Viewing a page with an A/B test" do
|
|
144
158
|
end
|
145
159
|
```
|
146
160
|
|
147
|
-
Please note that `with_variant` in `GovukAbTesting::RspecCapybaraHelpers`
|
148
|
-
expects both `page` (Capybara session) and RSpec expectations to be available.
|
149
|
-
|
150
161
|
As with the `minitest` version, you can also pass in the following options to
|
151
162
|
`with_variant`:
|
152
163
|
|
153
164
|
- `assert_meta_tag: false`
|
154
165
|
- `dimension: <number>`
|
155
166
|
|
167
|
+
### Current limitations
|
168
|
+
|
169
|
+
This library assumes we are only using one A/B test per page. The acceptance
|
170
|
+
test classes look for only one analytics' meta tag and will fail in the presence
|
171
|
+
of more than one.
|
172
|
+
|
156
173
|
### Running the test suite
|
157
174
|
|
158
175
|
`bundle exec rake`
|
@@ -10,10 +10,10 @@ module GovukAbTesting
|
|
10
10
|
@dimension = dimension
|
11
11
|
end
|
12
12
|
|
13
|
-
# @param request [
|
14
|
-
# controller.
|
15
|
-
def requested_variant(
|
16
|
-
RequestedVariant.new(self,
|
13
|
+
# @param request [ActionDispatch::Http::Headers] the `request.headers` in
|
14
|
+
# the controller.
|
15
|
+
def requested_variant(request_headers)
|
16
|
+
RequestedVariant.new(self, request_headers, @dimension)
|
17
17
|
end
|
18
18
|
|
19
19
|
# Internal name of the header
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module GovukAbTesting
|
2
|
+
module AcceptanceTests
|
3
|
+
class ActiveSupport
|
4
|
+
attr_reader :request, :request_headers, :scope
|
5
|
+
|
6
|
+
def initialize(scope)
|
7
|
+
@request = scope.instance_variable_get(:@request)
|
8
|
+
if @request.nil?
|
9
|
+
raise "Couldn't find '@request' defined, are you using ActiveSupport test cases?"
|
10
|
+
end
|
11
|
+
@scope = scope
|
12
|
+
@request_headers = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_header(name, value)
|
16
|
+
request.headers[name] = value
|
17
|
+
@request_headers[name] = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def vary_header(response)
|
21
|
+
response.headers['Vary']
|
22
|
+
end
|
23
|
+
|
24
|
+
def analytics_meta_tags
|
25
|
+
@analytics_meta_tags ||= scope.css_select(ANALYTICS_META_TAG_SELECTOR)
|
26
|
+
end
|
27
|
+
|
28
|
+
def analytics_meta_tag
|
29
|
+
analytics_meta_tags.first
|
30
|
+
end
|
31
|
+
|
32
|
+
def content
|
33
|
+
analytics_meta_tag.attributes['content'].value
|
34
|
+
end
|
35
|
+
|
36
|
+
def dimension
|
37
|
+
analytics_meta_tag.attributes['data-analytics-dimension'].value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module GovukAbTesting
|
2
|
+
module AcceptanceTests
|
3
|
+
class Capybara
|
4
|
+
attr_reader :capybara_page, :request_headers
|
5
|
+
|
6
|
+
def initialize(scope)
|
7
|
+
unless scope.respond_to?(:page)
|
8
|
+
raise "Page is not defined, are you using capybara?"
|
9
|
+
end
|
10
|
+
@capybara_page = scope.page
|
11
|
+
@request_headers = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def request
|
15
|
+
@capybara_page
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_header(name, value)
|
19
|
+
capybara_page.driver.options[:headers] = { name => value }
|
20
|
+
capybara_page.driver.header(name, value)
|
21
|
+
@request_headers[name] = value
|
22
|
+
end
|
23
|
+
|
24
|
+
def vary_header(*)
|
25
|
+
capybara_page.response_headers['Vary']
|
26
|
+
end
|
27
|
+
|
28
|
+
def analytics_meta_tags
|
29
|
+
@analytics_meta_tags ||=
|
30
|
+
capybara_page.all(ANALYTICS_META_TAG_SELECTOR, visible: :all)
|
31
|
+
end
|
32
|
+
|
33
|
+
def analytics_meta_tag
|
34
|
+
analytics_meta_tags.first
|
35
|
+
end
|
36
|
+
|
37
|
+
def content
|
38
|
+
analytics_meta_tag['content']
|
39
|
+
end
|
40
|
+
|
41
|
+
def dimension
|
42
|
+
analytics_meta_tag['data-analytics-dimension']
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module GovukAbTesting
|
2
|
+
class Configuration
|
3
|
+
VALID_FRAMEWORKS = %i(capybara active_support).freeze
|
4
|
+
attr_accessor :config, :acceptance_test_framework
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@config = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def acceptance_test_framework
|
11
|
+
config[:acceptance_test_framework]
|
12
|
+
end
|
13
|
+
|
14
|
+
def acceptance_test_framework=(framework)
|
15
|
+
unless VALID_FRAMEWORKS.include?(framework)
|
16
|
+
raise "Invalid acceptance test framework '#{framework}'"
|
17
|
+
end
|
18
|
+
|
19
|
+
config[:acceptance_test_framework] = framework
|
20
|
+
@framework_class = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def framework_class
|
24
|
+
@framework_class ||= begin
|
25
|
+
case config[:acceptance_test_framework]
|
26
|
+
when :capybara
|
27
|
+
then GovukAbTesting::AcceptanceTests::Capybara
|
28
|
+
when :active_support
|
29
|
+
then GovukAbTesting::AcceptanceTests::ActiveSupport
|
30
|
+
else
|
31
|
+
raise "Invalid framework #{acceptance_test_framework}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,5 +1,10 @@
|
|
1
1
|
module GovukAbTesting
|
2
2
|
module MinitestHelpers
|
3
|
+
def acceptance_test_framework
|
4
|
+
@acceptance_test_framework ||=
|
5
|
+
GovukAbTesting.configuration.framework_class.new(self)
|
6
|
+
end
|
7
|
+
|
3
8
|
def with_variant(args)
|
4
9
|
ab_test_name, variant = args.first
|
5
10
|
dimension = args[:dimension]
|
@@ -7,29 +12,28 @@ module GovukAbTesting
|
|
7
12
|
ab_test =
|
8
13
|
GovukAbTesting::AbTest.new(ab_test_name.to_s, dimension: dimension)
|
9
14
|
|
10
|
-
|
11
|
-
requested_variant = ab_test.requested_variant(
|
15
|
+
acceptance_test_framework.set_header(ab_test.request_header, variant)
|
16
|
+
requested_variant = ab_test.requested_variant(acceptance_test_framework.request_headers)
|
12
17
|
|
13
18
|
yield
|
14
19
|
|
15
|
-
|
20
|
+
vary_header_value = acceptance_test_framework.vary_header(response)
|
21
|
+
assert_match ab_test.response_header, vary_header_value,
|
16
22
|
"You probably forgot to use `configure_response`"
|
17
23
|
|
18
24
|
unless args[:assert_meta_tag] == false
|
19
25
|
expected_content =
|
20
26
|
ab_test.meta_tag_name + ':' + requested_variant.variant_name
|
21
27
|
message = "You probably forgot to add the `analytics_meta_tag` to the views"
|
22
|
-
meta_tags =
|
28
|
+
meta_tags = acceptance_test_framework.analytics_meta_tags
|
23
29
|
|
24
30
|
assert_equal(1, meta_tags.count, message)
|
25
31
|
|
26
|
-
|
27
|
-
content_value = meta_tag.attributes['content'].value
|
28
|
-
dimension_value = meta_tag.attributes['data-analytics-dimension'].value
|
32
|
+
dimension_value = acceptance_test_framework.dimension
|
29
33
|
|
30
34
|
assert_equal(
|
31
35
|
expected_content,
|
32
|
-
|
36
|
+
acceptance_test_framework.content,
|
33
37
|
"Meta tag's content doesn't match."
|
34
38
|
)
|
35
39
|
|
@@ -48,7 +52,7 @@ module GovukAbTesting
|
|
48
52
|
def setup_ab_variant(ab_test_name, variant, dimension = 300)
|
49
53
|
ab_test = GovukAbTesting::AbTest.new(ab_test_name, dimension: dimension)
|
50
54
|
|
51
|
-
|
55
|
+
acceptance_test_framework.set_header(ab_test.request_header, variant)
|
52
56
|
end
|
53
57
|
|
54
58
|
def assert_response_not_modified_for_ab_test
|
@@ -1,15 +1,15 @@
|
|
1
1
|
module GovukAbTesting
|
2
2
|
class RequestedVariant
|
3
|
-
attr_reader :ab_test, :
|
3
|
+
attr_reader :ab_test, :request_headers
|
4
4
|
|
5
5
|
# @param ab_test [AbTest] the A/B test being performed
|
6
|
-
# @param
|
7
|
-
# controller.
|
6
|
+
# @param request_headers [ActionDispatch::Http::Headers] the
|
7
|
+
# `request.headers` in the controller.
|
8
8
|
# @param dimension [Integer] the dimension registered with Google Analytics
|
9
9
|
# for this specific A/B test
|
10
|
-
def initialize(ab_test,
|
10
|
+
def initialize(ab_test, request_headers, dimension)
|
11
11
|
@ab_test = ab_test
|
12
|
-
@
|
12
|
+
@request_headers = request_headers
|
13
13
|
@dimension = dimension
|
14
14
|
end
|
15
15
|
|
@@ -17,7 +17,7 @@ module GovukAbTesting
|
|
17
17
|
#
|
18
18
|
# @return [String] the current variant, "A" or "B"
|
19
19
|
def variant_name
|
20
|
-
|
20
|
+
request_headers[ab_test.request_header] == "B" ? "B" : "A"
|
21
21
|
end
|
22
22
|
|
23
23
|
# @return [Boolean] if the user is to be served variant A
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module GovukAbTesting
|
2
|
+
module RspecHelpers
|
3
|
+
def acceptance_test_framework
|
4
|
+
@acceptance_test_framework ||=
|
5
|
+
GovukAbTesting.configuration.framework_class.new(self)
|
6
|
+
end
|
7
|
+
|
8
|
+
def with_variant(args)
|
9
|
+
ab_test_name, variant = args.first
|
10
|
+
dimension = args[:dimension]
|
11
|
+
|
12
|
+
ab_test =
|
13
|
+
GovukAbTesting::AbTest.new(ab_test_name.to_s, dimension: dimension)
|
14
|
+
|
15
|
+
acceptance_test_framework.set_header(ab_test.request_header, variant)
|
16
|
+
requested_variant = ab_test.requested_variant(acceptance_test_framework.request_headers)
|
17
|
+
|
18
|
+
yield
|
19
|
+
|
20
|
+
vary_header_value = acceptance_test_framework.vary_header
|
21
|
+
expect(ab_test.response_header).to eq(vary_header_value)
|
22
|
+
|
23
|
+
unless args[:assert_meta_tag] == false
|
24
|
+
content = [ab_test.meta_tag_name, requested_variant.variant_name].join(':')
|
25
|
+
ab_test_metatags = acceptance_test_framework.analytics_meta_tags
|
26
|
+
|
27
|
+
expect(ab_test_metatags.count).to eq(1)
|
28
|
+
|
29
|
+
expect(acceptance_test_framework.content).to eq(content)
|
30
|
+
dimension = acceptance_test_framework.dimension
|
31
|
+
|
32
|
+
if dimension.nil?
|
33
|
+
expect(dimension).to_not be_nil
|
34
|
+
else
|
35
|
+
expect(dimension).to eq(dimension.to_s)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/govuk_ab_testing.rb
CHANGED
@@ -1,8 +1,20 @@
|
|
1
1
|
require "govuk_ab_testing/version"
|
2
|
+
require 'govuk_ab_testing/configuration'
|
2
3
|
require "govuk_ab_testing/requested_variant"
|
3
4
|
require "govuk_ab_testing/ab_test"
|
4
5
|
require "govuk_ab_testing/minitest_helpers"
|
5
|
-
require "govuk_ab_testing/
|
6
|
+
require "govuk_ab_testing/rspec_helpers"
|
7
|
+
require 'govuk_ab_testing/acceptance_tests/capybara'
|
8
|
+
require 'govuk_ab_testing/acceptance_tests/active_support'
|
6
9
|
|
7
10
|
module GovukAbTesting
|
11
|
+
ANALYTICS_META_TAG_SELECTOR = "meta[name='govuk:ab-test']".freeze
|
12
|
+
|
13
|
+
def self.configuration
|
14
|
+
@configuration ||= GovukAbTesting::Configuration.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.configure
|
18
|
+
yield configuration if block_given?
|
19
|
+
end
|
8
20
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: govuk_ab_testing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GOV.UK Dev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -102,9 +102,12 @@ files:
|
|
102
102
|
- govuk_ab_testing.gemspec
|
103
103
|
- lib/govuk_ab_testing.rb
|
104
104
|
- lib/govuk_ab_testing/ab_test.rb
|
105
|
+
- lib/govuk_ab_testing/acceptance_tests/active_support.rb
|
106
|
+
- lib/govuk_ab_testing/acceptance_tests/capybara.rb
|
107
|
+
- lib/govuk_ab_testing/configuration.rb
|
105
108
|
- lib/govuk_ab_testing/minitest_helpers.rb
|
106
109
|
- lib/govuk_ab_testing/requested_variant.rb
|
107
|
-
- lib/govuk_ab_testing/
|
110
|
+
- lib/govuk_ab_testing/rspec_helpers.rb
|
108
111
|
- lib/govuk_ab_testing/version.rb
|
109
112
|
homepage: https://github.com/alphagov/govuk_ab_testing
|
110
113
|
licenses:
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module GovukAbTesting
|
2
|
-
module RspecCapybaraHelpers
|
3
|
-
def with_variant(args)
|
4
|
-
unless defined?(page)
|
5
|
-
raise "The variable 'page' is not defined, are you using capybara?"
|
6
|
-
end
|
7
|
-
|
8
|
-
ab_test_name, variant = args.first
|
9
|
-
dimension = args[:dimension]
|
10
|
-
ab_test =
|
11
|
-
GovukAbTesting::AbTest.new(ab_test_name.to_s, dimension: dimension)
|
12
|
-
|
13
|
-
page.driver.header(ab_test.response_header, variant)
|
14
|
-
|
15
|
-
yield
|
16
|
-
|
17
|
-
expect(ab_test.response_header).to eq(page.response_headers['Vary'])
|
18
|
-
|
19
|
-
unless args[:assert_meta_tag] == false
|
20
|
-
content = [ab_test.meta_tag_name, variant].join(':')
|
21
|
-
ab_test_metatag = page.find("meta[name='govuk:ab-test']", visible: :all)
|
22
|
-
|
23
|
-
expect(ab_test_metatag['content']).to eq(content)
|
24
|
-
|
25
|
-
if dimension.nil?
|
26
|
-
expect(ab_test_metatag['data-analytics-dimension']).to_not be_nil
|
27
|
-
else
|
28
|
-
expect(ab_test_metatag['data-analytics-dimension']).to eq(dimension.to_s)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|