split 3.3.0 → 4.0.1

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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc +1 -1
  3. data/.github/FUNDING.yml +1 -0
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +24 -0
  5. data/.github/dependabot.yml +7 -0
  6. data/.github/workflows/ci.yml +71 -0
  7. data/.rspec +1 -0
  8. data/.rubocop.yml +71 -1044
  9. data/.rubocop_todo.yml +226 -0
  10. data/Appraisals +4 -0
  11. data/CHANGELOG.md +116 -0
  12. data/CODE_OF_CONDUCT.md +3 -3
  13. data/Gemfile +2 -0
  14. data/README.md +63 -26
  15. data/Rakefile +2 -0
  16. data/gemfiles/{4.2.gemfile → 6.0.gemfile} +1 -1
  17. data/gemfiles/6.1.gemfile +9 -0
  18. data/gemfiles/7.0.gemfile +9 -0
  19. data/lib/split/algorithms/block_randomization.rb +2 -0
  20. data/lib/split/algorithms/weighted_sample.rb +2 -1
  21. data/lib/split/algorithms/whiplash.rb +3 -2
  22. data/lib/split/alternative.rb +4 -3
  23. data/lib/split/cache.rb +28 -0
  24. data/lib/split/combined_experiments_helper.rb +3 -2
  25. data/lib/split/configuration.rb +17 -14
  26. data/lib/split/dashboard/helpers.rb +3 -2
  27. data/lib/split/dashboard/pagination_helpers.rb +4 -4
  28. data/lib/split/dashboard/paginator.rb +1 -0
  29. data/lib/split/dashboard/public/dashboard.js +10 -0
  30. data/lib/split/dashboard/public/style.css +5 -0
  31. data/lib/split/dashboard/views/_controls.erb +13 -0
  32. data/lib/split/dashboard/views/layout.erb +1 -1
  33. data/lib/split/dashboard.rb +19 -1
  34. data/lib/split/encapsulated_helper.rb +3 -2
  35. data/lib/split/engine.rb +7 -4
  36. data/lib/split/exceptions.rb +1 -0
  37. data/lib/split/experiment.rb +98 -65
  38. data/lib/split/experiment_catalog.rb +1 -3
  39. data/lib/split/extensions/string.rb +1 -0
  40. data/lib/split/goals_collection.rb +2 -0
  41. data/lib/split/helper.rb +30 -10
  42. data/lib/split/metric.rb +2 -1
  43. data/lib/split/persistence/cookie_adapter.rb +6 -1
  44. data/lib/split/persistence/dual_adapter.rb +54 -12
  45. data/lib/split/persistence/redis_adapter.rb +5 -0
  46. data/lib/split/persistence/session_adapter.rb +1 -0
  47. data/lib/split/persistence.rb +4 -2
  48. data/lib/split/redis_interface.rb +9 -28
  49. data/lib/split/trial.rb +25 -17
  50. data/lib/split/user.rb +20 -4
  51. data/lib/split/version.rb +2 -4
  52. data/lib/split/zscore.rb +1 -0
  53. data/lib/split.rb +16 -3
  54. data/spec/alternative_spec.rb +1 -1
  55. data/spec/cache_spec.rb +88 -0
  56. data/spec/configuration_spec.rb +17 -15
  57. data/spec/dashboard/pagination_helpers_spec.rb +3 -1
  58. data/spec/dashboard_helpers_spec.rb +2 -2
  59. data/spec/dashboard_spec.rb +78 -17
  60. data/spec/encapsulated_helper_spec.rb +2 -2
  61. data/spec/experiment_spec.rb +116 -12
  62. data/spec/goals_collection_spec.rb +1 -1
  63. data/spec/helper_spec.rb +191 -112
  64. data/spec/persistence/cookie_adapter_spec.rb +1 -1
  65. data/spec/persistence/dual_adapter_spec.rb +160 -68
  66. data/spec/persistence/redis_adapter_spec.rb +9 -0
  67. data/spec/redis_interface_spec.rb +0 -69
  68. data/spec/spec_helper.rb +5 -6
  69. data/spec/trial_spec.rb +65 -19
  70. data/spec/user_spec.rb +45 -3
  71. data/split.gemspec +9 -9
  72. metadata +38 -29
  73. data/.travis.yml +0 -53
data/lib/split/trial.rb CHANGED
@@ -1,18 +1,21 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Split
3
4
  class Trial
