split 3.2.0 → 4.0.5

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 (87) hide show
  1. checksums.yaml +5 -5
  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 +63 -0
  7. data/.rspec +1 -0
  8. data/.rubocop.yml +67 -1043
  9. data/CHANGELOG.md +174 -0
  10. data/CODE_OF_CONDUCT.md +3 -3
  11. data/CONTRIBUTING.md +1 -1
  12. data/Gemfile +6 -1
  13. data/README.md +79 -33
  14. data/Rakefile +6 -5
  15. data/lib/split/algorithms/block_randomization.rb +7 -6
  16. data/lib/split/algorithms/weighted_sample.rb +2 -1
  17. data/lib/split/algorithms/whiplash.rb +17 -18
  18. data/lib/split/algorithms.rb +14 -0
  19. data/lib/split/alternative.rb +25 -25
  20. data/lib/split/cache.rb +27 -0
  21. data/lib/split/combined_experiments_helper.rb +6 -5
  22. data/lib/split/configuration.rb +94 -91
  23. data/lib/split/dashboard/helpers.rb +9 -9
  24. data/lib/split/dashboard/pagination_helpers.rb +86 -0
  25. data/lib/split/dashboard/paginator.rb +17 -0
  26. data/lib/split/dashboard/public/dashboard.js +10 -0
  27. data/lib/split/dashboard/public/style.css +19 -2
  28. data/lib/split/dashboard/views/_controls.erb +13 -0
  29. data/lib/split/dashboard/views/_experiment.erb +2 -1
  30. data/lib/split/dashboard/views/index.erb +24 -5
  31. data/lib/split/dashboard/views/layout.erb +1 -1
  32. data/lib/split/dashboard.rb +47 -20
  33. data/lib/split/encapsulated_helper.rb +15 -8
  34. data/lib/split/engine.rb +7 -4
  35. data/lib/split/exceptions.rb +1 -0
  36. data/lib/split/experiment.rb +160 -122
  37. data/lib/split/experiment_catalog.rb +7 -8
  38. data/lib/split/extensions/string.rb +2 -1
  39. data/lib/split/goals_collection.rb +10 -10
  40. data/lib/split/helper.rb +56 -24
  41. data/lib/split/metric.rb +6 -6
  42. data/lib/split/persistence/cookie_adapter.rb +52 -15
  43. data/lib/split/persistence/dual_adapter.rb +53 -12
  44. data/lib/split/persistence/redis_adapter.rb +8 -4
  45. data/lib/split/persistence/session_adapter.rb +1 -2
  46. data/lib/split/persistence.rb +8 -6
  47. data/lib/split/redis_interface.rb +16 -31
  48. data/lib/split/trial.rb +48 -41
  49. data/lib/split/user.rb +30 -15
  50. data/lib/split/version.rb +2 -4
  51. data/lib/split/zscore.rb +2 -3
  52. data/lib/split.rb +39 -25
  53. data/spec/algorithms/block_randomization_spec.rb +6 -5
  54. data/spec/algorithms/weighted_sample_spec.rb +6 -5
  55. data/spec/algorithms/whiplash_spec.rb +4 -5
  56. data/spec/alternative_spec.rb +35 -36
  57. data/spec/cache_spec.rb +84 -0
  58. data/spec/combined_experiments_helper_spec.rb +18 -17
  59. data/spec/configuration_spec.rb +41 -45
  60. data/spec/dashboard/pagination_helpers_spec.rb +202 -0
  61. data/spec/dashboard/paginator_spec.rb +38 -0
  62. data/spec/dashboard_helpers_spec.rb +19 -18
  63. data/spec/dashboard_spec.rb +153 -48
  64. data/spec/encapsulated_helper_spec.rb +47 -23
  65. data/spec/experiment_catalog_spec.rb +14 -13
  66. data/spec/experiment_spec.rb +224 -111
  67. data/spec/goals_collection_spec.rb +18 -16
  68. data/spec/helper_spec.rb +539 -419
  69. data/spec/metric_spec.rb +14 -14
  70. data/spec/persistence/cookie_adapter_spec.rb +105 -27
  71. data/spec/persistence/dual_adapter_spec.rb +158 -66
  72. data/spec/persistence/redis_adapter_spec.rb +35 -27
  73. data/spec/persistence/session_adapter_spec.rb +2 -3
  74. data/spec/persistence_spec.rb +1 -2
  75. data/spec/redis_interface_spec.rb +25 -82
  76. data/spec/spec_helper.rb +38 -24
  77. data/spec/split_spec.rb +18 -18
  78. data/spec/support/cookies_mock.rb +1 -2
  79. data/spec/trial_spec.rb +117 -70
  80. data/spec/user_spec.rb +69 -27
  81. data/split.gemspec +26 -22
  82. metadata +85 -37
  83. data/.travis.yml +0 -41
  84. data/Appraisals +0 -13
  85. data/gemfiles/4.2.gemfile +0 -9
  86. data/gemfiles/5.0.gemfile +0 -10
  87. data/gemfiles/5.1.gemfile +0 -10
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Split::Cache do
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
+ before { Split.configuration.cache = true }
14
+
15
+ it "clears the cache" do
16
+ expect(Time).to receive(:now).and_return(now).exactly(2).times
17
+ Split::Cache.fetch(namespace, key) { Time.now }
18
+ Split::Cache.clear
19
+ Split::Cache.fetch(namespace, key) { Time.now }
20
+ end
21
+ end
22
+
23
+ describe "clear_key" do
24
+ before { Split.configuration.cache = true }
25
+
26
+ it "clears the cache" do
27
+ expect(Time).to receive(:now).and_return(now).exactly(3).times
28
+ Split::Cache.fetch(namespace, :key1) { Time.now }
29
+ Split::Cache.fetch(namespace, :key2) { Time.now }
30
+ Split::Cache.clear_key(:key1)
31
+
32
+ Split::Cache.fetch(namespace, :key1) { Time.now }
33
+ Split::Cache.fetch(namespace, :key2) { Time.now }
34
+ end
35
+ end
36
+
37
+ describe "fetch" do
38
+ subject { Split::Cache.fetch(namespace, key) { Time.now } }
39
+
40
+ context "when cache disabled" do
41
+ before { Split.configuration.cache = false }
42
+
43
+ it "returns the yield" do
44
+ expect(subject).to eql(now)
45
+ end
46
+
47
+ it "yields every time" do
48
+ expect(Time).to receive(:now).and_return(now).exactly(2).times
49
+ Split::Cache.fetch(namespace, key) { Time.now }
50
+ Split::Cache.fetch(namespace, key) { Time.now }
51
+ end
52
+ end
53
+
54
+ context "when cache enabled" do
55
+ before { Split.configuration.cache = true }
56
+
57
+ it "returns the yield" do
58
+ expect(subject).to eql(now)
59
+ end
60
+
61
+ it "yields once" do
62
+ expect(Time).to receive(:now).and_return(now).once
63
+ Split::Cache.fetch(namespace, key) { Time.now }
64
+ Split::Cache.fetch(namespace, key) { Time.now }
65
+ end
66
+
67
+ it "honors namespace" do
68
+ expect(Split::Cache.fetch(:a, key) { :a }).to eql(:a)
69
+ expect(Split::Cache.fetch(:b, key) { :b }).to eql(:b)
70
+
71
+ expect(Split::Cache.fetch(:a, key) { :a }).to eql(:a)
72
+ expect(Split::Cache.fetch(:b, key) { :b }).to eql(:b)
73
+ end
74
+
75
+ it "honors key" do
76
+ expect(Split::Cache.fetch(namespace, :a) { :a }).to eql(:a)
77
+ expect(Split::Cache.fetch(namespace, :b) { :b }).to eql(:b)
78
+
79
+ expect(Split::Cache.fetch(namespace, :a) { :a }).to eql(:a)
80
+ expect(Split::Cache.fetch(namespace, :b) { :b }).to eql(:b)
81
+ end
82
+ end
83
+ end
84
+ end
@@ -1,57 +1,58 @@
1
1
  # frozen_string_literal: true
