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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3fd5d31f02d60fa9d23dc37c87747ad8ad3dfb4a
4
- data.tar.gz: 0f3521e417baebb222969d59b3214d4ca094c88c
3
+ metadata.gz: 74c9eed0d845a4d1139c618e74bbc6cab109f2c6
4
+ data.tar.gz: bca89588653315d83e4a3a8387695685bd21558d
5
5
  SHA512:
6
- metadata.gz: 2b11005c795f0f63ec5a4b9443448b4952355a167ef21cb7f3d27671016cdb6cf011a8748bc587b362d1d5553b8abe83adf8f323bfbc80198dbaef51b4f40fed
7
- data.tar.gz: 65b2c4e8462261800bcb707983853b104b5cf9fa53fbbdc5f85a63d6f4af02e24dfcba7ab20cf5346064b20446bb47383845f48960b198832ec9256d60e3739d
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
- Let's say you have this controller:
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 + Capybara
140
+ ##### RSpec
127
141
 
128
- It is also possible to use `with_variant` in RSpec tests that use Capybara. Here
129
- is an example of a spec file:
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::RspecCapybaraHelpers
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 [ApplicationController::Request] the `request` in the
14
- # controller.
15
- def requested_variant(request)
16
- RequestedVariant.new(self, request, @dimension)
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
- @request.headers[ab_test.request_header] = variant
11
- requested_variant = ab_test.requested_variant(@request)
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
- assert_match ab_test.response_header, response.headers['Vary'],
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 = css_select("meta[name='govuk:ab-test']")
28
+ meta_tags = acceptance_test_framework.analytics_meta_tags
23
29
 
24
30
  assert_equal(1, meta_tags.count, message)
25
31
 
26
- meta_tag = meta_tags.first
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
- content_value,
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
- @request.headers[ab_test.request_header] = variant
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, :request
3
+ attr_reader :ab_test, :request_headers
4
4
 
5
5
  # @param ab_test [AbTest] the A/B test being performed
6
- # @param request [ApplicationController::Request] the `request` in the
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, request, dimension)
10
+ def initialize(ab_test, request_headers, dimension)
11
11
  @ab_test = ab_test
12
- @request = request
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
- request.headers[ab_test.request_header] == "B" ? "B" : "A"
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
@@ -1,3 +1,3 @@
1
1
  module GovukAbTesting
2
- VERSION = "0.2.0".freeze
2
+ VERSION = "1.0.0".freeze
3
3
  end
@@ -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/rspec_capybara_helpers"
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.2.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-02-24 00:00:00.000000000 Z
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/rspec_capybara_helpers.rb
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