5
+ attr_accessor :goals
4
6
  attr_accessor :experiment
5
- attr_accessor :metadata
7
+ attr_writer :metadata
6
8
 
7
9
  def initialize(attrs = {})
8
10
  self.experiment = attrs.delete(:experiment)
9
11
  self.alternative = attrs.delete(:alternative)
10
12
  self.metadata = attrs.delete(:metadata)
13
+ self.goals = attrs.delete(:goals) || []
11
14
 
12
15
  @user = attrs.delete(:user)
13
16
  @options = attrs
14
17
 
15
- @alternative_choosen = false
18
+ @alternative_chosen = false
16
19
  end
17
20
 
18
21
  def metadata
@@ -33,7 +36,7 @@ module Split
33
36
  end
34
37
  end
35
38
 
36
- def complete!(goals=[], context = nil)
39
+ def complete!(context = nil)
37
40
  if alternative
38
41
  if Array(goals).empty?
39
42
  alternative.increment_completion
@@ -51,8 +54,9 @@ module Split
51
54
  def choose!(context = nil)
52
55
  @user.cleanup_old_experiments!
53
56
  # Only run the process once
54
- return alternative if @alternative_choosen
57
+ return alternative if @alternative_chosen
55
58
 
59
+ new_participant = @user[@experiment.key].nil?
56
60
  if override_is_alternative?
57
61
  self.alternative = @options[:override]
58
62
  if should_store_alternative? && !@user[@experiment.key]
@@ -68,23 +72,27 @@ module Split
68
72
  if exclude_user?
69
73
  self.alternative = @experiment.control
70
74
  else
71
- value = @user[@experiment.key]
72
- if value
73
- self.alternative = value
74
- else
75
- self.alternative = @experiment.next_alternative
76
-
77
- # Increment the number of participants since we are actually choosing a new alternative
78
- self.alternative.increment_participation
79
-
80
- run_callback context, Split.configuration.on_trial_choose
75
+ self.alternative = @user[@experiment.key]
76
+ if alternative.nil?
77
+ if @experiment.cohorting_disabled?
78
+ self.alternative = @experiment.control
79
+ else
80
+ self.alternative = @experiment.next_alternative
81
+
82
+ # Increment the number of participants since we are actually choosing a new alternative
83
+ self.alternative.increment_participation
84
+
85
+ run_callback context, Split.configuration.on_trial_choose
86
+ end
81
87
  end
82
88
  end
83
89
  end
84
90
 
85
- @user[@experiment.key] = alternative.name if should_store_alternative?
86
- @alternative_choosen = true
87
- run_callback context, Split.configuration.on_trial unless @options[:disabled] || Split.configuration.disabled?
91
+ new_participant_and_cohorting_disabled = new_participant && @experiment.cohorting_disabled?
92
+
93
+ @user[@experiment.key] = alternative.name unless @experiment.has_winner? || !should_store_alternative? || new_participant_and_cohorting_disabled
94
+ @alternative_chosen = true
95
+ run_callback context, Split.configuration.on_trial unless @options[:disabled] || Split.configuration.disabled? || new_participant_and_cohorting_disabled
88
96
  alternative
89
97
  end
90
98
 
data/lib/split/user.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
 
3
5
  module Split
@@ -6,11 +8,13 @@ module Split
6
8
  def_delegators :@user, :keys, :[], :[]=, :delete
7
9
  attr_reader :user
8
10
 
9
- def initialize(context, adapter=nil)
11
+ def initialize(context, adapter = nil)
10
12
  @user = adapter || Split::Persistence.adapter.new(context)
13
+ @cleaned_up = false
11
14
  end
12
15
 
13
16
  def cleanup_old_experiments!
17
+ return if @cleaned_up
14
18
  keys_without_finished(user.keys).each do |key|
15
19
  experiment = ExperimentCatalog.find key_without_version(key)
16
20
  if experiment.nil? || experiment.has_winner? || experiment.start_time.nil?
@@ -18,12 +22,14 @@ module Split
18
22
  user.delete Experiment.finished_key(key)
19
23
  end
20
24
  end
25
+ @cleaned_up = true
21
26
  end
22
27
 
23
28
  def max_experiments_reached?(experiment_key)
24
29
  if Split.configuration.allow_multiple_experiments == 'control'
25
30
  experiments = active_experiments