2
- require 'spec_helper'
3
- require 'split/combined_experiments_helper'
2
+
3
+ require "spec_helper"
4
+ require "split/combined_experiments_helper"
4
5
 
5
6
  describe Split::CombinedExperimentsHelper do
6
7
  include Split::CombinedExperimentsHelper
7
8
 
8
- describe 'ab_combined_test' do
9
+ describe "ab_combined_test" do
9
10
  let!(:config_enabled) { true }
10
- let!(:combined_experiments) { [:exp_1_click, :exp_1_scroll ]}
11
+ let!(:combined_experiments) { [:exp_1_click, :exp_1_scroll ] }
11
12
  let!(:allow_multiple_experiments) { true }
12
13
 
13
14
  before do
14
15
  Split.configuration.experiments = {
15
- :combined_exp_1 => {
16
- :alternatives => [ {"control"=> 0.5}, {"test-alt"=> 0.5} ],
17
- :metric => :my_metric,
18
- :combined_experiments => combined_experiments
16
+ combined_exp_1: {
17
+ alternatives: [ { "control"=> 0.5 }, { "test-alt"=> 0.5 } ],
18
+ metric: :my_metric,
19
+ combined_experiments: combined_experiments
19
20
  }
20
21
  }
21
22
  Split.configuration.enabled = config_enabled
22
23
  Split.configuration.allow_multiple_experiments = allow_multiple_experiments
23
24
  end
24
25
 
25
- context 'without config enabled' do
26
+ context "without config enabled" do
26
27
  let!(:config_enabled) { false }
27
28
 
28
29
  it "raises an error" do
29
- expect(lambda { ab_combined_test :combined_exp_1 }).to raise_error(Split::InvalidExperimentsFormatError )
30
+ expect { ab_combined_test :combined_exp_1 }.to raise_error(Split::InvalidExperimentsFormatError)
30
31
  end
31
32
  end
32
33
 
33
- context 'multiple experiments disabled' do
34
+ context "multiple experiments disabled" do
34
35
  let!(:allow_multiple_experiments) { false }
35
36
 
36
37
  it "raises an error if multiple experiments is disabled" do
37
- expect(lambda { ab_combined_test :combined_exp_1 }).to raise_error(Split::InvalidExperimentsFormatError)
38
+ expect { ab_combined_test :combined_exp_1 }.to raise_error(Split::InvalidExperimentsFormatError)
38
39
  end
39
40
  end
40
41
 
41
- context 'without combined experiments' do
42
+ context "without combined experiments" do
42
43
  let!(:combined_experiments) { nil }
43
44
 
44
45
  it "raises an error" do
45
- expect(lambda { ab_combined_test :combined_exp_1 }).to raise_error(Split::InvalidExperimentsFormatError )
46
+ expect { ab_combined_test :combined_exp_1 }.to raise_error(Split::InvalidExperimentsFormatError)
46
47
  end
47
48
  end
48
49
 
49
50
  it "uses same alternative for all sub experiments and returns the alternative" do
50
51
  allow(self).to receive(:get_alternative) { "test-alt" }
51
- expect(self).to receive(:ab_test).with(:exp_1_click, {"control"=>0.5}, {"test-alt"=>0.5}) { "test-alt" }
52
- expect(self).to receive(:ab_test).with(:exp_1_scroll, [{"control" => 0, "test-alt" => 1}])
52
+ expect(self).to receive(:ab_test).with(:exp_1_click, { "control"=>0.5 }, { "test-alt"=>0.5 }) { "test-alt" }
53
+ expect(self).to receive(:ab_test).with(:exp_1_scroll, [{ "control" => 0, "test-alt" => 1 }])
53
54
 
54
- expect(ab_combined_test('combined_exp_1')).to eq('test-alt')
55
+ expect(ab_combined_test("combined_exp_1")).to eq("test-alt")
55
56
  end
56
57
  end
57
58
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
- require 'spec_helper'
3
2
 
4
- describe Split::Configuration do
3
+ require "spec_helper"
5
4
 
5
+ describe Split::Configuration do
6
6
  before(:each) { @config = Split::Configuration.new }
7
7
 
8
8
  it "should provide a default value for ignore_ip_addresses" do
@@ -58,17 +58,15 @@ describe Split::Configuration do
58
58
  end
59
59
 
60
60
  it "should load a metric" do
61
- @config.experiments = {:my_experiment=>
62
- {:alternatives=>["control_opt", "other_opt"], :metric=>:my_metric}}
61
+ @config.experiments = { my_experiment: { alternatives: ["control_opt", "other_opt"], metric: :my_metric } }
63
62
 
64
63
  expect(@config.metrics).not_to be_nil
65
64
  expect(@config.metrics.keys).to eq([:my_metric])
66
65
  end
67
66
 
68
67
  it "should allow loading of experiment using experment_for" do
69
- @config.experiments = {:my_experiment=>
70
- {:alternatives=>["control_opt", "other_opt"], :metric=>:my_metric}}
71
- expect(@config.experiment_for(:my_experiment)).to eq({:alternatives=>["control_opt", ["other_opt"]]})
68
+ @config.experiments = { my_experiment: { alternatives: ["control_opt", "other_opt"], metric: :my_metric } }
69
+ expect(@config.experiment_for(:my_experiment)).to eq({ alternatives: ["control_opt", ["other_opt"]] })
72
70
  end
73
71
 
74
72
  context "when experiments are defined via YAML" do
@@ -82,12 +80,12 @@ describe Split::Configuration do
82
80
  - Alt One
83
81
  - Alt Two
84
82
  resettable: false
85
- eos
83
+ eos
86
84
  @config.experiments = YAML.load(experiments_yaml)
87
85
  end
88
86
 
89
- it 'should normalize experiments' do
90
- expect(@config.normalized_experiments).to eq({:my_experiment=>{:resettable=>false,:alternatives=>["Control Opt", ["Alt One", "Alt Two"]]}})
87
+ it "should normalize experiments" do
88
+ expect(@config.normalized_experiments).to eq({ my_experiment: { resettable: false, alternatives: ["Control Opt", ["Alt One", "Alt Two"]] } })
91
89
  end
92
90
  end
93
91
 
@@ -110,14 +108,14 @@ describe Split::Configuration do
110
108
  Alt Two:
111
109
  text: 'Alternative Two'
112
110
  resettable: false
113
- eos
111
+ eos
114
112
  @config.experiments = YAML.load(experiments_yaml)
115
113
  end
116
114
 
117
- it 'should have metadata on the experiment' do
115
+ it "should have metadata on the experiment" do
118
116
  meta = @config.normalized_experiments[:my_experiment][:metadata]
119
117
  expect(meta).to_not be nil
120
- expect(meta['Control Opt']['text']).to eq('Control Option')
118
+ expect(meta["Control Opt"]["text"]).to eq("Control Option")
121
119
  end
122
120
  end
123
121
 
@@ -138,25 +136,23 @@ describe Split::Configuration do
138
136
  alternatives:
139
137
  - a
140
138
  - b
141
- eos
139
+ eos
142
140
  @config.experiments = YAML.load(experiments_yaml)
143
141
  end
144
142
 
145
143
  it "should normalize experiments" do
146
- expect(@config.normalized_experiments).to eq({:my_experiment=>{:resettable=>false,:alternatives=>[{"Control Opt"=>0.67},
147
- [{"Alt One"=>0.1}, {"Alt Two"=>0.23}]]}, :another_experiment=>{:alternatives=>["a", ["b"]]}})
144
+ expect(@config.normalized_experiments).to eq({ my_experiment: { resettable: false, alternatives: [{ "Control Opt"=>0.67 },
145
+ [{ "Alt One"=>0.1 }, { "Alt Two"=>0.23 }]] }, another_experiment: { alternatives: ["a", ["b"]] } })
148
146
  end
149
147
 
150
148
  it "should recognize metrics" do
151
149
  expect(@config.metrics).not_to be_nil
152
150
  expect(@config.metrics.keys).to eq([:my_metric])
153
151
  end
154
-
155
152
  end
156
153
  end
157
154
 
158
155
  context "as symbols" do
159
-
160
156
  context "with valid YAML" do
161
157
  before do
162
158
  experiments_yaml = <<-eos
@@ -166,21 +162,20 @@ describe Split::Configuration do
166
162
  - Alt One
167
163
  - Alt Two
168
164
  :resettable: false
169
- eos
165
+ eos
170
166
  @config.experiments = YAML.load(experiments_yaml)
171
167
  end
172
168
 
173
169
  it "should normalize experiments" do
174
- expect(@config.normalized_experiments).to eq({:my_experiment=>{:resettable=>false,:alternatives=>["Control Opt", ["Alt One", "Alt Two"]]}})
170
+ expect(@config.normalized_experiments).to eq({ my_experiment: { resettable: false, alternatives: ["Control Opt", ["Alt One", "Alt Two"]] } })
175
171
  end
176
172
  end
177
173
 
178
174
  context "with invalid YAML" do
179
-
180
175
  let(:yaml) { YAML.load(input) }
181
176
 
182
177
  context "with an empty string" do
183
- let(:input) { '' }
178
+ let(:input) { "" }
184
179
 
185
180
  it "should raise an error" do
186
181
  expect { @config.experiments = yaml }.to raise_error(Split::InvalidExperimentsFormatError)
@@ -188,7 +183,7 @@ describe Split::Configuration do
188
183
  end
189
184
 
190
185
  context "with just the YAML header" do
191
- let(:input) { '---' }
186
+ let(:input) { "---" }
192
187
 
193
188
  it "should raise an error" do
194
189
  expect { @config.experiments = yaml }.to raise_error(Split::InvalidExperimentsFormatError)
@@ -200,35 +195,24 @@ describe Split::Configuration do
200
195
 
201
196
  it "should normalize experiments" do
202
197
  @config.experiments = {
203
- :my_experiment => {
204
- :alternatives => [
205
- { :name => "control_opt", :percent => 67 },
206
- { :name => "second_opt", :percent => 10 },
207
- { :name => "third_opt", :percent => 23 },
198
+ my_experiment: {
199
+ alternatives: [
200
+ { name: "control_opt", percent: 67 },
201
+ { name: "second_opt", percent: 10 },
202
+ { name: "third_opt", percent: 23 },
208
203
  ],
209
204
  }
210
205
  }
211
206
 
212
- expect(@config.normalized_experiments).to eq({:my_experiment=>{:alternatives=>[{"control_opt"=>0.67}, [{"second_opt"=>0.1}, {"third_opt"=>0.23}]]}})
213
- end
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
207
+ expect(@config.normalized_experiments).to eq({ my_experiment: { alternatives: [{ "control_opt"=>0.67 }, [{ "second_opt"=>0.1 }, { "third_opt"=>0.23 }]] } })
227
208
  end
228
209
 
229
210
  context "redis configuration" do
230
211
  it "should default to local redis server" do
231
- expect(@config.redis).to eq("redis://localhost:6379")
212
+ old_redis_url = ENV["REDIS_URL"]
213
+ ENV.delete("REDIS_URL")
214
+ expect(Split::Configuration.new.redis).to eq("redis://localhost:6379")
215
+ ENV["REDIS_URL"] = old_redis_url
232
216
  end
233
217
 
234
218
  it "should allow for redis url to be configured" do
@@ -238,8 +222,10 @@ describe Split::Configuration do
238
222
 
239
223
  context "provided REDIS_URL environment variable" do
240
224
  it "should use the ENV variable" do
241
- ENV['REDIS_URL'] = "env_redis_url"
225
+ old_redis_url = ENV["REDIS_URL"]
226
+ ENV["REDIS_URL"] = "env_redis_url"
242
227
  expect(Split::Configuration.new.redis).to eq("env_redis_url")
228
+ ENV["REDIS_URL"] = old_redis_url
243
229
  end
244
230
  end
245
231
  end
@@ -255,4 +241,14 @@ describe Split::Configuration do
255
241
  end
256
242
  end
257
243
 
244
+ context "persistence cookie domain" do
245
+ it "should default to nil" do
246
+ expect(@config.persistence_cookie_domain).to eq(nil)
247
+ end
248
+
249
+ it "should allow the persistence cookie domain to be configured" do
250
+ @config.persistence_cookie_domain = ".acme.com"
251
+ expect(@config.persistence_cookie_domain).to eq(".acme.com")
252
+ end
253
+ end
258
254
  end
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "split/dashboard/pagination_helpers"
5
+
6
+ describe Split::DashboardPaginationHelpers do
7
+ include Split::DashboardPaginationHelpers
8
+
9
+ let(:url) { "/split/" }
10
+
11
+ describe "#pagination_per" do
12
+ context "when params empty" do
13
+ let(:params) { Hash[] }
14
+
15
+ it "returns the default (10)" do
16
+ default_per_page = Split.configuration.dashboard_pagination_default_per_page
17
+ expect(pagination_per).to eql default_per_page
18
+ expect(pagination_per).to eql 10
19
+ end
20
+ end
21
+
22
+ context "when params[:per] is 5" do
23
+ let(:params) { Hash[per: 5] }
24
+
25
+ it "returns 5" do
26
+ expect(pagination_per).to eql 5
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "#page_number" do
32
+ context "when params empty" do
33
+ let(:params) { Hash[] }
34
+
35
+ it "returns 1" do
36
+ expect(page_number).to eql 1
37
+ end
38
+ end
39
+
40
+ context 'when params[:page] is "2"' do
41
+ let(:params) { Hash[page: "2"] }
42
+
43
+ it "returns 2" do
44
+ expect(page_number).to eql 2
45
+ end
46
+ end
47
+ end
48
+
49
+ describe "#paginated" do
50
+ let(:collection) { (1..20).to_a }
51
+ let(:params) { Hash[per: "5", page: "3"] }
52
+
53
+ it { expect(paginated(collection)).to eql [11, 12, 13, 14, 15] }
54
+ end
55
+
56
+ describe "#show_first_page_tag?" do
57
+ context "when page is 1" do
58
+ it { expect(show_first_page_tag?).to be false }
59
+ end
60
+
61
+ context "when page is 3" do
62
+ let(:params) { Hash[page: "3"] }
63
+ it { expect(show_first_page_tag?).to be true }
64
+ end
65
+ end
66
+
67
+ describe "#first_page_tag" do
68
+ it { expect(first_page_tag).to eql '<a href="/split?page=1&per=10">1</a>' }
69
+ end
70
+
71
+ describe "#show_first_ellipsis_tag?" do
72
+ context "when page is 1" do
73
+ it { expect(show_first_ellipsis_tag?).to be false }
74
+ end
75
+
76
+ context "when page is 4" do
77
+ let(:params) { Hash[page: "4"] }
78
+ it { expect(show_first_ellipsis_tag?).to be true }
79
+ end
80
+ end
81
+
82
+ describe "#ellipsis_tag" do
83
+ it { expect(ellipsis_tag).to eql "<span>...</span>" }
84
+ end
85
+
86
+ describe "#show_prev_page_tag?" do
87
+ context "when page is 1" do
88
+ it { expect(show_prev_page_tag?).to be false }
89
+ end
90
+
91
+ context "when page is 2" do
92
+ let(:params) { Hash[page: "2"] }
93
+ it { expect(show_prev_page_tag?).to be true }
94
+ end
95
+ end
96
+
97
+ describe "#prev_page_tag" do
98
+ context "when page is 2" do
99
+ let(:params) { Hash[page: "2"] }
100
+
101
+ it do
102
+ expect(prev_page_tag).to eql '<a href="/split?page=1&per=10">1</a>'
103
+ end
104
+ end
105
+
106
+ context "when page is 3" do
107
+ let(:params) { Hash[page: "3"] }
108
+
109
+ it do
110
+ expect(prev_page_tag).to eql '<a href="/split?page=2&per=10">2</a>'
111
+ end
112
+ end
113
+ end
114
+
115
+ describe "#show_prev_page_tag?" do
116
+ context "when page is 1" do
117
+ it { expect(show_prev_page_tag?).to be false }
118
+ end
119
+
120
+ context "when page is 2" do
121
+ let(:params) { Hash[page: "2"] }
122
+ it { expect(show_prev_page_tag?).to be true }
123
+ end
124
+ end
125
+
126
+ describe "#current_page_tag" do
127
+ context "when page is 1" do
128
+ let(:params) { Hash[page: "1"] }
129
+ it { expect(current_page_tag).to eql "<span><b>1</b></span>" }
130
+ end
131
+
132
+ context "when page is 2" do
133
+ let(:params) { Hash[page: "2"] }
134
+ it { expect(current_page_tag).to eql "<span><b>2</b></span>" }
135
+ end
136
+ end
137
+
138
+ describe "#show_next_page_tag?" do
139
+ context "when page is 2" do
140
+ let(:params) { Hash[page: "2"] }
141
+
142
+ context "when collection length is 20" do
143
+ let(:collection) { (1..20).to_a }
144
+ it { expect(show_next_page_tag?(collection)).to be false }
145
+ end
146
+
147
+ context "when collection length is 25" do
148
+ let(:collection) { (1..25).to_a }
149
+ it { expect(show_next_page_tag?(collection)).to be true }
150
+ end
151
+ end
152
+ end
153
+
154
+ describe "#next_page_tag" do
155
+ context "when page is 1" do
156
+ let(:params) { Hash[page: "1"] }
157
+ it { expect(next_page_tag).to eql '<a href="/split?page=2&per=10">2</a>' }
158
+ end
159
+
160
+ context "when page is 2" do
161
+ let(:params) { Hash[page: "2"] }
162
+ it { expect(next_page_tag).to eql '<a href="/split?page=3&per=10">3</a>' }
163
+ end
164
+ end
165
+
166
+ describe "#total_pages" do
167
+ context "when collection length is 30" do
168
+ let(:collection) { (1..30).to_a }
169
+ it { expect(total_pages(collection)).to eql 3 }
170
+ end
171
+
172
+ context "when collection length is 35" do
173
+ let(:collection) { (1..35).to_a }
174
+ it { expect(total_pages(collection)).to eql 4 }
175
+ end
176
+ end
177
+
178
+ describe "#show_last_ellipsis_tag?" do
179
+ let(:collection) { (1..30).to_a }
180
+ let(:params) { Hash[per: "5", page: "2"] }
181
+ it { expect(show_last_ellipsis_tag?(collection)).to be true }
182
+ end
183
+
184
+ describe "#show_last_page_tag?" do
185
+ let(:collection) { (1..30).to_a }
186
+
187
+ context "when page is 5/6" do
188
+ let(:params) { Hash[per: "5", page: "5"] }
189
+ it { expect(show_last_page_tag?(collection)).to be false }
190
+ end
191
+
192
+ context "when page is 4/6" do
193
+ let(:params) { Hash[per: "5", page: "4"] }
194
+ it { expect(show_last_page_tag?(collection)).to be true }
195
+ end
196
+ end
197
+
198
+ describe "#last_page_tag" do
199
+ let(:collection) { (1..30).to_a }
200
+ it { expect(last_page_tag(collection)).to eql '<a href="/split?page=3&per=10">3</a>' }
201
+ end
202
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "split/dashboard/paginator"
5
+
6
+ describe Split::DashboardPaginator do
7
+ context "when collection is 1..20" do
8
+ let(:collection) { (1..20).to_a }
9
+
10
+ context "when per 5 for page" do
11
+ let(:per) { 5 }
12
+
13
+ it "when page number is 1 result is [1, 2, 3, 4, 5]" do
14
+ result = Split::DashboardPaginator.new(collection, 1, per).paginate
15
+ expect(result).to eql [1, 2, 3, 4, 5]
16
+ end
17
+
18
+ it "when page number is 2 result is [6, 7, 8, 9, 10]" do
19
+ result = Split::DashboardPaginator.new(collection, 2, per).paginate
20
+ expect(result).to eql [6, 7, 8, 9, 10]
21
+ end
22
+ end
23
+
24
+ context "when per 10 for page" do
25
+ let(:per) { 10 }
26
+
27
+ it "when page number is 1 result is [1..10]" do
28
+ result = Split::DashboardPaginator.new(collection, 1, per).paginate
29
+ expect(result).to eql [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
30
+ end
31
+
32
+ it "when page number is 2 result is [10..20]" do
33
+ result = Split::DashboardPaginator.new(collection, 2, per).paginate
34
+ expect(result).to eql [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,41 +1,42 @@
1
1
  # frozen_string_literal: true
2
- require 'spec_helper'
3
- require 'split/dashboard/helpers'
2
+
3
+ require "spec_helper"
4
+ require "split/dashboard/helpers"
4
5
 
5
6
  include Split::DashboardHelpers
6
7
 
7
8
  describe Split::DashboardHelpers do
8
- describe 'confidence_level' do
9
- it 'should handle very small numbers' do
10
- expect(confidence_level(Complex(2e-18, -0.03))).to eq('Insufficient confidence')
9
+ describe "confidence_level" do
10
+ it "should handle very small numbers" do
11
+ expect(confidence_level(Complex(2e-18, -0.03))).to eq("Insufficient confidence")
11
12
  end
12
13
 
13
14
  it "should consider a z-score of 1.65 <= z < 1.96 as 90% confident" do
14
- expect(confidence_level(1.65)).to eq('90% confidence')
15
- expect(confidence_level(1.80)).to eq('90% confidence')
15
+ expect(confidence_level(1.65)).to eq("90% confidence")
16
+ expect(confidence_level(1.80)).to eq("90% confidence")
16
17
  end
17
18
 
18
19
  it "should consider a z-score of 1.96 <= z < 2.58 as 95% confident" do
19
- expect(confidence_level(1.96)).to eq('95% confidence')
20
- expect(confidence_level(2.00)).to eq('95% confidence')
20
+ expect(confidence_level(1.96)).to eq("95% confidence")
21
+ expect(confidence_level(2.00)).to eq("95% confidence")
21
22
  end
22
23
 
23
24
  it "should consider a z-score of z >= 2.58 as 99% confident" do
24
- expect(confidence_level(2.58)).to eq('99% confidence')
25
- expect(confidence_level(3.00)).to eq('99% confidence')
25
+ expect(confidence_level(2.58)).to eq("99% confidence")
26
+ expect(confidence_level(3.00)).to eq("99% confidence")
26
27
  end
27
28
 
28
- describe '#round' do
29
- it 'can round number strings' do
30
- expect(round('3.1415')).to eq BigDecimal.new('3.14')
29
+ describe "#round" do
30
+ it "can round number strings" do
31
+ expect(round("3.1415")).to eq BigDecimal("3.14")
31
32
  end
32
33
 
33
- it 'can round number strings for precsion' do
34
- expect(round('3.1415', 1)).to eq BigDecimal.new('3.1')
34
+ it "can round number strings for precsion" do
35
+ expect(round("3.1415", 1)).to eq BigDecimal("3.1")
35
36
  end
36
37
 
37
- it 'can handle invalid number strings' do
38
- expect(round('N/A')).to be_zero
38
+ it "can handle invalid number strings" do
39
+ expect(round("N/A")).to be_zero
39
40
  end
40
41
  end
41
42
  end