govuk_ab_testing 0.2.0 → 1.0.0
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.
- 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
|