26
- count_control = experiments.count {|k,v| k == experiment_key || v == 'control'}
31
+ experiment_key_without_version = key_without_version(experiment_key)
32
+ count_control = experiments.count {|k, v| k == experiment_key_without_version || v == 'control'}
27
33
  experiments.size > count_control
28
34
  else
29
35
  !Split.configuration.allow_multiple_experiments &&
@@ -32,13 +38,13 @@ module Split
32
38
  end
33
39
 
34
40
  def cleanup_old_versions!(experiment)
35
- keys = user.keys.select { |k| k.match(Regexp.new(experiment.name)) }
41
+ keys = user.keys.select { |k| key_without_version(k) == experiment.name }
36
42
  keys_without_experiment(keys, experiment.key).each { |key| user.delete(key) }
37
43
  end
38
44
 
39
45
  def active_experiments
40
46
  experiment_pairs = {}
41
- user.keys.each do |key|
47
+ keys_without_finished(user.keys).each do |key|
42
48
  Metric.possible_experiments(key_without_version(key)).each do |experiment|
43
49
  if !experiment.has_winner?
44
50
  experiment_pairs[key_without_version(key)] = user[key]
@@ -48,6 +54,16 @@ module Split
48
54
  experiment_pairs
49
55
  end
50
56
 
57
+ def self.find(user_id, adapter)
58
+ adapter = adapter.is_a?(Symbol) ? Split::Persistence::ADAPTERS[adapter] : adapter
59
+
60
+ if adapter.respond_to?(:find)
61
+ User.new(nil, adapter.find(user_id))
62
+ else
63
+ nil
64
+ end
65
+ end
66
+
51
67
  private
52
68
 
53
69
  def keys_without_experiment(keys, experiment_key)
data/lib/split/version.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Split
3
- MAJOR = 3
4
- MINOR = 3
5
- PATCH = 0
6
- VERSION = [MAJOR, MINOR, PATCH].join('.')
4
+ VERSION = "4.0.1"
7
5
  end
data/lib/split/zscore.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Split
3
4
  class Zscore
4
5
 
data/lib/split.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'redis'
3
4
 
4
5
  require 'split/algorithms/block_randomization'
5
6
  require 'split/algorithms/weighted_sample'
6
7
  require 'split/algorithms/whiplash'
7
8
  require 'split/alternative'
9
+ require 'split/cache'
8
10
  require 'split/configuration'
9
11
  require 'split/encapsulated_helper'
10
12
  require 'split/exceptions'
@@ -35,9 +37,9 @@ module Split
35
37
  # `Redis::DistRedis`, or `Redis::Namespace`.
36
38
  def redis=(server)
37
39
  @redis = if server.is_a?(String)
38
- Redis.new(:url => server, :thread_safe => true)
40
+ Redis.new(url: server)
39
41
  elsif server.is_a?(Hash)
40
- Redis.new(server.merge(:thread_safe => true))
42
+ Redis.new(server)
41
43
  elsif server.respond_to?(:smembers)
42
44
  server
43
45
  else
@@ -64,6 +66,17 @@ module Split
64
66
  self.configuration ||= Configuration.new
65
67
  yield(configuration)
66
68
  end
69
+
70
+ def cache(namespace, key, &block)
71
+ Split::Cache.fetch(namespace, key, &block)
72
+ end
67
73
  end
68
74
 
69
- Split.configure {}
75
+ # Check to see if being run in a Rails application. If so, wait until before_initialize to run configuration so Gems that create ENV variables have the chance to initialize first.
76
+ if defined?(::Rails)
77
+ class Split::Railtie < Rails::Railtie
78
+ config.before_initialize { Split.configure {} }
79
+ end
80
+ else
81
+ Split.configure {}
82
+ end
@@ -126,7 +126,7 @@ describe Split::Alternative do
126
126
 
127
127
  it "should save to redis" do
128
128
  alternative.save
129
- expect(Split.redis.exists('basket_text:Basket')).to be true
129
+ expect(Split.redis.exists?('basket_text:Basket')).to be true
130
130
  end
131
131
 
132
132
  it "should increment participation count" do
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+
4
+ describe Split::Cache do
5
+
6
+ let(:namespace) { :test_namespace }
7
+ let(:key) { :test_key }
8
+ let(:now) { 1606189017 }
9
+
10
+ before { allow(Time).to receive(:now).and_return(now) }
11
+
12
+ describe 'clear' do
13
+
14
+ before { Split.configuration.cache = true }
15
+
16
+ it 'clears the cache' do
17
+ expect(Time).to receive(:now).and_return(now).exactly(2).times
18
+ Split::Cache.fetch(namespace, key) { Time.now }
19
+ Split::Cache.clear
20
+ Split::Cache.fetch(namespace, key) { Time.now }
21
+ end
22
+ end
23
+
24
+ describe 'clear_key' do
25
+ before { Split.configuration.cache = true }
26
+
27
+ it 'clears the cache' do
28
+ expect(Time).to receive(:now).and_return(now).exactly(3).times
29
+ Split::Cache.fetch(namespace, :key1) { Time.now }
30
+ Split::Cache.fetch(namespace, :key2) { Time.now }
31
+ Split::Cache.clear_key(:key1)
32
+
33
+ Split::Cache.fetch(namespace, :key1) { Time.now }
34
+ Split::Cache.fetch(namespace, :key2) { Time.now }
35
+ end
36
+ end
37
+
38
+ describe 'fetch' do
39
+
40
+ subject { Split::Cache.fetch(namespace, key) { Time.now } }
41
+
42
+ context 'when cache disabled' do
43
+
44
+ before { Split.configuration.cache = false }
45
+
46
+ it 'returns the yield' do
47
+ expect(subject).to eql(now)
48
+ end
49
+
50
+ it 'yields every time' do
51
+ expect(Time).to receive(:now).and_return(now).exactly(2).times
52
+ Split::Cache.fetch(namespace, key) { Time.now }
53
+ Split::Cache.fetch(namespace, key) { Time.now }
54
+ end
55
+ end
56
+
57
+ context 'when cache enabled' do
58
+
59
+ before { Split.configuration.cache = true }
60
+
61
+ it 'returns the yield' do
62
+ expect(subject).to eql(now)
63
+ end
64
+
65
+ it 'yields once' do
66
+ expect(Time).to receive(:now).and_return(now).once
67
+ Split::Cache.fetch(namespace, key) { Time.now }
68
+ Split::Cache.fetch(namespace, key) { Time.now }
69
+ end
70
+
71
+ it 'honors namespace' do
72
+ expect(Split::Cache.fetch(:a, key) { :a }).to eql(:a)
73
+ expect(Split::Cache.fetch(:b, key) { :b }).to eql(:b)
74
+
75
+ expect(Split::Cache.fetch(:a, key) { :a }).to eql(:a)
76
+ expect(Split::Cache.fetch(:b, key) { :b }).to eql(:b)
77
+ end
78
+
79
+ it 'honors key' do
80
+ expect(Split::Cache.fetch(namespace, :a) { :a }).to eql(:a)
81
+ expect(Split::Cache.fetch(namespace, :b) { :b }).to eql(:b)
82
+
83
+ expect(Split::Cache.fetch(namespace, :a) { :a }).to eql(:a)
84
+ expect(Split::Cache.fetch(namespace, :b) { :b }).to eql(:b)
85
+ end
86
+ end
87
+ end
88
+ end
@@ -212,23 +212,12 @@ describe Split::Configuration do
212
212
  expect(@config.normalized_experiments).to eq({:my_experiment=>{:alternatives=>[{"control_opt"=>0.67}, [{"second_opt"=>0.1}, {"third_opt"=>0.23}]]}})
213
213
  end
214
214
 
215
- context 'redis_url configuration [DEPRECATED]' do
216
- it 'should warn on set and assign to #redis' do
217
- expect(@config).to receive(:warn).with(/\[DEPRECATED\]/) { nil }
218
- @config.redis_url = 'example_url'
219
- expect(@config.redis).to eq('example_url')
220
- end
221
-
222
- it 'should warn on get and return #redis' do
223
- expect(@config).to receive(:warn).with(/\[DEPRECATED\]/) { nil }
224
- @config.redis = 'example_url'
225
- expect(@config.redis_url).to eq('example_url')
226
- end
227
- end
228
-
229
215
  context "redis configuration" do
230
216
  it "should default to local redis server" do
231
- expect(@config.redis).to eq("redis://localhost:6379")
217
+ old_redis_url = ENV['REDIS_URL']
218
+ ENV.delete('REDIS_URL')
219
+ expect(Split::Configuration.new.redis).to eq("redis://localhost:6379")
220
+ ENV['REDIS_URL'] = old_redis_url
232
221
  end
233
222
 
234
223
  it "should allow for redis url to be configured" do
@@ -238,8 +227,10 @@ describe Split::Configuration do
238
227
 
239
228
  context "provided REDIS_URL environment variable" do
240
229
  it "should use the ENV variable" do
230
+ old_redis_url = ENV['REDIS_URL']
241
231
  ENV['REDIS_URL'] = "env_redis_url"
242
232
  expect(Split::Configuration.new.redis).to eq("env_redis_url")
233
+ ENV['REDIS_URL'] = old_redis_url
243
234
  end
244
235
  end
245
236
  end
@@ -255,4 +246,15 @@ describe Split::Configuration do
255
246
  end
256
247
  end
257
248
 
249
+ context "persistence cookie domain" do
250
+ it "should default to nil" do
251
+ expect(@config.persistence_cookie_domain).to eq(nil)
252
+ end
253
+
254
+ it "should allow the persistence cookie domain to be configured" do
255
+ @config.persistence_cookie_domain = '.acme.com'
256
+ expect(@config.persistence_cookie_domain).to eq('.acme.com')
257
+ end
258
+ end
259
+
258
260
  end
@@ -10,7 +10,9 @@ describe Split::DashboardPaginationHelpers do
10
10
  context 'when params empty' do
11
11
  let(:params) { Hash[] }
12
12
 
13
- it 'returns 10' do
13
+ it 'returns the default (10)' do
14
+ default_per_page = Split.configuration.dashboard_pagination_default_per_page
15
+ expect(pagination_per).to eql default_per_page
14
16
  expect(pagination_per).to eql 10
15
17
  end
16
18
  end
@@ -27,11 +27,11 @@ describe Split::DashboardHelpers do
27
27
 
28
28
  describe '#round' do
29
29
  it 'can round number strings' do
30
- expect(round('3.1415')).to eq BigDecimal.new('3.14')
30
+ expect(round('3.1415')).to eq BigDecimal('3.14')
31
31
  end
32
32
 
33
33
  it 'can round number strings for precsion' do
34
- expect(round('3.1415', 1)).to eq BigDecimal.new('3.1')
34
+ expect(round('3.1415', 1)).to eq BigDecimal('3.1')
35
35
  end
36
36
 
37
37
  it 'can handle invalid number strings' do
@@ -6,8 +6,16 @@ require 'split/dashboard'
6
6
  describe Split::Dashboard do
7
7
  include Rack::Test::Methods
8
8
 
9
+ class TestDashboard < Split::Dashboard
10
+ include Split::Helper
11
+
12
+ get '/my_experiment' do
13
+ ab_test(params[:experiment], 'blue', 'red')
14
+ end
15
+ end
16
+
9
17
  def app
10
- @app ||= Split::Dashboard
18
+ @app ||= TestDashboard
11
19
  end
12
20
 
13
21
  def link(color)
@@ -29,6 +37,10 @@ describe Split::Dashboard do
29
37
  let(:red_link) { link("red") }
30
38
  let(:blue_link) { link("blue") }
31
39
 
40
+ before(:each) do
41
+ Split.configuration.beta_probability_simulations = 1
42
+ end
43
+
32
44
  it "should respond to /" do
33
45
  get '/'
34
46
  expect(last_response).to be_ok
@@ -74,17 +86,49 @@ describe Split::Dashboard do
74
86
  end
75
87
 
76
88
  describe "force alternative" do
77
- let!(:user) do
78
- Split::User.new(@app, { experiment.name => 'a' })
79
- end
89
+ context "initial version" do
90
+ let!(:user) do
91
+ Split::User.new(@app, { experiment.name => 'red' })
92
+ end
80
93
 
81
- before do
82
- allow(Split::User).to receive(:new).and_return(user)
94
+ before do
95
+ allow(Split::User).to receive(:new).and_return(user)
96
+ end
97
+
98
+ it "should set current user's alternative" do
99
+ blue_link.participant_count = 7
100
+ post "/force_alternative?experiment=#{experiment.name}", alternative: "blue"
101
+
102
+ get "/my_experiment?experiment=#{experiment.name}"
103
+ expect(last_response.body).to include("blue")
104
+ end
105
+
106
+ it "should not modify an existing user" do
107
+ blue_link.participant_count = 7
108
+ post "/force_alternative?experiment=#{experiment.name}", alternative: "blue"
109
+
110
+ expect(user[experiment.key]).to eq("red")
111
+ expect(blue_link.participant_count).to eq(7)
112
+ end
83
113
  end
84
114
 
85
- it "should set current user's alternative" do
86
- post "/force_alternative?experiment=#{experiment.name}", alternative: "b"
87
- expect(user[experiment.name]).to eq("b")
115
+ context "incremented version" do
116
+ let!(:user) do
117
+ experiment.increment_version
118
+ Split::User.new(@app, { "#{experiment.name}:#{experiment.version}" => 'red' })
119
+ end
120
+
121
+ before do
122
+ allow(Split::User).to receive(:new).and_return(user)
123
+ end
124
+
125
+ it "should set current user's alternative" do
126
+ blue_link.participant_count = 7
127
+ post "/force_alternative?experiment=#{experiment.name}", alternative: "blue"
128
+
129
+ get "/my_experiment?experiment=#{experiment.name}"
130
+ expect(last_response.body).to include("blue")
131
+ end
88
132
  end
89
133
  end
90
134
 
@@ -120,7 +164,7 @@ describe Split::Dashboard do
120
164
  it "removes winner" do
121
165
  post "/reopen?experiment=#{experiment.name}"
122
166
 
123
- expect(experiment).to_not have_winner
167
+ expect(Split::ExperimentCatalog.find(experiment.name)).to_not have_winner
124
168
  end
125
169
 
126
170
  it "keeps existing stats" do
@@ -135,6 +179,28 @@ describe Split::Dashboard do
135
179
  end
136
180
  end
137
181
 
182
+ describe "update cohorting" do
183
+ it "calls enable of cohorting when action is enable" do
184
+ post "/update_cohorting?experiment=#{experiment.name}", { "cohorting_action": "enable" }
185
+
186
+ expect(experiment.cohorting_disabled?).to eq false
187
+ end
188
+
189
+ it "calls disable of cohorting when action is disable" do
190
+ post "/update_cohorting?experiment=#{experiment.name}", { "cohorting_action": "disable" }
191
+
192
+ expect(experiment.cohorting_disabled?).to eq true
193
+ end
194
+
195
+ it "calls neither enable or disable cohorting when passed invalid action" do
196
+ previous_value = experiment.cohorting_disabled?
197
+
198
+ post "/update_cohorting?experiment=#{experiment.name}", { "cohorting_action": "other" }
199
+
200
+ expect(experiment.cohorting_disabled?).to eq previous_value
201
+ end
202
+ end
203
+
138
204
  it "should reset an experiment" do
139
205
  red_link.participant_count = 5
140
206
  blue_link.participant_count = 7
@@ -167,19 +233,14 @@ describe Split::Dashboard do
167
233
  end
168
234
 
169
235
  it "should display the start date" do
170
- experiment_start_time = Time.parse('2011-07-07')
171
- expect(Time).to receive(:now).at_least(:once).and_return(experiment_start_time)
172
- experiment
236
+ experiment.start
173
237
 
174
238
  get '/'
175
239
 
176
- expect(last_response.body).to include('<small>2011-07-07</small>')
240
+ expect(last_response.body).to include("<small>#{experiment.start_time.strftime('%Y-%m-%d')}</small>")
177
241
  end
178
242
 
179
243
  it "should handle experiments without a start date" do
180
- experiment_start_time = Time.parse('2011-07-07')
181
- expect(Time).to receive(:now).at_least(:once).and_return(experiment_start_time)
182
-
183
244
  Split.redis.hdel(:experiment_start_times, experiment.name)
184
245
 
185
246
  get '/'
@@ -21,7 +21,7 @@ describe Split::EncapsulatedHelper do
21
21
  end
22
22
 
23
23
  it "calls the block with selected alternative" do
24
- expect{|block| ab_test('link_color', 'red', 'red', &block) }.to yield_with_args('red', nil)
24
+ expect{|block| ab_test('link_color', 'red', 'red', &block) }.to yield_with_args('red', {})
25
25
  end
26
26
 
27
27
  context "inside a view" do
@@ -33,7 +33,7 @@ describe Split::EncapsulatedHelper do
33
33
  static <%= alt %>
34
34
  <% end %>
35
35
  ERB
36
- expect(template.result(binding)).to match /foo static \d/
36
+ expect(template.result(binding)).to match(/foo static \d/)
37
37
  end
38
38
 
39
39
  end